Skip to content

Commit b0d7781

Browse files
johnjasaelenya-grantkbrunik
authored
Added generic storage model (#248)
* Added simple generic storage model * Merging * Updated battery cost model and example * Added simple generic storage model docs * Added test for example * Finishing touches * remove unused cherry-picked file * remove unused cherry-pick test yaml * minor details in example * test generic storage model against H2 storage model * remove super * Added more exact example test values * Updated tests! --------- Co-authored-by: elenya-grant <116225007+elenya-grant@users.noreply.github.com> Co-authored-by: kbrunik <kbrunik@gmail.com>
1 parent 492c638 commit b0d7781

17 files changed

Lines changed: 542 additions & 7 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
- Added `"custom_electrolyzer_cost"` model, an electrolyzer cost model that allows for user-defined capex and opex values
5252
- Made `pipe` and `cable` substance-agnostic rather than hard-coded for `hydrogen` and `electricity`
5353
- Change finance handling to use `finance_subgroups` and `finance_groups` defined in the `plant_config` rather than previous `financial_groups` in the `tech_config` and `technologies_to_include_in_metrics` in `plant_config`
54+
- Added generic storage model, useful for battery, hydrogen, CO2, or other resource storage.
5455

5556
## 0.3.0 [May 2 2025]
5657

docs/_toc.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ parts:
3434
- file: technology_models/pvwattsv8_solar_pv.md
3535
- file: technology_models/atb_costs_pv.md
3636
- file: technology_models/co2.md
37+
- file: technology_models/simple_generic_storage.md
3738

3839
- caption: Examples
3940
chapters:
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# Simple Generic Storage Model
2+
3+
The Simple Generic Storage model provides a flexible framework for modeling various types of energy storage systems in H2Integrate. While particularly useful for battery storage, this model can be used to simulate the storage of different resources including hydrogen, CO2, or any other commodity.
4+
5+
## Overview
6+
7+
The Simple Generic Storage model consists of two main components:
8+
9+
1. **SimpleGenericStorage**: A minimal component that defines the input interface for the storage system
10+
2. **DemandOpenLoopController**: The core logic component that handles storage operations, state of charge calculations, and resource management
11+
12+
This architecture allows the storage system to work with any resource type by simply configuring the resource name and units, making it quite versatile.
13+
14+
## Example Applications
15+
16+
### Battery Storage (Example 19)
17+
18+
Example 19 demonstrates a wind-battery dispatch system that showcases the Simple Generic Storage model in action. This example:
19+
20+
- Models a wind farm providing variable electricity generation
21+
- Uses battery storage with defined capacity and charge/discharge rates
22+
- Implements demand-based control with a constant electricity demand
23+
- Demonstrates realistic battery operations including state of charge management and curtailment
24+
25+
The example produces detailed plots showing:
26+
- Battery state of charge over time
27+
- Electricity flows (input, output, curtailed, missed load)
28+
- How the storage system balances variable wind generation with constant demand
29+
30+
### Hydrogen Storage
31+
32+
The model can be configured for hydrogen storage systems by setting:
33+
```yaml
34+
resource_name: "hydrogen"
35+
resource_rate_units: "kg/h"
36+
max_capacity: 1000.0 # kg
37+
```
38+
39+
This is useful for modeling hydrogen production from electrolyzers with variable renewable input and steady hydrogen demand for industrial processes.
40+
41+
### CO2 Storage
42+
43+
For carbon capture and utilization systems:
44+
```yaml
45+
resource_name: "co2"
46+
resource_rate_units: "kg/h"
47+
max_capacity: 50000.0 # kg
48+
```
49+
50+
This enables modeling of CO2 capture systems with temporary storage before utilization or permanent sequestration.

docs/technology_models/technology_overview.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ Connection: `[source_tech, dest_tech, transport_commodity, transport_technology]
7070
| :---------------- | :---------------: |
7171
| `h2_storage` | hydrogen |
7272
| `battery` | electricity |
73+
| `generic_storage` | Any |
7374

7475
(controller)=
7576
## Controller
@@ -199,6 +200,10 @@ Below summarizes the available performance, cost, and financial models for each
199200
+ `'hydrogen_tank_performance'`
200201
- cost models:
201202
+ `'hydrogen_tank_cost'`
203+
- `generic_storage`: any resource storage
204+
- `battery`: battery storage
205+
- cost models:
206+
+ `'atb_battery_cost'`
202207

203208
## Controller Models
204209
- `pass_through_controller`

examples/14_wind_hydrogen_dispatch/hydrogen_dispatch.ipynb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,7 @@
340340
],
341341
"metadata": {
342342
"kernelspec": {
343-
"display_name": "h2integrate-mcm",
343+
"display_name": "hopp",
344344
"language": "python",
345345
"name": "python3"
346346
},
@@ -354,7 +354,7 @@
354354
"name": "python",
355355
"nbconvert_exporter": "python",
356356
"pygments_lexer": "ipython3",
357-
"version": "3.11.12"
357+
"version": "3.11.13"
358358
}
359359
},
360360
"nbformat": 4,
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
name: "driver_config"
2+
description: "Runs a wind plant and battery with a demand open loop controller"
3+
4+
general:
5+
folder_output: wind_plant_run
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
name: "plant_config"
2+
description: "This plant is located in MN, USA..."
3+
4+
site:
5+
latitude: 47.5233
6+
longitude: -92.5366
7+
8+
# array of polygons defining boundaries with x/y coords
9+
boundaries: [
10+
{
11+
x: [0.0, 1000.0, 1000.0, 0.0],
12+
y: [0.0, 0.0, 100.0, 1000.0],
13+
},
14+
{
15+
x: [2000.0, 2500.0, 2000.0],
16+
y: [2000.0, 2000.0, 2500.0],
17+
}
18+
]
19+
20+
plant:
21+
plant_life: 30
22+
23+
# array of arrays containing left-to-right technology
24+
# interconnections; can support bidirectional connections
25+
# with the reverse definition.
26+
# this will naturally grow as we mature the interconnected tech
27+
technology_interconnections: [
28+
["wind", "battery", "electricity", "cable"],
29+
# etc
30+
]
31+
32+
finance_parameters:
33+
finance_groups:
34+
finance_model: "ProFastComp"
35+
model_inputs:
36+
params:
37+
analysis_start_year: 2032
38+
installation_time: 36 # months
39+
inflation_rate: 0.0 # 0 for nominal analysis
40+
discount_rate: 0.09 # nominal return based on 2024 ATB baseline workbook for land-based wind
41+
debt_equity_ratio: 2.62 # 2024 ATB uses 72.4% debt for land-based wind
42+
property_tax_and_insurance: 0.03 # percent of CAPEX estimated based on https://www.nrel.gov/docs/fy25osti/91775.pdf https://www.house.mn.gov/hrd/issinfo/clsrates.aspx
43+
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)
44+
capital_gains_tax_rate: 0.15 # H2FAST default
45+
sales_tax_rate: 0.07375 # total state and local sales tax in St. Louis County https://taxmaps.state.mn.us/salestax/
46+
debt_interest_rate: 0.07 # based on 2024 ATB nominal interest rate for land-based wind
47+
debt_type: "Revolving debt" # can be "Revolving debt" or "One time loan". Revolving debt is H2FAST default and leads to much lower LCOH
48+
loan_period_if_used: 0 # H2FAST default, not used for revolving debt
49+
cash_onhand_months: 1 # H2FAST default
50+
admin_expense: 0.00 # percent of sales H2FAST default
51+
capital_items:
52+
depr_type: "MACRS" # can be "MACRS" or "Straight line"
53+
depr_period: 5 # 5 years - for clean energy facilities as specified by the IRS MACRS schedule https://www.irs.gov/publications/p946#en_US_2020_publink1000107507
54+
refurb: [0.]
55+
cost_adjustment_parameters:
56+
cost_year_adjustment_inflation: 0.025 # used to adjust modeled costs to target_dollar_year
57+
target_dollar_year: 2022
58+
finance_subgroups:
59+
electricity:
60+
commodity: "electricity"
61+
technologies: ["wind","battery"]
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import matplotlib.pyplot as plt
2+
3+
from h2integrate.core.h2integrate_model import H2IntegrateModel
4+
5+
6+
h2i = H2IntegrateModel("wind_battery_dispatch.yaml")
7+
8+
# Run the model
9+
h2i.run()
10+
11+
h2i.post_process()
12+
13+
# Battery dispatch plotting
14+
model = h2i
15+
fig, ax = plt.subplots(2, 1, sharex=True)
16+
17+
start_hour = 0
18+
end_hour = 200
19+
total_time_steps = model.prob.get_val("battery.electricity_soc").size
20+
demand_profile = [
21+
model.technology_config["technologies"]["battery"]["model_inputs"]["control_parameters"][
22+
"demand_profile"
23+
]
24+
* 1e-3
25+
] * total_time_steps
26+
27+
ax[0].plot(
28+
range(start_hour, end_hour),
29+
model.prob.get_val("battery.electricity_soc", units="percent")[start_hour:end_hour],
30+
label="SOC",
31+
linewidth=2,
32+
)
33+
ax[0].set_ylabel("SOC (%)")
34+
ax[0].set_ylim([0, 110])
35+
36+
ax[1].plot(
37+
range(start_hour, end_hour),
38+
model.prob.get_val("battery.electricity_in", units="MW")[start_hour:end_hour],
39+
linestyle="-",
40+
label="Electricity In (MW)",
41+
linewidth=2,
42+
)
43+
ax[1].plot(
44+
range(start_hour, end_hour),
45+
model.prob.get_val("battery.electricity_curtailed", units="MW")[start_hour:end_hour],
46+
linestyle=":",
47+
label="Electricity Curtailed (MW)",
48+
linewidth=2,
49+
)
50+
ax[1].plot(
51+
range(start_hour, end_hour),
52+
model.prob.get_val("battery.electricity_missed_load", units="MW")[start_hour:end_hour],
53+
linestyle=":",
54+
label="Electricity Missed Load (MW)",
55+
linewidth=2,
56+
)
57+
ax[1].plot(
58+
range(start_hour, end_hour),
59+
model.prob.get_val("battery.electricity_out", units="MW")[start_hour:end_hour],
60+
linestyle="-",
61+
label="Electricity Out (MW)",
62+
linewidth=2,
63+
)
64+
ax[1].plot(
65+
range(start_hour, end_hour),
66+
demand_profile[start_hour:end_hour],
67+
linestyle="--",
68+
label="Electricity Demand (MW)",
69+
linewidth=2,
70+
)
71+
ax[1].set_ylabel("Electricity Hourly (MW)")
72+
ax[1].set_xlabel("Timestep (hr)")
73+
74+
plt.legend(ncol=2, frameon=False)
75+
plt.tight_layout()
76+
plt.show()
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
name: "technology_config"
2+
description: "This plant has wind feeding into an electrolyzer with optimization"
3+
4+
technologies:
5+
wind:
6+
performance_model:
7+
model: "wind_plant_performance"
8+
cost_model:
9+
model: "wind_plant_cost"
10+
resource:
11+
type: "pysam_wind"
12+
wind_speed: 9.
13+
model_inputs:
14+
shared_parameters:
15+
num_turbines: 2
16+
turbine_rating_kw: 8300
17+
performance_parameters:
18+
rotor_diameter: 196.
19+
hub_height: 130.
20+
layout_mode: "basicgrid"
21+
model_name: "pysam"
22+
model_input_file: null
23+
layout_params:
24+
row_D_spacing: 10.
25+
turbine_D_spacing: 10.
26+
rating_range_kw: [1000, 9000]
27+
floris_config: null
28+
operational_losses: 10.0
29+
timestep: [1, 60]
30+
fin_model: "default"
31+
name: "UtilityScaleWindPlant"
32+
turbine_name: "2023NREL_Bespoke_8.3MW_196" #turbine model to use
33+
turbine_group: "onshore"
34+
override_wind_resource_height: False
35+
adjust_air_density_for_elevation: True
36+
cost_parameters:
37+
cost_per_kw: 1500.0
38+
cost_year: 2019
39+
battery:
40+
performance_model:
41+
model: "simple_generic_storage"
42+
cost_model:
43+
model: "atb_battery_cost"
44+
control_strategy:
45+
model: "demand_open_loop_controller"
46+
model_inputs:
47+
shared_parameters:
48+
resource_name: "electricity"
49+
resource_rate_units: "kW"
50+
max_charge_rate: 5000.0 # kW/time step
51+
max_capacity: 30000.0 # kWh
52+
control_parameters:
53+
max_charge_percent: 1.0 # percent as decimal
54+
min_charge_percent: 0.1 # percent as decimal
55+
init_charge_percent: 0.25 # percent as decimal
56+
max_discharge_rate: 5000.0 # kW/time step
57+
charge_efficiency: 1.0 # percent as decimal
58+
discharge_efficiency: 1.0 # percent as decimal
59+
demand_profile: 5000
60+
cost_parameters:
61+
cost_year: 2022
62+
energy_capex: 300 # $/kWh
63+
power_capex: 100 # $/kW
64+
opex_fraction: 0.02 # percent of capex
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
name: "H2Integrate_config"
2+
3+
system_summary: "This reference hybrid plant..."
4+
5+
driver_config: "driver_config.yaml"
6+
technology_config: "tech_config.yaml"
7+
plant_config: "plant_config.yaml"

0 commit comments

Comments
 (0)