Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
711c087
Updating top-level docs, config, etc
johnjasa Oct 8, 2026
1db4f64
Added sphinx docs file
johnjasa Oct 8, 2026
a562226
Adding a bevy of new doc pages
johnjasa Oct 8, 2026
1cce8d6
Updating top-level docs, config, etc
johnjasa Oct 8, 2026
4d7e78c
Added sphinx docs file
johnjasa Oct 8, 2026
b248c0d
Merging
johnjasa Oct 8, 2026
f71214b
Add core framework support for non-annual simulation durations
jaredthomas68 Oct 8, 2026
26327f1
add per-model simulation duration check
jaredthomas68 Oct 8, 2026
99cadaa
Updating top-level docs, config, etc
johnjasa Oct 8, 2026
9dabc08
Added sphinx docs file
johnjasa Oct 8, 2026
a381dea
Added sphinx docs file
johnjasa Oct 8, 2026
4b694a1
Adding a bevy of new doc pages
johnjasa Oct 8, 2026
a1b1eda
Merge branch 'update_top_docs' of https://github.com/NREL/H2Integrate…
johnjasa Oct 9, 2026
ac18d48
Fixed doc build
johnjasa Oct 9, 2026
e74a7e7
Merging
johnjasa Oct 9, 2026
2c2311b
update docs and docstrings and break up cf and replacement schedule a…
jaredthomas68 Oct 9, 2026
fac83f5
add sim bounds check to control base classes
jaredthomas68 Oct 9, 2026
f65b77b
resolve merge conflicts
jaredthomas68 Oct 12, 2026
f9e695e
Updating top-level docs, config, etc
johnjasa Oct 8, 2026
542a012
Added sphinx docs file
johnjasa Oct 8, 2026
2be74d1
Fixed doc build
johnjasa Oct 9, 2026
7013dee
Merge branch 'nonannual-resource' into nonannual-framework
jaredthomas68 Oct 12, 2026
f2f0da5
Merge branch 'develop' of https://github.com/NREL/H2Integrate into up…
johnjasa Oct 12, 2026
4a683da
Merge branch 'update_top_docs' of https://github.com/NREL/H2Integrate…
johnjasa Oct 13, 2026
327ad8b
Updating changelog
johnjasa Oct 13, 2026
2d2a532
Merge branch 'update_top_docs' of https://github.com/NREL/H2Integrate…
johnjasa Oct 13, 2026
76110a0
Merging
johnjasa Oct 13, 2026
aa2b9f2
Merge branch 'new_docs_pages' of https://github.com/NREL/H2Integrate …
johnjasa Oct 13, 2026
d4a404c
Minor docs updates
johnjasa Oct 13, 2026
ee94134
Merge branch 'nonannual-resource' into nonannual-framework
jaredthomas68 Oct 13, 2026
33e6a3e
Merge branch 'nonannual-resource' into nonannual-framework
jaredthomas68 Oct 13, 2026
2f26c9f
Merging
johnjasa Oct 13, 2026
ac164e9
Fixing consistency test
johnjasa Oct 13, 2026
309affd
Merging
johnjasa Oct 13, 2026
0be62f3
Merge branch 'new_docs_pages' of https://github.com/NREL/H2Integrate …
johnjasa Oct 14, 2026
0d9ee06
Updating and expanding docs based on PR feedback
johnjasa Oct 14, 2026
5515ea3
merge
jaredthomas68 Oct 14, 2026
82bdc05
Added a note about the recorder excludes vs includes
johnjasa Oct 14, 2026
4f08833
merge
jaredthomas68 Oct 14, 2026
df7b3c4
updated wind model docs
elenya-grant Oct 14, 2026
5cf8c32
added more detail to floris doc page
elenya-grant Oct 14, 2026
47432c1
changed how I added info
elenya-grant Oct 14, 2026
0d3e734
Merge branch 'nonannual-resource' into nonannual-framework
jaredthomas68 Oct 14, 2026
2f4ed29
Merge branch 'nonannual-resource' into nonannual-framework
jaredthomas68 Oct 14, 2026
cf9dd1b
Addressing Elenya's comments on profast docs and more
johnjasa Oct 15, 2026
0856e2a
Merge branch 'develop' of https://github.com/NREL/H2Integrate into HEAD
johnjasa Oct 15, 2026
f6333ec
Merge branch 'nonannual-resource' into nonannual-framework
jaredthomas68 Oct 15, 2026
8f63639
Merge pull request #918 from NatLabRockies/new_docs_pages
johnjasa Oct 15, 2026
f02ca27
Update CHANGELOG.md
jaredthomas68 Oct 15, 2026
6d285b9
adjust cf and replacement helper to allow for multiple replacements i…
jaredthomas68 Oct 16, 2026
9884612
Merge branch 'develop' into nonannual-resource
jaredthomas68 Oct 16, 2026
a08064a
Merge remote-tracking branch 'origin/nonannual-resource' into nonannu…
jaredthomas68 Oct 16, 2026
e348433
merge
jaredthomas68 Oct 16, 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
6 changes: 3 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
## Unreleased [TBD]

- Added a documentation changelog and version indicator, and updated installation, environment variable, and contribution guidance. [PR 917](https://github.com/NatLabRockies/H2Integrate/pull/917)
- Added user references for PEM electrolysis, transport, FLORIS, configuration files, and additional registered models, with API and example links. [PR 918](https://github.com/NatLabRockies/H2Integrate/pull/918)
- Updated `make_price_breakdown()` and `run_profast()` in `profast_tools.py` so that fixed cost items and feedstock cost items are properly labelled. [PR 930](https://github.com/NatLabRockies/H2Integrate/pull/930)
- Add data resampling capabilities via pandas methods. [PR 933](https://github.com/NatLabRockies/H2Integrate/pull/933)


- Add data resampling capabilities to resource models via pandas methods. [PR 933](https://github.com/NatLabRockies/H2Integrate/pull/933)
- Add core framework support for non-annual simulation durations, including annual capacity-factor and replacement-count projections across the plant life. Leap-day-inclusive resources are rejected in full H2I runs until framework-wide calendar alignment is supported. [PR 934](https://github.com/NatLabRockies/H2Integrate/pull/934)

## 0.10.0 [October 12, 2026]

Expand Down
7 changes: 2 additions & 5 deletions docs/_templates/autosummary/module.rst
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,8 @@
{% endblock %}

{% block modules %}
{# Filter out pytest conftest modules; they cannot be imported outside a
pytest session (they use `from test.conftest import ...` which requires
the repo-root ``test/`` package to be on ``sys.path``) and so autosummary
raises "failed to import" warnings on them. #}
{% set filtered_modules = modules | reject('equalto', 'conftest') | list %}
{# Exclude pytest modules from the generated public API tree. #}
{% set filtered_modules = modules | reject('equalto', 'conftest') | reject('equalto', 'test') | list %}
{% if filtered_modules %}
.. rubric:: Modules

Expand Down
10 changes: 10 additions & 0 deletions docs/_toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ parts:
- file: user_guide/model_overview
- file: user_guide/model_registry
- file: user_guide/how_to_set_up_an_analysis
- file: user_guide/plant_config_reference
- file: user_guide/driver_config_reference
- file: user_guide/technology_config_reference
- file: user_guide/connecting_technologies
- file: user_guide/defining_sites_and_resources
- file: user_guide/populate_tech_yaml
Expand All @@ -26,6 +29,8 @@ parts:
- file: user_guide/plotting_geospatial_data_with_geopandas
- caption: Technology Models
chapters:
- file: technology_models/transport
- file: technology_models/combiners_and_splitters
- file: technology_models/feedstocks
- file: technology_models/marine_hydrokinetic
- file: technology_models/run_of_river
Expand All @@ -34,13 +39,17 @@ parts:
- file: technology_models/natural_gas
- file: technology_models/nuclear
- file: technology_models/wombat_electrolyzer_om
- file: technology_models/pem_electrolyzer
- file: technology_models/floris_wind_plant
- file: technology_models/pvwattsv8_solar_pv.md
- file: technology_models/windpower_wind_plant.md
- file: technology_models/atb_costs_pv.md
- file: technology_models/atb_costs_wind.md
- file: technology_models/co2.md
- file: technology_models/methanol.md
- file: technology_models/ammonia.md
- file: technology_models/nitrogen_asu
- file: technology_models/desalination
- file: technology_models/paper_mill.md
- file: technology_models/saf.md
- file: technology_models/ammonia_synloop_dynamics
Expand All @@ -52,6 +61,7 @@ parts:
- file: technology_models/h2_htse.md
- file: technology_models/iron_ewin.md
- file: technology_models/iron_mine.md
- file: technology_models/iron_transport
- file: technology_models/iron_dri.md
- file: technology_models/steel_eaf.md
- file: technology_models/steel_eaf_cmu.md
Expand Down
2 changes: 2 additions & 0 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,7 @@
storage
transporters
tools
preprocess
postprocess
demand
reliability
35 changes: 33 additions & 2 deletions docs/control/technology_level_control/pyomo_controllers.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,20 @@ The optimized dispatch method is specified by setting the storage control to `O

The optimized dispatch using Pyomo is implemented differently than the heuristic dispatch in order to be able to properly aggregate the individual Pyomo technology models into a cohesive Pyomo plant model for the optimization solver. The Pyomo plant model is from the perspective of the storage technology and is meant to track inflows of commodities and other parameters that might impact the dispatch of the storage from upstream technologies. Practically, this means that the Pyomo elements of the dispatch (including the individual technology models and the plant model) are not exposed to the main H2I code flow, and do not appear in the N2 diagram. The figure below shows a flow diagram of how the dispatch is implemented. The green blocks below represent what is represented in the N2 diagram of the system. The dispatch routine is currently self-contained within the storage technology of the system, though it includes solving an aggregated plant model in the optimization

```{note} Only the PySAM battery performance model can call Pyomo dispatch at this time.
```{note}
Optimized dispatch passes a `pyomo_dispatch_solver` callable to a storage performance model.
`StoragePerformanceBase` provides this interface, so optimized dispatch is not inherently limited
to the PySAM battery. To confirm that a storage model works with optimized dispatch:

- Check that the storage `performance_model` is derived from `StoragePerformanceBase`; the models
listed in the [Storage Models](../../storage/storage_models_index.md) page are valid.
- Check that `tech_to_dispatch_connections` in `plant_config.yaml` lists the storage technology as the
dispatching technology, for example `[battery, battery]`. Only then does the storage model add the
`pyomo_dispatch_solver` input and run with feedback control.
- Check that the controller `commodity` matches the storage model `commodity`.

[Example 30](https://github.com/NatLabRockies/H2Integrate/tree/develop/examples/30_pyomo_optimized_dispatch)
uses `PySAMBatteryPerformanceModel`.
```

![](./figures/Pyomo_dispatch_figure.png)
Expand All @@ -93,7 +106,25 @@ We have exposed the optimization cost (weighting) values to the user in this imp
```

For an example of how to use the optimized Pyomo control framework with the `OptimizedDispatchStorageController`, see
- `examples/30_pyomo_optimized_dispatch`
- [Example 30: Optimized Pyomo Dispatch](https://github.com/NatLabRockies/H2Integrate/tree/develop/examples/30_pyomo_optimized_dispatch)

### Minimum operating-cost rules

The optimized controller creates `PyomoDispatchGenericConverterMinOperatingCosts` blocks for
upstream converter technologies and a `PyomoRuleStorageMinOperatingCosts` block for the dispatching
storage technology. These registered Pyomo rule classes define the per-technology variables and
constraints used by the controller; the optimized controller constructs them internally rather than
requiring them to be added as performance models.

```{eval-rst}
.. autoclass:: h2integrate.control.control_rules.converters.generic_converter_min_operating_cost.PyomoDispatchGenericConverterMinOperatingCosts
:members: initialize_parameters
:no-index:

.. autoclass:: h2integrate.control.control_rules.storage.pyomo_storage_rule_min_operating_cost.PyomoRuleStorageMinOperatingCosts
:members: initialize_parameters
:no-index:
```


This controller only allows one incoming electricity stream and does not apply optimal dispatch of that stream back through the upstream technologies (no feedback). The dispatch can handle more than one generation technology, but the incoming electricity must be combined using an H2I combiner before going to the storage component, and the `cost_per_production`, which is defined in the storage technology section, needs to include the cost of production for all production technologies. This could be done using the following:
Expand Down
5 changes: 5 additions & 0 deletions docs/developer_guide/adding_a_new_technology.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@ from h2integrate.core.model_baseclass import PerformanceModelBaseClass
class SolarPerformanceClass(PerformanceModelBaseClass):
# (min, max) time step lengths (in seconds) compatible with this model
_time_step_bounds = (3600, 3600)
# (min, max) simulation duration (in years) supported by this model.
# Defaults to annual-only (1.0, 1.0) on the baseclass; override to allow
# sub-annual or multi-year simulations.
_simulation_duration_bounds = (1.0, 1.0)
# System-level control classifier; see the control classifier docs.
_control_classifier = "flexible"

Expand Down Expand Up @@ -142,6 +146,7 @@ class SolarPerformanceClass(PerformanceModelBaseClass):
Every model must define the following class attributes. These are typically set on the category baseclass so that all subclasses inherit them, but they can also be set or overridden on individual model classes.

- `_time_step_bounds` (tuple[int, int]): `(min, max)` simulation time-step lengths (in seconds) the model can run at. Use `(3600, 3600)` for hourly-only models and a wider range (e.g. `(300, 3600)`) for models that support sub-hourly time steps. The plant simulation `dt` must lie within every model's bounds.
- `_simulation_duration_bounds` (tuple[float, float]): `(min, max)` simulation durations (in years) the model can run over. Defaults to `(1.0, 1.0)` (annual-only) on the core baseclasses; override it (e.g. `(0.0, float("inf"))`) for models that support sub-annual or multi-year simulations. The total simulated duration (`n_timesteps * dt`) must lie within every model's bounds.

Performance models must also define the following class attributes:
- `commodity` (str), `commodity_rate_units` (str), `commodity_amount_units` (str): set in `initialize()` (or before calling `super().setup()`). These define the commodity produced by the model and the units used for its rate (e.g. `"kW"`, `"kg/h"`) and cumulative amount (e.g. `"kW*h"`, `"kg"`). `PerformanceModelBaseClass.setup()` uses them to register all of the standard outputs and will raise `NotImplementedError` if any are missing.
Expand Down
38 changes: 38 additions & 0 deletions docs/developer_guide/class_structure.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,44 @@ directly) means the baseclass:
- Reads `n_timesteps`, `dt`, `plant_life`, and `fraction_of_year_simulated` from `plant_config`.
- Validates that `commodity`, `commodity_rate_units`, and `commodity_amount_units` are set on the subclass and registers all of the standard production outputs from those attributes.
- Adds the command-value input and uncurtailed output for `flexible` models, and provides the `apply_curtailment()` helper.
- Provides `calculate_annual_cf_and_replacement_schedule()` to project annual capacity factors and a replacement schedule across the plant life from a simulated horizon of any length (optionally accounting for degradation).
- Declares `_time_step_bounds` (min, max in seconds) used to validate the configured simulation time step against the model.
- Declares `_simulation_duration_bounds` (min, max in years, default annual-only) used to validate the configured simulation duration against the model.

Similarly, inheriting from `CostModelBaseClass` (rather than `om.ExplicitComponent`
directly) means the baseclass:
- Declares the standard `driver_config` / `plant_config` / `tech_config` options.
- Reads `n_timesteps`, `dt`, `plant_life`, and `fraction_of_year_simulated` from `plant_config`.
- Registers the standard cost outputs `CapEx`, `OpEx`, and `VarOpEx` consumed by the finance models, along with the discrete `cost_year` output and a `marginal_cost` output used for dispatch decisions.
- Provides `calculate_annual_varopex()` to annualize timestep-level variable operating costs across the plant life.
- Declares `_time_step_bounds` (min, max in seconds) used to validate the configured simulation time step against the model.
- Declares `_simulation_duration_bounds` (min, max in years, default annual-only) used to validate the configured simulation duration against the model.

Annualization uses fixed 365-day plant years. Until framework-wide calendar alignment is
supported, `H2IntegrateModel` rejects site resources configured with `include_leap_day: true`
during configuration loading. Set this resource option to `false` or omit it. Standalone
resource models retain their leap-day processing support. This restriction does not change
the timestamps generated from the simulation's `start_time` and `dt`.

The CF/replacement helper requires finite, one-dimensional performance and SOH arrays
of identical length, positive rated performance and timestep, and a positive integer
plant life. SOH and its EOL threshold are fractions in [0, 1]. CF projection remains
annual: annual means retain their elapsed durations, including a partial final year.
When cycles repeat, their overlaps with plant years are duration-weighted; timestep
performance is not reconstructed or projected.

An upward SOH jump greater than `1e-9` is assumed to mark a replacement at that sample's
elapsed offset. It closes the outgoing unit's cycle, not the new unit's cycle. SOH alone
cannot reveal a first-sample reset or repeated replacements with identical fresh SOH.
The trailing cycle is completed using the last two post-reset samples' SOH loss per
elapsed year. Projected EOL is checked only at the end of each full annual projection
interval; threshold-crossing times are not interpolated. Fewer than two post-reset samples
or stalled degradation do not imply an EOL replacement. The completed history then repeats
as either all cycles or the final cycle, using identical durations for CF and events.
Replacement counts are discrete events in `[year, year + 1)`, never annualized or rounded.
An event on a year boundary belongs to the following year; initial installation and an
event at retirement are excluded. Observed and projected events can share a plant year.
For example, a completed half-year cycle produces counts `[1, 2, 2]` over three years.

### Multiple layers of inheritance

Expand Down
10 changes: 10 additions & 0 deletions docs/intro.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,16 @@ Appropriate time step bounds are included as class attributes when non-hourly ti
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.
```

```{note}
Simulations are no longer restricted to exactly one year. A simulation may cover any
positive duration up to the plant life (a fraction of a year, a single year, or multiple
years), set via `n_timesteps` and `dt` in `plant_config`. Performance, cost, and finance
results are annualized across the plant life using the simulated horizon. Each model
declares the durations it supports with a `_simulation_duration_bounds` class attribute
(min, max in years); the default is annual-only. All models in a given simulation must be
compatible with the specified duration.
```

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).
![A representation of a single technology model in H2Integrate](tech-model.png)

Expand Down
6 changes: 6 additions & 0 deletions docs/misc_resources/changelog.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,8 @@
# Changelog

The release and development history is maintained in the repository's
[CHANGELOG.md](https://github.com/NatLabRockies/H2Integrate/blob/develop/CHANGELOG.md).

```{include} ../../CHANGELOG.md
:start-line: 2
```
4 changes: 4 additions & 0 deletions docs/resource/solar_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@
- "MeteosatPrimeMeridianTMYSolarAPI"
- [OpenMeteo Historical API](#solar_resource:openmeteo_historical)

Some API resource models support multi-year simulations when the requested resource data covers
the configured time range. See the [Resource Model Index](resource_index.md) for details on
multi-year resource handling.

```{note}
Please refer to the [Setting Environment Variables](../getting_started/environment_variables) doc page for information on setting up an NLR API key if you haven't yet.
```
Expand Down
4 changes: 4 additions & 0 deletions docs/resource/wind_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
- "HRRRMETToolkitWindAPI"
- [**"OpenMeteoHistoricalWindResource"**](wind_resource:openmeteo_archive)

Some API resource models support multi-year simulations when the requested resource data covers
the configured time range. See the [Resource Model Index](resource_index.md) for details on
multi-year resource handling.


```{note}
Please refer to the [Setting Environment Variables](../getting_started/environment_variables) doc page for information on setting up an NLR API key if you haven't yet.
Expand Down
31 changes: 31 additions & 0 deletions docs/storage/storage_models_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,22 @@ H2I currently supports the following storage performance models:

The following sections detail the inputs and outputs of the storage performance models.

### Automatic storage sizing

`StorageAutoSizingModel` calculates the storage capacity and charge/discharge rate required to
balance a commodity production profile against demand, then simulates the storage operation. In the
`tech_config.yaml` set
`set_demand_as_avg_commodity_in: true` to use average input as demand, or set it to `false` and
provide `demand_profile`. The model requires either `round_trip_efficiency`, or both
`charge_efficiency` and `discharge_efficiency`. `min_soc_fraction` and `max_soc_fraction` bound the
usable state of charge. It supports one-hour time steps.

```{eval-rst}
.. autoclass:: h2integrate.storage.simple_storage_auto_sizing.StorageSizingModelConfig
:members:
:no-index:
```

```{note}
The inputs and outputs of storage performance models are generalized here for any commodity. If input and output names include the word `commodity`, the actual variable name would be the commodity defined for that storage model. For example, the `PySAMBatteryPerformanceModel` can only be used for the commodity `electricity`. Therefore, the `commodity_in` input to the `PySAMBatteryPerformanceModel` is actually named `electricity_in`.
```
Expand Down Expand Up @@ -61,3 +77,18 @@ The available storage cost models are:
- `SaltCavernStorageCostModel`
- `PipeStorageCostModel`
- `MCHTOLStorageCostModel`

### ATB battery cost model

`ATBBatteryCostModel` uses separate energy and power capital costs from the [NLR Annual Technology
Baseline utility-scale battery ATB](https://atb.nrel.gov/electricity/2024b/utility-scale_battery_storage).
It derives storage duration from capacity and charge rate, calculates
CapEx from the energy and power cost inputs, and calculates annual OpEx as a fraction of CapEx.
`commodity_rate_units` must be a power unit based on watts; the default amount unit is the rate unit
multiplied by hours.

```{eval-rst}
.. autoclass:: h2integrate.storage.battery.atb_battery_cost.ATBBatteryCostConfig
:members:
:no-index:
```
Loading