Skip to content

Conversation

@juflorez
Copy link
Collaborator

@juflorez juflorez commented Jul 31, 2025

The Demand Response component enables shifting of unserved energy to time periods when energy is more available. All parameters are time series dependent enabling greater flexibility and segment sizing, in line with the broader PRAS structure. The following key details define a demand response device:

  1. names of DR devices
  2. categories of DR devices
  3. Borrowing capacity (load that otherwise would have been unsered)
  4. Payback capacity (stored unserved energy that needs to be paid back)
  5. Energy capacity (how much unserved energy the DR device can hold)
  6. Borrowing efficiency
  7. Payback efficiency
  8. Carryover efficiency
  9. Allowable payback period (how many timesteps can the DR device hold unserved energy before being counted as unserved).
  10. Probability of failure
  11. Probability of repair

An example load shifting event is provided below for a demand response device with energy capacity of 5MWh, borrowing and payback capacity of 3MW, and allowable payback window of a constant 4 timesteps:
loss_of_load_day
Loss of load DR operations

@codecov-commenter
Copy link

codecov-commenter commented Jul 31, 2025

Codecov Report

❌ Patch coverage is 77.01493% with 77 lines in your changes missing coverage. Please review.
✅ Project coverage is 83.02%. Comparing base (8781d8e) to head (47c38bf).

Files with missing lines Patch % Lines
PRASCore.jl/src/Systems/assets.jl 43.47% 39 Missing ⚠️
PRASFiles.jl/src/Systems/write.jl 9.09% 20 Missing ⚠️
PRASFiles.jl/src/Systems/read.jl 65.00% 7 Missing ⚠️
PRASCore.jl/src/Systems/SystemModel.jl 70.58% 5 Missing ⚠️
...SCore.jl/src/Results/DemandResponseAvailability.jl 77.77% 4 Missing ⚠️
PRASCore.jl/src/Results/DemandResponseEnergy.jl 96.15% 1 Missing ⚠️
...Core.jl/src/Results/DemandResponseEnergySamples.jl 95.23% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main      #84      +/-   ##
==========================================
- Coverage   84.06%   83.02%   -1.05%     
==========================================
  Files          42       45       +3     
  Lines        2002     2309     +307     
==========================================
+ Hits         1683     1917     +234     
- Misses        319      392      +73     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

This comment was marked as outdated.

Copy link

Copilot AI left a comment

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 implements the initial structure for demand response components within the PRAS system. The changes add comprehensive support for demand response resources as a new asset type alongside existing generators, storage, and generator-storage devices.

  • Integration of demand response as a new core asset type with associated data structures, simulation logic, and file I/O support
  • Extension of existing result collection and analysis capabilities to include demand response-specific metrics
  • Update of system model constructors and file format versioning to accommodate the new component type

Reviewed Changes

Copilot reviewed 33 out of 33 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
SystemModel_HDF5_spec.md Updated specification to allow optional system attributes in HDF5 files
PRASFiles.jl/test/runtests.jl Modified test to use system attributes instead of user_attributes parameter
PRASFiles.jl/src/Systems/write.jl Added demand response processing and updated metadata handling
PRASFiles.jl/src/Systems/read.jl Added support for reading demand response components and system attributes
PRASFiles.jl/src/Results/write.jl Updated result generation to include system attributes
PRASFiles.jl/src/Results/utils.jl Added system attributes field to SystemResult struct
PRASFiles.jl/src/PRASFiles.jl Updated exports and imports for demand response support
PRASFiles.jl/Project.toml Version bump to 0.8.0
PRASCore.jl/test/Systems/assets.jl Added test cases for DemandResponses asset type
PRASCore.jl/test/Systems/SystemModel.jl Updated SystemModel constructor tests with demand response
PRASCore.jl/test/Simulations/runtests.jl Added demand response simulation tests
PRASCore.jl/test/Results/energy.jl Added demand response energy result tests
PRASCore.jl/test/Results/availability.jl Added demand response availability result tests
PRASCore.jl/src/Systems/units.jl Added long unit symbol functions
PRASCore.jl/src/Systems/assets.jl Implemented DemandResponses asset type with full functionality
PRASCore.jl/src/Systems/TestData.jl Added test systems with demand response components
PRASCore.jl/src/Systems/Systems.jl Updated exports to include DemandResponses
PRASCore.jl/src/Systems/SystemModel.jl Enhanced SystemModel with demand response support and display methods
PRASCore.jl/src/Simulations/utils.jl Added demand response payback counter logic
PRASCore.jl/src/Simulations/recording.jl Extended recording for demand response metrics
PRASCore.jl/src/Simulations/SystemState.jl Added demand response state variables
PRASCore.jl/src/Simulations/Simulations.jl Integrated demand response in simulation flow
PRASCore.jl/src/Simulations/DispatchProblem.jl Extended dispatch problem for demand response optimization
PRASCore.jl/src/Results/Results.jl Added demand response result specifications
PRASCore.jl/src/Results/DemandResponseShortfallSamples.jl New result type for demand response shortfall samples
PRASCore.jl/src/Results/DemandResponseShortfall.jl New result type for demand response shortfall metrics
PRASCore.jl/src/Results/DemandResponseEnergySamples.jl New result type for demand response energy samples
PRASCore.jl/src/Results/DemandResponseEnergy.jl New result type for demand response energy metrics
PRASCore.jl/src/Results/DemandResponseAvailability.jl New result type for demand response availability
PRASCore.jl/Project.toml Version bump to 0.8.0
PRASCapacityCredits.jl/Project.toml Updated compatibility for PRASCore 0.8.0
PRAS.jl/src/PRAS.jl Removed deprecated import
PRAS.jl/Project.toml Version bump to 0.8.0 and updated dependencies
Comments suppressed due to low confidence (2)

PRASFiles.jl/src/Systems/read.jl:87

  • Variable 'states' is used but should be 'state' (missing 's'). This will cause a compilation error.
        gen_regions = getindex.(Ref(regionlookup), gen_regionnames)

PRASCore.jl/src/Systems/SystemModel.jl:198

  • There are two constructors with similar signatures that both accept demandresponses parameter but have different logic. The second constructor (line 198) appears to be incorrectly defined as it creates empty demand responses despite accepting a demandresponses parameter.
function SystemModel(

@sriharisundar sriharisundar force-pushed the jf/add_demand_response_component branch from 0d77873 to 6561f9c Compare July 31, 2025 16:20
@sriharisundar sriharisundar force-pushed the jf/add_demand_response_component branch from 3a7d357 to 142c946 Compare September 9, 2025 02:21
Copy link
Member

@sriharisundar sriharisundar left a comment

Choose a reason for hiding this comment

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

I had started this long back, just wanted to finish it. Also added a few comments on the SystemModel constructors/pretty printing.
I didn't do an exhaustive check of all the code, it was a quick run through since I've been involved in some pieces here. Hoping Gord and/or Surya will be able to suggest any needed code refactoring.

soc = round(Int, soc * efficiency)

# Shed SoC above current energy limit
drs_energy[i] = min(soc, maxenergy)
Copy link
Member

Choose a reason for hiding this comment

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

Why shouldn't we allow increase beyond max_energy over here? Will that violate any DR asset struct constraints?
Since we do advance before solve and record, from the previous timestep to this, if the DR borrowed energy has ballooned over the energy capacity, either we will drop that much load, or we will payback what we can and then drop extra.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Need to track the case where soc is greater than maxenergy, and make sure that if its above, we track the unserved energy

Copy link
Collaborator Author

@juflorez juflorez Oct 10, 2025

Choose a reason for hiding this comment

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

did some rework to have it tracked by same state variable, example below with a 50% borrowed energy interest and a power capacity of 50, energy capacity of 200 (hits it at second 200 contribution with shortfall from DR) 3216eda
image

#for demand response-we want to borrow energy in devices with the longest payback window, and payback energy from devices with the smallest payback window
minpaybacktime_dr, maxpaybacktime_dr = minmax_payback_window_dr(sys)
min_paybackcost_dr = (- maxpaybacktime_dr - 50 + min_chargecost) #add min_chargecost to always have DR devices be paybacked first, and -50 for wheeling prevention
max_borrowcost_dr = - min_paybackcost_dr + minpaybacktime_dr + 1 + max_dischargecost #add max_dischargecost to always have DR devices be borrowed last
Copy link
Collaborator

Choose a reason for hiding this comment

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

To make the logic here a little more intuitive, I would put the min_chargecost / max_dischargecost at the start of the expression (to be clearer that the DR costs are being defined relative to those values).

Copy link
Collaborator

Choose a reason for hiding this comment

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

We should probably explicitly state the implicit merit order here, even after sketching a number line with the different cost bands it’s not obvious to me what we do/don’t want to allow as a far as cross charging between storage and DR (and therefore I’m not sure whether these cost offsets are right or not)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

will make a number line comparison, where variables can be changed

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Iterated through different comparisons and costs look correct
image

Copy link
Collaborator

Choose a reason for hiding this comment

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

Is the overlap between DR payback reward (9-12) and Stor/GenStor discharge penalty (5-11) intentional? It seems like in some cases storages would be discharged to pay back DRs, but in other cases they wouldn't?

Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't remember the offset equations off of the top of my head, but are we double-counting any offsets? Is the reason for the gap between the storage band (ends at +/- 11) to the DR band (starts at +/- 20) purely down to wheeling_cost_prevention? Same question applies for the gap between the DR payback reward (23) and the DR borrow penalty (36).

Copy link
Collaborator Author

@juflorez juflorez Oct 13, 2025

Choose a reason for hiding this comment

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

we would be double counting the max_dischargecost (in the max_borrowcost_dr we are adding them twice together). Full changes are here ca7ff88. Updated graphic below now
image

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@GordStephen simplified the costs here, think this is as bare bones as it can get. The cost_for_storage_wheeling_prevention seems ambiguous what on what a good number is, but I would think 75 is safe enough? 253bce0

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yeah, it's basically just to offset potential transmission costs, right? And since those are 1/MW/interface the offset should be around the longest distance we'd expect to be moving power from a battery to a DR repayment. 75 seems like plenty (in the ReEDS system you can get from Seattle to Miami in 32 hops)

Copy link
Collaborator

Choose a reason for hiding this comment

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

If we wanted to get really clever I guess we could set this dynamically based on the diameter of the network

@juflorez juflorez merged commit f406e9c into main Oct 17, 2025
33 checks passed
@sriharisundar sriharisundar deleted the jf/add_demand_response_component branch November 21, 2025 18:52
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