Enable load following optimization dispatch with Pyomo#407
Open
genevievestarke wants to merge 85 commits intoNatLabRockies:developfrom
Open
Enable load following optimization dispatch with Pyomo#407genevievestarke wants to merge 85 commits intoNatLabRockies:developfrom
genevievestarke wants to merge 85 commits intoNatLabRockies:developfrom
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
Collaborator
Author
|
This pull request is ready for an overall framework review! |
Apply comment suggestions Co-authored-by: John Jasa <john.jasa@nrel.gov>
This was referenced Feb 3, 2026
Co-authored-by: kbrunik <102193481+kbrunik@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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:

What's changed?
For the optimized dispatch, the control and dispatch functions are no longer separate (see figure above). This also means that the controller needs access to the dispatch parameters. Now all dispatch and controller classes have access to the
shared_parameterssection in the input yaml, meaning that parameters likecommodity_namethat were defined in multiple places before should now be defined once in theshared_parameterssection. This affects the pyomo heuristic dispatch, as well. Most control parameters are then defined in theshared_parameters, as well, because there is a large overlap with the performance variables. Control/dispatch specific variables can be defined in thecontrol_parameterssection. This include:cost_per_charge: in $/kW, cost to charge the storage (note that charging is incentivized)cost_per_discharge: in $/kW, cost to discharge the storagecommodity_met_value: in $/kW, penalty for not meeting the desired load demandcost_per_production: in $/kW, cost to use the incoming produced commodity (i.e. electricity from wind)time_weighting_factor: This parameter discounts each subsequent time step incrementally in the future in the horizon window by the defined amount (defaults to 0.995)tech_name: Name that the technology will be defined as in the dispatchNote that the
tech_namevariable must match with the names given in thetech_to_dispatch_connectionsin the plant_config file. TODO: should we eliminate thetech_namevariable and make it the same as the defined technology name? Why is it different?We have exposed the cost values to the user in this implementation, which was not the case for HOPP. This is good for visibility, but the weights are fussy. If your dispatch is taking an abnormally long time (or even longer than a minute), you should check to make sure your weights make sense, or change them to see if that makes a difference. Some rules of thumb and thoughts:
commodity_met_valueto be the largest because this is what drives meeting the load for load followingcost_per_chargeequal tocost_per_discharge. This will confuse the optimizer. In the objective function, charging the battery is incentivized and discharging is disincentivized, meaning that if they are the same value, they can cause the optimizer to oscillate the battery because they "cancel each other out" in value in the objective function. I usually setcost_per_chargea little bit less thancost_per_dischargeto not discourage charging the battery instead of meeting the load by discharging.cost_per_productionas not that important if the energy is already produced (i.e. from wind for example). I tend to set this to 0 to not discourage charging the batterycost_per_chargeandcost_per_dischargetwo orders of magnitude lower thancommodity_met_valueso that the charging the discharging of the battery don't interfere with the load very much. You can put them closer together, but it will increase the time that the optimization takes.This pull request does not enable the optimal dispatch of a storage technology that can charge from the grid. This implementation currently only involves incoming electricity from upstream (that could be bought using grid component). It also only allows one incoming electricity stream and does not apply optimal dispatch of that stream back through the upstream technologies (no feedback). The dispatch can handle more than one generation technology, but the incoming electricity must be combined using an H2I combiner before going to the storage component, and the
cost_per_production, which is defined in the storage technology section, needs to include the cost of production for all production technologies. This could be done using the following:The demand is also still set externally after the setup step. Ideally, the dispatch will be integrated with the load demand framework that already exists in H2I, and the demand can be defined that way.
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