Skip to content

Show balance slacks owned by buses and areas (#94) - #7

Open
PabloBotin wants to merge 1 commit into
feature/migrate-to-poweranalytics-new-apifrom
issue-94-bus-slacks
Open

Show balance slacks owned by buses and areas (#94)#7
PabloBotin wants to merge 1 commit into
feature/migrate-to-poweranalytics-new-apifrom
issue-94-bus-slacks

Conversation

@PabloBotin

Copy link
Copy Markdown
Owner

Closes Sienna-Platform#94.

Base is feature/migrate-to-poweranalytics-new-api, not main. The slack path is rewritten by the PowerAnalytics migration, so fixing this against main would patch code that is about to be replaced. Merge after that lands.

Root cause

Slack categories were read via PA.Metrics.calc_system_slack_up / its down counterpart, which resolve to PowerAnalytics.read_system_result (input_utils.jl:190). Its first line is:

key = make_key(entry, PSY.System)     # hardcoded component type

followed by @assert size(res, 2) == 2 — one time column plus exactly one data column.

But PowerSimulations attaches balance slacks to four different component types depending on the network formulation (network_slack_variables.jl:115-182):

Formulation Slack owner Encoded key
CopperPlatePowerModel, PTDFPowerModel System SystemBalanceSlackUp__System
AreaBalancePowerModel, AreaPTDFPowerModel Area …__Area
PM.AbstractActivePowerModel (DC, NFA) ACBus …__ACBus
PM.AbstractPowerModel (AC) ACBus + "P"/"Q" meta …__ACBus__P / __Q

Only the first is ever found. The other three are invisible — and being multi-column, they would have tripped the single-column assertion even if the key had matched. That is exactly the reported symptom: "the slacks at the node level don't appear."

Confirmed by solving: DCPPowerModel with use_slacks = true produces SystemBalanceSlackUp__ACBus, and PA.compute(calc_system_slack_up, results) raises on it.

The fix

Discover the slack keys actually present in the results by entry type, regardless of owning component, and sum across the per-bus/per-area columns into one series per direction. The System case has a single column, so summing is a no-op and its values are unchanged.

Only the active-power "P" slack is included for AC formulations — the fuel stack plots active power in MW, so reactive slack does not belong on it.

This works around the limitation rather than fixing it. read_system_result remains System-only for every other PowerAnalytics consumer; the proper fix is an upstream PA metric that discovers the owner type and aggregates. Worth filing.

Verification

Full suite 101/101, zero Error log events; formatter clean.

The regression test fails on the parent commit — stashing only src/call_plots.jl and re-running gives 52 passed, 2 failed, 3 errored, the failures being the two missing slack categories and the errors being only(...) on absent traces. The two "unchanged behavior" testsets pass on the parent, as intended.

Independently exercised against real solves:

check result
DCPPowerModel + slacks → keys are ACBus-owned
PA.calc_system_slack_up on those results raises (the bug)
plot_fuel shows Unserved Energy / Over Generation
wide frame genuinely multi-bus (>2 cols, so the sum isn't a no-op)
trace == row-wise sum over bus columns
unserved energy strictly positive (~977 MW)
CopperPlate trace == PA.compute exactly ✅ unchanged
results without slacks categories skipped, no error

Values were compared in native MW via auto_units = false; the auto-scaled form is exactly ref/1000.

Known limitations

  • The Area case is not covered by a test. c_sys5_uc has no Area or AreaInterchange components, so AreaBalancePowerModel fails to build (ConflictingInputsError). Hand-building that fixture would be fragile, and the ACBus test already exercises the identical code path — non-System owner, many columns, summed. No distinct branch is left untested.
  • key.meta is accessed as a field. No get_meta accessor exists in InfrastructureSystems or PowerSimulations (both grepped). The alternative — string-matching encode_key_as_string — is worse. An upstream accessor would be the clean fix.
  • The test forces nonzero slacks by scaling max_active_power ×3 on a deepcopy; with the unmodified system the nodal slacks solve to all-zero and the sum assertion would be vacuous.
  • Verified on macOS only.

@PabloBotin
PabloBotin force-pushed the feature/migrate-to-poweranalytics-new-api branch 2 times, most recently from b89dc10 to 39be6fe Compare July 29, 2026 22:34
Slack categories were read through `PA.Metrics.calc_system_slack_up` and its
down counterpart, which resolve to `PowerAnalytics.read_system_result`. That
function hardcodes `make_key(entry, PSY.System)` and asserts the result has a
single data column.

PowerSimulations attaches balance slacks to four different component types
depending on the network formulation: `System` for copper-plate and PTDF,
`Area` for the area-balance models, and `ACBus` for the PowerModels
formulations (with `"P"`/`"Q"` meta in the AC case). Only the first was ever
found, so node- and area-level slacks silently never appeared — and would
have tripped the single-column assertion had they been read at all.

Discover the slack keys present in the results by entry type instead,
regardless of which component owns them, and sum across the per-bus or
per-area columns into one series per direction. The `System` case has a
single column, so its values are unchanged.

Only the active-power `"P"` slack is included for AC formulations; the fuel
stack plots active power, so reactive slack does not belong on it.

This works around the limitation rather than fixing it: `read_system_result`
is still `System`-only for every other PowerAnalytics consumer.
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