Skip to content

Nonannual framework - #934

Open
jaredthomas68 wants to merge 13 commits into
nonannual-resourcefrom
nonannual-framework
Open

jaredthomas68 wants to merge 13 commits into
nonannual-resourcefrom
nonannual-framework

Conversation

@jaredthomas68

@jaredthomas68 jaredthomas68 commented Oct 14, 2026 •

Copy link
Copy Markdown
Collaborator

Add core framework support for non-annual simulation durations

Allow plant configurations to specify a positive simulation duration up to the configured plant
life, rather than requiring exactly one year. Duration is determined by n_timesteps * dt;
framework duration checks and annualization express that duration in nominal 8760-hour years
(31,536,000 seconds). This convention does not define resource calendar-year length or change
the include_leap_day setting, which controls leap-day handling in resource data.

Models declare supported simulation durations through _simulation_duration_bounds, a
(minimum_years, maximum_years) class attribute. The default remains (1.0, 1.0), so existing
models remain annual-only unless explicitly validated and opted in. A model that supports
arbitrary positive durations can declare:

_simulation_duration_bounds = (0.0, float("inf"))

Model assembly checks duration compatibility alongside timestep compatibility and reports an
error identifying a model whose bounds exclude the requested horizon. Performance, cost, and
control base classes declare annual-only defaults.

Add shared annualization helpers for capacity factors, degradation-aware replacement schedules,
and variable operating costs. The helpers account for partial simulated years and predict results
across plant life using explicit extrapolation strategies. ProFAST converts rated production from
its actual rate units into daily capacity and annual commodity production, avoiding hard-coded
hourly conversion assumptions and duplicate conversion in the NPV model.

This is PR #934, the second PR in the
nonannual-resource -> nonannual-framework -> nonannual stack. It depends on
PR #933 and is reviewed relative to
nonannual-resource. Resource resampling is implemented in #933; concrete model opt-ins and
the non-annual solar/battery/grid example belong to the subsequent nonannual PR.

Section 1: Type of Contribution

  • Feature Enhancement
    • Framework
    • New Model
    • Updated Model
    • Tools/Utilities
    • Other (please describe): N/A
  • Bug Fix
  • Documentation Update
  • CI Changes
  • Other (please describe): N/A

Section 2: Draft PR Checklist

  • Open draft PR
  • Describe the feature that will be added
  • Fill out TODO list steps
  • Describe requested feedback from reviewers on draft PR
  • [-] Complete Section 8: New Model Checklist (if applicable): N/A; no new model.

TODO:

  • Step 1: Implement duration validation, annualization helpers, finance conversions, and tests.
  • Step 2: Verify framework and finance tests and a fresh docs build on this branch.

Type of Reviewer Feedback Requested (on Draft PR)

Structural feedback: Annual-only defaults with explicit model opt-in, duration checks during
model assembly, and separation of framework support from individual model adaptations.

Implementation feedback: Partial-year annualization, lifetime extrapolation, SOH cycle and
replacement handling, variable operating costs, and unit-aware ProFAST capacity/production inputs.

Other feedback: Clarity of model-author guidance and unsupported-duration errors.

Section 3: General PR Checklist

  • PR description thoroughly describes the new feature, bug fix, etc.
  • Added tests for new functionality or bug fixes
  • Tests pass (If not, and this is expected, please elaborate in the Section 6: Test Results)
  • Documentation
    • Docstrings are up-to-date
    • Related docs/ files are up-to-date, or added when necessary
    • Documentation has been rebuilt successfully
    • [-] Examples have been updated (if applicable): N/A; concrete examples are in the next PR.
  • CHANGELOG.md
    • At least one complete sentence has been provided to describe the changes made in this PR
    • After the above, a hyperlink has been provided to the PR using the following format:
      "A complete thought. [PR XYZ]((https://github.com/NatLabRockies/H2Integrate/pull/XYZ)", where
      XYZ should be replaced with the actual number.

New-model checklist: N/A. The checked test item refers to the focused framework and finance
tests reported in Section 7, not the full repository suite. Documentation verification is
reported complete by the author; the earlier assistant-launched fresh build has no recorded
final result. Remote checks have not been verified.

Section 4: Related Issues

Related to #890. This PR provides
simulation duration validation.

Related to #636. This PR provides
simulation duration validation.

Related to #204. This PR provides
framework changes to pave the way for variable simulation duration.

Related to #733. This PR provides unit
conversions for financial modeling using rate and amount units provided by the input config.

Section 5: Impacted Areas of the Software

Section 5.1: New Files

  • h2integrate/core/test/test_model_baseclasses.py: Test annual capacity factors, extrapolation
    modes, degradation/replacement cycles, helper behavior, and invalid inputs.

Section 5.2: Modified Files

  • h2integrate/core/inputs/validation.py: Accept positive non-annual horizons up to plant life
    and reject non-positive or over-life horizons.
  • h2integrate/core/model_checks.py: Add check_model_simulation_duration with annual-only
    fallback bounds and model-specific compatibility errors.
  • h2integrate/core/h2integrate_model.py: Check duration compatibility when assembling models,
    including feedstock sources, combined models, controllers, and transporters.
  • h2integrate/core/model_baseclass.py: Add duration bounds and shared helpers to annualize
    capacity factors, degradation/replacements, and variable operating costs across plant life.
  • h2integrate/control/control_strategies/openloop_control_baseclass.py: Declare annual-only
    duration bounds for open-loop control models.
  • h2integrate/control/control_strategies/pyomo_storage_controller_baseclass.py: Declare
    annual-only duration bounds for Pyomo storage controllers.
  • h2integrate/control/control_rules/pyomo_rule_baseclass.py: Declare annual-only duration
    bounds for Pyomo control rules.
  • h2integrate/finances/profast_baseclass.py: Retain simulation timestep/count and convert
    production rates to ProFAST daily capacity and annual commodity amounts using their units.
  • h2integrate/finances/profast_npv.py: Use the shared production conversion instead of
    separately adjusting rated-production units in the NPV calculation.
  • h2integrate/core/test/test_framework.py: Test accepted horizons, invalid horizons,
    annual-only defaults, and explicitly declared model duration bounds.
  • h2integrate/finances/test/test_finances.py: Add finance coverage for sub-annual horizons.
  • h2integrate/finances/test/test_profast_lco.py: Add ProFAST levelized-cost duration coverage.
  • h2integrate/finances/test/test_profast_npv.py: Add NPV coverage for non-annual horizons and
    production-rate unit conversion.

Section 6: Additional Supporting Information

  • Compatibility: Accepting a non-annual plant configuration does not mean every model supports
    that duration. All models used in the simulation must satisfy their own duration bounds;
    models without declared bounds default to annual-only.
  • Annualization: calculate_annual_cf_and_replacement_schedule supports tile,
    final_sim_value, and average_sim_value extrapolation without degradation. With SOH data,
    it detects end-of-life/reset cycles, extrapolates an incomplete trailing cycle, and supports
    repeating all cycles or the final cycle across plant life.
  • Costs: calculate_annual_varopex separately annualizes variable operating costs, including
    partial-year segments, using the same named extrapolation strategies.
  • Time convention: The framework uses nominal 8760-hour years for duration bounds and
    annualization, not as a claim that every resource calendar year has 8760 hours. Resource
    calendar-year lengths and leap-day inclusion are handled separately by the resource layer.
  • Scope: Shared helpers provide the framework contract; concrete models must be checked and
    adapted before declaring wider bounds. Those model updates are deferred to the next PR.

Section 7: Test Results, if applicable

Focused framework and finance tests on nonannual-framework: 84 passed, 108 subtests passed.

python -m pytest h2integrate/core/test/test_model_baseclasses.py \
  h2integrate/core/test/test_framework.py \
  h2integrate/finances/test/test_finances.py \
  h2integrate/finances/test/test_profast_lco.py \
  h2integrate/finances/test/test_profast_npv.py --disable-warnings -q --tb=short

Ruff, import-order checks, and git diff --check passed for the framework PR changes.

The documentation-consistency test returned 2 passed, 1 failed: the local example-directory
scan found 09_wind_farm_layout_hybrid and 98_battery_degradation outside its expected
documentation-reference allowlist. This result is separate from the passing framework/finance
tests; a clean-checkout comparison has not yet been run.

Documentation verification is reported complete by the author. A fresh assistant-launched build
was also started, but its final result was not captured, so no independent passing result is
claimed for that run. Full repository CI and remote documentation checks remain unverified.

Section 8 (Optional): New Model Checklist

N/A; no new model is introduced. The checklist is retained as required by the template.

  • Model Structure:
    • Follows established naming conventions outlined in docs/developer_guide/coding_guidelines.md
    • Configuration class is named {ModelName}Config, where {ModelName} is the complete model class name registered with @register
    • Used attrs class to define the Config to load in attributes for the model
      • If applicable: inherit from BaseConfig or CostModelBaseConfig
    • Added: initialize() method, setup() method, compute() method
      • If applicable: inherit from PerformanceModelBaseClass and CostModelBaseClass
  • Integration: Model has been properly integrated into H2Integrate
    • Add the new model to the appropriate __init__.py file so it can be imported from its subpackage
    • Decorated the model class with @register from h2integrate/core/supported_models.py
  • Tests: Unit tests have been added for the new model
    • Pytest-style unit tests
    • Unit tests are in a "test" folder within the folder a new model was added to
    • If applicable, add integration tests
  • Example: If applicable, a working example demonstrating the new model has been created
    • Input file comments
    • Run file comments
    • Example has been tested and runs successfully in test_all_examples.py
  • Documentation:
    • Write docstrings using the Google style
    • Model documentation page added to the appropriate docs/ section
      • Path to model documentation page is added to the _toc.yml
    • Update the class hierarchy diagram docs/_static/class_hierarchy.html by doing one of the below options:
      • Navigate to docs folder and run sh build_book.sh
      • Run generate_class_hierarchy.py to update the class hierarchy diagram in docs/developer_guide/class_structure.md. You may need to delete the _autosummary folder before doing this.

@jaredthomas68
jaredthomas68 added this pull request to stack #935 October 14, 2026 15:48
@jaredthomas68
jaredthomas68 marked this pull request as ready for review October 14, 2026 18:17
@jaredthomas68 jaredthomas68 mentioned this pull request Oct 14, 2026
22 of 53 tasks
@jaredthomas68 jaredthomas68 self-assigned this Oct 14, 2026
@jaredthomas68 jaredthomas68 added the ready for review This PR is ready for input from folks label Oct 14, 2026
@coveralls

coveralls commented Oct 14, 2026 •

Copy link
Copy Markdown

Coverage Status

Coverage is 90.596% — nonannual-framework into nonannual-resource. No base build found for nonannual-resource.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready for review This PR is ready for input from folks

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants