Skip to content

Standardized Performance Outputs: Generic Storage & Control#493

Merged
johnjasa merged 88 commits intoNatLabRockies:developfrom
elenya-grant:converter_baseclass_storage
Feb 13, 2026
Merged

Standardized Performance Outputs: Generic Storage & Control#493
johnjasa merged 88 commits intoNatLabRockies:developfrom
elenya-grant:converter_baseclass_storage

Conversation

@elenya-grant
Copy link
Copy Markdown
Collaborator

@elenya-grant elenya-grant commented Feb 3, 2026

Standardized Performance Outputs: Generic Storage & Control

This PR is a follow-on to PR #463 and should not be merged in until after #463 is.

This PR updates the generic storage performance models and the control strategies so storage models have the same outputs as converters. There are 3 storage performance models:

  • h2integrate/storage/battery/pysam_battery.py: This was updated in PR Standardize performance model outputs #463
  • h2integrate/storage/simple_generic_storage.py: basically did nothing
  • h2integrate/storage/simple_storage_auto_sizing.py: calculates storage capacity and charge rate needed to meet a demand.

To be consistent with the pysam_battery model, the generic storage performance models needed to output the standardized outputs (like capacity factor, rated production, etc) but these generic performance models did very minimal calculations and did not output commodity_out, most calculations were done in the controllers, not the performance models.

Basically, the control strategies have been updated to output commodity_set_point instead of commodity_out. commodity_set_point is what the controller tells the performance model is the "target" commodity_out. Since the performance model may not be able to achieve the commodity_set_point (due to a variety of possible differences in the control model and the performance model, like losses), the performance model output commodity_out may not equal the commodity_set_point. The commodity_set_point is the input to the storage performance models, which now output commodity_out, annual_commodity_production, capacity_factor, etc. The computations now done in some of the storage performance models may be somewhat duplicative of the performance of some controllers, but this ensures that the storage controllers are compatible with any storage performance model.

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 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 7: New Model Checklist (if applicable)

TODO:

  • Change commodity_name to commodity for the generic storage performance models and control strategies
  • Change commodity_units to commodity_rate_units for the generic storage performance models and control strategies

Type of Reviewer Feedback Requested (on Draft PR)

Structural feedback:

Implementation feedback:

  • Thoughts on naming convention, specifically the use of commodity_set_point?
    Other feedback:

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

This partially resolves some of Issue #486, and would resolve a step in Issue #485

New issues made: #498, #521

Section 4: Impacted Areas of the Software

Section 4.1: New Files

N/A

Section 4.2: Modified Files

  • h2integrate/storage/simple_generic_storage.py: added commodity_set_point as an input, updated compute() method to better reflect performance of a "pass through" type storage component (this storage model has no inputs related to capacity, which is why the compute() method basically now acts like a pass-through component)
  • h2integrate/storage/simple_storage_auto_sizing.py: added commodity_set_point as an input, updated compute() method to better reflect simple performance of a storage component
  • h2integrate/control/control_strategies/demand_openloop_controller.py: changed output variable name commodity_out to commodity_set_point
  • h2integrate/control/control_strategies/passthrough_openloop_controller.py: changed output variable name commodity_out to commodity_set_point
  • h2integrate/postprocess/test/test_sql_timeseries_to_csv.py: updated subtest value, see justification in Section 6
  • pytest examples/test/test_all_examples.py::test_ammonia_synloop_example: added subtest for annual ammonia production and updated 3 subtest values that changed, see justification in Section 6.

Section 5: Additional Supporting Information

Section 6: Test Results, if applicable

  • h2integrate/postprocess/test/test_sql_timeseries_to_csv.py::test_save_csv_all_results: subtest for number of columns increased from 35 to 36 because of the new output commodity_set_point.

Example 1, 2 and 12 use the "StorageAutoSizingModel", which was changed to actually model the performance of the storage (a outputs a smoothed out profile of the input commodity profile) which was added in lines 128-the end of h2integrate/storage/simple_storage_auto_sizing.py. The performance of models downstream of the hydrogen system in Example 1 and 2 will not change if the hydrogen production profile changes in shape, only if the total hydrogen produced changes (which it doesn't because the total hydrogen output from the "StorageAutoSizingModel" does not change because the storage is sized such that no hydrogen is curtailed.). Example 12 uses the ammonia synloop model, which is the only model that has performance dependent on the hydrogen production profile, which has changed given the addition of the actual storage performance now simulated in the "StorageAutoSizingModel". This is why values changed in tests that test outputs from the ammonia model in Example 12 and why no test values changed in Example 1 or 2. Below is a more in-depth explanation of why the test values changed in Example 12:

  • pytest examples/test/test_all_examples.py::test_ammonia_synloop_example: subtests for ammonia OpEx, total adjusted OpEx for ammonia finance subgroup, and the LCOA failed, the test values decreased.
    • The OpEx values decreased because the variable OpEx portion of the OpEx in the ammonia synloop decreased. These costs are multipliers of the annual ammonia production. The variable opex is equal to cat_opex + h2o_opex - o2_opex, all of these are multipliers of the annual ammonia produced. All these opex values increased, but the o2_opex increased much more than the increase in cat_opex and h2o_opex combined, meaning a net reduction in OpEx.
    • The annual ammonia production increased from 364,095 t/year to 406,333 t/year.
    • The max_hydrogen_capacity of the ammonia_synloop model is 10589.36 kg/h, this value has not changed.
    • The maximum hydrogen_in to the ammonia_synloop model decreased from 12549 kg/h to 9306 kg/h. This indicates that now more hydrogen is being used to make ammonia rather than being "curtailed". The total hydrogen consumed is the same as before (meaning the total hydrogen_in is the same), but the hydrogen_out of the ammonia model decreased (likely because more hydrogen is being used)
    • Previously, the limiting factor on ammonia production was primarily the ammonia plant capacity
      • capacity limited: 4863 hrs
      • hydrogen limited: 3716 hrs
      • nitrogen limited: 181
    • Now, the limiting factor on ammonia production is always hydrogen. This is because the hydrogen output is more constant because it comes from the hydrogen storage output rather than the pass-through controller output. The pass through controller hydrogen set point is equal to the hydrogen produced from the electrolyzer. Aka - beforehand the hydrogen into the ammonia model was equal to the hydrogen out of the electrolyzer. Now that the hydrogen storage auto-sizing performance model actually represents the smoothing effect that the hydrogen storage has on the hydrogen production profile, the hydrogen into the ammonia synloop has less variability and is more constant, making hydrogen the limiting factor more often which means more hydrogen is consumed because when hydrogen is not the limiting factor, hydrogen is curtailed.
    • The test values would not likely have changed if we used the "generic_storage_model" as the performance model since this acts like the pass-through controller. This would make the hydrogen into the ammonia model the same as before (equal to the hydrogen out of the electrolyzer model). But - we would have to set the storage capacity and charge rate so that the hydrogen storage cost is the same as before.
    • Overall, the LCOA decreased because the OpEx costs decreased and the ammonia production increased.

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
  • 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 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

elenya-grant and others added 30 commits January 20, 2026 15:27
Copy link
Copy Markdown
Collaborator

@johnjasa johnjasa 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 this, Elenya! I think it's a great step in standardizing some of these storage and control models -- there's a lot of complex interactions here.

I've left some comments and pushed up minor changes. The comments are mostly questions to provoke introspection into the approaches used here or minor suggestions for clarity, nothing too major.

Comment on lines +43 to +44
# Estimate the rated commodity production as the maximum value from the commodity_set_point
outputs[f"rated_{self.commodity}_production"] = inputs[f"{self.commodity}_set_point"].max()
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Is this the best way of getting the rated production? If the set point never reaches the actual maximum of the system, is this a problem and could lead to confusing metrics? Legitimately asking, mostly about the generality of this and if it works well in all cases.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Idk a better way - but this is just a pass-through component. In this case, it'd be better for this to be done in the DemandOpenLoopStorageController instead, but then we wouldn't be consistent about whether these outputs are standardized in performance models vs control strategies which would lead to errors if someone used a controller with the standard outputs with a storage model that has the standard outputs. I think issue #498 explains this though.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I don't think we should determine rated production this way. I think both the storage and the controller need to require the technology rated production like we do with other systems. For storage, the charge and discharge rates and the rated storage capacity should be given at a minimum I think now that we are using the set_point paradigm, even for the generic pass-through component.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Does this mean you want the SimpleGenericStorage then to have inputs for charge rate and capacity?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Yeah, I agree with Jared on this that it makes sense for the controller and performance models to have access to this information. Even if we don't use it, it would need to be here to standardize across models that do use it.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

okay - I added max_charge_rate as an input to the SimpleGenericStorage model and used the max_charge_rate to calculate the capacity factor and rated commodity produced

Comment on lines +46 to +57
# Calculate the total and annual commodity produced
outputs[f"total_{self.commodity}_produced"] = outputs[f"{self.commodity}_out"].sum()
outputs[f"annual_{self.commodity}_produced"] = outputs[
f"total_{self.commodity}_produced"
] * (1 / self.fraction_of_year_simulated)

# Calculate the maximum commodity production over the simulation
max_production = (
inputs[f"{self.commodity}_set_point"].max() * self.n_timesteps * (self.dt / 3600)
)

outputs["capacity_factor"] = outputs[f"total_{self.commodity}_produced"] / max_production
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Tagging @jaredthomas68 and @genevievestarke for special attention to this math to make sure it matches what you expect.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The capacity factor should be based on rated production in the denominator, not max production. Rated production should be a user input to the system (see previous comment).

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Capacity factors for storage systems (at least batteries), as far as I understand, are typically based on the nameplate discharge rate of the system.

cp = total_product_discharged_in_time_period / (name_plate_discharge_rate*time_period_duration) # of course make sure units make sense

Of course, the above leads to some fairly low capacity factors, like about 0.167 for a 4 hour battery discharged daily as discussed in the ATB

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

@jaredthomas68 - the SimpleGenericStorage has no inputs related to nameplate discharge rate or anything. That information is only available in the DemandOpenLoopStorageController. This is captured in Issue #498.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

this has been updated - please see the new equations to calculate rated_commodity_production and capacity_factor in the SimpleGenericStorage

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I like this change, thank you.

Comment thread h2integrate/storage/simple_storage_auto_sizing.py Outdated
Comment on lines +78 to +80
self.commodity = self.config.commodity_name
self.commodity_rate_units = self.config.commodity_units
self.commodity_amount_units = f"({self.commodity_rate_units})*h"
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Move this to initialize to match other components?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

the tech config is not accessbile at the initialize(). I get this error:
RuntimeError: StorageAutoSizingModel: Option 'tech_config' is required but has not been set.

Comment on lines +28 to +30
self.commodity = self.config.commodity_name
self.commodity_rate_units = self.config.commodity_units
self.commodity_amount_units = f"({self.commodity_rate_units})*h"
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Move to initialize to match other components?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

the tech config is not accessbile at the initialize(). I get this error:
RuntimeError: StorageAutoSizingModel: Option 'tech_config' is required but has not been set.

Comment thread h2integrate/storage/simple_generic_storage.py Outdated
Comment on lines +161 to +197
# Step 2: Simulate the storage performance based on the sizes calculated

# Initialize output arrays of charge and discharge
discharge_storage = np.zeros(self.n_timesteps)
charge_storage = np.zeros(self.n_timesteps)
output_array = np.zeros(self.n_timesteps)

# Initialize state-of-charge value as the soc at t=0
soc = deepcopy(commodity_storage_soc[0])

# Simulate a basic storage component
for t, demand_t in enumerate(commodity_demand):
input_flow = commodity_production[t]
available_charge = float(commodity_storage_capacity_kg - soc)
available_discharge = float(soc)

# If demand is greater than the input, discharge storage
if demand_t > input_flow:
# Discharge storage to meet demand.
discharge_needed = demand_t - input_flow
discharge = min(discharge_needed, available_discharge, storage_max_fill_rate)
# Update SOC
soc -= discharge

discharge_storage[t] = discharge
output_array[t] = input_flow + discharge

# If input is greater than the demand, charge storage
else:
# Charge storage with unused input
unused_input = input_flow - demand_t
charge = min(unused_input, available_charge, storage_max_fill_rate)
# Update SOC
soc += charge

charge_storage[t] = charge
output_array[t] = demand_t
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The overall reason for this code logic to be necessary here is unclear to me. Is it because this component is only meant to be used with the passthrough controller, so we need to actually calculate these charge and discharge timeseries somewhere because it's not done in the controller, and that's here?

This is the biggest question I have where chatting about it might be useful (I just messaged you).

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

If the dispatch logic is needed, perhaps the sizing should be part of the controller instead of the storage model? @jmartin4u

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

See issue #498. This PR is not intended to fix the discrepancies between the storage control strategies and the storage performance models, but it sure makes those discrepancies more apparent. I agree that this could be refactored in the future but this PR intended to just make consistent outputs from performance models.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Got it, thanks for tagging the issue

Copy link
Copy Markdown
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.

Heading in the right direction. We need to correct some math and inputs and also discuss what lives where.

Comment thread h2integrate/storage/simple_generic_storage.py Outdated
Comment on lines +43 to +44
# Estimate the rated commodity production as the maximum value from the commodity_set_point
outputs[f"rated_{self.commodity}_production"] = inputs[f"{self.commodity}_set_point"].max()
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I don't think we should determine rated production this way. I think both the storage and the controller need to require the technology rated production like we do with other systems. For storage, the charge and discharge rates and the rated storage capacity should be given at a minimum I think now that we are using the set_point paradigm, even for the generic pass-through component.

outputs[f"total_{self.commodity}_produced"] = outputs[f"{self.commodity}_out"].sum()
outputs[f"annual_{self.commodity}_produced"] = outputs[
f"total_{self.commodity}_produced"
] * (1 / self.fraction_of_year_simulated)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think just dividing will be more clear

Suggested change
] * (1 / self.fraction_of_year_simulated)
]/self.fraction_of_year_simulated

Comment on lines +46 to +57
# Calculate the total and annual commodity produced
outputs[f"total_{self.commodity}_produced"] = outputs[f"{self.commodity}_out"].sum()
outputs[f"annual_{self.commodity}_produced"] = outputs[
f"total_{self.commodity}_produced"
] * (1 / self.fraction_of_year_simulated)

# Calculate the maximum commodity production over the simulation
max_production = (
inputs[f"{self.commodity}_set_point"].max() * self.n_timesteps * (self.dt / 3600)
)

outputs["capacity_factor"] = outputs[f"total_{self.commodity}_produced"] / max_production
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The capacity factor should be based on rated production in the denominator, not max production. Rated production should be a user input to the system (see previous comment).

Comment on lines +53 to +55
max_production = (
inputs[f"{self.commodity}_set_point"].max() * self.n_timesteps * (self.dt / 3600)
)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

We should calculate the maximum theoretical production based on name-plate capacity based on the user inputs (see comment above), not the actual in the given scenario as is being done here.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Also see comment below

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

done

Comment on lines +46 to +57
# Calculate the total and annual commodity produced
outputs[f"total_{self.commodity}_produced"] = outputs[f"{self.commodity}_out"].sum()
outputs[f"annual_{self.commodity}_produced"] = outputs[
f"total_{self.commodity}_produced"
] * (1 / self.fraction_of_year_simulated)

# Calculate the maximum commodity production over the simulation
max_production = (
inputs[f"{self.commodity}_set_point"].max() * self.n_timesteps * (self.dt / 3600)
)

outputs["capacity_factor"] = outputs[f"total_{self.commodity}_produced"] / max_production
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Capacity factors for storage systems (at least batteries), as far as I understand, are typically based on the nameplate discharge rate of the system.

cp = total_product_discharged_in_time_period / (name_plate_discharge_rate*time_period_duration) # of course make sure units make sense

Of course, the above leads to some fairly low capacity factors, like about 0.167 for a 4 hour battery discharged daily as discussed in the ATB

Comment on lines +161 to +197
# Step 2: Simulate the storage performance based on the sizes calculated

# Initialize output arrays of charge and discharge
discharge_storage = np.zeros(self.n_timesteps)
charge_storage = np.zeros(self.n_timesteps)
output_array = np.zeros(self.n_timesteps)

# Initialize state-of-charge value as the soc at t=0
soc = deepcopy(commodity_storage_soc[0])

# Simulate a basic storage component
for t, demand_t in enumerate(commodity_demand):
input_flow = commodity_production[t]
available_charge = float(commodity_storage_capacity_kg - soc)
available_discharge = float(soc)

# If demand is greater than the input, discharge storage
if demand_t > input_flow:
# Discharge storage to meet demand.
discharge_needed = demand_t - input_flow
discharge = min(discharge_needed, available_discharge, storage_max_fill_rate)
# Update SOC
soc -= discharge

discharge_storage[t] = discharge
output_array[t] = input_flow + discharge

# If input is greater than the demand, charge storage
else:
# Charge storage with unused input
unused_input = input_flow - demand_t
charge = min(unused_input, available_charge, storage_max_fill_rate)
# Update SOC
soc += charge

charge_storage[t] = charge
output_array[t] = demand_t
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

If the dispatch logic is needed, perhaps the sizing should be part of the controller instead of the storage model? @jmartin4u

model.prob.get_val("finance_subgroup_nh3.total_opex_adjusted")[0], rel=1e-6
)
== 79744581.00552343
== 79421959.33317558
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I don't think the test values should change for these code changes. Maybe you can convince me though. As I see it all the changes should be more a matter of where calculations are done and what components connect to what rather than changing any actual calculations.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Section 6 of the PR description has a very thorough explanation of why the values changed.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

That is a very thorough explanation, I'm sold. Thank you.

Copy link
Copy Markdown
Collaborator

@genevievestarke genevievestarke left a comment

Choose a reason for hiding this comment

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

Overall, I think this looks really great, and I think that this is a great addition to the code!
The two blocking comments I have are

  1. I think we should add charge/discharge rate and total storage capacity as inputs to the generic storage model
  2. I think we need to distinguish between input power and dispatched power in the generic storage model. I think it's currently total commodity out instead of just storage commodity out. We may have to add storage commodity out as an output from the demand open loop controller to differentiate these since the generic storage doesn't have that info.

Comment on lines +43 to +44
# Estimate the rated commodity production as the maximum value from the commodity_set_point
outputs[f"rated_{self.commodity}_production"] = inputs[f"{self.commodity}_set_point"].max()
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Yeah, I agree with Jared on this that it makes sense for the controller and performance models to have access to this information. Even if we don't use it, it would need to be here to standardize across models that do use it.

outputs[f"rated_{self.commodity}_production"] = inputs[f"{self.commodity}_set_point"].max()

# Calculate the total and annual commodity produced
outputs[f"total_{self.commodity}_produced"] = outputs[f"{self.commodity}_out"].sum()
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think this should be just the discharge:
[x for x in outputs[f"{self.commodity}_out"] if x > 0]

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Then I think the rest of the calculations would work

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Also, is outputs[f"{self.commodity}_out"] just the battery power, or the battery power + the incoming power?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

The DemandOpenLoopStorageController outputs commodity_set_point which is equal to commodity_set_point = incoming power - power_to_charge + power_discharged - excess_power

Since the SimpleGenericStorage is essentially a pass-through component, where commodity_out = commodity_set_point.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Ok, I think my previous comment is addressed. Based on this CF calculation, I thought this should take only the positive values, but because this is total energy and not just the storage energy, it's always positive. I think this is fine for now, but should be addressed with #521

Copy link
Copy Markdown
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 good. My main concerns have been addressed. I did leave a few non-blocking comments and suggestions.

commodity (str):
The name of the commodity being stored (e.g., "electricity", "hydrogen").
commodity_rate_units (str):
The units of the commodity being stored (e.g., "kW", "kg").
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Minor doc correction:

Suggested change
The units of the commodity being stored (e.g., "kW", "kg").
The flow rate units of the commodity being stored (e.g., "kW", "kg/h").

Comment on lines +817 to +824
cost_per_production (float):
The cost to use the incoming produced commodity (in $/commodity_rate_units).
cost_per_charge (float):
The cost per unit of charging the storage (in $/commodity_rate_units).
cost_per_discharge (float):
The cost per unit of discharging the storage (in $/commodity_rate_units).
commodity_met_value (float):
The penalty for not meeting the desired load demand (in $/commodity_rate_units).
Copy link
Copy Markdown
Collaborator

@jaredthomas68 jaredthomas68 Feb 12, 2026

Choose a reason for hiding this comment

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

These don't appear to all be used, and I'm not sure the ratings make sense. I think this should possibly be addressed in a future PR, but tagging here to keep connected. Related to #527

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I agree about the units, but I believe they are all used in the dispatch. We can chat about where they're being used, if that's helpful!

@@ -51,7 +60,7 @@ def compute(self, inputs, outputs):

# Calculate the maximum commodity production over the simulation
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Comment should be updated to state that it is the max theoretical production

Comment on lines +46 to +57
# Calculate the total and annual commodity produced
outputs[f"total_{self.commodity}_produced"] = outputs[f"{self.commodity}_out"].sum()
outputs[f"annual_{self.commodity}_produced"] = outputs[
f"total_{self.commodity}_produced"
] * (1 / self.fraction_of_year_simulated)

# Calculate the maximum commodity production over the simulation
max_production = (
inputs[f"{self.commodity}_set_point"].max() * self.n_timesteps * (self.dt / 3600)
)

outputs["capacity_factor"] = outputs[f"total_{self.commodity}_produced"] / max_production
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I like this change, thank you.

Comment on lines +161 to +197
# Step 2: Simulate the storage performance based on the sizes calculated

# Initialize output arrays of charge and discharge
discharge_storage = np.zeros(self.n_timesteps)
charge_storage = np.zeros(self.n_timesteps)
output_array = np.zeros(self.n_timesteps)

# Initialize state-of-charge value as the soc at t=0
soc = deepcopy(commodity_storage_soc[0])

# Simulate a basic storage component
for t, demand_t in enumerate(commodity_demand):
input_flow = commodity_production[t]
available_charge = float(commodity_storage_capacity_kg - soc)
available_discharge = float(soc)

# If demand is greater than the input, discharge storage
if demand_t > input_flow:
# Discharge storage to meet demand.
discharge_needed = demand_t - input_flow
discharge = min(discharge_needed, available_discharge, storage_max_fill_rate)
# Update SOC
soc -= discharge

discharge_storage[t] = discharge
output_array[t] = input_flow + discharge

# If input is greater than the demand, charge storage
else:
# Charge storage with unused input
unused_input = input_flow - demand_t
charge = min(unused_input, available_charge, storage_max_fill_rate)
# Update SOC
soc += charge

charge_storage[t] = charge
output_array[t] = demand_t
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Got it, thanks for tagging the issue

Comment thread CHANGELOG.md
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

It looks like you need to backmerge from develop

@@ -51,7 +60,7 @@ def compute(self, inputs, outputs):

# Calculate the maximum commodity production over the simulation
max_production = (
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
max_production = (
rated_production = (

Copy link
Copy Markdown
Collaborator

@genevievestarke genevievestarke left a comment

Choose a reason for hiding this comment

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

This looks good to me! I suggested on change in the comments to clarify what's being done. Otherwise, good to merge!

# Pass the commodity_out as the commodity_set_point
outputs[f"{self.commodity}_out"] = inputs[f"{self.commodity}_set_point"]

# Estimate the rated commodity production as the maximum value from the commodity_set_point
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
# Estimate the rated commodity production as the maximum value from the commodity_set_point
# Set the rated commodity production from the max_charg_rate input

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

done!

outputs[f"rated_{self.commodity}_production"] = inputs[f"{self.commodity}_set_point"].max()

# Calculate the total and annual commodity produced
outputs[f"total_{self.commodity}_produced"] = outputs[f"{self.commodity}_out"].sum()
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Ok, I think my previous comment is addressed. Based on this CF calculation, I thought this should take only the positive values, but because this is total energy and not just the storage energy, it's always positive. I think this is fine for now, but should be addressed with #521

Copy link
Copy Markdown
Collaborator

@johnjasa johnjasa left a comment

Choose a reason for hiding this comment

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

Fantastic PR, Elenya, and thank you all for iterating on best approaches until you reached consensus. Love seeing that.

@johnjasa johnjasa merged commit 7be893a into NatLabRockies:develop Feb 13, 2026
5 checks passed
RHammond2 added a commit that referenced this pull request Mar 3, 2026
* Ammonia Synloop: Units fix and small changes (#518)

* updated electricity units in synloop

* added feedstock consumption profiles and updated cost model to use capacity input

* added tests/subtests for ammonia synloop

* udpated electricity units to kW

* fixed nitrogen purge gas calc (#520)

* Minor fix in post-processing to save capacity factors (#519)

* minor update to sql_to_csv to save capacity factors

* simplified logic handling capacity factor in postprocessing

* Apply suggestion from @johnjasa

---------

Co-authored-by: John Jasa <john.jasa@nrel.gov>

* Enable load following optimization dispatch with Pyomo (#407)

* include code from usage: pyomo [-h] [--version]
             {build-extensions,convert,download-extensions,help,install-extras,model-viewer,run,solve,test-solvers}
             ...

This is the main driver for the Pyomo optimization software.

options:
  -h, --help            show this help message and exit
  --version             show program's version number and exit

subcommands:
  {build-extensions,convert,download-extensions,help,install-extras,model-viewer,run,solve,test-solvers}
    build-extensions    Build compiled extension modules
    convert             Convert a Pyomo model to another format
    download-extensions
                        Download compiled extension modules
    help                Print help information.
    install-extras      Install "extra" packages that Pyomo can leverage.
    model-viewer        Run the Pyomo model viewer
    run                 Execute a command from the Pyomo bin (or Scripts)
                        directory.
    solve               Optimize a model
    test-solvers        Test Pyomo solvers

-------------------------------------------------------------------------
Pyomo supports a variety of modeling and optimization capabilities,
which are executed either as subcommands of 'pyomo' or as separate
commands.  Use the 'help' subcommand to get information about the
capabilities installed with Pyomo.  Additionally, each subcommand
supports independent command-line options.  Use the -h option to
print details for a subcommand.  For example, type

   pyomo solve -h

to print information about the `solve` subcommand. branch that needs to be saved for later

* include storage rule file

* Halfway there for pyomo opt

* Add first objective function

* Updated dispatch optimization framework - add hybrid dispatch rule

* Adding hybrid linking constraints and connecting variables in pyomo min operating costs framework

* Final structural changes

* Fix import statement

* Fix imports and setter method

* First draft of running code

* Update example

* test

* fix precommits

* Fixing merge errors

* Minor spelling changes

* Update example

* Update controller problem state method from Elenya

* Update example and changelog

* Clean up pyomo storage baseclass file

* Cleanups to feature/pyomo opt (#2)

* refactored DispatchProblemState

* updated tech config and removed pysam options file

* minor cleanups to DispatchProblemState

* minor updates to generic_converter_opt

* initial cleanups to hybrid_rule.py

* minor cleanups to pyomo_storage_rule_min_operating_cost

* extra small cleanups to generic_converter_opt

* added storage capacities as inputs to optimized controller

* updated use of n_control_window and n_horizon_window

* Enable heuristic dispatch to run with new pyomo changes

* Clean up added files and example

* Adding first tests - do not pass yet

* Update docs and rename example

* Align naming with develop branch

* Update Ex 02 and update pyomo_controllers with naming in develop

* updated other example tech configs

* ran precommit on some files

* precommit on pyomo_controllers.py

* Update test formatting

* Update pyomo storage rule for test

* Fix SOC linking bug

* Testing update - partial

* Make new test for optimized pyomo dispatch

* Update optimal controller test

* Update test with new site definition

* Update example for merging in develop

* Minor updates to optimized dispatch

* PR updates from comments

* Add last file after merge issue

* Update example

* Made plots slightly larger

* Update h2integrate/control/control_strategies/controller_opt_problem_state.py

Apply comment suggestions

Co-authored-by: John Jasa <john.jasa@nrel.gov>

* Update h2integrate/control/control_strategies/controller_opt_problem_state.py

Apply comment suggestions

Co-authored-by: John Jasa <john.jasa@nrel.gov>

* Update h2integrate/control/control_rules/hybrid_rule.py

Apply comment suggestions

Co-authored-by: John Jasa <john.jasa@nrel.gov>

* Cleaning up pyomo_controllers

* Updated docstrings and battery mentions

* Initial init docs string and example update

* Update note about incentivizing charging in objective function

* Update generation and load variable definitions

* Update docs/control/pyomo_controllers.md

Co-authored-by: kbrunik <102193481+kbrunik@users.noreply.github.com>

* Update doc strings for updated_initial_soc parameter

* Make time_weighting_factor and round_digits not hardcoded

* remove round_digits from pyomo_rule_baseclass

* Updating controller names

* Fixed name check for controllers

* Updated optimal controller test

* remove unused properties

* Update example to run with new class definitions

* Fix converter name in test

* Fix ruff formatting

* Update doc strings for pyomo model classes

* Rename files to be more consistent and descriptive

* Update comments in the init portions of dispatch for parameters

* Remove comment

* Add doc strings to optimized dispatch config

* Give more details about DispatchProblemState class

* Remove todo comments

* Update docs with more in-depth dispatch description

* Minor typo

* Minor pyomo docs updates

* Minor pyomo docs updates

* Updated pyomo dispatch figure

* Minor comments based on PR review

* PR feedback

---------

Co-authored-by: Jared Thomas <jaredthomas68@gmail.com>
Co-authored-by: bayc <christopher.j.bay@gmail.com>
Co-authored-by: kbrunik <102193481+kbrunik@users.noreply.github.com>
Co-authored-by: kbrunik <kbrunik@gmail.com>
Co-authored-by: John Jasa <johnjasa11@gmail.com>
Co-authored-by: elenya-grant <116225007+elenya-grant@users.noreply.github.com>
Co-authored-by: John Jasa <john.jasa@nrel.gov>

* Remove `ElectricitySumComp` (follow-on to 463) (#524)

* removed usage of ElectricitySumComp

* removed ElectricitySumComp

* updated test value in test_save_csv_all_results

* Linearized H2 Fuel Cell Model (#525)

* linearized fuel cell

* added fuel cell to supported models

* fix units and output all necessary outputs

* docs on docs

* Review feedback changes

* add h2 fuel cell to elec tech

* Update docs/technology_models/h2_fuel_cell.md

Co-authored-by: Jared Thomas <jaredthomas68@users.noreply.github.com>

* Update h2integrate/converters/hydrogen/h2_fuel_cell.py

Co-authored-by: Jared Thomas <jaredthomas68@users.noreply.github.com>

* Update docs/technology_models/h2_fuel_cell.md

Co-authored-by: Jared Thomas <jaredthomas68@users.noreply.github.com>

* rename model to note linear performance

* docs

---------

Co-authored-by: Jared Thomas <jaredthomas68@users.noreply.github.com>

* Standardized Performance Outputs: Generic Storage & Control (#493)

* added draft of performance model baseclass to set outputs

* updated wind models for standardized outputs

* minor cleanup to wind pysam

* minor cleanup to floris outputs

* updated outputs of hydroplant

* updated solar pv models for standardized outputs

* updated solar resource integration tests with updated solar model outputs

* added fraction_of_year_simulated attribute to performance baseclass

* updated tests for turbine preprocessing tools

* fixed variable naming in test_all_examples for wind and solar

* started updating electrolyzer model outputs

* minor bugfix to test_sql_timeseries_to_csv for example 2

* fixed save_case_timeseries_as_csv

* update to sql_to_csv function just in case

* added attribute check in PerformanceModelBaseClass

* typo bugfix in refurb period calc in electrolyzer model

* added test for solar performance baseclass

* added solar test to check that all outputs are set in parent class

* commented out unused variables in new solar test

* generalized solar unit test so it can be easily used for other components

* updated natural gas plant

* started updating co2 models

* updated grid model

* updated asu model

* updated grid tests

* updated desal model

* updated co2 models and tests

* updated newest steel models

* started updating methanol models but not tested

* made it so ResizablePerformanceModelBaseClass inherits PerformanceModelBaseClass

* updated electrolyzer model and ammonia synloop model

* updated simple ammonia model

* updated hopp wrapper

* updated steel.py

* updated iron mine and dri models

* updated geoh2 models

* updated battery

* added todo comments to storage models

* added unit tests to check that outputs are populated

* working on updating combiners and h2imodel

* updated electrolyzer so test values dont change and other bugfixes so examples run

* updated how_to_set_up_an_analysis.md

* removed init file from new hydro power test folder

* updated remaining failing tests

* updated example 28 and iron_wrapper

* updated capacity factor strings in run_size_modes files

* removed commented out outputs

* updated changelog

* removed duplicate inheritance of PerformanceModelBaseClass in electrolyzer performance baseclass

* updated pysam battery outputs

* updated annual outputs to properly account for fraction of year simulated

* moved commodity defn to initialize

* updates based on reviewer feedback

* updated generic storage models and simple controllers

* bugfix in simple_storage_autosizing

* updated test for post-processing timeseries

* added subtest for ex 12

* updated ex 12 test values

* removed commented out code

* updated changelog

* added some comments to generic storage performance models

* added more inline and docstring comments

* Minor refactoring changes for clarity

* updated docstring in generic storage

* updated variable name in simple storage autosizing

* added charge rate and capacity inputs to simple generic storage

* removed capacity as input to simple_generic_storage

* update config names for commodity_name, commodity_units and commodity_storage_units

* updated ex 9

* minor updates based on feedback

* updated changelog

* updated inline comment in simple_generic_storage

---------

Co-authored-by: John Jasa <johnjasa11@gmail.com>

* Rename examples to avoid clash (#528)

* moved example and added test

* Simplified test

* Standard Combiner Outputs (follow-on to 463) (#501)

* updated combiner to have standard outputs

* updated h2imodel for connecting techs to the combiner

* added inline comments to generic combiner

* added integration test for generic combiner

* updated commodity_units to commodity_rate_units

* fixed issue from merge causing errors

* updated commodity_units to commodity_rate_units in splitter

* updated combiner inputs in a test

* adding docstring and docs about combiner

---------

Co-authored-by: John Jasa <johnjasa11@gmail.com>

* Update mcm dependency (#530)

* made saving figures and outputs a config option for co2 models

* updated oae to have save plots be optional

* update mcm dependency to pip

* revert environment.yml change

* fix merge issue

---------

Co-authored-by: elenya-grant <116225007+elenya-grant@users.noreply.github.com>

* Update CO2 model documentation (#533)

* made saving figures and outputs a config option for co2 models

* updated oae to have save plots be optional

* update mcm dependency to pip

* revert environment.yml change

* fix merge issue

* update docs

* update ci

---------

Co-authored-by: elenya-grant <116225007+elenya-grant@users.noreply.github.com>

* Iron: Example with independently sized mine and iron/steel plant (#433)

* added in-progress refactored iron winning models

* updated natural gas dri performance model

* updated docs in ng iron performance model

* updated iron cost model

* updated coefficient paths for rosner iron plant models

* updated example 21 and fixed bugs that were found

* minor updates to dri cost and perf models

* added basic tests for new dri models

* removed notes from rosner dri test

* double checked water costs and unit conversions and cleaned up some small things

* renamed ng iron reduction models and configs

* renamed ng iron dri files

* updated iron example tech config

* added h2 dri cost and performance models

* added h2 dri models to supported models

* update test values for wombat version bump

* update unit test

* update docstrings

* update feedstocks to be per ton pig iron

* refactored DRI performance and cost models to share baseclass

* updated electricity to not double count

* added test for performance with limited feedstock availability

* eaf wip

* steel tests wip

* undo align

* minor update on handling negative exponents in steel eaf cost model

* added carbon and lime to pipe

* update rosner eaf test

* draft update to iron example

* updated iron example to include steel eaf

* updated doc page and changelog

* added small test for example 21

* Fix run_iron.py to actually compare old and new

* Comparing old and new models

* Old and new models matched up

* Cleaning up PR

* Less goofy plant config and other cleanup

* Going back to old coefficients with electricity in processing cost

* Fixed tests, need to document

* updated example 21 so iron_ore_consumed is used for transport cost

* Some iron-related cleanup

* Fixing test

---------

Co-authored-by: elenya-grant <116225007+elenya-grant@users.noreply.github.com>
Co-authored-by: kbrunik <kbrunik@gmail.com>
Co-authored-by: John Jasa <johnjasa11@gmail.com>
Co-authored-by: kbrunik <102193481+kbrunik@users.noreply.github.com>

* Enhancement: Error out on duplicate configuration keys (#534)

* add duplicate key handling

* include all info in final error message and use load_yaml() in include()

* remove printing

* remove duplicate keys from floris v4 default template

* add more implementation details to the docstring

* update changelog

* place duplicate key error next to YAML loading

* add testing for duplicate keys

* provide further insight into design/implementation choices and clarify what is being passed through

* GeoH2: Arps decline curve (#454)

* WIP arps decline curve

* update decline curve

* update geoh2 and docstring

* update test

* update to be percent

* update documentation

* fix changelog

* Update h2integrate/converters/hydrogen/geologic/simple_natural_geoh2.py

Co-authored-by: Jonathan Martin <94018654+jmartin4u@users.noreply.github.com>

* Update h2integrate/converters/hydrogen/geologic/simple_natural_geoh2.py

Co-authored-by: Jonathan Martin <94018654+jmartin4u@users.noreply.github.com>

* precommit fix

* placeholder fix for lifetime simulation

* fix failing test units

* Fixing model name in example script

---------

Co-authored-by: Jonathan Martin <94018654+jmartin4u@users.noreply.github.com>
Co-authored-by: John Jasa <johnjasa11@gmail.com>
Co-authored-by: jmartin4 <jonathan.martin@nrel.gov>

* fix oae numpy (#535)

* fix oae numpy stuff

* actual fix

* Feedstock units update (#541)

* updated MMBtu to MMBtu/h

* updated naming for feedstock config inputs

* Re-organize "default" logic around `commodity_stream` and finance models (#536)

* moved tech-specific hard-coded logic to connect technologies to finance models from connect_technologies() to create_finance_model()

---------

Co-authored-by: kbrunik <102193481+kbrunik@users.noreply.github.com>

* Iron: Adding electrowinning capabilities (#432)

* Reading in Stinn input tables

* Sucessfully testing Stinn model

* Making humbert perf model

* Adding humbert/stinn cost model inputs only

* Progress on humbert cost

* Humbert Stinn complete, not debugged

* Debugging electrowinning

* Debugging ewinning

* Electrowinning cases running, need to add DRI case

* Cleanup of old trailing underscores

* Fix input csv names

* Integrate ewin example with feedstocks

* Moving functions out of h2i module and renumbering example

* Small fixes to PR

* start of unit test

* Testing example

* Expanding unit tests

* fix import

* Docstrings begun

* Docstrings plus a little reorg

* Everything but the docs page

* Finished documentation

* Split off humbert cost_model

* Config docstrings

* Refactoring iron ewinning calcs for clarity

* Addressing reviews

* site -> sites

* Updating test values

* Added autodoc to the electrowinning docs

* updated iron electrowinning tech config

* updated plant config for electrowinning example

* minor udpates to cost model

* minor updates to cost and performance models

* fixed initialization error in cost model

* removed duplicate costs and updated to use PerformanceModelBaseClass

* updated example test values

* Addressing elenya comments

* feedstocks wip

* updating integration

* classic units

* updated tech config to be compatible with develop

* merged changelog

* removed hard-coded cost numbers in compute() to config args with default values

* Minor cleanup

---------

Co-authored-by: Jonathan Martin <94018654+jmartin4nrel@users.noreply.github.com>
Co-authored-by: John Jasa <johnjasa11@gmail.com>
Co-authored-by: kbrunik <kbrunik@gmail.com>
Co-authored-by: kbrunik <102193481+kbrunik@users.noreply.github.com>
Co-authored-by: elenya-grant <116225007+elenya-grant@users.noreply.github.com>

* Consistently call `prob.get_val()` with units instead of `prob["<variable>"]` (#539)

* moved prob calls

* Fixing some tests

* Updated units for NG

* Updated based on PR feedback

* Generic transporter model (#540)

* added generic transporter and added to supported models

* updated generic transporter, added test, and updated iron example

* updated doc pages for generic transporter

* added error message to h2imodel for invalid tech names

* minor typo fix in feedstock documentation

* removed more components from pipe and updated iron examples

* Changed to using set notation for the reserved techs

---------

Co-authored-by: John Jasa <johnjasa11@gmail.com>

* Finance models to represent lifetime performance (#543)

* initial lifetime finance reorg with updated lcoh test values

* updated combiner output naming for capacity factor so combiners can be daisy-chained

* added new error message to check if a default commodity stream was not found

* fixed other failing tests

* updates to co2 models and new tests

* removed hard-codedl logic for handling co2

* minor fix on units input to ProFast

* cleaned up doc

* minor cleanups

* renamed variables in generic combiner

* minor other update to generic combiner

* minor cleanup

---------

Co-authored-by: John Jasa <johnjasa11@gmail.com>

* Maintenance: Test Revamp Phase 1 - Mild Refactor and Test Type Marking (#531)

* enable test coverage and update pytest settings

* add sort of working file cleanup fixture

* convert feedstock to pytest

* spacing

* convert utilities to pytest

* remove last unittest reference

* convert finance test to pytest

* fix import error and convert DOC to pytest

* use correct error for imports not found

* add infrastructure to have separate unit, regression, and integration tests

* refactor shared fixtures to conftest and mark doc with test type

* adopt enforcement of unit, regression, and test marking

* mark already modified files

* fix marker name gathering

* add docstring

* add documentation for test development and running

* convert oae test to pytest

* mark controller tests

* mark ammonia converter tests

* simplify fixtures and convert to pytest

* update docs for openmdao cleanup

* update fixtures and mark hopp tests

* remove generated output plots

* mark geologic h2 and handle temp data better

* update fixture usage and mark tests in h2

* update fixture structure and mark iron converter tests

* mark methanol tests

* mark natural gas tests

* mark nitrogen tests

* reorg solar fixtures and mark test types

* reorg steel converter fixtures and mark test types

* mark water power test types

* reorg wind converter models fixtures and mark test types

* mark  wind converter tools  test types

* remove repetitive .parent

* update temp directory usage and mark test types in frameworks

* refactor temp_dir to conftest

* mark recorder tests and use pytest temp dir factory

* mark supported models tests and use general temp_dir for utilities

* define temp_dir factory in top-level conftest and import everywhere else

* correct temp_dir refactor

* fix broken mcm tests and universalize package management

* mark new tests

* mark finance test types

* mark postprocessing tests and use temp_dir fixture

* mark preprocessing test types and use temp_dir

* refactor himawari tests to reduce repitive setup architecture and mark test type

* refactor himawari and open meteosat models to single parameterized test

* refactor nrel goes resource model to single parameterized test

* combine all resource model tests into single file and convert openmeteo historical to parameterized

* convert pv watts integration tests into single parameterized test

* move common resource fixtures to resource level conftest

* include which for solar vs win

* use site config setup for wind and mark wind resource tests

* refactor open meteo wind test to use generalized fixtures and mark test types

* apply ids and use timezone arg for plant

* mark resource utilities tests

* mark battery storage tests and unify test folders

* mark storage test types, use common fixtures, and parametrize common test routines

* mark tools test types

* mark transporter test types and reduce repetitive tests and fixtures

* mark pip tests

* mark examples test types

* ignore sqllite cache

* bump wombat version and remove remove test coverage options

* fix broken tests from lack of temp_dir updating

* mark newly merged tests

* mark missing integration test

* create fixture for temp_dir setup/teardown for example directories

* account for resource data and fix broken tests

* accept change from kbrunik for model vs technology language

Co-authored-by: kbrunik <102193481+kbrunik@users.noreply.github.com>

* add unit test example to docs and fix subtest comment

* add regression test example and integration test pointer

* add missing setup/teardown for meteosat nrel api

* update commodity units to commodity_rate_units

* mark newly merged test

* add code coverage workflow draft

* consolidate repetitive pytest session functionality

* account for differing resource year and file name resource year

* update resource year in integration test as well

* update naming

* remove redundant resource-year update and fix variable typo

* mark new tests

* Update docs/developer_guide/adding_a_new_technology.md

Co-authored-by: genevievestarke <103534902+genevievestarke@users.noreply.github.com>

* Update docs/developer_guide/adding_a_new_technology.md

Co-authored-by: Jared Thomas <jaredthomas68@users.noreply.github.com>

* Update docs/developer_guide/adding_a_new_technology.md

Co-authored-by: Jared Thomas <jaredthomas68@users.noreply.github.com>

* Update docs/developer_guide/adding_a_new_technology.md

Co-authored-by: Jared Thomas <jaredthomas68@users.noreply.github.com>

* add in docs for temporary data retention

* remove unlink for redundant cleanup

* remove unused parameter

* mark finnicky test with xfail

* move testing docs to a separate page

* add shared fixture commentary

* add codecov token

* make relative reference

* update changelog

* add use a generic fenceposting comment to mark docs literalinclude bounds

---------

Co-authored-by: kbrunik <102193481+kbrunik@users.noreply.github.com>
Co-authored-by: John Jasa <johnjasa11@gmail.com>
Co-authored-by: genevievestarke <103534902+genevievestarke@users.noreply.github.com>
Co-authored-by: Jared Thomas <jaredthomas68@users.noreply.github.com>

* Remove some doc warnings (#550)

* Beginning to address doc build warnings

* Removing duplicate kwarg docstrings

* updated ammonia model to need n2 and electricity inputs (#544)

Co-authored-by: John Jasa <johnjasa11@gmail.com>

* yamlfix all yaml files; add yamlfix to the pre-commit hook (#551)

* add yamlfix to the pre-commit

* yamlfix'd all yamls

* Update changelog

* Rolling back other pre-commit changes

* Excluded yamls needed for duplicate keys tests

* Excluded yamls needed for duplicate keys tests

* Switch to keep style for sequences

* Switch to keep style for sequences

* Apply suggestions from code review

Co-authored-by: Rob Hammond <13874373+RHammond2@users.noreply.github.com>

* Applying Rob's suggestions

* leave not on malfunctional config and reapply settings

---------

Co-authored-by: Rob Hammond <13874373+RHammond2@users.noreply.github.com>

* Enhancement: Automatically insert technology names in control strategy definitions (#558)

* add tech name to control parameters in setup

* remove obselete test and associate files

* handle no keys in control parameters and update testing

* Add a simple nuclear plant performance and cost model (#538)

* Added first pass at a simple nuclear model

* Adding nuclear doc page

* Added to changelog

* Doc cleanup

* Fixed OpEx calc for nuclear

* Simplifying nuclear test

* Addressing PR comments

* Marking tests

* Updates based on PR feedback

* Split out Pyomo Controllers to separate files (#549)

* split out pyomo control models

* added pyomo controller baseclass

* removed n_horizon_window from control strategies

* moved round_digits to pyomo base config

* other minor updates

* fixed failing tests

* Update changelog

---------

Co-authored-by: John Jasa <johnjasa11@gmail.com>

* Update Natural GeoH2 with Yearly CF (#552)

* reformat release updates

* bump version

* Added PR links to changelog

* Tech and system model figures (#554)

* tech and system model diagrams

* add comment about modularity and custom models

* Update docs/intro.md

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Apply suggestion from @bayc

Co-authored-by: Chris Bay <12664940+bayc@users.noreply.github.com>

* Apply suggestion from @bayc

Co-authored-by: Chris Bay <12664940+bayc@users.noreply.github.com>

* Apply suggestions from code review

Co-authored-by: Chris Bay <12664940+bayc@users.noreply.github.com>

* correct typo and unify figure specs

* typo correction

* update changelog

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Chris Bay <12664940+bayc@users.noreply.github.com>

* Removed hard-coded logic for electrolyzer replacement schedule (#555)

* draft

* updated finances to take in replacement schedules for all techs

* updated feedstock and added logic to prevent connecting replacement schedule for transport components

* added doc page about naming convention

* removed unnecessary elif statement in connect_technologies

* added test for new error message

* updated inline comments for finance models

* Minor refactor and cleanup for replacement cost vars

* updated naming for system finance model

* reverted change to ex 1 run file

---------

Co-authored-by: John Jasa <johnjasa11@gmail.com>

---------

Co-authored-by: elenya-grant <116225007+elenya-grant@users.noreply.github.com>
Co-authored-by: John Jasa <john.jasa@nrel.gov>
Co-authored-by: genevievestarke <103534902+genevievestarke@users.noreply.github.com>
Co-authored-by: Jared Thomas <jaredthomas68@gmail.com>
Co-authored-by: bayc <christopher.j.bay@gmail.com>
Co-authored-by: kbrunik <102193481+kbrunik@users.noreply.github.com>
Co-authored-by: kbrunik <kbrunik@gmail.com>
Co-authored-by: John Jasa <johnjasa11@gmail.com>
Co-authored-by: Jared Thomas <jaredthomas68@users.noreply.github.com>
Co-authored-by: Jonathan Martin <94018654+jmartin4u@users.noreply.github.com>
Co-authored-by: jmartin4 <jonathan.martin@nrel.gov>
Co-authored-by: Jonathan Martin <94018654+jmartin4nrel@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Chris Bay <12664940+bayc@users.noreply.github.com>
@RHammond2 RHammond2 mentioned this pull request Mar 13, 2026
RHammond2 added a commit that referenced this pull request Mar 13, 2026
* Ammonia Synloop: Units fix and small changes (#518)

* updated electricity units in synloop

* added feedstock consumption profiles and updated cost model to use capacity input

* added tests/subtests for ammonia synloop

* udpated electricity units to kW

* fixed nitrogen purge gas calc (#520)

* Minor fix in post-processing to save capacity factors (#519)

* minor update to sql_to_csv to save capacity factors

* simplified logic handling capacity factor in postprocessing

* Apply suggestion from @johnjasa

---------

Co-authored-by: John Jasa <john.jasa@nrel.gov>

* Enable load following optimization dispatch with Pyomo (#407)

* include code from usage: pyomo [-h] [--version]
             {build-extensions,convert,download-extensions,help,install-extras,model-viewer,run,solve,test-solvers}
             ...

This is the main driver for the Pyomo optimization software.

options:
  -h, --help            show this help message and exit
  --version             show program's version number and exit

subcommands:
  {build-extensions,convert,download-extensions,help,install-extras,model-viewer,run,solve,test-solvers}
    build-extensions    Build compiled extension modules
    convert             Convert a Pyomo model to another format
    download-extensions
                        Download compiled extension modules
    help                Print help information.
    install-extras      Install "extra" packages that Pyomo can leverage.
    model-viewer        Run the Pyomo model viewer
    run                 Execute a command from the Pyomo bin (or Scripts)
                        directory.
    solve               Optimize a model
    test-solvers        Test Pyomo solvers

-------------------------------------------------------------------------
Pyomo supports a variety of modeling and optimization capabilities,
which are executed either as subcommands of 'pyomo' or as separate
commands.  Use the 'help' subcommand to get information about the
capabilities installed with Pyomo.  Additionally, each subcommand
supports independent command-line options.  Use the -h option to
print details for a subcommand.  For example, type

   pyomo solve -h

to print information about the `solve` subcommand. branch that needs to be saved for later

* include storage rule file

* Halfway there for pyomo opt

* Add first objective function

* Updated dispatch optimization framework - add hybrid dispatch rule

* Adding hybrid linking constraints and connecting variables in pyomo min operating costs framework

* Final structural changes

* Fix import statement

* Fix imports and setter method

* First draft of running code

* Update example

* test

* fix precommits

* Fixing merge errors

* Minor spelling changes

* Update example

* Update controller problem state method from Elenya

* Update example and changelog

* Clean up pyomo storage baseclass file

* Cleanups to feature/pyomo opt (#2)

* refactored DispatchProblemState

* updated tech config and removed pysam options file

* minor cleanups to DispatchProblemState

* minor updates to generic_converter_opt

* initial cleanups to hybrid_rule.py

* minor cleanups to pyomo_storage_rule_min_operating_cost

* extra small cleanups to generic_converter_opt

* added storage capacities as inputs to optimized controller

* updated use of n_control_window and n_horizon_window

* Enable heuristic dispatch to run with new pyomo changes

* Clean up added files and example

* Adding first tests - do not pass yet

* Update docs and rename example

* Align naming with develop branch

* Update Ex 02 and update pyomo_controllers with naming in develop

* updated other example tech configs

* ran precommit on some files

* precommit on pyomo_controllers.py

* Update test formatting

* Update pyomo storage rule for test

* Fix SOC linking bug

* Testing update - partial

* Make new test for optimized pyomo dispatch

* Update optimal controller test

* Update test with new site definition

* Update example for merging in develop

* Minor updates to optimized dispatch

* PR updates from comments

* Add last file after merge issue

* Update example

* Made plots slightly larger

* Update h2integrate/control/control_strategies/controller_opt_problem_state.py

Apply comment suggestions

Co-authored-by: John Jasa <john.jasa@nrel.gov>

* Update h2integrate/control/control_strategies/controller_opt_problem_state.py

Apply comment suggestions

Co-authored-by: John Jasa <john.jasa@nrel.gov>

* Update h2integrate/control/control_rules/hybrid_rule.py

Apply comment suggestions

Co-authored-by: John Jasa <john.jasa@nrel.gov>

* Cleaning up pyomo_controllers

* Updated docstrings and battery mentions

* Initial init docs string and example update

* Update note about incentivizing charging in objective function

* Update generation and load variable definitions

* Update docs/control/pyomo_controllers.md

Co-authored-by: kbrunik <102193481+kbrunik@users.noreply.github.com>

* Update doc strings for updated_initial_soc parameter

* Make time_weighting_factor and round_digits not hardcoded

* remove round_digits from pyomo_rule_baseclass

* Updating controller names

* Fixed name check for controllers

* Updated optimal controller test

* remove unused properties

* Update example to run with new class definitions

* Fix converter name in test

* Fix ruff formatting

* Update doc strings for pyomo model classes

* Rename files to be more consistent and descriptive

* Update comments in the init portions of dispatch for parameters

* Remove comment

* Add doc strings to optimized dispatch config

* Give more details about DispatchProblemState class

* Remove todo comments

* Update docs with more in-depth dispatch description

* Minor typo

* Minor pyomo docs updates

* Minor pyomo docs updates

* Updated pyomo dispatch figure

* Minor comments based on PR review

* PR feedback

---------

Co-authored-by: Jared Thomas <jaredthomas68@gmail.com>
Co-authored-by: bayc <christopher.j.bay@gmail.com>
Co-authored-by: kbrunik <102193481+kbrunik@users.noreply.github.com>
Co-authored-by: kbrunik <kbrunik@gmail.com>
Co-authored-by: John Jasa <johnjasa11@gmail.com>
Co-authored-by: elenya-grant <116225007+elenya-grant@users.noreply.github.com>
Co-authored-by: John Jasa <john.jasa@nrel.gov>

* Remove `ElectricitySumComp` (follow-on to 463) (#524)

* removed usage of ElectricitySumComp

* removed ElectricitySumComp

* updated test value in test_save_csv_all_results

* Linearized H2 Fuel Cell Model (#525)

* linearized fuel cell

* added fuel cell to supported models

* fix units and output all necessary outputs

* docs on docs

* Review feedback changes

* add h2 fuel cell to elec tech

* Update docs/technology_models/h2_fuel_cell.md

Co-authored-by: Jared Thomas <jaredthomas68@users.noreply.github.com>

* Update h2integrate/converters/hydrogen/h2_fuel_cell.py

Co-authored-by: Jared Thomas <jaredthomas68@users.noreply.github.com>

* Update docs/technology_models/h2_fuel_cell.md

Co-authored-by: Jared Thomas <jaredthomas68@users.noreply.github.com>

* rename model to note linear performance

* docs

---------

Co-authored-by: Jared Thomas <jaredthomas68@users.noreply.github.com>

* Standardized Performance Outputs: Generic Storage & Control (#493)

* added draft of performance model baseclass to set outputs

* updated wind models for standardized outputs

* minor cleanup to wind pysam

* minor cleanup to floris outputs

* updated outputs of hydroplant

* updated solar pv models for standardized outputs

* updated solar resource integration tests with updated solar model outputs

* added fraction_of_year_simulated attribute to performance baseclass

* updated tests for turbine preprocessing tools

* fixed variable naming in test_all_examples for wind and solar

* started updating electrolyzer model outputs

* minor bugfix to test_sql_timeseries_to_csv for example 2

* fixed save_case_timeseries_as_csv

* update to sql_to_csv function just in case

* added attribute check in PerformanceModelBaseClass

* typo bugfix in refurb period calc in electrolyzer model

* added test for solar performance baseclass

* added solar test to check that all outputs are set in parent class

* commented out unused variables in new solar test

* generalized solar unit test so it can be easily used for other components

* updated natural gas plant

* started updating co2 models

* updated grid model

* updated asu model

* updated grid tests

* updated desal model

* updated co2 models and tests

* updated newest steel models

* started updating methanol models but not tested

* made it so ResizablePerformanceModelBaseClass inherits PerformanceModelBaseClass

* updated electrolyzer model and ammonia synloop model

* updated simple ammonia model

* updated hopp wrapper

* updated steel.py

* updated iron mine and dri models

* updated geoh2 models

* updated battery

* added todo comments to storage models

* added unit tests to check that outputs are populated

* working on updating combiners and h2imodel

* updated electrolyzer so test values dont change and other bugfixes so examples run

* updated how_to_set_up_an_analysis.md

* removed init file from new hydro power test folder

* updated remaining failing tests

* updated example 28 and iron_wrapper

* updated capacity factor strings in run_size_modes files

* removed commented out outputs

* updated changelog

* removed duplicate inheritance of PerformanceModelBaseClass in electrolyzer performance baseclass

* updated pysam battery outputs

* updated annual outputs to properly account for fraction of year simulated

* moved commodity defn to initialize

* updates based on reviewer feedback

* updated generic storage models and simple controllers

* bugfix in simple_storage_autosizing

* updated test for post-processing timeseries

* added subtest for ex 12

* updated ex 12 test values

* removed commented out code

* updated changelog

* added some comments to generic storage performance models

* added more inline and docstring comments

* Minor refactoring changes for clarity

* updated docstring in generic storage

* updated variable name in simple storage autosizing

* added charge rate and capacity inputs to simple generic storage

* removed capacity as input to simple_generic_storage

* update config names for commodity_name, commodity_units and commodity_storage_units

* updated ex 9

* minor updates based on feedback

* updated changelog

* updated inline comment in simple_generic_storage

---------

Co-authored-by: John Jasa <johnjasa11@gmail.com>

* Rename examples to avoid clash (#528)

* moved example and added test

* Simplified test

* Standard Combiner Outputs (follow-on to 463) (#501)

* updated combiner to have standard outputs

* updated h2imodel for connecting techs to the combiner

* added inline comments to generic combiner

* added integration test for generic combiner

* updated commodity_units to commodity_rate_units

* fixed issue from merge causing errors

* updated commodity_units to commodity_rate_units in splitter

* updated combiner inputs in a test

* adding docstring and docs about combiner

---------

Co-authored-by: John Jasa <johnjasa11@gmail.com>

* Update mcm dependency (#530)

* made saving figures and outputs a config option for co2 models

* updated oae to have save plots be optional

* update mcm dependency to pip

* revert environment.yml change

* fix merge issue

---------

Co-authored-by: elenya-grant <116225007+elenya-grant@users.noreply.github.com>

* Update CO2 model documentation (#533)

* made saving figures and outputs a config option for co2 models

* updated oae to have save plots be optional

* update mcm dependency to pip

* revert environment.yml change

* fix merge issue

* update docs

* update ci

---------

Co-authored-by: elenya-grant <116225007+elenya-grant@users.noreply.github.com>

* Iron: Example with independently sized mine and iron/steel plant (#433)

* added in-progress refactored iron winning models

* updated natural gas dri performance model

* updated docs in ng iron performance model

* updated iron cost model

* updated coefficient paths for rosner iron plant models

* updated example 21 and fixed bugs that were found

* minor updates to dri cost and perf models

* added basic tests for new dri models

* removed notes from rosner dri test

* double checked water costs and unit conversions and cleaned up some small things

* renamed ng iron reduction models and configs

* renamed ng iron dri files

* updated iron example tech config

* added h2 dri cost and performance models

* added h2 dri models to supported models

* update test values for wombat version bump

* update unit test

* update docstrings

* update feedstocks to be per ton pig iron

* refactored DRI performance and cost models to share baseclass

* updated electricity to not double count

* added test for performance with limited feedstock availability

* eaf wip

* steel tests wip

* undo align

* minor update on handling negative exponents in steel eaf cost model

* added carbon and lime to pipe

* update rosner eaf test

* draft update to iron example

* updated iron example to include steel eaf

* updated doc page and changelog

* added small test for example 21

* Fix run_iron.py to actually compare old and new

* Comparing old and new models

* Old and new models matched up

* Cleaning up PR

* Less goofy plant config and other cleanup

* Going back to old coefficients with electricity in processing cost

* Fixed tests, need to document

* updated example 21 so iron_ore_consumed is used for transport cost

* Some iron-related cleanup

* Fixing test

---------

Co-authored-by: elenya-grant <116225007+elenya-grant@users.noreply.github.com>
Co-authored-by: kbrunik <kbrunik@gmail.com>
Co-authored-by: John Jasa <johnjasa11@gmail.com>
Co-authored-by: kbrunik <102193481+kbrunik@users.noreply.github.com>

* Enhancement: Error out on duplicate configuration keys (#534)

* add duplicate key handling

* include all info in final error message and use load_yaml() in include()

* remove printing

* remove duplicate keys from floris v4 default template

* add more implementation details to the docstring

* update changelog

* place duplicate key error next to YAML loading

* add testing for duplicate keys

* provide further insight into design/implementation choices and clarify what is being passed through

* GeoH2: Arps decline curve (#454)

* WIP arps decline curve

* update decline curve

* update geoh2 and docstring

* update test

* update to be percent

* update documentation

* fix changelog

* Update h2integrate/converters/hydrogen/geologic/simple_natural_geoh2.py

Co-authored-by: Jonathan Martin <94018654+jmartin4u@users.noreply.github.com>

* Update h2integrate/converters/hydrogen/geologic/simple_natural_geoh2.py

Co-authored-by: Jonathan Martin <94018654+jmartin4u@users.noreply.github.com>

* precommit fix

* placeholder fix for lifetime simulation

* fix failing test units

* Fixing model name in example script

---------

Co-authored-by: Jonathan Martin <94018654+jmartin4u@users.noreply.github.com>
Co-authored-by: John Jasa <johnjasa11@gmail.com>
Co-authored-by: jmartin4 <jonathan.martin@nrel.gov>

* fix oae numpy (#535)

* fix oae numpy stuff

* actual fix

* Feedstock units update (#541)

* updated MMBtu to MMBtu/h

* updated naming for feedstock config inputs

* Re-organize "default" logic around `commodity_stream` and finance models (#536)

* moved tech-specific hard-coded logic to connect technologies to finance models from connect_technologies() to create_finance_model()

---------

Co-authored-by: kbrunik <102193481+kbrunik@users.noreply.github.com>

* Iron: Adding electrowinning capabilities (#432)

* Reading in Stinn input tables

* Sucessfully testing Stinn model

* Making humbert perf model

* Adding humbert/stinn cost model inputs only

* Progress on humbert cost

* Humbert Stinn complete, not debugged

* Debugging electrowinning

* Debugging ewinning

* Electrowinning cases running, need to add DRI case

* Cleanup of old trailing underscores

* Fix input csv names

* Integrate ewin example with feedstocks

* Moving functions out of h2i module and renumbering example

* Small fixes to PR

* start of unit test

* Testing example

* Expanding unit tests

* fix import

* Docstrings begun

* Docstrings plus a little reorg

* Everything but the docs page

* Finished documentation

* Split off humbert cost_model

* Config docstrings

* Refactoring iron ewinning calcs for clarity

* Addressing reviews

* site -> sites

* Updating test values

* Added autodoc to the electrowinning docs

* updated iron electrowinning tech config

* updated plant config for electrowinning example

* minor udpates to cost model

* minor updates to cost and performance models

* fixed initialization error in cost model

* removed duplicate costs and updated to use PerformanceModelBaseClass

* updated example test values

* Addressing elenya comments

* feedstocks wip

* updating integration

* classic units

* updated tech config to be compatible with develop

* merged changelog

* removed hard-coded cost numbers in compute() to config args with default values

* Minor cleanup

---------

Co-authored-by: Jonathan Martin <94018654+jmartin4nrel@users.noreply.github.com>
Co-authored-by: John Jasa <johnjasa11@gmail.com>
Co-authored-by: kbrunik <kbrunik@gmail.com>
Co-authored-by: kbrunik <102193481+kbrunik@users.noreply.github.com>
Co-authored-by: elenya-grant <116225007+elenya-grant@users.noreply.github.com>

* Consistently call `prob.get_val()` with units instead of `prob["<variable>"]` (#539)

* moved prob calls

* Fixing some tests

* Updated units for NG

* Updated based on PR feedback

* Generic transporter model (#540)

* added generic transporter and added to supported models

* updated generic transporter, added test, and updated iron example

* updated doc pages for generic transporter

* added error message to h2imodel for invalid tech names

* minor typo fix in feedstock documentation

* removed more components from pipe and updated iron examples

* Changed to using set notation for the reserved techs

---------

Co-authored-by: John Jasa <johnjasa11@gmail.com>

* Finance models to represent lifetime performance (#543)

* initial lifetime finance reorg with updated lcoh test values

* updated combiner output naming for capacity factor so combiners can be daisy-chained

* added new error message to check if a default commodity stream was not found

* fixed other failing tests

* updates to co2 models and new tests

* removed hard-codedl logic for handling co2

* minor fix on units input to ProFast

* cleaned up doc

* minor cleanups

* renamed variables in generic combiner

* minor other update to generic combiner

* minor cleanup

---------

Co-authored-by: John Jasa <johnjasa11@gmail.com>

* Maintenance: Test Revamp Phase 1 - Mild Refactor and Test Type Marking (#531)

* enable test coverage and update pytest settings

* add sort of working file cleanup fixture

* convert feedstock to pytest

* spacing

* convert utilities to pytest

* remove last unittest reference

* convert finance test to pytest

* fix import error and convert DOC to pytest

* use correct error for imports not found

* add infrastructure to have separate unit, regression, and integration tests

* refactor shared fixtures to conftest and mark doc with test type

* adopt enforcement of unit, regression, and test marking

* mark already modified files

* fix marker name gathering

* add docstring

* add documentation for test development and running

* convert oae test to pytest

* mark controller tests

* mark ammonia converter tests

* simplify fixtures and convert to pytest

* update docs for openmdao cleanup

* update fixtures and mark hopp tests

* remove generated output plots

* mark geologic h2 and handle temp data better

* update fixture usage and mark tests in h2

* update fixture structure and mark iron converter tests

* mark methanol tests

* mark natural gas tests

* mark nitrogen tests

* reorg solar fixtures and mark test types

* reorg steel converter fixtures and mark test types

* mark water power test types

* reorg wind converter models fixtures and mark test types

* mark  wind converter tools  test types

* remove repetitive .parent

* update temp directory usage and mark test types in frameworks

* refactor temp_dir to conftest

* mark recorder tests and use pytest temp dir factory

* mark supported models tests and use general temp_dir for utilities

* define temp_dir factory in top-level conftest and import everywhere else

* correct temp_dir refactor

* fix broken mcm tests and universalize package management

* mark new tests

* mark finance test types

* mark postprocessing tests and use temp_dir fixture

* mark preprocessing test types and use temp_dir

* refactor himawari tests to reduce repitive setup architecture and mark test type

* refactor himawari and open meteosat models to single parameterized test

* refactor nrel goes resource model to single parameterized test

* combine all resource model tests into single file and convert openmeteo historical to parameterized

* convert pv watts integration tests into single parameterized test

* move common resource fixtures to resource level conftest

* include which for solar vs win

* use site config setup for wind and mark wind resource tests

* refactor open meteo wind test to use generalized fixtures and mark test types

* apply ids and use timezone arg for plant

* mark resource utilities tests

* mark battery storage tests and unify test folders

* mark storage test types, use common fixtures, and parametrize common test routines

* mark tools test types

* mark transporter test types and reduce repetitive tests and fixtures

* mark pip tests

* mark examples test types

* ignore sqllite cache

* bump wombat version and remove remove test coverage options

* fix broken tests from lack of temp_dir updating

* mark newly merged tests

* mark missing integration test

* create fixture for temp_dir setup/teardown for example directories

* account for resource data and fix broken tests

* accept change from kbrunik for model vs technology language

Co-authored-by: kbrunik <102193481+kbrunik@users.noreply.github.com>

* add unit test example to docs and fix subtest comment

* add regression test example and integration test pointer

* add missing setup/teardown for meteosat nrel api

* update commodity units to commodity_rate_units

* mark newly merged test

* add code coverage workflow draft

* consolidate repetitive pytest session functionality

* account for differing resource year and file name resource year

* update resource year in integration test as well

* update naming

* remove redundant resource-year update and fix variable typo

* mark new tests

* Update docs/developer_guide/adding_a_new_technology.md

Co-authored-by: genevievestarke <103534902+genevievestarke@users.noreply.github.com>

* Update docs/developer_guide/adding_a_new_technology.md

Co-authored-by: Jared Thomas <jaredthomas68@users.noreply.github.com>

* Update docs/developer_guide/adding_a_new_technology.md

Co-authored-by: Jared Thomas <jaredthomas68@users.noreply.github.com>

* Update docs/developer_guide/adding_a_new_technology.md

Co-authored-by: Jared Thomas <jaredthomas68@users.noreply.github.com>

* add in docs for temporary data retention

* remove unlink for redundant cleanup

* remove unused parameter

* mark finnicky test with xfail

* move testing docs to a separate page

* add shared fixture commentary

* add codecov token

* make relative reference

* update changelog

* add use a generic fenceposting comment to mark docs literalinclude bounds

---------

Co-authored-by: kbrunik <102193481+kbrunik@users.noreply.github.com>
Co-authored-by: John Jasa <johnjasa11@gmail.com>
Co-authored-by: genevievestarke <103534902+genevievestarke@users.noreply.github.com>
Co-authored-by: Jared Thomas <jaredthomas68@users.noreply.github.com>

* Remove some doc warnings (#550)

* Beginning to address doc build warnings

* Removing duplicate kwarg docstrings

* updated ammonia model to need n2 and electricity inputs (#544)

Co-authored-by: John Jasa <johnjasa11@gmail.com>

* yamlfix all yaml files; add yamlfix to the pre-commit hook (#551)

* add yamlfix to the pre-commit

* yamlfix'd all yamls

* Update changelog

* Rolling back other pre-commit changes

* Excluded yamls needed for duplicate keys tests

* Excluded yamls needed for duplicate keys tests

* Switch to keep style for sequences

* Switch to keep style for sequences

* Apply suggestions from code review

Co-authored-by: Rob Hammond <13874373+RHammond2@users.noreply.github.com>

* Applying Rob's suggestions

* leave not on malfunctional config and reapply settings

---------

Co-authored-by: Rob Hammond <13874373+RHammond2@users.noreply.github.com>

* Enhancement: Automatically insert technology names in control strategy definitions (#558)

* add tech name to control parameters in setup

* remove obselete test and associate files

* handle no keys in control parameters and update testing

* Add a simple nuclear plant performance and cost model (#538)

* Added first pass at a simple nuclear model

* Adding nuclear doc page

* Added to changelog

* Doc cleanup

* Fixed OpEx calc for nuclear

* Simplifying nuclear test

* Addressing PR comments

* Marking tests

* Updates based on PR feedback

* Split out Pyomo Controllers to separate files (#549)

* split out pyomo control models

* added pyomo controller baseclass

* removed n_horizon_window from control strategies

* moved round_digits to pyomo base config

* other minor updates

* fixed failing tests

* Update changelog

---------

Co-authored-by: John Jasa <johnjasa11@gmail.com>

* Update Natural GeoH2 with Yearly CF (#552)

* Tech and system model figures (#554)

* tech and system model diagrams

* add comment about modularity and custom models

* Update docs/intro.md

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Apply suggestion from @bayc

Co-authored-by: Chris Bay <12664940+bayc@users.noreply.github.com>

* Apply suggestion from @bayc

Co-authored-by: Chris Bay <12664940+bayc@users.noreply.github.com>

* Apply suggestions from code review

Co-authored-by: Chris Bay <12664940+bayc@users.noreply.github.com>

* correct typo and unify figure specs

* typo correction

* update changelog

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Chris Bay <12664940+bayc@users.noreply.github.com>

* Removed hard-coded logic for electrolyzer replacement schedule (#555)

* draft

* updated finances to take in replacement schedules for all techs

* updated feedstock and added logic to prevent connecting replacement schedule for transport components

* added doc page about naming convention

* removed unnecessary elif statement in connect_technologies

* added test for new error message

* updated inline comments for finance models

* Minor refactor and cleanup for replacement cost vars

* updated naming for system finance model

* reverted change to ex 1 run file

---------

Co-authored-by: John Jasa <johnjasa11@gmail.com>

* Require a PR link in the CHANGELOG (#572)

* update PR template to better describe changelog requirements

* update changelog

* Update to PySAM Battery so size is changed in compute() (#557)

* made storage capacity and charge rate inputs for PySAM Battery model

* removed outputs object from pysam battery

* fixed logic in PySAM Battery model if not using a pyomo controller 

* refactored pyomo rule baseclasses to set default values from config

* updated heuristic controller to take in storage capacity input

* renamed battery outputs for charge and discharge

---------

Co-authored-by: John Jasa <johnjasa11@gmail.com>

* Minor cleanup of yamls after tech_name change (#559)

* Minor cleanup of yamls after tech_name change

* Updates to make more tests pass

* Updates to make more tests pass

* Renamed percent to fraction for storage (#581)

* Renamed percent to fraction for storage

* Updated changelog

* Reorganizing utilities (#586)

* Reorganizing utilities

* Updated changelog

* Added missing tools file

* Added missing tools file

* Generic Storage Model that's Compatible with Pyomo Controllers (#571)

* Added generic storage model (`StoragePerformanceModel`) that is compatible with Pyomo control strategies

* Updated the `simulate()` method of PySAM battery model have same inputs and similar calculations as `StoragePerformanceModel`


---------

Co-authored-by: John Jasa <johnjasa11@gmail.com>

* Moving NREL to NLR API keys and naming throughout (#583)

* Moving NREL to NLR API keys and naming throughout

* Added to changelog

* Moved a few more references from nrel to nlr

* Updating a few more NLR references

* Fix incorrect system sizing for Papadias hydrogen storage cost models (#588)

* Moving NREL to NLR API keys and naming throughout

* Added to changelog

* Moved a few more references from nrel to nlr

* Updating a few more NLR references

* Changing hydrogen storage units

* Updated changelog

* Updated test values based on h2 storage cost bugfix

* Fixing examples drift (#584)

* Fixing examples drift

* Added to changelog

* Fixing example notebook

* Updating jupytext

* Adding test and doc changes (#582)

* Adding test and doc changes

* Update docs/user_guide/postprocessing_results.md

Co-authored-by: genevievestarke <103534902+genevievestarke@users.noreply.github.com>

---------

Co-authored-by: genevievestarke <103534902+genevievestarke@users.noreply.github.com>

* Update openmdao version constraint in pyproject.toml (#592)

* Fixing OM 3.43 related bug (#595)

* Fixing OM 3.43 related bug

* Added to changelog

* Steam Methane Reforming Hydrogen Converter (#594)

* SMR wip

* test performance

* cost regression test

* docstrings and changelog

* Added hydrogen and electricity unmet

* Minor docstring update

---------

Co-authored-by: John Jasa <johnjasa11@gmail.com>

* reorder system when transporters are used (#591)

* reorder system when transporters are used
* remove battery to steel cable
* update changelog
* Apply suggestion from @RHammond2

Co-authored-by: Rob Hammond <13874373+RHammond2@users.noreply.github.com>

---------

Co-authored-by: Rob Hammond <13874373+RHammond2@users.noreply.github.com>
Co-authored-by: John Jasa <johnjasa11@gmail.com>

* Bugfix and Renaming in SMR model (#596)

* minor renaming to smr model

* fixed bug in smr variable om cost calculation

* removed commented out code

* Minor: added option to print results in `post_process()` method (#597)

* added option to print results in h2i.post_process method

* Updated docstring and changelog

---------

Co-authored-by: John Jasa <johnjasa11@gmail.com>

* reorg changelog and bump version

---------

Co-authored-by: elenya-grant <116225007+elenya-grant@users.noreply.github.com>
Co-authored-by: John Jasa <john.jasa@nrel.gov>
Co-authored-by: genevievestarke <103534902+genevievestarke@users.noreply.github.com>
Co-authored-by: Jared Thomas <jaredthomas68@gmail.com>
Co-authored-by: bayc <christopher.j.bay@gmail.com>
Co-authored-by: kbrunik <102193481+kbrunik@users.noreply.github.com>
Co-authored-by: kbrunik <kbrunik@gmail.com>
Co-authored-by: John Jasa <johnjasa11@gmail.com>
Co-authored-by: Jared Thomas <jaredthomas68@users.noreply.github.com>
Co-authored-by: Jonathan Martin <94018654+jmartin4u@users.noreply.github.com>
Co-authored-by: jmartin4 <jonathan.martin@nrel.gov>
Co-authored-by: Jonathan Martin <94018654+jmartin4nrel@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Chris Bay <12664940+bayc@users.noreply.github.com>
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.

4 participants