Skip to content
Open
Show file tree
Hide file tree
Changes from 47 commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
1b9f88d
renamed outputs in converter openloop controllers
elenya-grant Mar 26, 2026
2b3d0c1
updated tests and examples with updated naming
elenya-grant Mar 26, 2026
2af6329
Merge remote-tracking branch 'h2i_upstream/develop' into dispatch/con…
elenya-grant Mar 27, 2026
c59a20e
Merge branch 'develop' into dispatch/converter_ol_sync
kbrunik Mar 31, 2026
eab6d09
added performance model outputs to the converter control strategies
elenya-grant Apr 1, 2026
1e54ed2
udpated plant configs in test_openloop_controllers.py
elenya-grant Apr 1, 2026
1852db4
Merge branch 'develop' into dispatch/converter_ol_sync
johnjasa Apr 1, 2026
b1b62c9
moved converter control strategies to separate file
elenya-grant Apr 1, 2026
a97292c
Merge branch 'dispatch/converter_ol_sync' of github.com:elenya-grant/…
elenya-grant Apr 6, 2026
2d6b0c5
moved converter control strategies to demand folder
elenya-grant Apr 6, 2026
0e6b3e8
updated import paths for demand components
elenya-grant Apr 6, 2026
2986dff
removed commodity_set_point as output
elenya-grant Apr 6, 2026
1f71c9d
Merge remote-tracking branch 'h2i_upstream/develop' into dispatch/con…
elenya-grant Apr 7, 2026
9672ecc
renamed demand files
elenya-grant Apr 7, 2026
7ad7b59
renamed demand classes and configs
elenya-grant Apr 7, 2026
9ace0ad
Merge branch 'develop' into dispatch/converter_ol_sync
kbrunik Apr 8, 2026
8444cdf
renamed demand component to be performance model instead of control s…
elenya-grant Apr 8, 2026
2093b81
moved output calculations to shared method in baseclass
elenya-grant Apr 8, 2026
1510bd2
Merge branch 'dispatch/converter_ol_sync' of github.com:elenya-grant/…
elenya-grant Apr 8, 2026
d116ad8
removed the word controller from docstrings
elenya-grant Apr 8, 2026
7c5b9e5
draft update to docs
elenya-grant Apr 8, 2026
1046419
removed system level calcs from storage and updated most tests
elenya-grant Apr 9, 2026
65dc07b
updated optimal controller tests
elenya-grant Apr 9, 2026
89a4ced
updated example 1 and 2
elenya-grant Apr 9, 2026
5032e41
updated examples 1, 2, and 12
elenya-grant Apr 10, 2026
24cb283
updated examples with storage but dont use storage as a commodity_str…
elenya-grant Apr 10, 2026
92d9854
updated examples with storage defined as a commodity_stream for a fin…
elenya-grant Apr 10, 2026
3001732
tried to fix doc page
elenya-grant Apr 10, 2026
572a020
fixed test failing in sql_to_csv because of additional outputs
elenya-grant Apr 10, 2026
2bd17d8
updated example 12 so tests pass
elenya-grant Apr 10, 2026
c71ba77
Merge branch 'develop' into storage/standalone_outputs
johnjasa Apr 10, 2026
7596e27
updated framework tests that used example 1
elenya-grant Apr 10, 2026
f47966d
Merge remote-tracking branch 'upstream/develop' into HEAD
kbrunik Apr 11, 2026
9c8b7c1
made demand an input only if using feedback control
elenya-grant Apr 13, 2026
90e56be
merged in develop
elenya-grant Apr 13, 2026
8988ae8
undid change for demand only input for feedback controllers
elenya-grant Apr 13, 2026
140c368
Merge remote-tracking branch 'origin/storage/standalone_outputs' into…
elenya-grant Apr 13, 2026
7e3b3f3
updated demand docs with figures
elenya-grant Apr 13, 2026
d9ad39e
added time_step_bounds to demand components
elenya-grant Apr 13, 2026
a0202a7
added small bit about demand component to controller_demostrations.md
elenya-grant Apr 13, 2026
8094ee8
updated plotting part of controller demo doc page
elenya-grant Apr 13, 2026
738173c
added test for example 23
elenya-grant Apr 13, 2026
eaa17e8
added example for different demand between storage and demand component
elenya-grant Apr 13, 2026
75fa5f5
added subtests for example 13
elenya-grant Apr 13, 2026
a01b5af
minor update to controller_demonstrations doc page to use EXAMPLE_DIR
elenya-grant Apr 13, 2026
5a510aa
added wind resource file for example 23
elenya-grant Apr 13, 2026
21082b7
added demo for the demand components using example 13
elenya-grant Apr 13, 2026
9ecd357
fixed demand_demo.md
elenya-grant Apr 13, 2026
6ebd865
made it so demand is only input to battery performance model if using…
elenya-grant Apr 13, 2026
ed710a5
typo fix in example 13 plant config
elenya-grant Apr 13, 2026
46116b6
Merge branch 'develop' into storage/standalone_outputs
johnjasa Apr 14, 2026
0c9c665
minor changes to docs and some logic based on feedback
elenya-grant Apr 14, 2026
6308329
Merge branch 'storage/standalone_outputs' of github.com:elenya-grant/…
elenya-grant Apr 14, 2026
6720139
added battery performance plots to demand demo
elenya-grant Apr 14, 2026
34f5a8c
added subtests for example and minor updates based on feedback
elenya-grant Apr 14, 2026
41a177b
added xdsm to demand_demo doc page
elenya-grant Apr 14, 2026
864b124
added standard_capacity_factor as output from storage
elenya-grant Apr 14, 2026
ff1b98c
minor updates to example 13 subtests
elenya-grant Apr 14, 2026
62347b5
make some subtests more loose because of floating point precision dif…
elenya-grant Apr 14, 2026
98047ec
added doc page for storage performance models
elenya-grant Apr 14, 2026
0704c24
added subtests for capacity factor calcs and minor update to docs
elenya-grant Apr 14, 2026
15e7573
minor update to example tests
elenya-grant Apr 14, 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 docs/_toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ parts:
- caption: Demand
chapters:
- file: demand/demand_components
- file: demand/demand_demo
- caption: Finance Models
chapters:
- file: finance_models/financial_analyses
Expand Down
48 changes: 26 additions & 22 deletions docs/control/controller_demonstrations.md
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I was looking at the docs for this and I think it could make sense to update example 14 and the outputs in this demonstration to show how the demand to the h2_storage and the h2_load_demand can be different and how that impacts the analysis. I would also recommend updating the tech_config executable block because it doesn't include the h2_load_demand and I think that would be helpful to showcase.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

That's a good idea - I will work on that!

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I think that example 1 or 2 may be a better candidate. Those ones have a battery and we could set the battery demand profile to 10% of the electrolyzer rated capacity and then add/set an electricity component demand profile equal to the electrolyzer rated capacity. There is nothing that hydrogen is going to in example 14 which makes it hard to come up with a storage why the storage demand profile would be different than the demand component demand profile.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I made a new example (example 13) and used that to make a demand_demo.md page in the demand docs folder.

Original file line number Diff line number Diff line change
Expand Up @@ -17,28 +17,39 @@ kernelspec:
from pathlib import Path
from matplotlib import pyplot as plt
from h2integrate.core.h2integrate_model import H2IntegrateModel
from h2integrate import EXAMPLE_DIR

```

## Hydrogen Dispatch

The following example is an expanded form of `examples/14_wind_hydrogen_dispatch`.

Here, we're highlighting the dispatch controller setup from
`examples/14_wind_hydrogen_dispatch/inputs/tech_config.yaml`. Please note some sections are removed
simply to highlight the controller sections
`examples/14_wind_hydrogen_dispatch/inputs/tech_config.yaml`. Please note some sections are removed simply to highlight the controller sections

```{literalinclude} ../../examples/14_wind_hydrogen_dispatch/inputs/tech_config.yaml
:language: yaml
:lineno-start: 52
:linenos: true
:lines: 52,53-54,57-58,59-71
```

We also include a demand technology to calculate how much demand is met, how much commodity is unused to meet the demand, and how much demand is remaining:

```{literalinclude} ../../examples/14_wind_hydrogen_dispatch/inputs/tech_config.yaml
:language: yaml
:lineno-start: 54
:lineno-start: 79
:linenos: true
:lines: 54,59-61,67-74
:lines: 79-86
```


Using the primary configuration, we can create, run, and postprocess an H2Integrate model.

```{code-cell} ipython3
# Create an H2Integrate model
model = H2IntegrateModel(Path("../../examples/14_wind_hydrogen_dispatch/inputs/h2i_wind_to_h2_storage.yaml"))
model = H2IntegrateModel(EXAMPLE_DIR/"14_wind_hydrogen_dispatch"/"inputs"/"h2i_wind_to_h2_storage.yaml")

# Run the model
model.run()
Expand All @@ -50,15 +61,8 @@ Now, we can visualize the demand profiles over time.
```{code-cell} ipython3
fig, (ax1, ax2) = plt.subplots(2, 1, sharex=True, figsize=(8, 6), layout="tight")

start_hour = 0
end_hour = 200
total_time_steps = model.prob.get_val("h2_storage.SOC").size
demand_profile = [
model.technology_config["technologies"]["h2_storage"]["model_inputs"]["shared_parameters"][
"demand_profile"
]
* 1e-3
] * total_time_steps
start_hour = 800
end_hour = 1000
xvals = list(range(start_hour, end_hour))

ax1.plot(
Expand All @@ -74,41 +78,41 @@ ax2.plot(
)
ax2.plot(
xvals,
model.prob.get_val("h2_storage.unused_hydrogen_out", units="t/h")[start_hour:end_hour],
model.prob.get_val("h2_load_demand.unused_hydrogen_out", units="t/h")[start_hour:end_hour],
linestyle=":",
label="H$_2$ Unused (kg)",
)
ax2.plot(
xvals,
model.prob.get_val("h2_storage.unmet_hydrogen_demand_out", units="t/h")[start_hour:end_hour],
model.prob.get_val("h2_load_demand.unmet_hydrogen_demand_out", units="t/h")[start_hour:end_hour],
linestyle=":",
label="H$_2$ Unmet Demand (kg)",
)
ax2.plot(
xvals,
model.prob.get_val("h2_storage.hydrogen_out", units="t/h")[start_hour:end_hour],
model.prob.get_val("h2_load_demand.hydrogen_out", units="t/h")[start_hour:end_hour],
linestyle="-",
label="H$_2$ Delivered (kg)",
)
ax2.plot(
xvals,
demand_profile[start_hour:end_hour],
model.prob.get_val("h2_load_demand.hydrogen_demand", units="t/h")[start_hour:end_hour],
linestyle="--",
label="H$_2$ Demand (kg)",
)

ax1.set_ylabel("SOC (%)")
ax1.grid()
ax1.set_axisbelow(True)
ax1.set_xlim(0, 200)
ax1.set_ylim(0, 50)
ax1.set_xlim(start_hour, end_hour)
ax1.set_ylim(60, 100)

ax2.set_ylabel("H$_2$ Hourly (t)")
ax2.set_xlabel("Timestep (hr)")
ax2.grid()
ax2.set_axisbelow(True)
ax2.set_ylim(0, 20)
ax2.set_yticks(range(0, 21, 2))
ax2.set_ylim(0, 18)
ax2.set_yticks(range(0, 19, 2))

plt.legend(ncol=3)
fig.show()
Expand Down
20 changes: 20 additions & 0 deletions docs/demand/demand_components.md
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think these figures really help illustrate what the calculations are doing :) thank you!

Original file line number Diff line number Diff line change
Expand Up @@ -88,3 +88,23 @@ model_inputs:
ramp_up_rate_fraction: 0.5
min_utilization: 0
```

(demand-component-inputs-and-outputs)=
## Demand component inputs and outputs
The inputs to the demand components are the `{commodity}_demand` profile and the `{commodity}_in` profile. The `{commodity}_in` profile is the initial commodity used to satisfy the demand. Suppose the `{commodity}_in` (pink) and `{commodity}_demand` (green) profiles look like whats shown below:

![](./figures/demand_inputs.png)

The demand components then compute the following output profiles:
- `unmet_{commodity}_demand_out`: Unmet demand (non-zero when supply < demand, otherwise 0.)
- `unused_{commodity}_out`: Unused commodity (non-zero when supply > demand, otherwise 0.)

![](./figures/demand_calcs.png)

- `commodity_out`: Delivered output (commodity supplied to demand sink)

![](./figures/demand_commodity_out.png)

The demand components also compute the following outputs:
- `capacity_factor`: the ratio of the demand that's been met to the full demand
- `rated_commodity_production`: the maximum value of the demand profile
213 changes: 213 additions & 0 deletions docs/demand/demand_demo.md
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This is a very nice demo, well done putting this together!

Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
---
jupytext:
text_representation:
extension: .md
format_name: myst
format_version: 0.13
jupytext_version: 1.18.1
kernelspec:
display_name: native-ard-h2i
language: python
name: python3
---

# Demand Demonstration
Different usage of demand components is shown in the following examples:
- `13_dispatch_for_electrolyzer`: sets the battery demand differently than the demand profile of the demand component
- `23_solar_wind_ng_demand`: compares usage of the `GenericDemandComponent` and `FlexibleDemandComponent`
- `24_solar_battery_grid`: sells excess electricity to grid

This demonstration will focus on the `13_dispatch_for_electrolyzer` example

## Electrolyzer load demand

The following example is an expanded form of `examples/13_dispatch_for_electrolyzer`.

The technology interconnections:
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think the user would benefit from an xdsm diagram here.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

heard - how do I make that?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

image

We could use the built-in XDSM-maker in H2I, callable via create_xdsm on the H2IntegrateModel object.
Or, we could use pyXDSM manually like I did for this above.
@jaredthomas68, is this in line with what you're thinking? What would you change?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Yes, that is exactly what I was thinking. It would be nice to use the built-in method as part of the example, but all I was asking for is including the figure to visualize the connections.

Copy link
Copy Markdown
Collaborator Author

@elenya-grant elenya-grant Apr 14, 2026

Choose a reason for hiding this comment

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

thanks! I added it in


```{literalinclude} ../../examples/13_dispatch_for_electrolyzer/plant_config.yaml
:language: yaml
:lineno-start: 16
:linenos: true
:lines: 16-27
```


The electrolyzer system is comprised of 6 stacks, each rated at 10 MW, resulting in a total capacity of 60 MW. The minimum operating point of the electrolyzer (`turndown_ratio`) is 10% of the rated capacity, meaning that the electrolyzer is turned off if there is less than 6 MW of input electricity.

```{literalinclude} ../../examples/13_dispatch_for_electrolyzer/tech_config.yaml
:language: yaml
:lineno-start: 121
:linenos: true
:lines: 121-123,126-127,130,131,135
```

We want to dispatch the battery to *keep the electrolyzer on*. We set the demand profile for the battery as the minimum power required to keep the electrolyzer on, 6 MW.

```{note}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Helpful note!

Note: the demand profile for the battery is only used by the battery controller and is not used in the battery performance model for any calculations.
```


```{literalinclude} ../../examples/13_dispatch_for_electrolyzer/tech_config.yaml
:language: yaml
:lineno-start: 79
:linenos: true
:lines: 79-90, 100
```

We don't want to send more electricity to the electrolyzer than the electrolyzer can use. We use the demand component to saturate the electricity generation to the electrolyzer's rated capacity (equal to 60 MW).

```{literalinclude} ../../examples/13_dispatch_for_electrolyzer/tech_config.yaml
:language: yaml
:lineno-start: 113
:linenos: true
:lines: 113-120
```



We initialize and setup the H2I model

```{code-cell} ipython3
from matplotlib import pyplot as plt
from h2integrate.core.h2integrate_model import H2IntegrateModel
from h2integrate import EXAMPLE_DIR

# Create an H2Integrate model
h2i = H2IntegrateModel(EXAMPLE_DIR/"13_dispatch_for_electrolyzer"/"inputs"/"h2i_wind_to_h2_storage.yaml")

# Setup the model
h2i.setup()
```

If we wanted to change the demand profiles for the battery (`battery`) or the demand component (`elec_load_demand`) to be different than the demand profiles specified in the technology config, we can do that using `set_val`:

```{code-cell} ipython3
electrolyzer_capacity_MW = 60

# Set the battery demand equal to the minimum electricity needed to keep the electrolyzer on
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Wait, didn't you already set this in the tech_config?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

yes - I may have pushed up changes since your comment (the line numbers are confusing me). Before the part where I call set_val() now - I have this line:

If we wanted to change the demand profiles for the battery (battery) or the demand component (elec_load_demand) to be different than the demand profiles specified in the technology config, we can do that using set_val:

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

commented out the python code in that section but left the note so folks understand.

h2i.prob.set_val("battery.electricity_demand", 0.1 * electrolyzer_capacity_MW, units="MW")

# Set the demand of the demand component equal to the rated electrical capacity of the electrolyzer
h2i.prob.set_val("elec_load_demand.electricity_demand", electrolyzer_capacity_MW, units="MW")
```
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Since you want to show the code but not run the code, just render the code in the standard non-running code block.

electrolyzer_capacity_MW = 60

# Set the battery demand equal to the minimum electricity needed to keep the electrolyzer on
h2i.prob.set_val("battery.electricity_demand", 0.1 * electrolyzer_capacity_MW, units="MW")

# Set the demand of the demand component equal to the rated electrical capacity of the electrolyzer
h2i.prob.set_val("elec_load_demand.electricity_demand", electrolyzer_capacity_MW, units="MW")

Which, non-rendered, looks like this:

Image


We then run the model:
```{code-cell} ipython3
# Run the model
h2i.run()
h2i.prob.get_val("finance_subgroup_hydrogen.LCOH", units="USD/kg")[0]
```


### Plotting outputs

First, we get the main inputs and outputs of the `GenericDemandComponent` technology:

```{code-cell} ipython3
generation_with_battery = h2i.prob.get_val("elec_load_demand.electricity_in", units="MW")
full_demand = h2i.prob.get_val("elec_load_demand.electricity_demand", units="MW")
unmet_demand = h2i.prob.get_val("elec_load_demand.unmet_electricity_demand_out", units="MW")
excess_electricity = h2i.prob.get_val("elec_load_demand.unused_electricity_out", units="MW")
```

Plot the inputs to the `GenericDemandComponent` and outputs calculated in the `GenericDemandComponent`:

```{code-cell} ipython3
import matplotlib.pyplot as plt
fig, ax = plt.subplots(1, 1, figsize=[6.4, 2.4])

start_hour = 800
end_hour = 1000

x = list(range(start_hour, end_hour))
where_unmet_demand = [True if d>0 else False for d in unmet_demand[start_hour:end_hour]]
where_excess_commodity = [True if d>0 else False for d in excess_electricity[start_hour:end_hour]]

ax.fill_between(x, full_demand[start_hour:end_hour], generation_with_battery[start_hour:end_hour], where=where_unmet_demand, color="tab:red", alpha=0.5, zorder=0, label="unmet_commodity_demand_out")
ax.fill_between(x, full_demand[start_hour:end_hour], generation_with_battery[start_hour:end_hour], where=where_excess_commodity, color="tab:blue", alpha=0.5, zorder=1, label="unused_commodity_out")
ax.plot(x, full_demand[start_hour:end_hour], color="tab:green", lw=2.0, ls='solid', zorder=4, label="commodity_demand")
ax.plot(x, generation_with_battery[start_hour:end_hour], color="tab:pink", lw=2.0, ls='solid', zorder=3, label="commodity_in")

ax.set_xlim([start_hour,end_hour])
ax.spines[['right', 'top']].set_visible(False)
ax.legend(bbox_to_anchor=(0.10, 0.95), loc="lower left", borderaxespad=0.0, framealpha=0.0, ncols=2)
ax.set_ylabel("GenericDemandComponent \nElectricity (MW)")
ax.set_xlabel("Time (hours)")
```


Plot the main inputs and outputs of the `GenericDemandComponent`:

```{code-cell} ipython3
fig, ax = plt.subplots(1, 1, figsize=[6.4, 2.4])
ax.fill_between(x, full_demand[start_hour:end_hour], generation_with_battery[start_hour:end_hour], where=where_excess_commodity, color="tab:grey", alpha=0.25, zorder=1, label="_unused_commodity_out")
ax.plot(x, generation_with_battery[start_hour:end_hour], color="tab:grey", lw=2.0, ls='solid', zorder=2, label="commodity_in", alpha=0.25)

ax.plot(x, full_demand[start_hour:end_hour], color="tab:green", alpha=0.5, lw=1.5, ls='-.', zorder=3, label="commodity_demand")

ax.plot(x, h2i.prob.get_val("elec_load_demand.electricity_out", units="MW")[start_hour:end_hour], color="tab:purple", lw=2.0, ls='solid', zorder=4, label="commodity_out")
ax.spines[['right', 'top']].set_visible(False)
ax.set_xlim([start_hour, end_hour])
ax.set_ylabel("GenericDemandComponent \nElectricity (MW)")
ax.set_xlabel("Time (hours)")
ax.legend(bbox_to_anchor=(0.0, 0.95), loc="lower left", ncols=3, borderaxespad=0.0, framealpha=0.0)
```


Plot the battery performance:
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Sick plot!!!!


```{code-cell} ipython3
fig, ax = plt.subplots(1, 1, figsize=[7.2, 2.4])

start_hour = 900
end_hour = 1000
x = list(range(start_hour, end_hour))

generation = h2i.prob.get_val("battery.electricity_in", units="MW")
battery_demand = h2i.prob.get_val("battery.electricity_demand", units="MW")
battery_charge_discharge = h2i.prob.get_val("battery.electricity_out", units="MW")

where_charge = [True if d<0 else False for d in battery_charge_discharge[start_hour:end_hour]]
where_discharge = [True if d>0 else False for d in battery_charge_discharge[start_hour:end_hour]]

ax.plot(x, battery_demand[start_hour:end_hour], color="tab:green", alpha=0.5, lw=1.5, ls='-.', zorder=2, label="battery.electricity_demand")
ax.plot(x, generation[start_hour:end_hour], color="tab:blue", alpha=1.0, lw=1.5, ls='--', zorder=3, label="battery.electricity_in")
ax.plot(x, generation_with_battery[start_hour:end_hour], color="tab:pink", alpha=1.0, lw=1.5, ls='-', zorder=3, label="elec_combiner.electricity_out")
ax.fill_between(x, generation[start_hour:end_hour], generation_with_battery[start_hour:end_hour], where=where_charge, color="tab:cyan", alpha=0.5, zorder=0, label="battery charging")
ax.fill_between(x, generation[start_hour:end_hour], generation_with_battery[start_hour:end_hour], where=where_discharge, color="tab:orange", alpha=0.5, zorder=0, label="battery discharging")


ax.spines[['right', 'top']].set_visible(False)
ax.set_xlim([start_hour, end_hour])
ax.set_ylim([0, 70])
ax.legend(bbox_to_anchor=(1.0, 0.5), loc="center left", borderaxespad=0, framealpha=0.0)
ax.set_ylabel("Electricity (MW)")
ax.set_xlabel("Time (hours)")
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

You did a very nice job with the figures in this page. Clean and clear. Non-blocking suggestion here, but I would like to see the storage SOC and charge and discharge plots directly. I think it would make the storage operation a little more clear.

Copy link
Copy Markdown
Collaborator Author

@elenya-grant elenya-grant Apr 14, 2026

Choose a reason for hiding this comment

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

done!

```


### Changing the battery demand

Lets see what the LCOH is when the battery is used to keep the electrolyzer on:
```{code-cell} ipython3
h2i.prob.get_val("finance_subgroup_hydrogen.LCOH", units="USD/kg")[0]
```

If we re-run H2I and set the battery demand equal to the electrolyzer capacity instead, we can see that the LCOH increases:

```{code-cell} ipython3

# Set the battery demand equal to the rated electrical capacity of the electrolyzer
h2i.prob.set_val("battery.electricity_demand",electrolyzer_capacity_MW, units="MW")

# Set the demand of the demand component equal to the rated electrical capacity of the electrolyzer
h2i.prob.set_val("elec_load_demand.electricity_demand", electrolyzer_capacity_MW, units="MW")

# Re-run H2I
h2i.run()

# Get the LCOH
h2i.prob.get_val("finance_subgroup_hydrogen.LCOH", units="USD/kg")[0]
```
Binary file added docs/demand/figures/demand_calcs.png
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

are the added figures color-blind friendly?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I'm not sure...could you suggest some colors that you'd like instead?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Noted in Issue #667

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/demand/figures/demand_commodity_out.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/demand/figures/demand_inputs.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Looking at this figure, the red is really hard to see even for a color seeing person. I realize this isn't a new addition in this PR but do we have to code to maybe change to color and that wouldn't be too hard? If the answer is no then don't do it.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I don't remember where the code is that I had to make this figure initially - so - I can try to re-make it but it'd take 30min+

File renamed without changes
9 changes: 8 additions & 1 deletion examples/01_onshore_steel_mn/plant_config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,15 @@ technology_interconnections:
- [wind, combiner, electricity, cable]
- [solar, combiner, electricity, cable]
- [combiner, battery, electricity, cable]
- [battery, electrolyzer, electricity, cable]
# combine the battery output with the wind and solar generation
- [battery, elec_combiner, electricity, cable]
- [combiner, elec_combiner, electricity, cable]
# connect the electricity supply to the electrolyzer
- [elec_combiner, electrolyzer, electricity, cable]
- [electrolyzer, h2_storage, hydrogen, pipe]
# combine the h2 from the electrolyzer and the h2_storage
- [electrolyzer, h2_combiner, hydrogen, pipe]
- [h2_storage, h2_combiner, hydrogen, pipe]
- [finance_subgroup_hydrogen, steel, [LCOH_delivered, LCOH]]
# etc
tech_to_dispatch_connections:
Expand Down
Loading
Loading