Skip to content

Add GroupStepwiseReserveCurve: elastic group ASDC over sub-service supply - #219

Draft
rodrigomha wants to merge 3 commits into
rh/dev_refactor_servicesfrom
rh/group_stepwise_reserve
Draft

Add GroupStepwiseReserveCurve: elastic group ASDC over sub-service supply#219
rodrigomha wants to merge 3 commits into
rh/dev_refactor_servicesfrom
rh/group_stepwise_reserve

Conversation

@rodrigomha

Copy link
Copy Markdown
Contributor

Summary

Adds GroupStepwiseReserveCurve, a reserve formulation that composes GroupReserve aggregation with StepwiseCostReserve demand pricing. It models an elastic group Ancillary Service Demand Curve (ASDC) whose single demand is met by the awards of several contributing sub-services - e.g. one RRS product cleared over PFR/FFR/UFR sub-types.

A ReserveDemandCurveGroup service (new PSY struct, on the local psy6 branch) adds:

  • one endogenous ServiceRequirementVariable, priced by the group ASDC (a decremental benefit), and
  • one RequirementConstraint binding the sum of the contributing sub-services' ActivePowerReserveVariable awards to that demand.

So supply merit order lives on the sub-services (their per-resource offers) and the single clearing (one MCPC) lives on the group.

Changes

  • GroupStepwiseReserveCurve formulation type + export.
  • Elastic aggregation constraint (reserve_group.jl) - same _group_member_variables aggregation as GroupReserve, RHS is the demand variable rather than a fixed requirement.
  • Group two-stage construct_service! + a _is_deferred_group_formulation dispatcher so group formulations construct after their sub-services.
  • Widen the reserve cost/expression/offer chain (add_reserves_variable_cost!, add_expressions!, add_to_expression!, add_pwl_term_delta!, get_offer_curves) and the problem-template contributing-device check to the group, which is a Service (not a Reserve).
  • Storage/hybrid reserve meta-key fixes (key on the concrete typeof(service) so the unit-system type parameter is not dropped) and the ReserveDemandCurve storage upper-bound fix.

Testing

New self-contained test/test_group_stepwise_reserve.jl on c_sys5_uc: builds + solves, asserts a single demand variable / single clearing constraint, the aggregation balance holds every hour, cheap sub-service clears while the pricey one does not, and a no-group regression procures nothing. Test.detect_ambiguities clean; existing test_device_reserve_offers and test_services_constructor still pass.

Notes for review

  • Depends on the local psy6 ReserveDemandCurveGroup struct (not yet merged); the [sources] PowerSystems path override is a local dev-only change and is intentionally NOT part of this branch.
  • Draft while the load-side reserve work proceeds on a separate branch.

…pply

Composes GroupReserve aggregation with StepwiseCostReserve demand pricing. A
ReserveDemandCurveGroup service adds one endogenous ServiceRequirementVariable
priced by the group ASDC and one RequirementConstraint binding the sum of the
contributing sub-services' ActivePowerReserveVariable awards to that demand, so
merit order lives on the sub-services and the single clearing (one MCPC) lives
on the group.

- GroupStepwiseReserveCurve formulation + export
- Elastic aggregation constraint (reserve_group.jl)
- Group two-stage construct_service! + deferred-group dispatcher
- Widen the reserve cost/expression/offer chain and the problem-template
  contributing-device check to the group (a Service, not a Reserve)
- Storage/hybrid reserve meta-key fixes (key on the concrete service type) and
  the ReserveDemandCurve storage upper-bound fix
- Self-contained end-to-end test: build/solve, single clearing, aggregation,
  sub-service merit order, no-group regression

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

Adds a new reserve formulation (GroupStepwiseReserveCurve) to model an elastic group ancillary service demand curve cleared against the aggregated awards of multiple contributing sub-services, and wires it through construction, cost plumbing, and expressions so the group has a single clearing constraint/MCPC.

Changes:

  • Introduces GroupStepwiseReserveCurve formulation + export, plus an elastic group balance constraint binding sub-service awards to a group demand variable.
  • Updates service construction to defer all group formulations and adds two-stage construct_service! for ReserveDemandCurveGroup.
  • Extends ORDC/ASDC offer + expression plumbing to support ReserveDemandCurveGroup, and adds a dedicated test covering build/solve/clearing behavior.

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
test/test_group_stepwise_reserve.jl Adds a self-contained integration test for group ASDC clearing over sub-services.
src/services_models/services_constructor.jl Defers group formulations and adds two-stage constructors for GroupStepwiseReserveCurve.
src/services_models/reserves.jl Extends reserve-variable/cost dispatch to include ReserveDemandCurveGroup + group objective logic.
src/services_models/reserve_group.jl Adds elastic group requirement constraint tying sub-service awards to group demand variable.
src/core/formulations.jl Defines the new GroupStepwiseReserveCurve formulation type.
src/PowerOperationsModels.jl Exports GroupStepwiseReserveCurve.
src/common_models/add_expressions.jl Adds a cost-expression container path for ReserveDemandCurveGroup services.
src/common_models/add_to_expression.jl Widens ReserveDemand* dispatch unions to include ReserveDemandCurveGroup.
src/common_models/market_bid_plumbing.jl Extends get_offer_curves to accept ReserveDemandCurveGroup.
src/common_models/market_bid_overrides.jl Extends PWL delta-term dispatch to accept ReserveDemandCurveGroup.
src/core/problem_template.jl Exempts ReserveDemandCurveGroup from contributing-device-map validation (device-less group).
src/energy_storage_models/storage_models.jl Fixes ReserveDemandCurve(*) storage reserve upper bound and aligns expression meta-keying on typeof(service).
src/hybrid_system_models/hybrid_systems.jl Aligns expression meta-keying on typeof(service) to avoid unit-system param loss.

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

Comment on lines +22 to +24
get_variable_binary(::Type{ServiceRequirementVariable}, ::Type{<:Union{PSY.ReserveDemandCurve, PSY.ReserveDemandTimeSeriesCurve, PSY.ReserveDemandCurveGroup}}, ::Type{<:AbstractReservesFormulation}) = false
get_variable_upper_bound(::Type{ServiceRequirementVariable}, ::Union{PSY.ReserveDemandCurve, PSY.ReserveDemandTimeSeriesCurve, PSY.ReserveDemandCurveGroup}, d::PSY.Component, ::Type{<:AbstractReservesFormulation}) = PSY.get_max_active_power(d, PSY.SU)
get_variable_lower_bound(::Type{ServiceRequirementVariable}, ::Union{PSY.ReserveDemandCurve, PSY.ReserveDemandTimeSeriesCurve, PSY.ReserveDemandCurveGroup}, ::PSY.Component, ::Type{<:AbstractReservesFormulation}) = 0.0
Comment on lines 55 to +58
function get_variable_upper_bound(::Type{ActivePowerReserveVariable}, r::Union{PSY.ReserveDemandCurve, PSY.ReserveDemandTimeSeriesCurve}, d::PSY.Storage, ::Type{<:AbstractReservesFormulation})
return PSY.get_max_output_fraction(r) * (PSY.get_output_active_power_limits(d, PSY.SU).max + PSY.get_input_active_power_limits(d, PSY.SU).max)
# ReserveDemandCurve has no `max_output_fraction` field (unlike VariableReserve/ConstantReserve),
# so the full charge+discharge range is available - matching the AncillaryServiceVariableCharge/
# Discharge ReserveDemandCurve methods above.
PSY now provides an abstract ReserveGroup supertype of ConstantReserveGroup and
ReserveDemandCurveGroup. Dispatch on it in problem_template.jl instead of listing
the concrete types: the contributing-device exemption becomes
`service_type <: PSY.ReserveGroup`, and _add_services_to_device_model! now skips
every group (not just ConstantReserveGroup), which also fixes a latent gap where a
ReserveDemandCurveGroup relied on the empty-contributing-devices fallback. Behavior
is unchanged - ReserveGroup is exactly the two concrete group types.
Scrub vendor-specific jargon from the GroupStepwiseReserveCurve comments/docstrings
so POM stays market-neutral: drop the explicit ERCOT / RTC+B references, and replace
the ERCOT acronyms ASDC and MCPC (neither used elsewhere in POM) with the house terms
- 'demand curve'/'ORDC' and 'clearing price'. Comments only; no behavior change.
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.

2 participants