Skip to content

Migrate services to the psy6 reserve tree (OnlineReserve/OfflineReserve/GroupReserve) - #235

Open
rodrigomha wants to merge 5 commits into
mainfrom
rh/reserve_refactor
Open

Migrate services to the psy6 reserve tree (OnlineReserve/OfflineReserve/GroupReserve)#235
rodrigomha wants to merge 5 commits into
mainfrom
rh/reserve_refactor

Conversation

@rodrigomha

@rodrigomha rodrigomha commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Reserve dispatch re-expressed on the psy6 reserve tree, with distinctions recovered from state instead of struct types: direction and non-spin via reserve_traits.jl, ORDCs via the demand curve on variable, static vs time-varying requirements via the attached series. Includes the degenerate-demand supply-only skip (services can serve a GroupReserve), the GroupRangeReserve rename, instance-derived container metas, and two independent fixes in their own commits: the Symbol(T)-vs-nameof silent service-device wiring skip under parallel test workers, and the units-mandatory get_max_shunt_current read.

Full suite green locally against the local psy6 stack: 105891/105891.

Scope note: this PR is the refactor only. The elastic group ORDC (GroupStepwiseCostReserve) and load reserve provision (PowerLoadDispatch up/down reserves with the costless-load guard) are NEW features, not migrations of existing behavior, and will land in a follow-up PR together with the market integration test and their formulation_library.md entries.

Depends on the PSY reserve changes (jd/schema_matching), Sienna-Platform/PowerSystemsTestData#124, and Sienna-Platform/PowerSystemCaseBuilder.jl#212 - CI cannot pass until those merge and [sources] resolve.

🤖 Generated with Claude Code

Reserve dispatch moves from the retired struct types to OnlineReserve/
OfflineReserve/GroupReserve, with distinctions recovered from state:
direction and non-spin via the reserve_traits helpers, ORDCs via the demand
curve on the reserve's variable field, static vs time-varying requirements
via the attached requirement series.

- Degenerate-demand skip: a reserve with no demand driver (zero requirement,
  or no curve under StepwiseCostReserve) is built as supply only, so it can
  serve a GroupReserve; standalone cases get a warning.
- ServiceModel types can be partially applied (OnlineReserve{ReserveUp}, a
  UnionAll), so service-vector signatures are decoupled from the model type
  and device-side container metas derive from the service instance
  (_service_container_meta).
- POM formulation GroupReserve renamed GroupRangeReserve to avoid clashing
  with the PSY.GroupReserve component type.
- Formulation-combination enumeration includes parametric leaf service types
  (IS.get_all_concrete_subtypes drops UnionAll leaves).
- Storage complete-coverage expectations updated: constraints are built once
  per unique service type and side, plus a guard that award variables are
  wired into each storage total-reserve balance.
The device-template lookup used Symbol(device_type), which module-qualifies
the name whenever the type is not visible from Main - true in every parallel
test worker - while the template keys are made with nameof(D). The key miss
silently skipped wiring reserve award variables into device-side expressions
(thermal range expressions, storage TotalReserveOffering), letting services
clear with no physical backing while all constraint counts still matched.
Look up with nameof(device_type).
get_max_shunt_current requires the unit-system argument; PSY.SU returns the
system-base per-unit value directly, replacing the manual base-power
division.
@rodrigomha

Copy link
Copy Markdown
Contributor Author

Test fixtures for this branch come from the paired PRs Sienna-Platform/PowerSystemsTestData#124 (reserve fixtures on the new tree, required base_current, market-bid cost API) and Sienna-Platform/PowerSystemCaseBuilder.jl#212 (reserve selection via has_demand_curve, base_current construction, psy6_compat.jl removal). Merge order: PSY jd/schema_matching -> TestData#124 -> new TestData dev artifact -> PSB#212 (re-pins DATA_DIR) -> this PR.

…disable-stubs

#232 commented out the reserve code referencing retired PSY types so POM loads
against psy6; this branch migrates those same sites for real, so the merge keeps
our implementations everywhere and takes only the docs-workflow change from main.
@rodrigomha
rodrigomha marked this pull request as ready for review August 12, 2026 21:50
PSY moved GroupReserve into the reserve tree (it was <: Service), so the
type split no longer separates groups from device-backed reserve code.
Restore the boundaries explicitly:

- add_reserve_offer_costs! on a GroupReserve throws: per-resource offers
  live on a group's contributing services, never on the group.
- _is_group_member(::System, ::GroupReserve) = false: groups do not nest,
  and the AbstractReserve method now matches groups.
- Formulation-pairing guards fail at ServiceModel declaration with an
  actionable ArgumentError: a GroupReserve accepts only group formulations,
  group formulations accept only GroupReserve, and a bare (direction-less)
  GroupReserve declaration says to apply the direction. Extra methods
  disambiguate against the generic reserve defaults now that the bounds
  overlap.
- Comment updates where AbstractReserve bounds admit groups type-wise but
  the paths are unreachable for them (slacks, the deviceless exemption).
- Fix a latent misspelled log-group constant in the @debug skip branch.

Full suite green with the new PSY tree: 105898/105898.
rodrigomha added a commit that referenced this pull request Aug 13, 2026
PSY moved GroupReserve into the reserve tree, so the RESERVE_PRODUCT_TYPES
alias and the per-type methods that existed only because groups sat outside
it are gone: every former Union bound is plain PSY.AbstractReserve, the
group get_initial_conditions_service_model and the CostExpressions container
sibling fold into the AbstractReserve methods, and uses_compact_power opens
to the abstract type. Formulation-pair bounds
(Union{StepwiseCostReserve, GroupStepwiseCostReserve}) and the group demand
predicates stay - they encode formulation semantics, not typing.

The pairing guards merge with the #235 hardening set: valid direction-applied
pairs for both group formulations, the generic-defaults disambiguator, the
inverse guard over both formulations, and a direction-required error for
bare GroupReserve declarations.
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.

1 participant