Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
85 commits
Select commit Hold shift + click to select a range
168766a
include code from usage: pyomo [-h] [--version]
jaredthomas68 Oct 2, 2025
df63126
include storage rule file
jaredthomas68 Oct 9, 2025
73f87d7
Merge remote-tracking branch 'origin/develop' into pyomo_opt
bayc Nov 17, 2025
80782e1
Merge branch 'pyomo_opt' into feature/pyomo_opt
genevievestarke Nov 18, 2025
6971220
Halfway there for pyomo opt
genevievestarke Dec 2, 2025
5a2ce87
Add first objective function
genevievestarke Dec 5, 2025
c7923c4
Updated dispatch optimization framework - add hybrid dispatch rule
genevievestarke Dec 9, 2025
38c929d
Merge remote-tracking branch 'origin/develop' into feature/pyomo_opt
bayc Dec 9, 2025
03d7c1d
Adding hybrid linking constraints and connecting variables in pyomo m…
genevievestarke Dec 11, 2025
9509748
Merge branch 'feature/pyomo_opt' of https://github.com/genevievestark…
bayc Dec 11, 2025
5fa7165
Merge remote-tracking branch 'origin/develop' into feature/pyomo_opt
bayc Dec 11, 2025
2194701
Merge remote-tracking branch 'origin/develop' into feature/pyomo_opt
bayc Dec 15, 2025
c2f1bbb
Final structural changes
genevievestarke Dec 16, 2025
4d676fb
Fix import statement
genevievestarke Dec 16, 2025
0c642d0
Fix imports and setter method
genevievestarke Dec 16, 2025
22e411f
First draft of running code
genevievestarke Dec 23, 2025
efc2069
Update example
genevievestarke Dec 30, 2025
8650683
Merge branch 'develop' into feature/pyomo_opt
kbrunik Jan 2, 2026
e947332
test
kbrunik Jan 2, 2026
581e101
fix precommits
kbrunik Jan 2, 2026
1dbb52b
Merge branch 'develop' into feature/pyomo_opt
johnjasa Jan 5, 2026
4f2faea
Fixing merge errors
johnjasa Jan 6, 2026
39ad186
Minor spelling changes
johnjasa Jan 6, 2026
ec3ad18
Update example
genevievestarke Jan 7, 2026
b1cc99b
Update controller problem state method from Elenya
genevievestarke Jan 7, 2026
24cc0b8
Update example and changelog
genevievestarke Jan 7, 2026
abf9d49
Clean up pyomo storage baseclass file
genevievestarke Jan 7, 2026
a2da8b9
Merge branch 'develop' into feature/pyomo_opt
johnjasa Jan 7, 2026
5a26e58
Cleanups to feature/pyomo opt (#2)
elenya-grant Jan 9, 2026
72205b2
Enable heuristic dispatch to run with new pyomo changes
genevievestarke Jan 12, 2026
5c16393
Clean up added files and example
genevievestarke Jan 17, 2026
6de9803
Adding first tests - do not pass yet
genevievestarke Jan 21, 2026
dc059b9
Merge branch 'develop' into feature/pyomo_opt
genevievestarke Jan 21, 2026
5d36008
Update docs and rename example
genevievestarke Jan 21, 2026
ba6d65d
Align naming with develop branch
genevievestarke Jan 21, 2026
bb8e7d8
Update Ex 02 and update pyomo_controllers with naming in develop
genevievestarke Jan 21, 2026
6a1d050
updated other example tech configs
elenya-grant Jan 21, 2026
57bf534
ran precommit on some files
elenya-grant Jan 21, 2026
0acaa30
precommit on pyomo_controllers.py
elenya-grant Jan 21, 2026
e3d0315
Update test formatting
genevievestarke Jan 21, 2026
a509354
Update pyomo storage rule for test
genevievestarke Jan 21, 2026
2342730
Fix SOC linking bug
genevievestarke Jan 23, 2026
c2604b6
Testing update - partial
genevievestarke Jan 26, 2026
5f33847
Make new test for optimized pyomo dispatch
genevievestarke Jan 26, 2026
363bfbf
Update optimal controller test
genevievestarke Jan 27, 2026
f42f935
Update test with new site definition
genevievestarke Jan 27, 2026
bfd4e9c
Merge branch 'develop' into feature/pyomo_opt
genevievestarke Jan 27, 2026
db32b3a
Update example for merging in develop
genevievestarke Jan 27, 2026
2c6e461
Minor updates to optimized dispatch
johnjasa Jan 29, 2026
75f9ff6
PR updates from comments
genevievestarke Jan 29, 2026
1f6dfbb
Add last file after merge issue
genevievestarke Jan 29, 2026
d3802a9
Update example
genevievestarke Jan 30, 2026
53a8da9
Made plots slightly larger
johnjasa Jan 30, 2026
948f11d
Merge branch 'develop' into feature/pyomo_opt
johnjasa Feb 2, 2026
99ad4d1
Merge branch 'develop' into feature/pyomo_opt
johnjasa Feb 3, 2026
5a7ced1
Update h2integrate/control/control_strategies/controller_opt_problem_…
genevievestarke Feb 3, 2026
1d7d659
Update h2integrate/control/control_strategies/controller_opt_problem_…
genevievestarke Feb 3, 2026
7324fb1
Update h2integrate/control/control_rules/hybrid_rule.py
genevievestarke Feb 3, 2026
028598e
Merge branch 'develop' into feature/pyomo_opt
johnjasa Feb 3, 2026
297c149
Cleaning up pyomo_controllers
johnjasa Feb 3, 2026
4b4a857
Updated docstrings and battery mentions
johnjasa Feb 3, 2026
0471fa7
Initial init docs string and example update
genevievestarke Feb 4, 2026
e474c2c
Update note about incentivizing charging in objective function
genevievestarke Feb 4, 2026
fb85bc7
Update generation and load variable definitions
genevievestarke Feb 4, 2026
40d5f57
Update docs/control/pyomo_controllers.md
genevievestarke Feb 4, 2026
31a9bd4
Update doc strings for updated_initial_soc parameter
genevievestarke Feb 4, 2026
fbf290e
Make time_weighting_factor and round_digits not hardcoded
genevievestarke Feb 4, 2026
c8ee38f
remove round_digits from pyomo_rule_baseclass
genevievestarke Feb 4, 2026
54885a7
Merging with develop
johnjasa Feb 4, 2026
2f500e3
Updating controller names
johnjasa Feb 5, 2026
9e1e9e9
Fixed name check for controllers
johnjasa Feb 5, 2026
d13e55c
Updated optimal controller test
johnjasa Feb 5, 2026
6768934
remove unused properties
bayc Feb 5, 2026
e21b33f
Update example to run with new class definitions
genevievestarke Feb 5, 2026
4e8ee73
Fix converter name in test
genevievestarke Feb 5, 2026
8c23c1b
Fix ruff formatting
genevievestarke Feb 5, 2026
0198e0d
Merge branch 'develop' into feature/pyomo_opt
johnjasa Feb 6, 2026
617c030
Update doc strings for pyomo model classes
genevievestarke Feb 6, 2026
695615b
Rename files to be more consistent and descriptive
genevievestarke Feb 6, 2026
8c6dca8
Update comments in the init portions of dispatch for parameters
genevievestarke Feb 6, 2026
16f6441
Merge branch 'develop' into feature/pyomo_opt
johnjasa Feb 6, 2026
b1242c6
Remove comment
genevievestarke Feb 6, 2026
d1dd047
Add doc strings to optimized dispatch config
genevievestarke Feb 6, 2026
f36429e
Give more details about DispatchProblemState class
genevievestarke Feb 6, 2026
7e93ef3
Remove todo comments
genevievestarke Feb 6, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
- Added standalone iron DRI and steel EAF performance and cost models
- Added capability to have transport models that require user input parameters
- Add geologic hydrogen surface processing converter
- Add optimal dispatch of storage for load following
- Add baseclass for caching functionality
- Added postprocessing function to save timeseries
- Minor reorg for profast tools
Expand Down
16 changes: 13 additions & 3 deletions docs/control/pyomo_controllers.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,25 @@
# Pyomo control framework
[Pyomo](https://www.pyomo.org/about) is an open-source optimization software package. It is used in H2Integrate to facilitate modeling and solving control problems, specifically to determine optimal dispatch strategies for dispatchable technologies.

Pyomo control, allows for the possibility of feedback control at specified intervals, but can also be used for open-loop control if desired. In the pyomo control framework in H2Integrate, each technology can have control rules associated with them that are in turn passed to the pyomo control component, which is owned by the storage technology. The pyomo control component combines the technology rules into a single pyomo model, which is then passed to the storage technology performance model inside a callable dispatch function. The dispatch function also accepts a simulation method from the performance model and iterates between the pyomo model for dispatch commands and the performance simulation function to simulated performance with the specified commands. The dispatch function runs in specified time windows for dispatch and performance until the whole simulation time has been run.
Pyomo control allows for the possibility of feedback control at specified intervals, but can also be used for open-loop control if desired. In the pyomo control framework in H2Integrate, each technology can have control rules associated with them that are in turn passed to the pyomo control component, which is owned by the storage technology. The pyomo control component combines the technology rules into a single pyomo model, which is then passed to the storage technology performance model inside a callable dispatch function. The dispatch function also accepts a simulation method from the performance model and iterates between the pyomo model for dispatch commands and the performance simulation function to simulate performance with the specified commands. The dispatch function runs in specified time windows for dispatch and performance until the whole simulation time has been run.

An example of an N2 diagram for a system using the pyomo control framework for hydrogen storage and dispatch is shown below ([click here for an interactive version](./figures/pyomo-n2.html)). Note the control rules being passed to the dispatch component and the dispatch function, containing the full pyomo model, being passed to the performance model for the battery/storage technology. Another important thing to recognize, in contrast to the open-loop control framework, is that the storage technology outputs (commodity out, SOC, unused commodity, etc) are passed out of the performance model when using the Pyomo control framework rather than from the control component.

![](./figures/pyomo-n2.png)

The pyomo control framework currently supports both a simple heuristic method and an optimized dispatch method for load following control.

(heuristic-load-following-controller)=
## Heuristic Load Following Controller
The pyomo control framework currently supports only a simple heuristic method, `HeuristicLoadFollowingController`, but we plan to extend the framework to be able to run a full dispatch optimization using a pyomo solver. When using the pyomo framework, a `dispatch_rule_set` for each technology connected to the storage technology must also be specified. These will typically be `PyomoDispatchGenericConverter` for generating technologies, and `PyomoRuleStorageBaseclass` for storage technologies. More complex rule sets may be developed as needed.

For an example of how to use the pyomo control framework with the `HeuristicLoadFollowingController`, see
The simple heuristic method is specified by setting the storage control to `HeuristicLoadFollowingController`. When using the pyomo framework, a `dispatch_rule_set` for each technology connected to the storage technology must also be specified. These will typically be `PyomoDispatchGenericConverter` for generating technologies, and `PyomoRuleStorageBaseclass` for storage technologies. More complex rule sets may be developed as needed.

For an example of how to use the heuristic pyomo control framework with the `HeuristicLoadFollowingController`, see
- `examples/18_pyomo_heuristic_wind_battery_dispatch`

(optimized-load-following-controller)=
## Optimized Load Following Controller
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After further reviewing the PR, I think there's some additions to the doc page that would really help with overall understanding.

  1. Adding something similar to the diagram in the PR body to help explain the mechanics of the interactions.
  2. A bit more about "Arcs", "Ports", "parameters" and "constraints" that are used within the context of the optimized load following controller
  3. It's a little confusing what "system-level" is within the context of the work because everything seems to be housed within the storage model. Explaining that might help users understand what's going on.
  4. I know we talked about making a note about users setting weights and how monkeying around with those can change how the Pyomo solver runs. I still think that would be a helpful addition

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some responses to the comments above:

  1. We can add some figures to the docs, happy to get some feedback on which parts of the diagram need to be clearer!
  2. "parameters" are values that the model needs to run the optimization (e.g. available production of the commodity to be used for storage), but that are not changed in the optimization like the "variables" are. "Constraints" define constraints on the variables, sometimes involving the parameters, that are enforced in the pyomo optimization. "Ports" define names of the variables that can be used to create external links between pyomo modules. The pyomo dispatch is implemented in a modular structure, where the dispatch for each plant technology is defined individually, and then all of the dispatch models are collected into a single optimization problem for the pyomo solver by hybrid_rule. The ports define what variables are passed from the individual technology dispatch models to the hybrid_rule problem. "Arcs" are what actually form the connection between the ports. This means that the storage dispatch pyomo model has a port for system_production, hybrid_rule has a port for system_production, and an arc connects these two endpoints to connect this variable across the pyomo models.
  3. In this PR, I've used "system-level" to define things that apply to the whole system (i.e. commodity load demand, etc.), and "storage" to define things that are specific to the storage technology (i.e. maximum charge rate, SOC, etc.). These are both included in the storage pyomo model at the moment. In HOPP, these were separated into 'storage' and 'grid' pyomo models, but the grid paradigm doesn't work with H2I at the moment, so they were combined into the storage pyomo model. We could separate these out into two different models (maybe "storage" and "system"). They are not defined in hybrid_rule because hybrid_rule currently only works as an aggregator of models and only includes things that you would need access to all the individual pyomo models to know.
  4. There are some thoughts about the weight settings in the body of the PR. I can distill those down to what would be helpful to have as a note in the docs.

The optimized dispatch method is specified by setting the storage control to `optimized_dispatch_controller`. The same `dispatch_rule_set` for each technology connected to the storage technology is followed as in the heuristic case, where each technology must also have a `dispatch_rule_set` defined in the `tech_config`. This method maximizes the load met while minimizing the cost of the system (operating cost) over each specified time window.

For an example of how to use the optimized pyomo control framework with the `optimized_dispatch_controller`, see
- `examples/27_pyomo_optimized_dispatch`
5 changes: 1 addition & 4 deletions examples/01_onshore_steel_mn/tech_config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ technologies:
model_inputs:
shared_parameters:
commodity_name: "electricity"
commodity_storage_units: "kW"
max_charge_rate: 375740.4 #kW
max_capacity: 375745.2 #kWh
n_control_window: 24
Expand All @@ -100,11 +101,7 @@ technologies:
power_capex: 311 # $/kW from 2024 ATB year 2025
opex_fraction: 0.024999840573439444
control_parameters:
commodity_storage_units: "kW"
tech_name: "battery"
dispatch_rule_parameters:
commodity_name: "electricity"
commodity_storage_units: "kW"

electrolyzer:
performance_model:
Expand Down
6 changes: 2 additions & 4 deletions examples/02_texas_ammonia/tech_config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ technologies:
model_inputs:
shared_parameters:
commodity_name: "electricity"
commodity_storage_units: "kW"
max_charge_rate: 96.0 #kW
max_capacity: 96.0 #kWh
n_control_window: 24
Expand All @@ -98,11 +99,8 @@ technologies:
power_capex: 311 # $/kW from 2024 ATB year 2025
opex_fraction: 0.025
control_parameters:
commodity_storage_units: "kW"
tech_name: "battery"
dispatch_rule_parameters:
commodity_name: "electricity"
commodity_storage_units: "kW"

electrolyzer:
performance_model:
model: "ECOElectrolyzerPerformanceModel"
Expand Down
6 changes: 2 additions & 4 deletions examples/09_co2/direct_ocean_capture/tech_config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ technologies:
model_inputs:
shared_parameters:
commodity_name: "electricity"
commodity_storage_units: "kW"
max_charge_rate: 50000 #kW
max_capacity: 200000 #kWh
n_control_window: 24
Expand All @@ -91,11 +92,8 @@ technologies:
power_capex: 317 # $/kW from 2024 ATB year 2025
opex_fraction: 0.02536510376633359
control_parameters:
commodity_storage_units: "kW"
tech_name: "battery"
dispatch_rule_parameters:
commodity_name: "electricity"
commodity_storage_units: "kW"

doc:
performance_model:
model: "DOCPerformanceModel"
Expand Down
6 changes: 2 additions & 4 deletions examples/09_co2/ocean_alkalinity_enhancement/tech_config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ technologies:
model_inputs:
shared_parameters:
commodity_name: "electricity"
commodity_storage_units: "kW"
max_charge_rate: 50000 #kW
max_capacity: 200000 #kWh
n_control_window: 24
Expand All @@ -68,11 +69,8 @@ technologies:
power_capex: 317 # $/kW from 2024 ATB year 2025
opex_fraction: 0.02536510376633359
control_parameters:
commodity_storage_units: "kW"
tech_name: "battery"
dispatch_rule_parameters:
commodity_name: "electricity"
commodity_storage_units: "kW"

oae:
performance_model:
model: "OAEPerformanceModel"
Expand Down
6 changes: 2 additions & 4 deletions examples/12_ammonia_synloop/tech_config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ technologies:
model_inputs:
shared_parameters:
commodity_name: "electricity"
commodity_storage_units: "kW"
max_charge_rate: 96.0 #kW
max_capacity: 96.0 #kWh
n_control_window: 24
Expand All @@ -98,11 +99,8 @@ technologies:
power_capex: 311 # $/kW from 2024 ATB year 2025
opex_fraction: 0.025
control_parameters:
commodity_storage_units: "kW"
tech_name: "battery"
dispatch_rule_parameters:
commodity_name: "electricity"
commodity_storage_units: "kW"

electrolyzer:
performance_model:
model: "ECOElectrolyzerPerformanceModel"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@
range(start_hour, end_hour),
demand_profile[start_hour:end_hour],
linestyle="--",
label="Eletrical Demand (MW)",
label="Electrical Demand (MW)",
)
ax[1].set_ylim([-7e2, 7e2])
ax[1].set_ylabel("Electricity Hourly (MW)")
Expand Down
5 changes: 1 addition & 4 deletions examples/18_pyomo_heuristic_dispatch/tech_config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ technologies:
model_inputs:
shared_parameters:
commodity_name: "electricity"
commodity_storage_units: "kW"
max_charge_rate: 100000
max_capacity: 500000
n_control_window: 24
Expand All @@ -64,8 +65,4 @@ technologies:
power_capex: 311 # $/kW from 2024 ATB year 2025
opex_fraction: 0.25 # 0.25% of capex per year from 2024 ATB
control_parameters:
commodity_storage_units: "kW"
tech_name: "battery"
dispatch_rule_parameters:
commodity_name: "electricity"
commodity_storage_units: "kW"
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ technologies:
model: "ATBBatteryCostModel"
model_inputs:
shared_parameters:
commodity_name: "electricity"
commodity_storage_units: "kW"
max_charge_rate: 100000
max_capacity: 500000
n_control_window: 24
Expand All @@ -65,9 +67,4 @@ technologies:
power_capex: 311 # $/kW from 2024 ATB year 2025
opex_fraction: 0.25 # 0.25% of capex per year from 2024 ATB
control_parameters:
commodity_name: "electricity"
commodity_storage_units: "kW"
tech_name: "wrong_tech_name"
dispatch_rule_parameters:
commodity_name: "electricity"
commodity_storage_units: "kW"
5 changes: 5 additions & 0 deletions examples/27_pyomo_optimized_dispatch/driver_config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
name: "driver_config"
description: "This analysis runs a hybrid plant to dispatch storage optimally to meet an electrical load."

general:
folder_output: outputs
76 changes: 76 additions & 0 deletions examples/27_pyomo_optimized_dispatch/plant_config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
name: "plant_config"
description: "This plant is located in TX, USA..."

sites:
site:
latitude: 35.2018863
longitude: -101.945027

resources:
wind_resource:
resource_model: "WTKNRELDeveloperAPIWindResource"
resource_parameters:
resource_year: 2012

plant:
plant_life: 30

# array of arrays containing left-to-right technology
# interconnections; can support bidirectional connections
# with the reverse definition.
# this will naturally grow as we mature the interconnected tech
technology_interconnections: [
["wind", "battery", "electricity", "cable"],
]

# array of arrays containing left-to-right technology, technology doing the dispatching
# in this case, battery is connected to battery because there are controls rules for
# the battery and battery is controlling the dispatching
tech_to_dispatch_connections: [
["wind", "battery"],
["battery", "battery"],
]

resource_to_tech_connections: [
# connect the wind resource to the wind technology
['site.wind_resource', 'wind', 'wind_resource_data'],
]

finance_parameters:
finance_groups:
profast_model:
commodity: "electricity"
finance_model: "ProFastLCO"
model_inputs:
params:
analysis_start_year: 2032
installation_time: 36 # months
inflation_rate: 0.0 # 0 for nominal analysis
discount_rate: 0.09 # nominal return based on 2024 ATB baseline workbook for land-based wind
debt_equity_ratio: 2.62 # 2024 ATB uses 72.4% debt for land-based wind
property_tax_and_insurance: 0.03 # p-tax https://www.house.mn.gov/hrd/issinfo/clsrates.aspx # insurance percent of CAPEX estimated based on https://www.nrel.gov/docs/fy25osti/91775.pdf
total_income_tax_rate: 0.257 # 0.257 tax rate in 2024 atb baseline workbook, value here is based on federal (21%) and state in MN (9.8)
capital_gains_tax_rate: 0.15 # H2FAST default
sales_tax_rate: 0.07375 # total state and local sales tax in St. Louis County https://taxmaps.state.mn.us/salestax/
debt_interest_rate: 0.07 # based on 2024 ATB nominal interest rate for land-based wind
debt_type: "Revolving debt" # can be "Revolving debt" or "One time loan". Revolving debt is H2FAST default and leads to much lower LCOH
loan_period_if_used: 0 # H2FAST default, not used for revolving debt
cash_onhand_months: 1 # H2FAST default
admin_expense: 0.00 # percent of sales H2FAST default
capital_items:
depr_type: "MACRS" # can be "MACRS" or "Straight line" - MACRS may be better and can reduce LCOH by more than $1/kg and is spec'd in the IRS MACRS schedule https://www.irs.gov/publications/p946#en_US_2020_publink1000107507
depr_period: 5 # years - for clean energy facilities as specified by the IRS MACRS schedule https://www.irs.gov/publications/p946#en_US_2020_publink1000107507
cost_adjustment_parameters:
cost_year_adjustment_inflation: 0.025 # used to adjust modeled costs to target_dollar_year
target_dollar_year: 2022
finance_subgroups:
all_electricity:
commodity: "electricity"
finance_groups: ["profast_model"]
technologies: ["wind", "battery"]


# dispatched_electricity:
# commodity: "electricity"
# commodity_stream: "battery" #use only dispatched electricity from battery in finance calc
# technologies: ["wind", "battery"]
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
name: "H2Integrate_config"

system_summary: "This hybrid plant contains wind and battery storage technologies. The system is designed to dispatch storage optimally meet a specific electrical load."

driver_config: "driver_config.yaml"
technology_config: "tech_config.yaml"
plant_config: "plant_config.yaml"
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import numpy as np
from matplotlib import pyplot as plt

from h2integrate.core.h2integrate_model import H2IntegrateModel


# Create an H2Integrate model
model = H2IntegrateModel("pyomo_optimized_dispatch.yaml")

demand_profile = np.ones(8760) * 100.0


# TODO: Update with demand module once it is developed
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you want to address this TODO? I thought you could set the demand in the tech config?

Copy link
Collaborator Author

@genevievestarke genevievestarke Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently, you have to set up the problem, then assign the battery demand signal to the battery model, then run the model. #385 is an issue to address this

model.setup()
model.prob.set_val("battery.electricity_demand", demand_profile, units="MW")

# Run the model
model.run()

# Plot the results
fig, ax = plt.subplots(2, 1, sharex=True, figsize=(8, 6))

start_hour = 0
end_hour = 200

ax[0].plot(
range(start_hour, end_hour),
model.prob.get_val("battery.SOC", units="percent")[start_hour:end_hour],
label="SOC",
)
ax[0].set_ylabel("SOC (%)")
ax[0].set_ylim([0, 110])
ax[0].axhline(y=90.0, linestyle=":", color="k", alpha=0.5, label="Max Charge")
ax[0].legend()

ax[1].plot(
range(start_hour, end_hour),
model.prob.get_val("battery.electricity_in", units="MW")[start_hour:end_hour],
linestyle="-",
label="Electricity In (MW)",
)
ax[1].plot(
range(start_hour, end_hour),
model.prob.get_val("battery.unused_electricity_out", units="MW")[start_hour:end_hour],
linestyle=":",
label="Unused Electricity (MW)",
)
ax[1].plot(
range(start_hour, end_hour),
model.prob.get_val("battery.unmet_electricity_demand_out", units="MW")[start_hour:end_hour],
linestyle=":",
label="Unmet Electrical Demand (MW)",
)
ax[1].plot(
range(start_hour, end_hour),
model.prob.get_val("battery.electricity_out", units="MW")[start_hour:end_hour],
linestyle="-",
label="Electricity Out (MW)",
)
ax[1].plot(
range(start_hour, end_hour),
model.prob.get_val("battery.battery_electricity_discharge", units="MW")[start_hour:end_hour],
linestyle="-.",
label="Battery Electricity Out (MW)",
)
ax[1].plot(
range(start_hour, end_hour),
demand_profile[start_hour:end_hour],
linestyle="--",
label="Electrical Demand (MW)",
)
ax[1].set_ylim([-1e2, 2.5e2])
ax[1].set_ylabel("Electricity Hourly (MW)")
ax[1].set_xlabel("Timestep (hr)")

plt.legend(ncol=2, frameon=False)
plt.tight_layout()
plt.savefig("optimized_dispatch_plot.png", dpi=300)
Loading