Skip to content

multi ref bus - #340

Merged
jd-lara merged 5 commits into
mainfrom
jd/multi_ref_bus
Aug 3, 2026
Merged

multi ref bus#340
jd-lara merged 5 commits into
mainfrom
jd/multi_ref_bus

Conversation

@jd-lara

@jd-lara jd-lara commented Jul 30, 2026

Copy link
Copy Markdown
Member

These are other fixes for the multiref

jd-lara added 3 commits July 30, 2026 14:07
v0.24.3 fixed get_ref_bus_position only for Ybus, leaving PTDF, BA_Matrix,
IncidenceMatrix, AdjacencyMatrix and VirtualPTDF on the bare
`get_bus_lookup(M)[x]` lookup. Those still threw a raw KeyError whenever a
reduction merged away the bus that assign_reference_buses! picked as an
island's representative.

Hoist _resolve_ref_bus_position into a single
get_ref_bus_position(M::PowerNetworkMatrix) and drop the six per-type copies.
ABA_Matrix keeps its own method (it stores ref_bus_position directly).

LODF and VirtualLODF keep their per-type definitions: they are arc-indexed and
have no get_bus_lookup, so they throw on any call regardless of reduction. That
is pre-existing and left alone here.
"Small island corner cases" built PTDF/LODF/VirtualPTDF/VirtualLODF three ways
(plain, single-bus island, radial-reduced) and asserted bitwise equality. The
paths are electrically equivalent but leave different `valid_ix` orderings, so
the factorization sums the same terms in a different order and results drift by
a few ULP. Any change touching reference bus positions reshuffled which entries
mismatched, making the testset flaky -- it fails on main today.

Use atol = 1e-10, matching the tolerance already used elsewhere for the
analogous ptdf_rad/vptdf_rad comparison. That is ~6 orders above the round-off
noise and ~9 below the O(0.1) error a genuinely wrong reference bus produces.
An absolute tolerance is required for the near-zero entries, where the values
differ by 1e-17 against an exact 0.0.

Split each three-way chain into two pairwise assertions so failures report the
offending values instead of collapsing into one a == b == c.
get_ref_bus_position re-implemented the reduction-aware bus resolution that
get_bus_index (common.jl) already provides, so call that instead and drop
_resolve_ref_bus_position. This trades a descriptive error for the bare KeyError
get_bus_index raises, on a path only reachable when a reference bus resolves to
neither a surviving bus nor a reduction-mapped one.

In the test fixture, _mk_star_jumper! and _mk_star_normal_line! each inlined the
full Line construction that _add_test_line! already provides and differed only in
x; collapse both onto one builder.

Also drop a comment that speculated about a future design decision rather than
documenting a why.
@jd-lara
jd-lara requested a review from m-bossart July 30, 2026 21:07
@github-actions

github-actions Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Performance Results

Precompile Time

Main This Branch Delta
2.3 s 2.2 s -2.1%

Execution Time

Cells show median (min–max) over 5 samples; delta compares medians.

Test Main This Branch Delta
matpower_ACTIVSg2000_sys-Build Ybus 10.1 (9.9–10.5) ms 9.1 (8.8–9.5) ms -9.1%
matpower_ACTIVSg2000_sys-Build PTDF 74.9 (74.7–75.7) ms 75.6 (74.9–76.8) ms +0.9%
matpower_ACTIVSg2000_sys-Build LODF 153.0 (152.2–155.1) ms 152.9 (151.5–153.9) ms -0.0%
matpower_ACTIVSg2000_sys-Build VirtualMODF 85.8 (84.6–86.7) ms 880.8 (879.4–883.4) ms +927.0%
matpower_ACTIVSg2000_sys-VirtualMODF Query 10 rows 0.6 (0.5–0.7) ms 0.6 (0.6–1.4) ms +7.8%
matpower_ACTIVSg2000_sys-Radial network reduction 0.8 (0.7–0.9) ms 0.8 (0.7–1.0) ms +0.5%
matpower_ACTIVSg2000_sys-Degree two network reduction 1.0 (1.0–1.1) ms 1.0 (1.0–1.6) ms -0.6%
Base_Eastern_Interconnect_515GW-Build Ybus 575.4 (571.3–584.2) ms 478.4 (476.4–483.1) ms -16.9%
Base_Eastern_Interconnect_515GW-Build VirtualPTDF 816.9 (813.7–820.8) ms 721.9 (717.1–725.0) ms -11.6%
Base_Eastern_Interconnect_515GW-VirtualPTDF Query 10 rows 27.8 (27.7–28.1) ms 27.6 (27.4–29.0) ms -0.8%
Base_Eastern_Interconnect_515GW-Build VirtualMODF 806.3 (802.5–812.0) ms 790.0 (786.6–796.0) ms -2.0%
Base_Eastern_Interconnect_515GW-VirtualMODF Query 10 rows 33.1 (33.0–33.4) ms 32.8 (32.4–33.5) ms -1.0%
Base_Eastern_Interconnect_515GW-Radial network reduction 36.9 (35.5–38.4) ms 33.8 (33.3–34.0) ms -8.4%
Base_Eastern_Interconnect_515GW-Degree two network reduction 49.4 (48.8–49.6) ms 43.0 (42.6–44.0) ms -12.8%

@m-bossart m-bossart 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.

Does it still make sense to have a function get_ref_bus that doesn't consider multiple ref buses in the same sychronous subsystem?

jd-lara added 2 commits July 30, 2026 15:43
_make_subnetwork_axes replaced a reference bus that a reduction removed with
pop!(axis_1), which had two problems. The pick was an arbitrary island member,
never excluded from bus_numbers_to_remove, so subnetwork_axes could end up keyed
by a bus the same reduction pass deleted -- reproduced on a ZIBR star where the
key became bus 14 while bus_lookup held only {10, 20, 21}. And pop! deleted the
chosen bus from the island's own bus list, so the reference bus was missing from
its own subnetwork axis even when it did survive.

The key was only usable because get_ref_bus_position resolves through
reverse_bus_search_map at lookup time. Establish the invariant at the source
instead: pick the bus the old representative was merged into, so the reference
bus stays electrically the same one that resolution already selected, and PTDF
numerics do not move. A removal that merges nothing has no reverse map entry and
falls back to an order-independent pick. An island that loses every bus is left
for the existing empty-subnetwork sweep rather than re-keyed to another dead bus.

get_ref_bus_position keeps its get_bus_index resolution; it is now redundant for
this path but still covers any reduction that leaves a stale key.
Three testsets asserted that AutoTolerance is a no-op by comparing a matrix built
with it against one built at eps() using exact equality. Two separate builds are
not bitwise reproducible: with the AppleAccelerate backend active, measuring 20
build pairs shows one mismatch at 1 ULP (1.11e-16), while 20 repeated identical
builds show none. So each of these assertions fails roughly 1 run in 20; they had
simply been getting lucky.

Compare with atol = 1e-10, which still proves the no-op -- genuinely sparsifying
at these data_precision values would zero entries, moving them by their full
magnitude (>=1e-3 in these systems). Same fix already applied once in this file
for the sparsified VirtualPTDF row comparison (04b9901).

The remaining exact comparison, apply_cutoff on a literal row, is a deterministic
in-memory operation and stays exact.
@jd-lara

jd-lara commented Jul 30, 2026

Copy link
Copy Markdown
Member Author

Does it still make sense to have a function get_ref_bus that doesn't consider multiple ref buses in the same sychronous subsystem?

Good question, my inclination is that we should make the behavior more concrete and if there are multiple Swing buses in a case, only one is a ref and the rest are slacks and the model for slacks -> fixed V and \theta and all refs behave like slacks but we need to validate that with PSSe

@m-bossart
m-bossart self-requested a review July 30, 2026 23:10
@m-bossart

Copy link
Copy Markdown
Contributor

Does it still make sense to have a function get_ref_bus that doesn't consider multiple ref buses in the same sychronous subsystem?

Good question, my inclination is that we should make the behavior more concrete and if there are multiple Swing buses in a case, only one is a ref and the rest are slacks and the model for slacks -> fixed V and \theta and all refs behave like slacks but we need to validate that with PSSe

Doesn't this have to be true given PSSE only has PV,PQ, Swing,Disconnected bus types. They they can't distinguish between refs/slacks with just these designations.

@m-bossart m-bossart 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.

I'm ok with these changes if we want to save the larger discussion about the semantics of ref/slack/swing for another change

@jd-lara
jd-lara merged commit 8521b90 into main Aug 3, 2026
9 checks passed
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