Skip to content

Converter PLM dispatch - #773

Open
jaredthomas68 wants to merge 50 commits into
NatLabRockies:developfrom
jaredthomas68:control-converter
Open

Converter PLM dispatch#773
jaredthomas68 wants to merge 50 commits into
NatLabRockies:developfrom
jaredthomas68:control-converter

Conversation

@jaredthomas68

@jaredthomas68 jaredthomas68 commented May 28, 2026

Copy link
Copy Markdown
Collaborator

Converter PLM control

This PR adds peak load management dispatch for converters. It handles two load profiles, one representing an upstream (grid) load and the other representing either a local load that must be met or a price profile. The converter will be dispatched when either of the demand profiles exceeds their respective peak limits. If a price profile is given, then dispatching will only occur when the price exceeds the provided cutoff.

This PR also provides minor updates to the fuel cell model to determine VarOpEx.

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:

  • Add tests
  • Finish example
  • Add documentation

Type of Reviewer Feedback Requested (on Draft PR)

Ready for any and all feedback. Let's get this PR polished and merged.

Structural feedback:
Looking for high-level feedback at this point from any who wish to give it.

Implementation feedback:

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 4: Related Issues

Section 5: Impacted Areas of the Software

Section 5.1: New Files

  • plm_openloop_converter_controller.py
    • PeakLoadManagementHeuristicOpenLoopConverterControllerConfig: New config plm converter control
    • PeakLoadManagementHeuristicOpenLoopConverterController.setup
    • PeakLoadManagementHeuristicOpenLoopConverterController.compute: Computes a per-timestep {commodity}_command_value that peak-shaves using primary/upstream cutoffs, clipped to instantaneous demand and rated production.
  • init.py
    • Exports the new PeakLoadManagementHeuristicOpenLoopConverterController
  • run_peak_load_management.py, 33_plm_converter_heuristic.yaml, tech_config.yaml, plant_config.yaml, driver_config.yaml
    • New converter example that dispatches a fuel cell against local and upstream demand profiles and plots the peak-shaving result.

Section 5.2: Modified Files

  • h2integrate_model.py

    • _process_model: Adds a NonlinearBlockGS/DirectSolver to the tech group when a controller sets _reads_performance_outputs, so the controller/performance data cycle converges instead of using stale defaults.
  • h2_fuel_cell.py

    • LinearH2FuelCellPerformanceConfig/compute: Adds uptime_hours_until_eol and computes a replacement_schedule from cumulative "on" hours for use by the finance model.
    • H2FuelCellCostConfig/setup/compute: Adds variable_opex_per_kwh and an annual_electricity_produced input to output VarOpEx.
  • test_h2_fuel_cell.py

    • Adds subtests for the replacement schedule (integer/non-integer/multiple-per-year cases) and variable OpEx, and converts the cost test to a unit test.
  • test_all_examples.py

    • Repoints the storage test to 33_peak_load_management/plm_storage and adds test_plm_converter_heuristic_example covering the new converter example.
  • model_baseclasses.py

    • CostModelBaseClass: Removed a stray blank line (whitespace only).
  • .pre-commit-config.yaml: adjusted excluded files for demand_profile.yaml rename

  • Renamed demand profile files (.../demand_profiles/demand_profile.yaml, demand_profile_upstream.yaml)

    • Files relocated under the reorganized example folders; contents unchanged (rename only).

Section 6: Additional Supporting Information

Section 7: Test Results, if applicable

Section 8 (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
    • Run generate_class_hierarchy.py to update the class hierarchy diagram in docs/developer_guide/class_structure.md

@jaredthomas68
jaredthomas68 requested a review from johnjasa May 28, 2026 21:03
@jaredthomas68 jaredthomas68 mentioned this pull request May 29, 2026
51 tasks
@jaredthomas68
jaredthomas68 requested a review from kbrunik June 1, 2026 22:01

@kbrunik kbrunik left a 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.

Looking pretty good! Remarkable how much simpler the code is when you don't have to track state of charge! I know there was already some planned work that still needs to happen, so happy to re-review at that point

A few high-level notes for the PR:

  1. Be sure to update the changelog
  2. Add some basic tests to make sure the dispatch is occurring as expected (i.e., the tech is turns off after peak is "managed")
  3. Write a doc page for the converter PLM

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'm thinking it might be good to move this file to the resource_files folder at the top level of H2I so that it can be used in this example and 33_peak_load_management.

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.

or the library folder at the top level of H2I instead?

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 moved the demand profiles to the library

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.

For both this and the upstream file, could you please add comments at the start of the file about what this data means, where it came from, units, what example uses it, and anything else relevant?

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'm thinking it might be good to move this file to the resource_files folder at the top level of H2I so that it can be used in this example and 33_peak_load_management.

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.

moved to library

@@ -0,0 +1,5 @@
name: driver_config
description: Driver configuration for multivariable streams example

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.

Update description

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

control_parameters:
commodity: electricity
commodity_rate_units: kW
system_capacity_rate: 1000.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.

Remove from control section, example didn't run with it included.

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 removed system_capacity_rates, I think the others should stay there.

"""
Example 33: Peak load management dispatch

This example demonstrates:

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.

Update example description based on fuel cell/converter example.

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

@@ -0,0 +1,65 @@
name: technology_config
description: This plant charges a battery from the grid to reduce peak demand

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.

Update description

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

StorageOpenLoopControlBaseConfig
):
"""
Configuration class for the PeakLoadManagementHeuristicOpenLoopStorageController.

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.

update docstring

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


"""

system_capacity_kw: int | float = field()

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.

@elenya-grant and @johnjasa, I was discussing with Jared about how to make this control strategy tech agnostic. Right now it's hardcoded to have system_capacity_kw in the config. I'm wondering if y'all have ideas of what the best path forward is for this to work with all dispatchable converters.

We have access to the standardized commodity_rate_units but there's no standardization across how we define system capacity, which makes this challenging.

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.

well system capacity is a standard output of performance models (rated_{commodity}_production). I get confused on the order that models are added, but if the performance model is created in the tech subgroup before the controller, then the performance model would output the rated_{commodity}_production and that could then be an input to the PLM converter controller.

This is a good question though. I think the best thing (if possible) would be to have rated_{commodity}_production as an openmdao input.

Also - I don't think the capacity input in the config or the openmdao inputs should be standardized for a few reasons:

  1. In the config, it seems more acceptable to include units in config parameters to make it more clear to users what units that parameter should be in. The units are not included in OpenMDAO input names.
  2. Some technologies do not have a single capacity-related input. Some technologies have an input that would instead be a multiplier on some number of units (like number of electrolyzer clusters and cluster capacity OR number of wind turbines and wind turbine capacity).

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.

@elenya-grant, thank you for the idea. I successfully implemented the switch to rated_{commodity}_production to make the model technology agnositc. It does require a non-linear solver to resolve the output of the performance model into an input of the controller, which takes 3 iterations to converge. This should be fine as long as the performance model in question is fast-running. I also like how it opens up a possible path for more realistic control interactions between the performance and tech control models. We should probably discuss this more, somewhat related to #711 in that this solution adds a feedback loop in the tech model between the performance and tech control.

jaredthomas68 and others added 13 commits June 9, 2026 13:15
* varopex

* refurb schedule

* update fuel cell schedule calc to allow for non-integer multiples of eol and timesteps

* extend liftime for schedule test

* Update h2integrate/converters/hydrogen/h2_fuel_cell.py

---------

Co-authored-by: Jared Thomas <jaredthomas68@gmail.com>
Co-authored-by: Jared Thomas <jaredthomas68@users.noreply.github.com>
@jaredthomas68
jaredthomas68 marked this pull request as ready for review August 4, 2026 19:27

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR introduces a new open-loop “peak load management” dispatch controller for converter technologies (e.g., fuel cells), updates the fuel cell model to support refurbishment scheduling and variable OpEx, and reorganizes/extends peak-load-management examples and documentation to cover the new converter heuristic workflow.

Changes:

  • Added PeakLoadManagementHeuristicOpenLoopConverterController and registered it as a supported control strategy.
  • Extended the H2 fuel cell performance and cost models with a replacement schedule output and VarOpEx, plus new/updated unit tests.
  • Added a new converter PLM heuristic example and updated docs/example tests accordingly.

Reviewed changes

Copilot reviewed 23 out of 27 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
h2integrate/core/supported_models.py Registers the new converter controller for dynamic loading.
h2integrate/core/model_baseclasses.py Minor whitespace-only cleanup in CostModelBaseClass.
h2integrate/core/h2integrate_model.py Adds a nonlinear/linear solver when a controller reads performance outputs (to converge the intra-tech coupling).
h2integrate/converters/hydrogen/h2_fuel_cell.py Adds fuel-cell replacement scheduling and variable OpEx computation.
h2integrate/converters/hydrogen/test/test_h2_fuel_cell.py Adds/updates unit tests for replacement schedule and variable OpEx.
h2integrate/control/control_strategies/converters/plm_openloop_converter_controller.py New open-loop peak-shaving converter controller implementation.
h2integrate/control/control_strategies/converters/init.py Exports the new converter controller from the package.
examples/test/test_all_examples.py Repoints PLM storage example and adds integration coverage for new converter example.
examples/33_peak_load_management/driver_config.yaml Removes the old example driver config after example re-org.
examples/33_peak_load_management_heuristics/plm_storage/tech_config.yaml Updates description for reorganized PLM storage heuristic example.
examples/33_peak_load_management_heuristics/plm_storage/run_peak_load_management.py Updates the run script to use the new storage heuristic YAML entrypoint.
examples/33_peak_load_management_heuristics/plm_storage/plant_config.yaml Updates description for the reorganized storage heuristic example.
examples/33_peak_load_management_heuristics/plm_storage/driver_config.yaml Adds driver config for the reorganized storage heuristic example.
examples/33_peak_load_management_heuristics/plm_storage/33_plm_storage_heuristic.yaml New top-level YAML entrypoint for the storage heuristic example.
examples/33_peak_load_management_heuristics/plm_converter/tech_config.yaml Adds a new converter-based PLM heuristic example tech configuration.
examples/33_peak_load_management_heuristics/plm_converter/run_peak_load_management.py Adds run/plot script for the converter heuristic example.
examples/33_peak_load_management_heuristics/plm_converter/plant_config.yaml Adds plant config for the converter heuristic example.
examples/33_peak_load_management_heuristics/plm_converter/driver_config.yaml Adds driver config for the converter heuristic example.
examples/33_peak_load_management_heuristics/plm_converter/33_plm_converter_heuristic.yaml New top-level YAML entrypoint for the converter heuristic example.
docs/user_guide/model_overview.md Documents the new converter controller in the control models list.
docs/control/technology_level_control/technology_control_overview.md Adds the converter controller to the supported controllers list.
docs/control/technology_level_control/open-loop_controllers.md Documents open-loop converter controllers and points to the new example.
docs/_static/class_hierarchy.html Updates autogenerated class hierarchy to include the new controller class.
CHANGELOG.md Adds a changelog entry for the new converter controller.
.pre-commit-config.yaml Updates YAML formatting exclusions to reflect demand-profile file location changes.
Suppressed comments (2)

h2integrate/converters/hydrogen/test/test_h2_fuel_cell.py:171

  • Same issue as above: this block reuses and mutates the tech_config fixture in-place, which can leak changes into later subtests/tests. Make a deep copy before editing nested fields.
        tech_config_copy = tech_config
        tech_config_copy["model_inputs"]["performance_parameters"]["uptime_hours_until_eol"] = (
            8760 * 0.75
        )

examples/33_peak_load_management_heuristics/plm_storage/tech_config.yaml:5

  • This example's tech_config still references demand_profiles/demand_profile.yaml / demand_profile_upstream.yaml via !include, but there is no demand_profile*.yaml anywhere in the repo checkout. As a result, this example config will fail to load with FileNotFoundError unless those profile files are added/moved or the include paths are updated.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread h2integrate/converters/hydrogen/h2_fuel_cell.py
Comment thread h2integrate/converters/hydrogen/test/test_h2_fuel_cell.py Outdated
Comment thread h2integrate/converters/hydrogen/test/test_h2_fuel_cell.py

@johnjasa johnjasa left a 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.

This is great stuff, Jared! Glad to see it slot in so cleanly. I appreciate the new examples, updated docs, and expanded fuel cell models. I pushed up some small changes and left some comments/suggestions. I think some of them are worth considering, especially the vectorization of the compute for the PLM converter. Otherwise I think this is quite close to ready! Thanks for your great work and iterating.

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.

For both this and the upstream file, could you please add comments at the start of the file about what this data means, where it came from, units, what example uses it, and anything else relevant?

Comment on lines +34 to +36
demand_profile_upstream_kind (str): Interpretation mode for
``demand_profile_upstream``. One of ``"electricity"`` or ``"price"``.
Defaults to ``"electricity"``.

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.

Question about your mindset for this component and its limitations: is the demand profile truly just for electricity or price, or could this be extended as-is for other commodities, e.g. hydrogen or ammonia? I understand that peak load management is most often considered with electricity, but it's more me trying to figure out what would need to change to enable others, or if that was on your mind at all, or a purposeful limitation to simplify code or logic.

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.

No, they could be other things. This is tech/commodity agnostic. Price could technically be anything you want to use to trigger dispatch. I changed the name from electricity to commodity to reflect the commodity agnostic code changes.

super().__attrs_post_init__()


class PeakLoadManagementHeuristicOpenLoopConverterController(StorageOpenLoopControlBase):

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.

Both this and the config class inherit the baseclasses for Storage. Should we generalize the naming of those baseclasses, move them out of the storage folder, or something else? Or maybe it's a non-issue. I'm more thinking about potential confusion around a converter controller inheriting from a storage base.



@define(kw_only=True)
class PeakLoadManagementHeuristicOpenLoopConverterControllerConfig(

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 name and the class name are pretty lengthy; could we reasonably shorten it without loss of clarity? Maybe PLMHeuristicOpenLoopConverterControllerConfig or even more?

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.

since it inherits the StorageOpenLoopControlBaseConfig - I think you could at least remove OpenLoop from the name. What if it was just PLMHeuristicConverterControllerConfig

demand_profile_peak_cutoff = self.config.demand_profile_peak_cutoff
demand_profile_upstream = self.config.demand_profile_upstream
demand_profile_upstream_peak_cutoff = inputs["demand_profile_upstream_peak_cutoff"][0]
self.command_value = np.zeros(self.n_timesteps)

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.

Does this need to be an attribute of self? I would recommend just having it locally available within compute

Comment on lines +154 to +186
for idx, val in enumerate(demand_profile):
val_upstream = demand_profile_upstream[idx]
if (
val > demand_profile_peak_cutoff
or val_upstream > demand_profile_upstream_peak_cutoff
):
desired_dispatch = val - demand_profile_peak_cutoff

if self.config.demand_profile_upstream_kind == "electricity":
desired_dispatch_upstream = val_upstream - demand_profile_upstream_peak_cutoff

self.command_value[idx] = min(
max(
max(desired_dispatch, 0),
max(desired_dispatch_upstream, 0),
),
val,
rated_production,
)
elif self.config.demand_profile_upstream_kind == "price":
if val_upstream > demand_profile_upstream_peak_cutoff:
self.command_value[idx] = min(
max(desired_dispatch, 0),
val,
rated_production,
)
else:
raise (
ValueError(
f"Invalid demand_profile_upstream_kind \
'{self.config.demand_profile_upstream_kind}'"
)
)

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 section could be rewritten to avoid the top-level Python for-loop and to rely on numpy operations for speed and clarity. I've done that here and confirmed that the example gets the exact same results. I didn't want to push this directly to the branch as this is a good bit of changes, but please consider something like this.

Suggested change
for idx, val in enumerate(demand_profile):
val_upstream = demand_profile_upstream[idx]
if (
val > demand_profile_peak_cutoff
or val_upstream > demand_profile_upstream_peak_cutoff
):
desired_dispatch = val - demand_profile_peak_cutoff
if self.config.demand_profile_upstream_kind == "electricity":
desired_dispatch_upstream = val_upstream - demand_profile_upstream_peak_cutoff
self.command_value[idx] = min(
max(
max(desired_dispatch, 0),
max(desired_dispatch_upstream, 0),
),
val,
rated_production,
)
elif self.config.demand_profile_upstream_kind == "price":
if val_upstream > demand_profile_upstream_peak_cutoff:
self.command_value[idx] = min(
max(desired_dispatch, 0),
val,
rated_production,
)
else:
raise (
ValueError(
f"Invalid demand_profile_upstream_kind \
'{self.config.demand_profile_upstream_kind}'"
)
)
# Primary exceedance above the demand peak cutoff, clipped at zero.
desired_dispatch = np.maximum(demand_profile - demand_profile_peak_cutoff, 0.0)
if self.config.demand_profile_upstream_kind == "electricity":
# Upstream exceedance uses the same units as the commodity set-point.
desired_dispatch_upstream = np.maximum(
demand_profile_upstream - demand_profile_upstream_peak_cutoff, 0.0
)
# Dispatch is considered when either primary or upstream profile exceeds cutoff.
active_mask = (demand_profile > demand_profile_peak_cutoff) | (
demand_profile_upstream > demand_profile_upstream_peak_cutoff
)
# Follow the larger exceedance and cap by demand and rated production.
desired_command = np.maximum(desired_dispatch, desired_dispatch_upstream)
capped_command = np.minimum(np.minimum(desired_command, demand_profile), rated_production)
self.command_value = np.where(active_mask, capped_command, 0.0)
elif self.config.demand_profile_upstream_kind == "price":
# Price mode only dispatches when upstream price exceeds its threshold.
active_mask = demand_profile_upstream > demand_profile_upstream_peak_cutoff
# Cap dispatch by demand and rated production after clipping at zero.
capped_command = np.minimum(np.minimum(desired_dispatch, demand_profile), rated_production)
self.command_value = np.where(active_mask, capped_command, 0.0)
else:
raise ValueError(
f"Invalid demand_profile_upstream_kind "
f"'{self.config.demand_profile_upstream_kind}'"
)

Comment on lines +1079 to +1089
# Some controllers read performance-model outputs (e.g.
# ``rated_<commodity>_production``) as inputs, which creates a
# controller<->performance data cycle within the technology group. Add a
# nonlinear solver so this coupling is resolved instead of using stale
# (default) values on a single execution pass.
if model_type == "control_strategy" and getattr(
model_object, "_reads_performance_outputs", False
):
tech_group.nonlinear_solver = om.NonlinearBlockGS()
tech_group.linear_solver = om.DirectSolver()

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.

Fun to see some slick logic and easily-added solvers!

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.

Thanks!

@elenya-grant elenya-grant left a 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.

Hi Jared! Cool to see a converter controller! I agree with the comments by John and Kaitlin - but I think this looks good besides those! This is a nitpick and I'm not asking for you to change this (just noting it) - but I did get a little lost with the similarities in the variable names demand_profile_peak_cutoff and demand_profile_upstream_peak_cutoff



@define(kw_only=True)
class PeakLoadManagementHeuristicOpenLoopConverterControllerConfig(

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.

since it inherits the StorageOpenLoopControlBaseConfig - I think you could at least remove OpenLoop from the name. What if it was just PLMHeuristicConverterControllerConfig


_time_step_bounds = (
1e-12,
np.inf,

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 an infinite timestep is a little unlikely. Could we just make this a big number? Like - would it ever make sense to run this controller with a time step of 1 year? Even if it can handle a 1-year timestep - would that make sense?

Comment on lines +137 to +138
demand_profile_peak_cutoff = self.config.demand_profile_peak_cutoff
demand_profile_upstream = self.config.demand_profile_upstream

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.

should these be made as inputs if they aren't None in the config? Why is demand_profile_upstream_peak_cutoff an input but these aren't?

demand_profile_upstream_peak_cutoff (int | float | None): Threshold applied to
``demand_profile_upstream``. Units depend on
``demand_profile_upstream_kind``.
demand_profile_upstream_kind (str): Interpretation mode for

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.

For my own edification, you would have to use the demand_profile_upstream if you wanted to dispatch based on price only? and if that was the case would the demand profile basically be a dummy profile? An example of this case might be helpful to show the functionality or more explanation of this in the docs with perhaps an example tech config.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants