Sienna-Platform / Sienna-Platform/PowerFlowFileParser.jl
Switched shunt: `Y` stores PSS/E's `BINIT` (a total) where PSY documents an N=0 base, double-counting engaged blocks on v35
@luke-kiernan is already working on this.
Since Aug 25, 2026.
- Dominant language
- Julia
- Stars
- 1
- Forks
- 2
- Avg merge
- 22h 29m
- Merged PRs (30d)
- 10
Description
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 branch →Ytreated 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.
BINITisn't always an exact block combination — MODSW=2 shunts sit between steps, and the existing 14-bus fixture hasBINIT = 50against a singleB1 = 100block. Leftover lands inY, which preserves the total exactly and leaves that fixture's parsed values unchanged. This seems plainly right, but it does meanYisn't purely an "N=0 base" in those cases. - Reconstruction order. Greedy in record order, engaging only blocks whose sign matches
BINIT, capped atN_isteps. Mirrors how PSS/E switches them.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.