Skip to content
Open
Show file tree
Hide file tree
Changes from 29 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 @@ -5,6 +5,7 @@
- Added standlone 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

## 0.5.1 [December 18, 2025]
Expand Down
2 changes: 1 addition & 1 deletion docs/control/pyomo_controllers.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ An example of an N2 diagram for a system using the pyomo control framework for h

(heuristic-load-following-controller)=
## Heuristic Load Following Controller
The pyomo control framework currently supports only a simple heuristic method, `heuristic_load_following_controller`, 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 `pyomo_dispatch_generic_converter` for generating technologies, and `pyomo_dispatch_generic_storage` for storage technologies. More complex rule sets may be developed as needed.
The pyomo control framework currently supports only a simple heuristic method, `heuristic_load_following_controller`, 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 `pyomo_generic_converter` for generating technologies, and `pyomo_generic_storage` 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 `heuristic_load_following_controller`, see
- `examples/18_pyomo_heuristic_wind_battery_dispatch`
4 changes: 2 additions & 2 deletions examples/18_pyomo_heuristic_dispatch/tech_config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ technologies:
cost_model:
model: "atb_wind_cost"
dispatch_rule_set:
model: "pyomo_dispatch_generic_converter"
model: "pyomo_generic_converter"
resource:
type: "pysam_wind"
wind_speed: 9.
Expand Down Expand Up @@ -39,7 +39,7 @@ technologies:
commodity_storage_units: "kW"
battery:
dispatch_rule_set:
model: "pyomo_dispatch_generic_storage"
model: "pyomo_generic_storage"
control_strategy:
model: "heuristic_load_following_controller"
performance_model:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ technologies:
cost_model:
model: "atb_wind_cost"
dispatch_rule_set:
model: "pyomo_dispatch_generic_converter"
model: "pyomo_generic_converter"
resource:
type: "pysam_wind"
wind_speed: 9.
Expand Down Expand Up @@ -39,7 +39,7 @@ technologies:
commodity_storage_units: "kW"
battery:
dispatch_rule_set:
model: "pyomo_dispatch_generic_storage"
model: "pyomo_generic_storage"
control_strategy:
model: "heuristic_load_following_controller"
performance_model:
Expand Down
5 changes: 5 additions & 0 deletions examples/25_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 meet an electrical load."

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

site:
latitude: 35.2018863
longitude: -101.945027

resources:
wind_resource:
resource_model: "wind_toolkit_v2_api"
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
['wind_resource', 'wind', 'wind_resource_data'],
]

finance_parameters:
finance_groups:
commodity: "electricity"
finance_model: "ProFastComp"
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"
commodity_stream: "wind" # use all electricity generated from wind in finance calc
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 meet a specific electrical load."

driver_config: "driver_config.yaml"
technology_config: "tech_config_error_for_testing.yaml"
plant_config: "plant_config.yaml"
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
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)

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="Eletrical Demand (MW)",
)
ax[1].set_ylim([-7e2, 7e2])
ax[1].set_ylabel("Electricity Hourly (MW)")
ax[1].set_xlabel("Timestep (hr)")

plt.legend(ncol=2, frameon=False)
plt.tight_layout()
plt.savefig("plot.png", dpi=300)
80 changes: 80 additions & 0 deletions examples/25_pyomo_optimized_dispatch/tech_config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
name: "technology_config"
description: "This hybrid plant produces electricity from wind and battery storage."


technologies:
wind:
performance_model:
model: "pysam_wind_plant_performance"
cost_model:
model: "atb_wind_cost"
dispatch_rule_set:
model: "pyomo_generic_converter"
resource:
type: "pysam_wind"
wind_speed: 9.
model_inputs:
performance_parameters:
num_turbines: 100
turbine_rating_kw: 8300
rotor_diameter: 196.
hub_height: 130.
create_model_from: "default"
config_name: "WindPowerSingleOwner"
pysam_options: !include pysam_options_8.3MW.yaml
run_recalculate_power_curve: False
layout:
layout_mode: "basicgrid"
layout_options:
row_D_spacing: 10.0
turbine_D_spacing: 10.0
rotation_angle_deg: 0.0
row_phase_offset: 0.0
layout_shape: "square"
cost_parameters:
capex_per_kW: 1500.0
opex_per_kW_per_year: 45
cost_year: 2019
dispatch_rule_parameters:
commodity_name: "electricity"
commodity_storage_units: "kW"
battery:
dispatch_rule_set:
model: "pyomo_generic_storage"
control_strategy:
model: "optimized_dispatch_controller"
performance_model:
model: "pysam_battery"
cost_model:
model: "atb_battery_cost"
model_inputs:
shared_parameters:
commodity_name: "electricity"
max_charge_rate: 100000
max_capacity: 500000
n_control_window: 24
n_horizon_window: 48
init_charge_percent: 0.5
max_charge_percent: 0.9
min_charge_percent: 0.1
system_commodity_interface_limit: 1e12
time_weighting_factor: 0.995
charge_efficiency: 0.938
discharge_efficiency: 0.938
commodity_storage_units: "kW"
cost_per_charge: 0.05
cost_per_discharge: 0.1
commodity_met_value: 0.5
cost_per_production: 0.01
performance_parameters:
system_model_source: "pysam"
chemistry: "LFPGraphite"
cost_parameters:
cost_year: 2022
commodity_units: "kW"
energy_capex: 310 # $/kWh from 2024 ATB year 2025
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"
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
name: "technology_config"
description: "This hybrid plant produces hydrogen"

technologies:
wind:
performance_model:
model: "pysam_wind_plant_performance"
cost_model:
model: "atb_wind_cost"
dispatch_rule_set:
model: "pyomo_generic_converter"
resource:
type: "pysam_wind"
wind_speed: 9.
model_inputs:
performance_parameters:
num_turbines: 100
turbine_rating_kw: 8300
rotor_diameter: 196.
hub_height: 130.
create_model_from: "default"
config_name: "WindPowerSingleOwner"
pysam_options: !include pysam_options_8300MW.yaml
run_recalculate_power_curve: False
layout:
layout_mode: "basicgrid"
layout_options:
row_D_spacing: 10.0
turbine_D_spacing: 10.0
rotation_angle_deg: 0.0
row_phase_offset: 0.0
layout_shape: "square"
cost_parameters:
capex_per_kW: 1500.0
opex_per_kW_per_year: 45
cost_year: 2019
dispatch_rule_parameters:
commodity_name: "electricity"
commodity_storage_units: "kW"
battery:
dispatch_rule_set:
model: "pyomo_generic_storage"
control_strategy:
model: "heuristic_load_following_controller"
performance_model:
model: "pysam_battery"
cost_model:
model: "atb_battery_cost"
model_inputs:
shared_parameters:
max_charge_rate: 100000
max_capacity: 500000
n_control_window: 24
n_horizon_window: 48
init_charge_percent: 0.5
max_charge_percent: 0.9
min_charge_percent: 0.1
system_commodity_interface_limit: 1e12
performance_parameters:
system_model_source: "pysam"
chemistry: "LFPGraphite"
cost_parameters:
cost_year: 2022
energy_capex: 310 # $/kWh from 2024 ATB year 2025
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"
Loading
Loading