Skip to content

Demand Response Optimization for Battery Energy Storage (Stage 2) - #808

Open
abhineet-gupta wants to merge 11 commits into
NatLabRockies:developfrom
abhineet-gupta:PLM_DR
Open

Demand Response Optimization for Battery Energy Storage (Stage 2)#808
abhineet-gupta wants to merge 11 commits into
NatLabRockies:developfrom
abhineet-gupta:PLM_DR

Conversation

@abhineet-gupta

@abhineet-gupta abhineet-gupta commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator

Demand Response Optimization for Battery Energy Storage (Stage 2)

This PR is a continuation of PR 679.
It adds a Pyomo optimization based controller for the BESS system.
The controller optimizes the battery dispatch to minimize the Co-Op's expenditure while allowing demand response capabilities based on G&T requirements during peak window.

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:

Type of Reviewer Feedback Requested (on Draft PR)

Structural feedback:
Do you agree with the terminology used in describing this approach in documentation.

Implementation feedback:
Is this the correct way to add highs solver for pyomo to github workflow. It has already been added to environment.yml.
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

Section 5.2: Modified Files

  • h2integrate/control/control_strategies/storage/plm_optimized_storage_controller.py
    • Modified implementation under PeakLoadManagementOptimizedStorageController

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

@abhineet-gupta
abhineet-gupta changed the base branch from main to develop July 21, 2026 13:56
@abhineet-gupta
abhineet-gupta requested a review from vijay092 July 21, 2026 13:58
@abhineet-gupta
abhineet-gupta force-pushed the PLM_DR branch 3 times, most recently from 96a279f to bb33d74 Compare July 21, 2026 14:20

@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, thanks for the follow-on PR, @abhineet-gupta! I love how you updated the code, examples, tests, and docs. Really slick stuff with some good solver generalizations as well.

I've left some questions and suggestions, nothing huge but I do think some of the clarifications will be useful for users approaching this code.

Comment thread environment.yml
Comment thread docs/control/technology_level_control/pyomo_controllers.md

@vijay092 vijay092 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.

Thank you for working on this! I had some minor comments.


**Given:**
- $\lambda_t$ := `supervisory_signal`: price, demand, or price $\times$ demand time series at timestep $t$
- $\lambda_t$ := `lmp_signal`: electricity price time series at timestep $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.

I'd call this a supervisory signal, since it could be either LMP or demand- essentially any external signal that the battery needs to respond to.

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 controller needs both LMP and demand signals as inputs at the same time, hence the distinction.


$$
\max_{u_t, v_t,p_{d,t}, p_{c,t}} \quad \gamma \cdot \Delta t \sum_{t \in \mathcal{T}} p_{d,t}
\min_{u_{gt,t},u_{coop,t},v_t,p^d_{gt,t},p^d_{coop,t},pc_t,\text{SOC}_t,p_{gt2coop,t}} \quad

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.

Could you confirm if the units are uniform in the first and second term? Incentive is $ / kwh and LMP is $/MWh?

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 optimization setup expects $/kWh for both. I will double check the input data in example to ensure that they follow the convention.


# Incentive revenue is earned for every kWh discharged.
# Power transmitted to CoOp
m.p_tocoop = pyomo.Var(

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.

p_tocoop is unbounded above which might cause issues later on. Good to add an upper bound.

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.

That's a good point. There would be a practical upper bound (based on interconnections etc). However, there is no theoretical upper bound (we can potentially model as large a G&T and CoOp under this framework as we like). Not sure how to pick a reasonable upper bound here.

Comment thread examples/34_plm_optimized_dispatch/run_plm_optimized_dispatch.py Outdated
with subtests.test("Discharge never above max_charge_rate"):
assert np.all(discharge <= 1.0 + 1e-4)

with subtests.test("SOC at t=0 equal to init_soc_fraction"):

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 is this removed?

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.

SOC at t = 0 is calculated after the first iteration is complete. So this test is only valid if battery doesn't discharge at the first step. Previously, that wasn't possible because the battery could only discharge during peak window but now it can also discharge at the first step.

@abhineet-gupta

Copy link
Copy Markdown
Collaborator Author

@johnjasa,
Thanks for the review, I have addressed your comments with and tagged them with specific commit hashes.

@abhineet-gupta
abhineet-gupta marked this pull request as ready for review July 30, 2026 17:38
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.

3 participants