Conversation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace the placeholder `Float64` getter target with explicit unit systems: `SU` (system base) for impedances, admittances, and shunts used in matrix construction, and `DU` (device base) for equivalent ratings, which must work on detached branches. Thread `units::IS.AbstractUnitSystem` through the `get_series_susceptance` accessors (PSY now requires it). Pin InfrastructureSystems/PowerSystems (and test deps PowerSystemCaseBuilder/ PowerFlowFileParser) to their units-aware branches via `[sources]` so CI can resolve the as-yet-unreleased upstream changes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Re-point [sources] from the units feature branches to the next-release
branches now that they are merged upstream:
- InfrastructureSystems: lk/units-domain-agnostic-is4 -> IS4
- PowerSystems: lk/units-fold-psu -> psy6
- PowerSystemCaseBuilder lk/units-table-driven -> psy6
- PowerFlowFileParser: fork lk/units-fixes -> NREL-Sienna main
(the fix is merged to main but predates the only release, v0.1.0)
Fix two breakages surfaced by the bump to psy6:
- virtual_lodf_calculations.jl: pass PSY.SU to get_series_susceptance
in _extract_branch_susceptances_by_arc (missed call sites; the
accessor now requires an explicit unit system).
- test_arc_types_and_reductions.jl: attach the MixedBranchesParallel
test's Line/TapTransformer to a System so the system-base getters in
ybus_branch_entries resolve (psy6 now throws on detached components).
- test_connectivity.jl: relax the VirtualPTDF/VirtualLODF cross-method
equality checks from == to isapprox; the on-demand paths now differ
by ULPs on the IS4/psy6 stack.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Finish threading the unit-aware PSY getter API through the subsystems the original migration missed, surfaced by the bump to the psy6/IS4 release branches: - connectivity_checks.jl: reimplement find_connected_components with PNM's own union-find instead of PowerModels.calc_connected_components, which PowerSystems no longer re-exports (PowerModels dependency dropped). - common.jl, network_modification.jl: pass PSY.SU to get_series_susceptance and get_impedance_active/reactive_power in the contingency/MODF delta computations (all operate on system-base, system-attached components). - Ybus.jl, BranchesSeries.jl: the synthetic ward-equivalent GenericArcImpedance is detached by design and stores system-base impedance; read it back with device base (DU, identity) since system base would need a system base power a detached component cannot resolve. - BranchesParallel.jl: document why get_impedance_averaged_rating uses system base (consistent weighting across the group; requires attached branches). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- test_connectivity.jl: compare the dense PTDF/LODF cross-method checks with isapprox + an absolute tolerance; the dense and on-demand paths drift by a few ULPs run-to-run on the IS4/psy6 stack and many entries are physically zero (relative tolerance is meaningless there). - test_equivalent_getters.jl: attach the parallel branches to the system (skip_validation) so the system-base susceptance weighting can resolve. - test_modf_lodf_reductions.jl: clone transformers via deepcopy + fresh identity instead of re-reading each field, and pass PSY.SU to get_series_susceptance on attached branches. - test_ward_reduction.jl: read the detached ward-equivalent's susceptance with device base (DU). - test_ybus_reductions.jl: setters now require unitful values (val * PSY.SU). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
VirtualPTDF, VirtualMODF, and VirtualLODF each rebuilt and stored the same DC factorization (K/BA/A/ABA) and topology, duplicated the Woodbury outer wrappers, and recomputed the same PTDF_A_diag / branch_susceptances_by_arc. Introduce a private, unexported VirtualFactorCore that owns the factorization, topology, solve scratch, solver lock, and the lazily-computed shared PTDF_A_diag / branch_susceptances_by_arc. The three virtual matrices become thin wrappers holding a `core` and forwarding to it via accessors and a `getproperty` shim, so all existing public APIs (getindex, accessors, get_post_modification_ptdf_row, ...) keep working unchanged. A single core can now be shared across wrappers so the ABA matrix is factorized once: vptdf = VirtualPTDF(Ybus(sys)) vmodf = VirtualMODF(vptdf, sys) vlodf = VirtualLODF(vptdf) - New src/virtual_factor_helpers.jl relocates _create_factorization, _solve_factorization, _extract_arc_susceptances, _extract_branch_susceptances_by_arc, _get_PTDF_A_diag so they precede the core. - New src/virtual_factor_core.jl defines the struct, accessors, lazy getters, and the Ybus constructor. - Woodbury outer wrappers collapse to one method on the core; the wrappers forward to it. - dist_slack stays a PTDF/LODF wrapper concern (unused by MODF), never on core. - Added test/test_virtual_shared_core.jl asserting one shared factorization and numerical equivalence vs independently built objects.
Confine getfield to the field-getter definitions and the getproperty hooks. Introduce get_core (and get_cache/get_dist_slack/... companions) on VirtualPTDF/VirtualMODF/VirtualLODF and read wrapper fields through these accessors everywhere else, including the shared-core tests.
…truct-Og8R3 Extract shared VirtualFactorCore from virtual matrices
# Conflicts: # src/virtual_lodf_calculations.jl
Claude/bold volta h bbpi
Units migration: adopt unitful PowerSystems getter API
remove dynamic branch
Introduce `populate_cache` for VirtualPTDF, VirtualLODF, and VirtualMODF so callers can pre-warm row caches over an iterable of components/contingencies using the solver backends' batched multi-RHS `solve_sparse!` instead of one single-RHS solve per row. This amortizes the ABA factorization solves that dominate optimization-problem builds on large systems. - VirtualPTDF / VirtualLODF: gather requested arc rows, build one sparse RHS of the corresponding BA columns, solve in a single batched call, then scatter (PTDF) or apply the LODF map (A*X .* inv_PTDF_A_diag) across all columns at once. Accepts integer indices, arc bus-pair tuples, or branch-name strings. - VirtualMODF: batch the pre-contingency BA-column solves over the union of all monitored arcs and every contingency's modified arcs. The monitored-arc solve is contingency-independent, so it is computed once and reused; Woodbury factors per contingency are assembled from the precomputed solves with no further linear solves. Monitored set is supplied by the user. - RowCache: add set_persistent_row!/pin_row! so populated rows are pinned and never evicted by later lazy lookups, plus warn_if_over_capacity. - Backend dispatch routes to KLU's solve_sparse! or AccelerateWrapper's, with a column-by-column fallback for other factorization backends. - Export populate_cache; add tests covering PTDF/LODF/MODF equivalence with the lazy getindex path, pinning, identifier resolution, and error handling. https://claude.ai/code/session_011TFwa4Hsdse4XYEeuHmYro
…property Merge of origin/psy6 moved the virtual matrices' shared state into VirtualFactorCore. Rework populate_cache so every wrapper field is read through its proper getter (get_core, get_cache, get_cache_lock, get_dist_slack, get_dist_slack_normalized, get_inv_PTDF_A_diag, get_row_caches, get_woodbury_cache, get_max_cache_size_bytes, get_contingency_cache, get_arc_lookup, get_tol, get_ref_bus_position) instead of relying on the wrappers' getproperty forwarding. Core container fields are accessed directly on the VirtualFactorCore, matching _compute_ptdf_row/_compute_lodf_row and the Woodbury kernel. The batched multi-RHS solve and the from-precomputed-solve Woodbury assembly are unchanged and remain byte-for-byte consistent with the post-merge woodbury_kernel.jl. Tests read cache/core/contingency state through getters too.
The redesigned backend routes array getindex through to_index, which resolves the row against the arc-tuple lookup, so vptdf[i, :] with an integer row no longer works (integer+integer and arc-tuple+colon do). Rework the PTDF/LODF tests to index rows by arc tuple, and cover integer-keyed population by inspecting the row cache directly. VirtualMODF keeps its integer getindex(::Int, contingency) method, so those tests are unchanged in spirit. Also apply the JuliaFormatter wrapping flagged by reviewdog on src/populate_cache.jl (generic _solve_multi_rhs!, the mods comprehension, and the _woodbury_factors_from_base call) and restructure the solver-looped testsets to a begin/for form that stays within the line-length margin.
…back - VirtualMODF populate_cache resolves monitored arc tuples through _monitored_arc_index, so an arc eliminated by network reduction yields the descriptive VirtualMODF error instead of a raw KeyError. - VirtualPTDF/VirtualLODF populate_cache build each stored row (scatter, dist-slack, sparsify) outside cache_lock and take the lock only for the double-check + insert, matching the cached_row_lookup pattern and shortening the critical section. - The generic _solve_multi_rhs! fallback now checks `applicable` and raises a clear, actionable error when a backend implements neither solve_sparse! nor _solve_factorization, instead of surfacing a raw MethodError. - Add shared RowCacheValue alias for the cached-row value type.
Replace the remaining `vmodf.arc_susceptances` field reads in the populate_cache tests with the `_get_arc_susceptances` accessor, so no code in this change reaches struct fields through the wrappers' getproperty forwarding.
Add a test that backs a VirtualPTDF, VirtualLODF, and VirtualMODF with one shared VirtualFactorCore (the common ABA factorization) and runs populate_cache on each. Asserts the populated rows match independently-built matrices and that populate_cache reuses the single shared factorization object (get_core(...).K === core.K) rather than rebuilding it — confirming the feature is built on the common factorization backend introduced by the psy6 redesign.
- Generic _solve_multi_rhs! fallback now uses the value returned by _solve_factorization instead of assuming an in-place solve, so a backend that returns a fresh vector is handled correctly. - VirtualMODF populate_cache docstring no longer claims branch-name strings are valid monitored identifiers; _resolve_monitored_index accepts integer indices and bus-pair tuples (matching the MODF getindex API). - Resolve contingencies/monitored arcs inside core.solver_lock so the contingency_cache reads (UUID/Outage inputs) match getindex's locking and cannot race with clear_all_caches!.
…e-KFeWX Add populate_cache for batched multi-RHS warming of virtual matrices
…ance, three_winding_arcs) Promote the π-model admittance helpers that lived inline in PowerSimulations.jl's PowerModels translator (get_branch_to_pm) and were later copied into PowerOperationsModels.jl's native DCP/ACP builders as private `_*` helpers, into a single public, reduction-aware home in PNM: - branch_admittance(b) for lines / 2-winding transformers, and branch_admittance(segment, nr) for reduction-aggregated arcs - reduced_arc_admittance(nr, from_no, to_no) - winding_admittance(w) and three_winding_arcs(d) for 3-winding transformers - branch_flow_limits(branch) New file src/BranchAdmittance.jl (included after common.jl); exports added. Tests in test/test_branch_admittance.jl ported from POM's native model tests plus a three_winding_arcs decomposition check. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…guard Let reverse indexing defer to the forward filter verdict
Lk/arc derived entry names
fix pin irreducible buses bug
A branch with r == x == 0 has series susceptance 1/x == Inf. Ybus assembly substitutes the reduction's minimum retained impedance for such a branch, but the DC scalar accessors read the stored reactance, so the two sides disagreed. compute_parallel_multiplier and get_series_phase_shift divided Inf by Inf and returned NaN. BA_Matrix's phase-shifter arm read the component, got Inf, and its non-finite fallback turned that into zero DC coupling for the arc, while the same matrix's symmetric-branch arm read Ybus and got the substituted value. _finite_series_susceptance guards the accessor and substitutes only when the result is non-finite, so every non-degenerate branch keeps one source of truth.
Move every get_series_susceptance body into _series_susceptance_raw and make get_series_susceptance delegate. _finite_series_susceptance now calls the raw layer directly instead of the public accessor, so a future change making get_series_susceptance reject non-finite values won't affect the zero-impedance substitution path.
Chain equivalents now sum member reactances directly instead of inverting each member's susceptance, so a zero-impedance segment contributes exactly 0.0 reactance rather than a transient Inf that the old reciprocal-sum had to absorb.
network_modification.jl read a branch's raw series susceptance, which is Inf for r==x==0, while BA-derived delta_b already used the substituted epsilon. The mismatch misclassified a full outage as partial and scaled the Pi-model delta by delta_b / Inf, leaving the Ybus unchanged. Switch every call site to _finite_series_susceptance(segment, nr), and thread nr into _direct_arc_ybus_delta so it can also pass min_x_eps into ybus_branch_entries, whose Pi-model entries were themselves non-finite.
…actor weights _extract_branch_susceptances_by_arc collects per-member susceptances that are normalized downstream, so an Inf member produces NaN for the whole arc. Replace get_series_susceptance reads with _finite_series_susceptance, which substitutes the reduction's minimum retained impedance only when the raw value is non-finite.
_segment_susceptance_after_outage and _compute_series_outage_delta_b read raw series susceptance, which is Inf for r == x == 0 branches, so their outage deltas disagreed with the epsilon-substituted matrices. Both now take nr and call _finite_series_susceptance instead.
…on sweep BA_Matrix now substitutes the zero-impedance epsilon for a series-mapped arc's susceptance instead of reading 1/x raw, matching every other site converted for this bug class. Adds a property-based test verifying component-derived susceptance agrees with the Ybus-derived value across fixtures, guarding the whole class of Inf/NaN divergence rather than one instance.
…ituting accessor get_series_susceptance now throws for a branch with r == x == 0 instead of silently returning Inf, across all six delegating methods (ACTransmission, TwoWindingTransformer, TransformerCircuit, ThreeWindingTransformerCircuit, AbstractBranchesParallel, BranchesSeries). get_effective_series_susceptance is exported as the accessor consumers should use when they need the value Ybus/BA assembly actually uses for such a branch.
get_impedance_averaged_rating read the asserting accessor, so a group holding a zero-impedance member raised ErrorException rather than the ArgumentError its docstring promised, and the non-finite guard below it was unreachable. It now substitutes like the sibling multiplier does; a share is a ratio, so the constant cancels. _direct_arc_ybus_delta's three-winding arm built its Pi-model entries with the default epsilon while the generic arm used the reduction's configured value, so a non-default minimum_retained_impedance made a winding outage disagree with the matrix it modifies. get_effective_series_susceptance substitutes whenever the stored reactance is zero, while Ybus assembly substitutes only when resistance and reactance are both zero, so a purely resistive branch is outside the agreement its docstring claimed. Comments and docstrings that named get_series_susceptance now name the substituting accessor the code actually calls. The BA_Matrix ybus-constructor docstring was detached by an intervening comment and bound nowhere. The susceptance property sweep counts its checks so it cannot pass with none, and the series chain now exercises a tapped leg with non-zero reactance, where a reciprocal slip would otherwise hide.
`_finite_series_susceptance`, `get_effective_series_susceptance`, and the error helpers took an untyped segment. Segments carrying a series impedance span two unrelated supertypes, since `PSY.TransformerCircuit` is a `DeviceParameter` rather than an `ACTransmission`, so `SeriesSegment` names that union and the signatures use it. Comments that restated the code they sit above are gone; the rest keep the fact that is not visible from the call and drop the surrounding explanation.
InfrastructureSystems#634 and PowerSystems#1791 renamed the per-unit marker `DU`/`DeviceBaseUnit` to `CU`/`ComponentBaseUnit`, with no shims. 26 sites: the rating/impedance/flow-limit getters in BranchesSeries.jl, BranchAdmittance.jl and ThreeWindingTransformerCircuit.jl, plus the tests. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LP6eB1zx4eyE3hd7tpvSue
A bare TransformerCircuit never reaches these accessors as a segment. A two-winding transformer holds one circuit and one arc, so the transformer is the branch; a three-winding transformer spans three arcs and enters wrapped in ThreeWindingTransformerCircuit, which subtypes ACTransmission. Every bare circuit in the package is an internal delegation target reached from the two-winding arm. The public circuit method on get_series_susceptance was the only thing forcing the accessors to straddle ACTransmission and DeviceParameter, so it is gone and the SeriesSegment union with it. _series_susceptance_raw keeps its circuit method, which is where the tap-divided arithmetic lives.
Same shape as the susceptance accessors: a bare TransformerCircuit reaches the public accessor through its transformer at two windings, or through ThreeWindingTransformerCircuit at three, and never arrives as a segment of its own. The circuit method moves to _circuit_phase_shift, leaving the public accessor uniformly on ACTransmission.
The substituted reactance is still divided by the tap, which is what keeps the component value equal to what BA derives from Ybus: for a symmetric arc imag(1/Y_ft) is x * tap, the reciprocal of the tap-divided susceptance. Existing tests covered tap and zero impedance separately and nothing held them together, so dropping the tap would have moved the value 5% at tap = 1.05 with a green suite. BA_Matrix is the oracle rather than a restated constant.
Follow the DeviceBaseUnit -> ComponentBaseUnit rename
Test fixtures pass OperationalStates.ONLINE instead of a Bool and drop must_run; the device-base marker DU is CU everywhere.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QDkZStcFzebhh9XFpPKRSo
Made DC path respect minimum reactance
The OpenAPI model packages pinned `jd/openapi_deps_update`, which predates the 0.1.0 release, its precompilation fix and the Julia 1.13 work; point them at main. InfrastructureSystems moves from the jd/openapi-1x-suite branch to IS4, where it merged. Restore the CI Julia matrices to '1' now that the 1.13 fix has landed.
Julia 1.13 changed collection iteration order, and the arc axis for one system remaps completely across versions (index 9 was (7,8) on 1.12, (6,13) on 1.13), so anything selecting by position picked a different object and the suite failed. - `all([6, 1] .∈ keys(...))` broadcast over both sides, pairing element-wise; it only ever passed because the KeySet happened to iterate in that order. `issubset` says what was meant. - Both 3W transformers in the PST fixture are phase-shifting, so `first` picked whichever came first; name the one whose taps the testset asserts. - The Woodbury contingency arcs were fixed indices; name them by endpoints. - The zero-impedance cluster collapses to one bus, but which member survives is order-dependent, so assert that exactly one is left rather than naming it. Also iterate a snapshot of the keys in `assign_reference_buses!`: it `pop!`s from the Dict it is iterating, which is undefined and can skip entries outright.
PowerSystems drops the deprecated TwoTerminalHVDCLine constructors. These four call sites are a straight rename: the kwargs and the loss default are identical on the generic type.
Pin co-developed dependencies to the OpenAPI.jl 1.x branches
The openapi-1x branches are merged across the line, so the branch pins now track psy6 directly.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
opening to trigger CI/CD