Skip to content

Switched shunt: Y stores PSS/E's BINIT (a total) where PSY documents an N=0 base, double-counting engaged blocks on v35 #48

Description

@luke-kiernan

PSS/E's BINIT is a switched shunt's current total in-service admittance — the value it is actually providing in the solved case. Bi/Ni/Si describe the adjustment range around it, not additions to it. The parser stores BINIT verbatim into PSY's Y, which is documented as "Initial admittance at N = 0", and on v35 additionally populates initial_status from S1..S8. Any consumer that follows the PSY docstring and adds the engaged blocks to Y therefore counts the engaged reactive twice. v33 escapes this only because #44 zeroes initial_status, so the block sum vanishes — the same workaround, tuned so the bug cancels rather than removed.

Concretely, this v35 record:

312045,'1 ',   0,  1,  1,1.10000,0.90000,312045,   0, 100.0,'            ',-1320.0,
       1, 1,-330.00, 1, 1,-330.00, 1, 1,-330.00, 1, 1,-330.00, 1, 1,-330.00, 1, 1,-330.00, 1, 1,-165.00

parses to bs = -13.2, y_increment = [-3.3 ×6, -1.65], initial_status = [1,1,1,1,1,1,1] (all per-unitization correct). A consumer then computes -13.2 + (-21.45) = -34.65 pu where the answer is -13.2 pu — a 2145 MVAr error at one bus, which is what surfaced this. The record also shows S_i is availability, not engagement: BINIT = -1320 is exactly 4 × (-330), yet all seven blocks carry S_i = 1. If S_i meant engaged, BINIT would have to be -2145.

Two findings

initial_status is read and written as step counts, not binary flags — by PowerFlows at least. Its discrete control computes d.block_n[k] = min(remaining, d.block_steps[k]), which exceeds 1 whenever a block has multiple steps, and write_device_settings! puts it straight into the field via PSY.set_initial_status!(sa, copy(d.block_n)). So a first-party consumer treats sum(initial_status .* Y_increase) as steps × increment, and partial-block engagement is representable. That is in tension with the PSY docstring ("one for in-service and zero for out-of-service"), and which one is authoritative is worth settling separately — but it does not affect this issue either way, since S_i is a 0/1 availability flag under both readings and so does not belong in this field regardless.

PowerFlows already has a convention detector, and v35 falls through the wrong side of it:

_is_binit_shunt(init_status) = !isempty(init_status) && all(iszero, init_status)
  • v33 → status all zeros → BINIT branch → b_fixed = 0, current = imag(Y0) → correct
  • v35 → status [1,1,1,1,1,1,1]API branchY treated as the fixed base and blocks added → double count

So the same root cause bites twice: once in the injection at common.jl:139, once in the susceptance model that drives discrete control. Fixing the parser collapses both, and retires the psse_convention special-casing as a bonus.

Proposed change

Applied to all versions (v33's zeroing is the same workaround, just tuned so the sum happens to vanish):

engaged = reconstruct(BINIT, B_i, N_i)   # greedy in record order, sign-matched, capped at N_i
Y       = BINIT - Σ engaged_i * B_i      # residual — 0 for an exactly-stepped solved case
initial_status = engaged                 # step counts

The invariant to assert is Y + Σ(initial_status .* Y_increase) == BINIT, exactly, for every record. The bank above gives engaged = [1,1,1,1,0,0,0], Y = 0 — no fixed part, which is physically right since a SWITCHED SHUNT record has no non-switchable portion.

Open questions

  • Residual policy. BINIT isn't always an exact block combination — MODSW=2 shunts sit between steps, and the existing 14-bus fixture has BINIT = 50 against a single B1 = 100 block. Leftover lands in Y, which preserves the total exactly and leaves that fixture's parsed values unchanged. This seems plainly right, but it does mean Y isn't purely an "N=0 base" in those cases.
  • Reconstruction order. Greedy in record order, engaging only blocks whose sign matches BINIT, capped at N_i steps. Mirrors how PSS/E switches them.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions