Skip to content

Baseclass for Storage Performance Models#624

Merged
johnjasa merged 20 commits into
NatLabRockies:developfrom
elenya-grant:dispatch/storage_model_baseclass
Mar 31, 2026
Merged

Baseclass for Storage Performance Models#624
johnjasa merged 20 commits into
NatLabRockies:developfrom
elenya-grant:dispatch/storage_model_baseclass

Conversation

@elenya-grant
Copy link
Copy Markdown
Collaborator

@elenya-grant elenya-grant commented Mar 25, 2026

Baseclass for Storage Performance Models

This PR introduces a storage performance baseclass. The baseclass will help standardize the inputs and outputs of storage performance models.

The storage baseclass is in h2integrate/storage/storage_baseclass.py. This would be inherited by the following storage performance models:

  • StoragePerformanceModel in h2integrate/storage/storage_performance_model.py
  • PySAMBatteryPerformanceModel in h2integrate/storage/battery/pysam_battery.py
  • StorageAutoSizingModel in h2integrate/storage/simple_storage_auto_sizing.py

Some notes on the current performance baseclass:

  • the setup() method includes logic that would make it easy-to-use for all storage performance models, that is why it checks the configuration class for certain keys.
  • the run_storage() method is the main method that should be called at the end of compute(). This takes in the necessary information to simulate the storage performance and set the outputs
  • the simulate() method currently exists in the baseclass. This method would be over-written in the PySAMBatteryPerformanceModel

This PR is ready for a full-review.

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 (once reviewers provided initial feedback):

  • Update storage performance models and configuration classes to inherit the baseclass and base configuration class
  • Remove commented out code from the baseclass file

Type of Reviewer Feedback Requested (on Draft PR)

Structural feedback:

Implementation feedback:

  • I like the setup() method as-is, but I was curious what folks think about inputs that are added based on what's in the config? (Like charge rate and storage capacity only being inputs if those corresponding keys are in the config)
  • Options for the simulate() method (which is preferred?)
    1. simulate() lives in the baseclass and is over-written by the PySAM battery model (as it is currently) OR
    2. simulate() raises a not-implemented error in the baseclass and each model has their own version of it (meaning that the method would be the same in the StoragePerformanceModel and StorageAutoSizingModel

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
    • At least one complete sentence has been provided to describe the changes made in this PR
    • After the above, a hyperlink has been provided to the PR using the following format:
      "A complete thought. [PR XYZ]((https://github.com/NatLabRockies/H2Integrate/pull/XYZ)", where
      XYZ should be replaced with the actual number.

Section 3: Related Issues

Part of Issue #564

Section 4: Impacted Areas of the Software

Section 4.1: New Files

  • h2integrate/storage/storage_baseclass.py
    • StoragePerformanceBaseConfig: Base configuration class for storage performance models, currently includes the attributes that are shared across all storage performance models.
    • StoragePerformanceBase: new baseclass for storage performance models
      • setup(): initializes inputs and outputs of storage performance models
      • run_storage(): method that runs the simulate() method and calculates the outputs of the storage performance model
      • simulate(): simple storage simulate method that's used by StoragePerformanceModel and StorageAutoSizingModel (the PySAM battery model overwrites this method)

Section 4.2: Modified Files

  • h2integrate/storage/battery/pysam_battery.py: updated to use base config and baseclass
    • with the inheritance of the new baseclass, some output naming changed. Specifically:
      • battery_electricity_discharge -> storage_electricity_discharge
      • battery_electricity_charge -> storage_electricity_charge
      • battery_electricity_out -> storage_electricity_out
  • h2integrate/storage/simple_storage_auto_sizing.py: updated to use base config and baseclass
  • h2integrate/storage/storage_performance_model.py: updated to use base config and baseclass

Section 4.3: Removed Files

  • h2integrate/storage/battery/battery_baseclass.py

Section 5: Additional Supporting Information

Section 6: Test Results, if applicable

@elenya-grant elenya-grant added ready for review This PR is ready for input from folks dispatch related to dispatch and control labels Mar 25, 2026
# StoragePerformanceModel
# Do whatever pre-calculations are necessary, then run storage
self.dt_hr = int(self.options["plant_config"]["plant"]["simulation"]["dt"]) / (
60**2
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.

switching it up from the 3600 I see

)

# Input storage design parameters
if "max_charge_rate" in self.config.as_dict():
Copy link
Copy Markdown
Collaborator

@kbrunik kbrunik Mar 25, 2026

Choose a reason for hiding this comment

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

I like the if statements in the setup()

# create inputs for pyomo control model
if "tech_to_dispatch_connections" in self.options["plant_config"]:
# get technology group name
# TODO: The split below seems brittle
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.

Are we worried about this TODO now that it's coming into a base class?

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.

I'm not sure. I can't think of a better way to do it. Any changes to this logic may also require updating the logic in the setup() method of PyomoControllerBaseClass. I personally don't know of a better way to handle this logic at the moment

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.

@johnjasa or @RHammond2 any thoughts?

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.

Perhaps pulling a specific entry from the split() output would be better. However, I think this should generally work as long as no two technologies have the same name, even if they are the same type (e.g. two batteries in the system should be battery_1 and battery_2 or something similar.

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 feel like @johnjasa's framing of "should be ok" spells trouble down the road. I tend to avoid this kind of maneuver unless there's a validation involved. In a separate project, I entirely removed a large series of these combined naming structures as it created some extreme brittleness in naming conventions. The other concern is it becomes less clear over time what information is actually being captured, leading to difficulty in maintainability.

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.

If someone has a suggestion on an alternative approach then I'm happy to try it.

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.

Potentially we could give each tech it's own unique name, which I think could be helpful on multiple fronts not just dispatch.

self.tech_name = self.options.get("tech_name")

Then:

using_feedback_control = False

connections = self.options["plant_config"].get("tech_to_dispatch_connections", [])

for _source_tech, intended_dispatch_tech in connections:
    if intended_dispatch_tech == self.tech_name:
        self.add_discrete_input("pyomo_dispatch_solver", val=dummy_function)
        using_feedback_control = True
        break

I think this would be a bit more work than this PR is meant to do and relates very much so to #620. I think we could leave this for another PR.

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.

Sounds great, let's tackle this in the future as part of #620.

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.

Just to clarify, I do think that we could do battery_1 and battery_2. For example, if we're using a battery and named it battery_1, then the pathname attribute when running H2I is plant.battery.PySAMBatteryPerformanceModel, so the self.tech_group_name is ["plant", "battery_1", "PySAMBatteryPerformanceModel"]. And tech_to_dispatch_connections would be something like:

tech_to_dispatch_connections:
  - [wind, battery_1]
  - [battery_1, battery_1]

so - as its looping through the tech_to_dispatch_connections, the statement if any(intended_dispatch_tech in name for name in self.tech_group_name) should evaluate to True because battery_1 is in the list. I think this would be brittle if the "in" was checking the "in" of a string, but since its an "in" a list - I think its actually sound logic.

)
return outputs

def simulate(
Copy link
Copy Markdown
Collaborator

@kbrunik kbrunik Mar 25, 2026

Choose a reason for hiding this comment

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

I like the simulate method in the base class because then it makes it clear that it's a storage method. And I don't mind that it's overwritten in the PySAM battery model

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.

Ditto, I'm glad to see this in the base class

charge_rate, discharge_rate, storage_capacity, inputs, outputs, discrete_inputs
)

def run_storage(
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.

When this is ready I would love to see a more robust docstring explaining how to use run_storage() in classes that inherit this base class

Copy link
Copy Markdown
Collaborator Author

@elenya-grant elenya-grant Mar 25, 2026

Choose a reason for hiding this comment

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

I just pushed up a doctoring to for the run_storage() method, but I will also add comments in the compute() method once we update the other storage performance models to inherit it.

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.

Thank you for including an example in the docstring! Very helpful.

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.

I generally like where this is going. I have a lot of thoughts about the inputs and outputs, some of which I have shared in #521 and will likely need to be addressed in later PRs. Good job bringing more order and clarity to the dispatch/control code.

# create inputs for pyomo control model
if "tech_to_dispatch_connections" in self.options["plant_config"]:
# get technology group name
# TODO: The split below seems brittle
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.

Perhaps pulling a specific entry from the split() output would be better. However, I think this should generally work as long as no two technologies have the same name, even if they are the same type (e.g. two batteries in the system should be battery_1 and battery_2 or something similar.

)
return outputs

def simulate(
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.

Ditto, I'm glad to see this in the base class

@elenya-grant elenya-grant marked this pull request as ready for review March 27, 2026 16:26
Copy link
Copy Markdown
Collaborator

@kbrunik kbrunik left a comment

Choose a reason for hiding this comment

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

Thanks @elenya-grant for putting this together! It really helps clarify what's unique to each storage method and the base class is very clearly laid out with nice docstrings 😎

I had a couple of small comments regarding the docstrings but should be pretty easy to resolve and won't require another re-review. Thanks again!

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.

Love how much simpler the pysam_battery is now!!

Comment thread h2integrate/storage/storage_baseclass.py Outdated
Comment thread h2integrate/storage/storage_baseclass.py Outdated
# create inputs for pyomo control model
if "tech_to_dispatch_connections" in self.options["plant_config"]:
# get technology group name
# TODO: The split below seems brittle
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.

Potentially we could give each tech it's own unique name, which I think could be helpful on multiple fronts not just dispatch.

self.tech_name = self.options.get("tech_name")

Then:

using_feedback_control = False

connections = self.options["plant_config"].get("tech_to_dispatch_connections", [])

for _source_tech, intended_dispatch_tech in connections:
    if intended_dispatch_tech == self.tech_name:
        self.add_discrete_input("pyomo_dispatch_solver", val=dummy_function)
        using_feedback_control = True
        break

I think this would be a bit more work than this PR is meant to do and relates very much so to #620. I think we could leave this for another PR.

charge_rate, discharge_rate, storage_capacity, inputs, outputs, discrete_inputs
)

def run_storage(
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.

Thank you for including an example in the docstring! Very helpful.

Comment thread h2integrate/storage/storage_baseclass.py Outdated
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.

Thank you for this, Elenya! It makes so many of the storage models much cleaner and easier to understand.

I have some very minor questions/suggestions and also pushed up some small changes. We're on a call right now so we already chatted about my big picture thoughts.

demand_profile: int | float | list = field()


class StoragePerformanceBase(PerformanceModelBaseClass):
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.

Why not have this live in model_baseclasses.py? We have performance and cost baseclasses there already. Should the storage one live here instead of with those others?

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.

I think that sense this is specific to storage performance models it makes sense to live in the storage folder. A model in converters should not inherit this. I think baseclasses that can be used for storage or converters can live in model_baseclasses.py but I think that baseclasses that should only be inherited by a specific type of model should live in that folder. Just like how it wouldn't make sense (to me) for h2integrate/control/control_strategies/pyomo_controller_baseclass.py to live in model_baseclasses - because its specific to control strategies.

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.

Thanks, I agree with your logic!

Comment on lines +98 to +99
if not self.config.as_dict().get("charge_equals_discharge", True):
# add if max_discharge_rate in config
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.

This if statement and the comment don't seem to match up. Could you clarify in the code, either the logic or the comment?

Copy link
Copy Markdown
Collaborator Author

@elenya-grant elenya-grant Mar 31, 2026

Choose a reason for hiding this comment

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

@johnjasa did you already address this? it says its outdated for me

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 haven't modified the part that I have a question about:

        if not getattr(self.config, "charge_equals_discharge", True):
            # add if max_discharge_rate in config
            self.add_input(
                "max_discharge_rate",
                val=self.config.max_discharge_rate,
                units=commodity_rate_units,
                desc="Storage discharge rate",
            )

It's checking for charge_equals_discharge but then the comment mentions max_discharge_rate; it seems unrelated? I might be misinterpreting

Copy link
Copy Markdown
Collaborator Author

@elenya-grant elenya-grant Mar 31, 2026

Choose a reason for hiding this comment

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

They're super related. If charge_equals_dicharge is True - then the discharge rate is the same as the charge rate so we don't need the input for max_discharge rate. If charge_equals_discharge is False, then the discharge rate has to be specified in the config and we set it as its own input. I updated the inline comment to be a bit more clear hopefully.

# create inputs for pyomo control model
if "tech_to_dispatch_connections" in self.options["plant_config"]:
# get technology group name
# TODO: The split below seems brittle
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.

Sounds great, let's tackle this in the future as part of #620.

@johnjasa johnjasa enabled auto-merge (squash) March 31, 2026 22:18
@johnjasa johnjasa merged commit 95d3dff into NatLabRockies:develop Mar 31, 2026
5 checks passed
johnjasa added a commit that referenced this pull request Apr 15, 2026
* Remove old iron models (#601)

* removed temporary iron and steel models

* supported models

* update example 28 with updated models

* remove outdated files

* consolidate iron examples

* fix all the stuff I broke

* update docs and changelog

* update example name and add model names to docs

* update changelog

---------

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

* update SMR energy conversion ratio and tests (#606)

* update SMR energy conversion ratio and tests

* update changelog

* docs

* minor updates to inline comments

* update usage based on hydrogen production

---------

Co-authored-by: Rob Hammond <13874373+RHammond2@users.noreply.github.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: 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>

* Break out Pyomo controller simulation code (#587)

* Breakout simulation code

* Remove duplicate code

* Fix testing

* Update doc strings and docs

* added docstring for heuristic controller config

* fixed issue that I made with the HeuristicLoadFollowingControllerConfig

* Update docs/control/pyomo_controllers.md

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

* Make initialize_parameters() functions the same for both controllers

* Apply suggestion from @jaredthomas68

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

* Fix doc string format

* Update h2integrate/control/control_strategies/heuristic_pyomo_controller.py

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

* Update h2integrate/control/control_strategies/heuristic_pyomo_controller.py

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

* Fix ruff formatting

---------

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

* Bugfix in Charge/Discharge efficiency handling in `StoragePerformanceModel` (#600)

* fixed how charge and discharge rates are applied in generic_storage_pyo model

---------

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

* Tests for non-one battery efficiencies (#610)

* add unit tests for  openloop storage controller with non-one efficiencies

* rearrange usage of pytest.approx so the target comes second and uses approx

* update changelog:

* Sync Storage Autosizing Model and Pass-Through controller to Align with Pyomo Controllers (#608)

* updated the storage autosizing and pass through controller for new control and performance model structure

* added subtest for integration with pass through controller

* updated to use commodity_set_point instead of input-demand

* made set_demand_as_avg_commodity_in a required input parameter

* moved passthrough controller to storage subfolder

* Added minor changes that got  missed in PR 600

---------

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

* Sync Demand Open Loop Controller and Simple Generic Storage to Align with Pyomo Controllers (#612)

* updated simple_generic_storage to simulate storage performance

* updated demand openloop controller so discharge setpoint is correct

* PySAM Marine Models (#607)

* tidal resource model and performance model

* add performance values for testing

* test power curve scaling

* marine cost model

* tidal example

* integration test

* documentation

* update docs

* small changes

* update for failing tests

* fix equation rendering

* fix segmentation fault

* add test for using default pysam config

* address reviewer feedback

* fix wind pysam

* attempt to modify file path handling for resource data

* fix path stuff

* Sync PySAM Battery and Generic Storage Pyomo Performance models with open-loop controllers (#613)

* refactored pysam battery and storage performance models to be compatible with updated open-loop controllers

* updated generic storage pyo to use pass through controller

* renamed PassThroughOpenLoopController to SimpleStorageOpenLoopController

* removed simple_generic_storage

* renamed xx_charge_fraction to xx_soc_fraction

---------

Co-authored-by: Genevieve Starke <genevieve.starke@nrel.gov>

* minor error msg and doc corrections (#618)

* CO2 Model Fixes (#617)

* units and connecting variables

* fix doc page

* catch if annual_input_energy is 0

* remove base class

* fix oae test

* failing doc test

* fix value call

* final touchs

* Bugfix: Update storage models so can use different control types per storage type (#615)

* updated logic in pysam batery and generic storage to handle multiple storage types

* added tests and bugfix in heuristic with setting efficiencies

---------

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

* Baseclass for Openloop Storage Control Strategies (#619)

* added openloop control storage baseclass

* Enable use of Pyomo Controllers in the Storage Autosizing Model (#621)

* updated autosizing storage model to be compatible with pyomo controllers

* renamed fixures in heuristic controller tests

* aggregated heuristic controller tests into one file

* added simple test for storage autosizing and heuristic

* renamed fixtures in optimized controller tests

* aggregated optimized control tests into one file

* updated test for heuristic controller with autosizing

* updated so simple storage autosizing can run with optimal pyomo control

* Generic cost model for converters (#622)

* added generic cost model

* added tests and updated model

* updated example 3 to use new converter model and added to supported models

* updated docstrings and input descriptions

* updated changelog

* added to model overview

* Clarified error message

---------

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

* Introducing multivariable commodity streams (#480)

* Working out first steps of multivariable streams

* Adding to multivariable streams work

* expanding multivariable stream definition

* Building out more of the gas combiner

* Improving example for multivariable streams

* Updating examples and merging

* Apply suggestions from code review

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

* Many updates based on PR feedback

* Updated to mass fractions for multicommodity stream

* renamed a few multicommodity variable names

* Added helper functions for multivariable stream definitions

* Updated names throughout for multivariable streams

* Updated docs for multivariable streams

* Added commodity stream tests

* Addressing PR feedback

* Updates based on PR feedback

* Updates based on PR feedback

---------

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

* Removed some shape_by_conn calls due to OM issues (#632)

* Removed some shape_by_conn calls due to OM issues

* Moved last calls of copy_shape

* Fixing test_pipe fail

---------

Co-authored-by: jmartin4 <jonathan.martin@nrel.gov>

* Move xdsm call in H2IntegrateModel to its own method (#629)

* move xdsm creation in H2IModel to a method so user has to call it if desired instead of having it be automatic

* update changelog and docs and adjust create_xdsm logic to reflect that it is an optional call

* update docs

* update docs

* update docs for visualization

* change xdsm code cell to not execute on build

* switch pdf for png

* minor comments update in code example

* remove xdsm call

* Updated XDSM explanation link

---------

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

* Handling Leap Years in OpenMeteo Resource Data (#633)

* updated openmeteo resource models to handle leap day

* added resource files for leap year

* added tests for leap year resource data

* added integration w pvwatts test for leap year

* Methodized leap day handling for base class for future resource handling

---------

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

* Baseclass for Storage Performance Models (#624)

* added draft storage model baseclass

* updated draft storage baseclass

* added docstring to run_storage method

* minor updates to comments in storage_model_baseclass

* updated storage performance model to inherit baseclass

* fixed storage performance model and storage baseclass

* updated storage autosizing model

* updated pysam battery to use baseclass and removed battery_baseclass

* removed commented out code and simplified some docstrings

* removed unnecessary docstrings

* updated changelog

* PR review: minor personal preferences

* updated inline comment

---------

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

* Modified the calc tilt angle function for pysam solar (#646)

* Oxygen output from PEM electrolyzer (#642)

* added oxygen output from electrolyzer

* updated example 14 to calc LCOO

---------

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

* Removed remaining naming dependencies (#654)

* Removed remaining naming dependencies

* Added to changelog and fixed CI issue

* PR feedback

* Fixed schema reqs

* Update validators to allow zero values for costs for nuclear

* Bugfix: Connecting resource to multiple techs (#655)

* bugfix and added test for connecting one resource model to multiple techs

* updated changelog

* Updated test to call setup to trigger connection error

---------

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

* Update urls for NLR pages and github repositories (#658)

* Update urls for NLR pages and github repositories

* Updated from WISDM to NLRWindSystems org for some repos

* Updated and removed more NREL references

---------

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

* Enhancement/windows ci (#590)

* update installation instructions

* add windows to CI and use conda consistently for Python across OS

* merge develop and update changelog

* adopt 'extras' flag for analysis extras to correctly use optional CI

* update changelog

* reorder ci setup

* update order

* attempt python version restructure

* reinstate old python version

* update URL

* move runs-on back to below strategy

* add os to optional matrix

* reorganize the conditional for extras

* convert lists to yaml bullet lists

* skip gis-dependent tests when not installed

* update changelog

* use optional deps for all tests and add single runner for validating dependency-determined skipped tests

* reformatting

* reduce sql file access scope to functions to avoid windows errors

* downgrade temp_dir to a function scope to avoid windows access errors

* move temp examples fixture to be in main conftest, and enable repo-wide access

* add module scoped example directory copy

* apply module scoped example copy fixture to recorder tests and update

* update changelog

* update miniconda action usage

* use the example copy fixture to isolate sql access in tests for potential windows resolution

* reorganize changelog

* fix typo in parameter name

* update workflow

* remove max-parallel

* Revert "reorganize changelog"

This reverts commit 679a38d.

* add shell to all tests

* don't allow coverage upload to fail ci

* uncomment windows

* add garbage collection to help with sql file handling

* temporarily only run windows to reduce runner usage

* add sleep to see if small timeout resolves issue

* only run single test

* remove garbage collection

* fix typo

* remove manual folder removal in favor of pytest cleanup

* reinstate normal CI processes

* add file removal to avoid OpenMDAO silent failure

* temporarily only run single file on single windows runner

* fix typo

* fix typo

* move manual recorder_path deletion

* put ci back

* handle None case

* track model state using IntEnum

* add xfail for windows because of OpenMDAO improper SQL file handling for Windows

* add todo about removing manual removal

* restore accidentally deleted file

---------

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

* updated CI workflow to disable windows (#668)

* Check for extraneous or mis-categorized input parameters (#647)

* added dictionary utilities

* bugfix in setup() and added start of a test

* added fix to calling check_inputs

* fixed check_inputs

* added another check in check_inputs

* updated error messages in check_inputs and fixed example 1

* Added tests

* added another subtest to test_check_inputs

* fixed tech configs for examples

* added some inline comments

* check for requisite models and return if none

* perform check from note and collapse checking logic

* collapse user input regeneration to reduce number of loops

* remove nested looping approach

* move assert to proper context manager level

* add indent level back

* fix min combinations logic for passing tests

* flatten improperly shared parameterization checking logic

* update test to include multiple categories for improperly shared

* fix typo

* update docstring

* added handling for combined cost and performance models

* Added tech config fpath to the error messages for parameter checking

* changlog

* changed strict to True for six converter models

* reverted strict setting for electrolyzer

---------

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

* Update Naming Convention in Open-Loop Converter Control Strategies (#631)

* renamed outputs in converter openloop controllers

* updated tests and examples with updated naming

* added performance model outputs to the converter control strategies

* moved converter control strategies to demand folder

* removed commodity_set_point as output

* renamed demand component to be performance model instead of control strategy

* moved output calculations to shared method in baseclass
---------

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

* modified state check for setup and run (#669)

* Add interactive class hierarchy visualization to docs (#643)

* Added class hierarchy diagram

* merging

* Updated the PR template

* Changed class hierarchy embedding

* moved class hierachy

* Breaking the sphinx narrow rule

* Combined colors based on feedback

* Updated colors based on Gen's feedback

* Shifted diagram location right

* Reformatting embedding the class viz structure

* Updated dev pyproject for viz tool

---------

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

* Standardize Feedstock Outputs (follow-on to 463) (#523)

* added capacity and price as inputs to feedstock component

* updated feedstock mdoel to have standard outputs

* added commodity_amount_units to FeedstockCostConfig

* added standard output to feedstock cost model and connected commodity_out between performance and cost model

* renamed config inputs for feedstocks

* updated ex 23 which is untested and cleaned up feedstocks.py

* minor docstring updates and added comments

* added in notes of questions for reviewers to feedstocks.py

* updated MMBtu units to MMBtu/h

* fixed iron and steel tests

* made changes to feedstock

* fixed tests

* removed unused comment

* removed shape from price input

* added integration test for feedstock integrated with finance model

* updated feedstocks.md

* changed based on reviewer feedback

* added subtests to test_feedstocks

* small changes to feedstock doc

* docs

---------

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>

* Framework to allow for variable dt (#653)

* add draft for variable timestep framework

* Changed error wording

* switch bounds to tuple

* minor test correction

* include time-series generation functions

* add tests for variable dt and update simulation length check for non-hourly dt

* update docs

* update changelog

* update docs and doc strings

* restore develop version of utilities.py

* update dt bounds error

* Apply suggestions from code review

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

* Fixing tests

* Adding time step bounds to all models

* Adding time step bounds to the combiner/splitter/transporters

* Updates for tests

* Adding time step bounds to the custom paper mill performance model

* Added time step bounds to paper mill cost

---------

Co-authored-by: John Jasa <johnjasa11@gmail.com>
Co-authored-by: kbrunik <102193481+kbrunik@users.noreply.github.com>
Co-authored-by: John Jasa <john.jasa@nrel.gov>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* patch: Variable dt (#671)

* update dt bounds error to require `_time_step_bounds`
* Apply suggestions from code review
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Add time step bounds to all tech models
* Apply time step bounds check to feedstocks, transporters, and combiners
---------

Co-authored-by: John Jasa <johnjasa11@gmail.com>
Co-authored-by: kbrunik <102193481+kbrunik@users.noreply.github.com>
Co-authored-by: John Jasa <john.jasa@nrel.gov>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Remove system-level outputs from storage and replace with demand component (follow-on to 631) (#666)

* removed system level calcs from storage and updated most tests

* updated examples with storage but dont use storage as a commodity_stream in finances

* updated examples with storage defined as a commodity_stream for a finance subgroup

* added time_step_bounds to demand components

* added test for example 23

* added example for different demand between storage and demand component

* added demo for the demand components using example 13

* made it so demand is only input to battery performance model if using feedback control

* added standard_capacity_factor as output from storage

* added doc page for storage performance models

---------

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

* Updating the readme and intro doc page ahead of v0.8 release (#677)

* Updating the readme

* Updated intro.md and changelog

* Minor readme updates

* Addressing Kaitlin's comments

* Added more lingo to readme

* Addressing PR suggestions

---------

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

* Move storage controllers into `storage` folder in `control_strategies` (#678)

* moved controllers to storage folder

* renamed pyomo controllers to include storage in the control name

* moved control strategy tests

* moved tests to the right folder

* moved pyomo baseclass and controller_opt_problem_state to top-level control_strategies

* updated import statements

* renamed pyomo baseclass file

* updated import for pyomo storage baseclass

* made storage chapter in docs

* removed backslashes from eqn since they dont render properly on readthedocs

* Making changes per Gen's suggestion (#681)

* Bump version from 0.7.2 to 0.8.0

---------

Co-authored-by: kbrunik <102193481+kbrunik@users.noreply.github.com>
Co-authored-by: elenya-grant <116225007+elenya-grant@users.noreply.github.com>
Co-authored-by: Rob Hammond <13874373+RHammond2@users.noreply.github.com>
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: 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>
Co-authored-by: Genevieve Starke <genevieve.starke@nrel.gov>
Co-authored-by: kbrunik <kbrunik@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dispatch related to dispatch and control ready for review This PR is ready for input from folks

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants