Expand description
A switch as an exact equality constraint, rather than as a stiff branch.
This is approach 3 of docs/src/powerflow/zero_impedance_branches.md:
SwitchTreatment::Constrain.
A closed switch does not become an admittance at all. Instead its power flow
becomes an unknown, and the physics it enforces — the two ends are the same
point electrically — becomes a pair of exact equations:
θ_i − θ_j = 0 V_i − V_j = 0§The system
Two unknowns and two equations per switch, on top of the ordinary
Newton-Raphson state. The switch’s flow (P_s, Q_s) enters the power balance
of both its ends, leaving i and arriving at j:
[ J Aᵀ ] [ Δx ] [ mismatch ]
[ A 0 ] [ Δs ] = [ −c(x) ]where J is the Jacobian the unconstrained solver already builds, A holds
the ±1 of each constraint, and Aᵀ the ±1 with which each flow enters a
mismatch row. It is the same KKT shape
se::constraints assembles for zero-injection
buses, for the same reason: a hard equality is not a heavily weighted
measurement.
§What this buys over Regularize
Not scale — switches records that the expected
conditioning ceiling did not materialize, and Regularize solves every
node-breaker configuration in the tree. Three other things:
- No arbitrary constant.
Regularizepicks a stiffness; the answer depends on it, slightly, everywhere. Here there is no constant to pick, so a closed switch is exactly a short circuit rather than very nearly one. - An honest answer for a loop of closed switches. Their flows are
genuinely indeterminate — the network does not decide how a bus coupler
splits current with a parallel one — and this reports them
(
ConstrainedSolution::indeterminate) rather than inventing a split from the stiffness it happened to choose. - An open switch costs nothing. It contributes no admittance and no
coupling, where
Regularizestill carries its structural entries.
§What it costs
The matrix grows by two rows and columns per switch, and it stops being the
power-flow Jacobian: the multiplier block on the diagonal is zero, so the
system is indefinite and no longer has a nonzero diagonal everywhere. Every
gridoxide backend is a general sparse LU, so this is assembly work rather
than a new solver — the same argument se::constraints makes.
§The sparsity pattern still does not move
An open switch keeps its rows and columns. It simply constrains a different
thing: P_s = Q_s = 0 instead of θ_i = θ_j and V_i = V_j. Both forms’
entries are stamped in every state, one of the two carrying a structural
zero, so flipping a switch changes values and not the pattern — a
property
switches::regularized_branches reaches by the other route (an open
terminal’s all-zero stamp).
Structs§
- Constrained
Solution - The outcome of a constrained solve.
- Constrained
Switch - A switch to be represented as a constraint rather than as a branch.
Functions§
- solve_
constrained - Runs Newton-Raphson with each closed switch enforced as an exact equality.
- solve_
constrained_ with solve_constrainedagainst a chosen linear-solver backend.