Skip to content

Commit 5421981

Browse files
jaredthomas68johnjasakbrunikCopilot
authored
Framework to allow for variable dt (#653)
* add draft for variable timestep framework * Changed error wording * switch bounds to tuple * minor test correction * include time-series generation functions * add tests for variable dt and update simulation length check for non-hourly dt * update docs * update changelog * update docs and doc strings * restore develop version of utilities.py * update dt bounds error * Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Fixing tests * Adding time step bounds to all models * Adding time step bounds to the combiner/splitter/transporters * Updates for tests * Adding time step bounds to the custom paper mill performance model * Added time step bounds to paper mill cost --------- Co-authored-by: John Jasa <johnjasa11@gmail.com> Co-authored-by: kbrunik <102193481+kbrunik@users.noreply.github.com> Co-authored-by: John Jasa <john.jasa@nrel.gov> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent 1caa9f6 commit 5421981

87 files changed

Lines changed: 455 additions & 17 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
- The `FlexibleDemandOpenLoopConverterController` has been renamed to `FlexibleDemandComponent`
5252
- The `DemandOpenLoopConverterController` has been renamed to `GenericDemandComponent`
5353
- Modified CI setup so Windows is temporarily disabled and also so unit, regression, and integration tests are run in separate jobs to speed up testing and provide more information on test failures. [PR 668](https://github.com/NatLabRockies/H2Integrate/pull/668)
54+
- Added infrastructure for running models with non-hourly time steps via a class attribute `_time_step_bounds` and sets new time step bounds of 5-minutes to 1-hour for the grid components. [PR 653](https://github.com/NatLabRockies/H2Integrate/pull/653)
5455

5556
## 0.7.2 [April 9, 2026]
5657

docs/developer_guide/adding_a_new_technology.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,19 @@ In the middle-ground case where the models might use a shared object that is com
258258
This would require additional logic to first check if the cached object exists and is valid before attempting to load it, otherwise it would create the object from scratch.
259259
There is an example of this in the `hopp_wrapper.py` file.
260260

261+
### Specifying allowable time step for your model
262+
If you want your model to run with time steps other than 1 hour (3600 s), then you must specify the `_time_step_bounds` as a class attribute in each of your model classes. To run a simulation with a given time step, all models in the plant must be compatible with the desired time step.
263+
264+
```python
265+
class ECOElectrolyzerPerformanceModel(ElectrolyzerPerformanceBaseClass):
266+
"""
267+
An OpenMDAO component that wraps the PEM electrolyzer model.
268+
Takes electricity input and outputs hydrogen and oxygen generation rates.
269+
"""
270+
271+
# (min, max) time step lengths (in seconds) compatible with this model
272+
_time_step_bounds = (300, 3600) # (5-min, 1-hour)
273+
```
261274

262275
### Other cases
263276

docs/intro.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,11 +64,17 @@ H2Integrate was previously known as GreenHEART. The name was updated to H2Integr
6464

6565
## How does H2Integrate work?
6666

67-
H2Integrate models energy systems on a yearly basis using hourly timesteps (i.e., 8760 operational data points across a year).
67+
H2Integrate typically models energy systems on a yearly basis using hourly timesteps (i.e., 8760 operational data points across a year).
6868
Results from these simulations are then processed across the project's lifecycle to provide insights into the system's performance, costs, and financial viability.
6969
Depending on the models used and the size of the system, H2Integrate can simulate systems ranging from the kW to GW scale in seconds on a personal computer.
7070
Additionally, H2Integrate tracks the flow of electricity, molecules (e.g., hydrogen, ammonia, methanol), and other products (e.g., steel) between different technologies in the energy system.
7171

72+
```{note}
73+
Some models are now able to operate with non-hourly time steps.
74+
Appropriate time step bounds are included as class attributes when non-hourly time steps are permitted.
75+
Check individual model docs and definitions for time step bounds for individual models. All models in a given simulation must be compatible with the specified time step.
76+
```
77+
7278
For each technology there are 4 different types of models: control, performance, cost, and finance. These model categories allow for modular pieces to be brought in or re-used throughout H2Integrate, as well as ease of development and organization. Note that the only required models for a technology are performance and cost, while control and finance are optional. The figure below shows these four categories and some of the technologies included in H2Integrate. For a full list of models available, please see [Model Overview](user_guide/model_overview.md).
7379
![A representation of a single technology model in H2Integrate](tech-model.png)
7480

docs/technology_models/grid.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,3 +67,7 @@ The **revenue** of selling electricity to the grid is represented as a variable
6767
```{note}
6868
If you're using a price-maker financial model (e.g., calculating the LCOE) and selling all of the electricity to the grid, then the `electricity_sell_price` should most likely be set to 0. since you want to know the breakeven price of selling that electricity.
6969
```
70+
71+
```{note}
72+
The grid components are currently compatible with 5-minute (300-second) to 1-hour (3600-second) time steps.
73+
```

examples/06_custom_tech/user_defined_model/paper_mill.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ class PaperMillConfig(BaseConfig):
1616

1717

1818
class PaperMillPerformance(om.ExplicitComponent):
19+
_time_step_bounds = (3600, 3600) # (min, max) time step lengths compatible with this model
20+
1921
def initialize(self):
2022
self.options.declare("driver_config", types=dict)
2123
self.options.declare("plant_config", types=dict)
@@ -53,6 +55,8 @@ class PaperMillCostConfig(CostModelBaseConfig):
5355

5456

5557
class PaperMillCost(CostModelBaseClass):
58+
_time_step_bounds = (3600, 3600) # (min, max) time step lengths compatible with this model
59+
5660
def setup(self):
5761
self.config = PaperMillCostConfig.from_dict(
5862
merge_shared_inputs(self.options["tech_config"]["model_inputs"], "cost")

h2integrate/control/control_rules/converters/generic_converter.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99

1010

1111
class PyomoDispatchGenericConverter(PyomoRuleBaseClass):
12+
_time_step_bounds = (3600, 3600) # (min, max) time step lengths compatible with this model
13+
1214
def setup(self):
1315
self.config = PyomoRuleBaseConfig.from_dict(
1416
merge_shared_inputs(self.options["tech_config"]["model_inputs"], "dispatch_rule"),

h2integrate/control/control_rules/pyomo_rule_baseclass.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ class PyomoRuleBaseConfig(BaseConfig):
2222

2323

2424
class PyomoRuleBaseClass(om.ExplicitComponent):
25+
_time_step_bounds = (3600, 3600) # (min, max) time step lengths compatible with this model
26+
2527
def initialize(self):
2628
self.options.declare("driver_config", types=dict)
2729
self.options.declare("plant_config", types=dict)

h2integrate/control/control_rules/storage/pyomo_storage_rule_baseclass.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ class PyomoStorageRuleBaseConfig(PyomoRuleBaseConfig):
2424
class PyomoRuleStorageBaseclass(PyomoRuleBaseClass):
2525
"""Base class defining Pyomo rules for generic commodity storage components."""
2626

27+
_time_step_bounds = (3600, 3600) # (min, max) time step lengths compatible with this model
28+
2729
def setup(self):
2830
self.config = PyomoStorageRuleBaseConfig.from_dict(
2931
merge_shared_inputs(self.options["tech_config"]["model_inputs"], "dispatch_rule"),

h2integrate/control/control_strategies/heuristic_pyomo_controller.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ class HeuristicLoadFollowingController(PyomoControllerBaseClass):
7070
7171
"""
7272

73+
_time_step_bounds = (3600, 3600) # (min, max) time step lengths compatible with this model
74+
7375
def setup(self):
7476
"""Initialize the heuristic load-following controller."""
7577
self.config = HeuristicLoadFollowingControllerConfig.from_dict(

h2integrate/control/control_strategies/optimized_pyomo_controller.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,8 @@ class OptimizedDispatchController(PyomoControllerBaseClass):
100100
101101
"""
102102

103+
_time_step_bounds = (3600, 3600) # (min, max) time step lengths compatible with this model
104+
103105
def setup(self):
104106
"""Initialize the optimized dispatch controller."""
105107
self.config = OptimizedDispatchControllerConfig.from_dict(

0 commit comments

Comments
 (0)