-
Notifications
You must be signed in to change notification settings - Fork 27
Enable load following optimization dispatch with Pyomo #407
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Enable load following optimization dispatch with Pyomo #407
Conversation
{build-extensions,convert,download-extensions,help,install-extras,model-viewer,run,solve,test-solvers}
...
This is the main driver for the Pyomo optimization software.
options:
-h, --help show this help message and exit
--version show program's version number and exit
subcommands:
{build-extensions,convert,download-extensions,help,install-extras,model-viewer,run,solve,test-solvers}
build-extensions Build compiled extension modules
convert Convert a Pyomo model to another format
download-extensions
Download compiled extension modules
help Print help information.
install-extras Install "extra" packages that Pyomo can leverage.
model-viewer Run the Pyomo model viewer
run Execute a command from the Pyomo bin (or Scripts)
directory.
solve Optimize a model
test-solvers Test Pyomo solvers
-------------------------------------------------------------------------
Pyomo supports a variety of modeling and optimization capabilities,
which are executed either as subcommands of 'pyomo' or as separate
commands. Use the 'help' subcommand to get information about the
capabilities installed with Pyomo. Additionally, each subcommand
supports independent command-line options. Use the -h option to
print details for a subcommand. For example, type
pyomo solve -h
to print information about the `solve` subcommand. branch that needs to be saved for later
Merging in current pyomo opt branch
…in operating costs framework
…e/H2Integrate into feature/pyomo_opt
|
This pull request is ready for an overall framework review! |
elenya-grant
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is super cool new functionality! Thanks for adding this into H2I!
I think eventually (not in this PR) that we may need to consider a framework specific to handling supervisory or plant-level control (rather than technology-level control). But - I think this is a great starting point!
Most of my comments are high-level or are small things that could be updated later. I think that making this flexible to different timesteps, rounding precision, and checking that this can be used for non-electricity commodities would be good future steps to take but not necessarily required at this moment!
I'll review again, but wanted to provide my initial feedback!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It doesn't look PyomoControlOptions is being used anywhere?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I almost think it'd be nice to split out these controllers into separate files. Like PyomoControllerBaseConfig and PyomoControllerBaseClass (maybe even SolverOptions if that's used across the Pyomo controllers) could be moved into h2integrate/control/control_strategies/controller_baseclass.py. Then separate files for SimpleBatteryControllerHeuristic, HeuristicLoadFollowingControllerConfig and HeuristicLoadFollowingController, and OptimizedDispatchControllerConfig and OptimizedDispatchController. This could be a separate clean-up thing though and is non-blocking at the moment. But - if you wanted to move OptimizedDispatchControllerConfig and OptimizedDispatchController to a separate file now, that'd also seem good to me :)
| # ) | ||
|
|
||
| self.source_techs = source_techs | ||
| self.options = dispatch_options |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The only option used in dispatch_options is the time_weighting_factor. Could we replace dispatch_options with time_weighting_factor and then remove the options attribute altogether?
| self.arcs = [] | ||
|
|
||
| self.block_set_name = block_set_name | ||
| self.round_digits = 4 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could we make round_digits an input that can be user-set at the config level (it could default to 4 if desired)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
new test for the OptimizedDispatchController would be nice!
| } | ||
|
|
||
| self.n_control_window = self.config.n_control_window | ||
| self.n_horizon_window = self.config.n_control_window |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why is self.n_control_window and self.n_horizon_window both being set to self.config.n_control_window? Rather than having these as attributes, could we just always access them from the config? It also doesn't seem like self.config.n_horizon_window is used anywhere within the Pyomo classes. I think in OptimizedDispatchController._create_dispatch_optimization_model() the forecast horizon should be set as:
model.forecast_horizon = pyomo.Set(
doc="Set of time periods in time horizon",
initialize=range(self.config.n_control_window),
)* refactored DispatchProblemState * updated tech config and removed pysam options file * minor cleanups to DispatchProblemState * minor updates to generic_converter_opt * initial cleanups to hybrid_rule.py * minor cleanups to pyomo_storage_rule_min_operating_cost * extra small cleanups to generic_converter_opt * added storage capacities as inputs to optimized controller * updated use of n_control_window and n_horizon_window
jaredthomas68
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is an excellent piece of work, well done. Thank you for the effort to make it relatively clean and straight forward. My thoughts are:
- structurally I think it is very good. I guess we will find ways to improve as we move forward, but I like where it is at generally.
- I am concerned with the number of getter and setter methods, are they really necessary?
- more tests are needed for added functionality
- lots of comments and print statements need to be removed, but that is expected in a draft
- more comments would be helpful as there are some methods with little to no comments
|
|
||
|
|
||
| class PyomoDispatchGenericConverterMinOperatingCosts: | ||
| def __init__( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
before going to production we will need some more comments and explanation here I think
| def _create_parameters(self, pyomo_model: pyo.ConcreteModel): | ||
| """Create technology Pyomo parameters to add to the Pyomo model instance. | ||
| Method is currently passed but this can serve as a template to add parameters to the Pyomo |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure what you mean by "Method is currently passed"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was a copy/past mistake. Fixed now!
| def _create_constraints(self, pyomo_model: pyo.ConcreteModel): | ||
| """Create technology Pyomo parameters to add to the Pyomo model instance. | ||
| Method is currently passed but this can serve as a template to add constraints to the Pyomo |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
see above comment
| # initial_soc = self._check_initial_soc(initial_soc) | ||
| # pyomo_model.initial_soc = round(initial_soc, self.round_digits) | ||
|
|
||
| @property |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wish we could avoid having so many getter and setter methods, but I don't have any good ideas for that at the moment (non-blocking).
| solver_problem_dict = { | ||
| k.lower().replace(" ", "_"): v.value for k, v in solver_results.problem._list[0].items() | ||
| } | ||
| prob_to_attr_map = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
some unclear names in the solver, but this is a fairly nice solution for dealing with it
| upper_bound = solver_problem_dict["upper_bound"] | ||
| lower_bound = solver_problem_dict["lower_bound"] | ||
| if upper_bound != 0.0: | ||
| gap = abs(upper_bound - lower_bound) / abs(upper_bound) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
some comments here would be good. I think I get what you are doing, but it took me a minute
| but has not been implemented yet." | ||
| ) | ||
| ) | ||
| # TODO: implement optimized solutions; this is where pyomo_model would be used |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove comment
| return dispatch_inputs | ||
|
|
||
|
|
||
| class OptimizedDispatchController(SimpleBatteryControllerHeuristic): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This inheritance strikes me as odd since the parent and child are technically at the same level in their usage. If there are common elements they both need I think we should make a base class for them both to inherit from.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a good point. I've fixed this in the most recent update. Now it inherits from PyomoControllerBaseClass
| wind_speed: 9. | ||
| model_inputs: | ||
| performance_parameters: | ||
| num_turbines: 100 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The wind farm rating is really high in comparison with the battery. I think the plots in the example would be more meaningful if you reduced the size of the wind farm
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed!
Bring Pyomo load following dispatch capabilities into H2I
This feature would enable dispatch optimization using one storage technology and multiple generation technologies (using the combiner) to determine optimal storage operation to follow a load while minimizing costs. This feature adds the optimization problem formulation, framework, and solver for the optimization in Pyomo. To do this, a
hybrid_rule.pyfile was added to aggregate the storage and production variables to create one problem for the optimizer. The added files areOptimizedDispatchControllerclass inpyomo_controllers.pyhybrid_rule.pyfile: Aggregates optimization pieces from the various components and formulates the model for the optimization. This file collects the objective function, initializes model parameters, and updates time series parameters.generic_converter_opt.pyfile: Houses the variables, parameters, constraints, and objective function for the generator technology in the optimization model.pyomo_storage_rule_min_operating_cost.pyfile: houses the storage and system variables, parameters, constraints, and objective function. Ideally the system parameters would be in a separate file, perhaps, but have been combined for efficiency in the first implementation.controller_opt_problem_state.pyfile: Formulates the optimization model for the solverThe below is an example of how and where the dispatch is run using a battery for electricity storage:

Section 1: Type of Contribution
Section 2: Draft PR Checklist
TODO:
Type of Reviewer Feedback Requested (on Draft PR)
Structural feedback:
This dispatch model differs significantly from the heuristic block formulation in H2I. The dispatch for the generation + storage system is aggregated in
hybrid_rule.py, which pulls fromgeneric_converter_opt.pyandpyomo_storage_rule_min_operating_cost.py, all three of which are classes, and thus outside of the OpenMDAO input/output framework. This was implemented because the previous dispatch_rule framework was difficult to aggregate in the way that was needed to formulate the optimization problem. I would like feedback on this structure, and whether we could make it more OpenMDAO-friendly.Implementation feedback:
Other feedback:
The inputs to the dispatch are all defined in the storage technology config, including the
cost_per_productionterm, which describes the cost of the production technologies. I think this should be defined under the production technologies in the config, so any feedback on how to pull this into the battery dispatch would be appreciated!Section 3: General PR Checklist
docs/files are up-to-date, or added when necessaryCHANGELOG.mdhas been updated to describe the changes made in this PRSection 3: Related Issues
This resolves #386
Section 4: Impacted Areas of the Software
Section 4.1: New Files
path/to/file.extensionmethod1: What and why something was changed in one sentence or less.Section 4.2: Modified Files
path/to/file.extensionmethod1: What and why something was changed in one sentence or less.Section 5: Additional Supporting Information
Section 6: Test Results, if applicable
Section 7 (Optional): New Model Checklist
docs/developer_guide/coding_guidelines.mdattrsclass to define theConfigto load in attributes for the modelBaseConfigorCostModelBaseConfiginitialize()method,setup()method,compute()methodCostModelBaseClasssupported_models.pycreate_financial_modelinh2integrate_model.pytest_all_examples.pydocs/user_guide/model_overview.mddocs/section<model_name>.mdis added to the_toc.yml