Skip to content

Add a simple nuclear plant performance and cost model#538

Open
johnjasa wants to merge 17 commits intoNatLabRockies:developfrom
johnjasa:simple_nuclear
Open

Add a simple nuclear plant performance and cost model#538
johnjasa wants to merge 17 commits intoNatLabRockies:developfrom
johnjasa:simple_nuclear

Conversation

@johnjasa
Copy link
Collaborator

@johnjasa johnjasa commented Feb 19, 2026

Add a simple nuclear plant performance and cost model

We often get asked if we have a nuclear plant model in H2I.
This PR introduces an extremely simple model based on Quinn et al.

This model only produces electricity and outputs costs based on scaling factors.
It's up to the user to use reasonable cost inputs; there are no defaults assumed here.
Because of this generality, this plant could be seen as LWR or SMR or any other architecture.

Section 1: Type of Contribution

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

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)
  • CHANGELOG.md has been updated to describe the changes made in this PR

Section 3: Related Issues

Section 4: Impacted Areas of the Software

Section 5: Additional Supporting Information

Section 6: Test Results, if applicable

Section 7 (Optional): New Model Checklist

  • Model Structure:
    • Follows established naming conventions outlined in docs/developer_guide/coding_guidelines.md
    • 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 CostModelBaseClass
  • Integration: Model has been properly integrated into H2Integrate
    • Added to supported_models.py
    • [-] If a new commodity_type is added, update create_financial_model in h2integrate_model.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
  • Documentation:
    • Write docstrings using the Google style
    • Model added to the main models list in docs/user_guide/model_overview.md
      • Model documentation page added to the appropriate docs/ section
      • <model_name>.md is added to the _toc.yml

Copy link
Collaborator

@elenya-grant elenya-grant left a comment

Choose a reason for hiding this comment

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

Nice clean PR! Thanks John! I have one question that I'd be interested in your response on (related to the cost model) before approving but it looks good!

Args:
system_capacity_mw (float): Rated electric capacity in MW.
reactor_type (str): Key selecting a reactor type in type_costs.
type_costs (dict): Reactor type cost data with keys:
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why have type_costs as a dict instead of having the keys in type_costs be attributes of NuclearCostModelConfig? I understand the convince of having different costs dictionaries in the tech config so that the costs can be easily changed by just changing the reactor_type.

Alternatives to the imbedded dictionaries would be to use the modify_tech_config function or having a different file set-up where it's easy to switch the cost parameters.

I'm just curious - not necessarily asking for a change.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

You know what, I don't think there's a great reason to have the costs specified differently here. I've made them directly part of the cost config. Thanks for the good question! I think it was just a holdover in logic from me trying out different reactor_types, which should be handled at the config level instead.

# array of arrays containing left-to-right technology
# interconnections; can support bidirectional connections
# with the reverse definition.
# this will naturally grow as we mature the interconnected tech
Copy link
Collaborator

Choose a reason for hiding this comment

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

thanks for cleaning up this example file!

@johnjasa johnjasa added the ready for review This PR is ready for input from folks label Feb 26, 2026
@elenya-grant elenya-grant self-requested a review February 26, 2026 17:55
Copy link
Collaborator

@elenya-grant elenya-grant left a comment

Choose a reason for hiding this comment

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

Thanks for the quick change!

system_capacity_mw (float): Rated electric capacity in MW.
capex_per_kw (float): Capital cost per kW.
fixed_opex_per_kw_year (float): Fixed O&M per kW per year.
variable_opex_per_mwh (float): Variable O&M per MWh.
Copy link
Collaborator

Choose a reason for hiding this comment

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

thanks for updating this!

Copy link
Collaborator

@jaredthomas68 jaredthomas68 left a comment

Choose a reason for hiding this comment

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

Looks pretty good. I just have a few comments to be addressed.

C_{\text{capex}} = (c_{\text{capex}} \cdot (P / P_{\text{ref}})^{(e-1)}) \cdot P
$$

Where $c_{\text{capex}}$ is `capex_per_kw`, $P$ is plant capacity (kW), $P_{\text{ref}}$ is `reference_capacity_mw`, and $e$ is `capex_scaling_exponent`.
Copy link
Collaborator

Choose a reason for hiding this comment

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

I would avoid using $e$ in the equation unless it is Euler's number

Comment on lines 4 to 5
Cost defaults are intended to be populated from literature, such as Quinn et al. (2023) on SMR LWR techno-economic analysis.
See the paper here: https://doi.org/10.1016/j.apenergy.2023.120669.
Copy link
Collaborator

Choose a reason for hiding this comment

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

I suggest using markdown reference here linking to the reference given at the bottom and moving the url to the references section.

+ `'NaturalGasCostModel'`
- `nuclear`: nuclear power plant
- performance models:
+ `'NuclearPerformanceModel'`
Copy link
Collaborator

Choose a reason for hiding this comment

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

Would it make since to delineate this as the QuinnNuclearPerformanceModel or something similar to make room for future nuclear models?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Good idea!

Comment on lines 96 to 102
prob.set_val("system_capacity", system_capacity)
prob.set_val("electricity_out", electricity_out)
prob.run_model()

capex = prob.get_val("CapEx")[0]
opex = prob.get_val("OpEx")[0]
cost_year = prob.get_val("cost_year")
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can you specify units here?


The model limits output by a fixed capacity factor and optional demand profile.

Based on Quinn, J. et al., 2023.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Thanks for including the source in the doc string

capex_scaling_exponent = self.config.capex_scaling_exponent

system_capacity_mw = inputs["system_capacity"]
capacity_kw = system_capacity_mw * 1000
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why use MW in inputs if you later convert to kW? Given that most other techs use kW, it may make more sense. Non-blocking.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Good call, modified!

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.

3 participants