Nuclear with thermal connection with HTSE - #807
Conversation
…e a unified example
… how it uses electricity when heat is not available.
Nuchtse develop
…optimization and we need a single analysis test value in this case
update naming and units
|
Check out this pull request on See visual diffs & provide feedback on Jupyter Notebooks. Powered by ReviewNB |
bayc
left a comment
There was a problem hiding this comment.
Overall, looks great Jared, thanks for getting this pulled together! Added a couple of non-blocking comments regarding the documentation.
There was a problem hiding this comment.
Would be nice to have the pump and heat exchanger labeled for those not as familiar with the setup.
There was a problem hiding this comment.
Label the heat exchanger? Also, what does the red dashed line represent?
There was a problem hiding this comment.
Done. I also made a note in the doc page that the red dashed arrow represents the heat extraction (upstream of the LPT).
…inks to autogenerated doc pages
johnjasa
left a comment
There was a problem hiding this comment.
Thanks for this great PR, @mikkdm and @jaredthomas68! It's great to see it all come together.
I have some minor suggestions for clarity and cleanup, nothing huge. I'm requesting changes to make sure I come back when the CI is passing for one last go-through!
|
|
||
| ## Performance Model | ||
|
|
||
| Use this model by setting: |
There was a problem hiding this comment.
| Use this model by setting: | |
| To use this model, within your `tech_config.yaml` file set: |
|
|
||
| ### Dispatch and sizing behavior | ||
|
|
||
| Installed size is first inferred from: |
There was a problem hiding this comment.
| Installed size is first inferred from: | |
| Installed size is first calculated as: |
| The model forms: | ||
|
|
||
| $$ | ||
| \text{total\_specific\_energy} = nominal\_heat\_required + nominal\_electricity\_required |
There was a problem hiding this comment.
Specific energy has certain meaning in some contexts, like energy per mass (energy density). Is that the expected meaning here? If not, I'd suggest a different phrase, like total_energy_required or similar for clarity.
| Available heat is used first up to the requested `heat_demand`. The remaining required energy is supplied electrically when possible. Hydrogen production is then limited by the combined energy available and by the turndown threshold. | ||
|
|
||
| ```{note} | ||
| The current implementation is intentionally simple and should be interpreted as a reduced-order plant representation, not a detailed SOEC stack model with thermal transients, degradation coupling, startup dynamics, or detailed balance-of-plant behavior. |
There was a problem hiding this comment.
For those unfamiliar with SOEC, could you please define it somewhere on this page? It's used here and in one diagram
|
|
||
| ## Cost Model | ||
|
|
||
| Use this model by setting: |
There was a problem hiding this comment.
| Use this model by setting: | |
| To use this model, in your `tech_config.yaml` file, set: |
|
|
||
| heat_available_kw = inputs["heat_in"] | ||
| electricity_available_kw = inputs["electricity_in"] | ||
| # hydrogen_demand = inputs["hydrogen_command_value"] |
There was a problem hiding this comment.
| # hydrogen_demand = inputs["hydrogen_command_value"] |
| # heat_demand_kw = hydrogen_out * self.config.nominal_heat_required | ||
| # electricity_demand_kw = hydrogen_out * total_specific_energy - actual_heat_kw | ||
| # electricity_demand_kw = np.minimum(electricity_demand_kw, electricity_available_kw) | ||
| outputs["electricity_consumed"] = electricity_demand_kw | ||
| outputs["hydrogen_out"] = hydrogen_produced | ||
| # outputs["heat_demand"] = np.minimum(rated_hydrogen_production, | ||
| # inputs["hydrogen_command_value"]) * self.config.nominal_heat_required |
There was a problem hiding this comment.
Remove commented out code?
| hydrogen_produced = np.where(hydrogen_produced >= min_turn_down, hydrogen_produced, 0.0) | ||
|
|
||
| # Density of liquid water at 20°C (293.15 K) and 1 atm (101325 Pa) | ||
| PropsSI("D", "T", 293.15, "P", 101325, "Water") |
| self.add_output("high_pressure_heat_demanded", val=0.0, shape=self.n_timesteps, units="kW") | ||
| self.add_output("high_pressure_heat", val=0.0, shape=self.n_timesteps, units="kW") | ||
| self.add_output("low_pressure_heat", val=0.0, shape=self.n_timesteps, units="kW") | ||
| self.add_output("heat_out", val=0.0, shape=self.n_timesteps, units="kW") |
There was a problem hiding this comment.
If you change these units to "MW" we can directly output the arrays instead of manually converting from kW to MW below. I'll note it there too.
| high_pressure_heat_kw = np.full(self.n_timesteps, available_process_heat_mw * 1000.0) | ||
| low_pressure_heat_kw = low_pressure_heat_remaining_mw * 1000.0 | ||
| electricity_out_kw = electricity_out_mw * 1000.0 | ||
| heat_out_kw = heat_out_mw * 1000.0 |
There was a problem hiding this comment.
Can remove these manual conversions between kW and MW with the change noted above; OpenMDAO handles the units conversion.
|
Oh, and I'd suggest @jaredthomas68 to add a high-res version of this figure you have in the Q3 presentation slides to the docs; it's nice!
|
vijay092
left a comment
There was a problem hiding this comment.
Very nice addition, @jaredthomas68! I only looked at nuclear_plant_thermal.py and left some line level comments.
|
|
||
| operating_mode: str = field(validator=contains(["heat", "electricity"])) | ||
| electricity_command_value: float = field(validator=gt_zero) | ||
| high_pressure_electrical_efficiency: float = field(validator=gt_zero) |
There was a problem hiding this comment.
Do we need a <= 1 condition here?
|
|
||
| delivered_electricity_kwh = np.sum(inputs["electricity_out"]) * (self.dt / 3600.0) | ||
| annual_variable_om = variable_om_per_kwh * delivered_electricity_kwh | ||
| outputs["VarOpEx"] = np.full(self.plant_life, annual_variable_om) |
There was a problem hiding this comment.
I'm wondering if like the performance model, we need to use the fraction of the year simulated?
| "rated_capacity", | ||
| val=self.config.rated_capacity, | ||
| units=self.commodity_rate_units, | ||
| desc="Available reactor thermal capacity", |
There was a problem hiding this comment.
Is this the electrical capacity or thermal?
| available_process_heat_mw | ||
| - (electricity_out_mw - high_pressure_electricity_mw) / lp_eff | ||
| ) | ||
| heat_out_mw = np.clip(heat_out_mw, 0.0, available_process_heat_mw) |
There was a problem hiding this comment.
Do we need to use minimum_heat_extract in electricity mode?

Nuclear with thermal connection with HTSE
This pull request adds a nuclear model capable of thermal integration and high temperature steam electrolysis. The models are coupled, but can be used individually and coupled with other components. This PR replaces #761.
Section 1: Type of Contribution
Section 2: Draft PR Checklist
TODO:
Type of Reviewer Feedback Requested (on Draft PR)
Structural feedback:
Implementation feedback:
Other feedback:
Section 3: General PR Checklist
docs/files are up-to-date, or added when necessaryCHANGELOG.md"A complete thought. [PR XYZ]((https://github.com/NatLabRockies/H2Integrate/pull/XYZ)", where
XYZshould be replaced with the actual number.Section 4: Related Issues
Section 5: Impacted Areas of the Software
Section 5.1: New Files
examples/36_nuclear_reactor_htse/driver_config.yamldriver.optimization/design_variables: Adds optimization inputs for the thermal nuclear + HTSE example so reactor dispatch and HTSE sizing can be varied.examples/36_nuclear_reactor_htse/nuclear_reactor_thermal_htse.yamlexamples/36_nuclear_reactor_htse/plant_config.yamltechnology_interconnections/finance_parameters: Adds the plant wiring and finance subgroup definitions for coupled nuclear electricity, process heat, HTSE hydrogen production, and grid sales.examples/36_nuclear_reactor_htse/run_nuclear_reactor_thermal.pyexamples/36_nuclear_reactor_htse/tech_config.yamltechnologies: Adds the thermal nuclear reactor, HTSE, feedstock, demand, and grid component definitions needed to run the integrated example.h2integrate/converters/hydrogen/htse_electrolyzer.pyHTSEPerformanceModel: Adds a simplified HTSE performance model that converts available heat and electricity into hydrogen production, water use, efficiency, and replacement outputs.HTSECostModel: Adds a size-based HTSE cost model that computes CapEx and fixed OpEx from installed electrolyzer capacity.h2integrate/converters/hydrogen/test/test_htse_electrolyzer.pyh2integrate/converters/nuclear/nuclear_plant_thermal.pySimpleThermalNuclearReactorPerformanceModel: Adds a thermal nuclear reactor model that dispatches between electricity generation and process heat delivery.SimpleThermalNuclearReactorCostModel: Adds a reactor cost model that computes capital, fixed operating, and variable operating costs.h2integrate/converters/nuclear/test/test_nuclear_plant_thermal.pySection 5.2: Modified Files
examples/test/test_all_examples.pytest_nuclear_reactor_htse_example: Adds an integration test for the new thermal nuclear + HTSE example to validate annual electricity, hydrogen output, grid exports, and thermal-balance behavior.h2integrate/converters/hydrogen/__init__.pyHTSEPerformanceModelandHTSECostModelto the hydrogen converter package exports.h2integrate/converters/nuclear/__init__.pySimpleThermalNuclearReactorPerformanceModelandSimpleThermalNuclearReactorCostModelto the nuclear converter package exports.h2integrate/core/supported_models.pysupported_models: Registers the new HTSE and thermal nuclear model classes so they can be instantiated from YAML configuration.h2integrate/transporters/pipe.pyPipePerformanceModel.setup: Updates water pipe units fromgalUStogalUS/hso water transport matches the HTSE feedstock rate units.Section 6: Additional Supporting Information
Section 7: Test Results, if applicable
Section 8 (Optional): New Model Checklist
docs/developer_guide/coding_guidelines.mdattrsclass to define theConfigto load in attributes for the modelBaseConfigorCostModelBaseConfiginitialize()method,setup()method,compute()methodCostModelBaseClass__init__.pyfile to ensure it is properly imported and used insupported_models.pysupported_models.pycreate_financial_modelinh2integrate_model.pytest_all_examples.pydocs/user_guide/model_overview.mddocs/section<model_name>.mdis added to the_toc.ymlgenerate_class_hierarchy.pyto update the class hierarchy diagram indocs/developer_guide/class_structure.md