Skip to content
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
933ae89
begin open loop peak load management control
jaredthomas68 Mar 27, 2026
90b82d8
Merge branch 'develop' into peakload
jaredthomas68 Mar 27, 2026
d338cce
peak finding working as expected
jaredthomas68 Mar 28, 2026
e8a2dfb
peak merging working
jaredthomas68 Mar 30, 2026
7276db6
all elements present
jaredthomas68 Mar 31, 2026
226f0a4
Merge branch 'develop' into peakload
johnjasa Mar 31, 2026
6fc22a7
update comments and doc strings
jaredthomas68 Mar 31, 2026
ecc0abd
Merge remote-tracking branch 'myfork/peakload' into peakload
jaredthomas68 Mar 31, 2026
db0c72a
seperate the demand and time series to separate config inputs
jaredthomas68 Mar 31, 2026
d5b4250
regression tests working
jaredthomas68 Mar 31, 2026
b7b2573
move time series method to a utility function
jaredthomas68 Apr 1, 2026
9b021ee
wip: add example
jaredthomas68 Apr 1, 2026
ad97f44
Merge branch 'develop' into peakload
jaredthomas68 Apr 1, 2026
b4508be
remove debug statements
jaredthomas68 Apr 1, 2026
4473244
remove defaults and add input of min_proximity
jaredthomas68 Apr 1, 2026
4b6e25f
exclude example 33 demand profile yamls from yamlfix
jaredthomas68 Apr 2, 2026
d7774d2
example runs
jaredthomas68 Apr 2, 2026
74fc2ed
example complete
jaredthomas68 Apr 2, 2026
b456d75
remove n_timesteps since it comes from base class
jaredthomas68 Apr 2, 2026
a624bd4
remove erroneous changes to example 32
jaredthomas68 Apr 2, 2026
0aa1ee2
Merge branch 'develop' into peakload
johnjasa Apr 6, 2026
767eab2
Merge branch 'develop' into peakload
johnjasa Apr 6, 2026
cc03ac3
Merge branch 'develop' into peakload
jaredthomas68 Apr 8, 2026
e3d824f
Merge remote-tracking branch 'myfork/peakload' into peakload
jaredthomas68 Apr 8, 2026
7b3d3da
add doc strings, input checks, time series function updates, test upd…
jaredthomas68 Apr 8, 2026
5e6ef34
move dispatch strategy outline to doc string
jaredthomas68 Apr 8, 2026
1a344e6
shift merge_peaks method from using primary and secondary to peaks_1 …
jaredthomas68 Apr 8, 2026
ca878ed
add check against peaks_1 being None
jaredthomas68 Apr 8, 2026
a0bb555
rename get_allowed_discharge to get_allowed_charge
jaredthomas68 Apr 8, 2026
cef5b02
minor renaming and logic adjustments based on pr reviews
jaredthomas68 Apr 9, 2026
fe01f8f
rename from supervisor and secondary to _1 and _2
jaredthomas68 Apr 9, 2026
5527570
adjust variable naming
jaredthomas68 Apr 9, 2026
52f5e45
make example 33_peak_load_management run full 8760
jaredthomas68 Apr 9, 2026
af66754
Merge branch 'develop' into peakload
jaredthomas68 Apr 9, 2026
797e1d6
refine example 33 plot
jaredthomas68 Apr 9, 2026
830b6ee
fix color coordination in plot for peak load example 33 and remove de…
jaredthomas68 Apr 9, 2026
dfe8380
improve code reuse
jaredthomas68 Apr 9, 2026
9c0b212
added common compute checks to baseclass
jaredthomas68 Apr 9, 2026
7cbfe2f
move input file contents to fixture
jaredthomas68 Apr 9, 2026
725838b
add storage parameters to baseclass but optional
jaredthomas68 Apr 9, 2026
55d6d7a
create plant_config fixture
jaredthomas68 Apr 9, 2026
b5689a4
complete and refine example 33 peak load
jaredthomas68 Apr 10, 2026
83c0e4f
add docs page for peak load control
jaredthomas68 Apr 10, 2026
6ebe8ea
update changelog
jaredthomas68 Apr 10, 2026
b0f3036
undue minor changes
jaredthomas68 Apr 10, 2026
f63fd63
add PeakLoadManagementOpenLoopStorageController to model_overview
jaredthomas68 Apr 10, 2026
8d3d82a
resolve merge conflicts in CHANGELOG.md
jaredthomas68 Apr 10, 2026
15d178a
resolve merge conflicts
jaredthomas68 Apr 13, 2026
983d770
Merge branch 'develop' into peakload
johnjasa Apr 14, 2026
b65478e
merge from develop
jaredthomas68 Apr 14, 2026
6cfc269
update changelog
jaredthomas68 Apr 14, 2026
71d9737
add test for example 33 and add grid purchase profile to plot
jaredthomas68 Apr 14, 2026
840304d
merge develop
jaredthomas68 Apr 15, 2026
2867695
update to be compatible with demand components (from PR 666)
jaredthomas68 Apr 15, 2026
5a81860
fix merge conflicts
jaredthomas68 Apr 16, 2026
01d44c5
Merge branch 'develop' into peakload
jaredthomas68 Apr 16, 2026
7c878cb
resolve merge conflicts
jaredthomas68 Apr 20, 2026
617f819
add comments. simplify documentation. remove meta data in favor of do…
jaredthomas68 Apr 20, 2026
30484e3
update docs and example to demarcate override peaks
jaredthomas68 Apr 20, 2026
a1bab83
update docs
jaredthomas68 Apr 20, 2026
9d7b7a3
add doc comment
jaredthomas68 Apr 20, 2026
c0a1561
move static method to after compute
jaredthomas68 Apr 20, 2026
515557d
Update docs/control/open-loop_controllers.md
jaredthomas68 Apr 20, 2026
2e2820a
Merge remote-tracking branch 'myfork/peakload' into peakload
jaredthomas68 Apr 20, 2026
e103abe
minor update
jaredthomas68 Apr 20, 2026
4d921cc
Merge branch 'develop' into peakload
johnjasa Apr 22, 2026
750b61b
rename demand_profile_2 to demand_profile_upstream and add Heuristic …
jaredthomas68 Apr 22, 2026
c527ad7
update changelog
jaredthomas68 Apr 22, 2026
c024eb6
Rename demand_profile_upstream.yaml
johnjasa Apr 23, 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
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ repos:
- id: yamlfix
# Exclude YAML files that are used as inputs for testing or examples, or ones for desired schedule in HOPP as they
# expect misformatted YAML files.
exclude: ^(h2integrate/core/test/inputs/.*|examples/11_hybrid_energy_plant/tech_inputs/desired_schedules/.*)$
exclude: ^(h2integrate/core/test/inputs/.*|examples/11_hybrid_energy_plant/tech_inputs/desired_schedules/.*|examples/33_hybrid_energy_plant/demand_profiles/.*)$
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.8.1
Expand Down
5 changes: 5 additions & 0 deletions examples/33_peak_load_management/33_peak_load_management.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
name: H2Integrate_config
system_summary: Peak load management dispatch
driver_config: driver_config.yaml
technology_config: tech_config.yaml
plant_config: plant_config.yaml
5 changes: 5 additions & 0 deletions examples/33_peak_load_management/driver_config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
name: driver_config
description: Driver configuration for multivariable streams example
general:
folder_output: outputs
create_om_reports: false
11 changes: 11 additions & 0 deletions examples/33_peak_load_management/plant_config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
name: plant_config
description: Demonstrates multivariable streams with a gas combiner
Comment thread
elenya-grant marked this conversation as resolved.
plant:
plant_life: 30
simulation:
n_timesteps: 168
dt: 3600
timezone: -6
start_time: 2025/07/01 00:00:00
technology_interconnections:
- [battery, grid_buy, [electricity_out, electricity_in]]
72 changes: 72 additions & 0 deletions examples/33_peak_load_management/run_peak_load_management.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
"""
Example 33: Peak load management dispatch

This example demonstrates:
1. Peak load management dispatch open loop control
2. Battery charging without an input stream, assuming purchase from the grid

"""

import numpy as np
import matplotlib.pyplot as plt

from h2integrate.core.utilities import build_time_series_from_plant_config
from h2integrate.core.file_utils import load_yaml
from h2integrate.core.h2integrate_model import H2IntegrateModel


# Create and setup the H2Integrate model
model = H2IntegrateModel("33_peak_load_management.yaml")

model.setup()

model.run()

plant_config = load_yaml("plant_config.yaml")
supervisor_demand = np.asarray(
load_yaml("demand_profiles/demand_profile_supervisor.yaml"), dtype=float
)
secondary_demand = np.asarray(
load_yaml("demand_profiles/demand_profile_secondary.yaml"), dtype=float
)

time_series = build_time_series_from_plant_config(plant_config)

# Example profiles may be shorter than the simulation horizon; plot over shared length.
n_plot = min(len(time_series), len(supervisor_demand), len(secondary_demand))
time_plot = time_series[:n_plot]

fig, ax = plt.subplots(4, 1, sharex=True)

ax[0].plot(time_plot, supervisor_demand[:n_plot] * 1e-3, label="Supervisory demand (MW)")
ax[0].plot(time_plot, secondary_demand[:n_plot] * 1e-3, label="Secondary demand (MW)")
ax[0].set_ylabel("Power (MW)")
ax[0].legend(loc="upper right")

ax[1].plot(time_plot, model.prob.get_val("battery.SOC", units="percent"))
ax[1].set(ylabel="SOC")

ax[2].plot(time_plot, secondary_demand[:n_plot] * 1e-3, label="Original demand (MW)")
ax[2].plot(
time_plot,
model.prob.get_val("battery.electricity_out", units="MW"),
label="Battery charge/discharge",
)
ax[2].set(ylabel="Power (MW)")
ax[2].legend()

ax[3].plot(time_plot, secondary_demand[:n_plot] * 1e-3, label="Original demand (MW)")
ax[3].plot(
time_plot,
model.prob.get_val("battery.unmet_electricity_demand_out", units="MW"),
label="New demand profile",
)
ax[3].set(ylabel="Power (MW)")
ax[3].legend()


ax[3].tick_params(axis="x", labelrotation=90)

# import pdb; pdb.set_trace()
plt.tight_layout()
plt.show()
61 changes: 61 additions & 0 deletions examples/33_peak_load_management/tech_config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
name: technology_config
description: This plant charges a battery from the grid to reduce peak demand
technologies:
battery:
performance_model:
model: StoragePerformanceModel
cost_model:
model: ATBBatteryCostModel
control_strategy:
model: PeakLoadManagementOpenLoopStorageController
model_inputs:
shared_parameters:
commodity: electricity
commodity_rate_units: kW
max_charge_rate: 2500.0 # kW/time step, 1, 2.5, or 5 MW
max_capacity: 10000.0 # kWh, 80 MWh
Comment thread
elenya-grant marked this conversation as resolved.
Outdated
max_soc_fraction: 0.9 # percent as decimal
min_soc_fraction: 0.1 # percent as decimal
init_soc_fraction: 0.9 # percent as decimal
demand_profile: !include demand_profiles/demand_profile_secondary.yaml
charge_efficiency: 1.0 # percent as decimal
discharge_efficiency: 1.0 # percent as decimal
control_parameters:
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.

thanks for the comments here! they're helpful!

max_discharge_rate: 2500.0 # kW/time step
charge_equals_discharge: true
demand_profile_supervisor: !include demand_profiles/demand_profile_supervisor.yaml
dispatch_priority_demand_profile: demand_profile_supervisor
max_supervisor_events: 2
Comment thread
elenya-grant marked this conversation as resolved.
Outdated
max_supervisor_event_period: W
peak_range:
start: 12:00:00
end: 17:00:00
advance_discharge_period:
units: h
val: 2
delay_charge_period:
units: h
val: 4
allow_charge_in_peak_range: false
min_peak_proximity:
units: h
val: 4
cost_parameters:
cost_year: 2024
energy_capex: 408 # $/kWh # conservative case for 2024 ATB utility scale batteries
power_capex: 379 # $/kW # conservative case for 2024 ATB utility scale batteries
opex_fraction: 0.025 # 2.5% percent of capex as per 2024 ATB for utility scale batteries
grid_buy:
performance_model:
model: GridPerformanceModel
cost_model:
model: GridCostModel
model_inputs:
shared_parameters:
interconnection_size: 100000
cost_parameters:
cost_year: 2024
fixed_interconnection_cost: 0.0
interconnection_capex_per_kw: 0.0
interconnection_opex_per_kw: 0.0
electricity_buy_price: 0.09
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,18 @@ class StorageOpenLoopControlBaseConfig(BaseConfig):
Attributes:
commodity (str): Name of the commodity being stored (e.g., "hydrogen").
commodity_rate_units (str): Rate units of the commodity (e.g., "kg/h" or "kW").
demand_profile (int | float | list): Demand values for each timestep, in
demand_profile (int | float | list | dict): Demand values for each timestep, in
the same units as `commodity_rate_units`. May be a scalar for constant
demand or a list/array for time-varying demand.
demand or a list/array/dict for time-varying demand. If a dict is provided, it
it should have two keys: "time_date" and "demand".
Comment thread
elenya-grant marked this conversation as resolved.
commodity_amount_units (str | None, optional): Units of the commodity as an amount
(i.e., kW*h or kg). If not provided, defaults to `commodity_rate_units*h`.

"""

commodity: str = field()
commodity_rate_units: str = field()
demand_profile: int | float | list = field()
demand_profile: int | float | list | dict = field()
commodity_amount_units: str = field(default=None)

def __attrs_post_init__(self):
Expand All @@ -48,9 +49,11 @@ def setup(self):

commodity = self.config.commodity

demand_data = self.config.demand_profile

self.add_input(
f"{commodity}_demand",
val=self.config.demand_profile,
val=demand_data if not isinstance(demand_data, dict) else demand_data["demand"],
shape=self.n_timesteps,
units=self.config.commodity_rate_units,
desc=f"Demand profile of {commodity}",
Expand Down
Loading
Loading