Unbalanced three-phase distribution state estimation (DSSE)¶
Status (verified 1.95.091): Partly implemented and experimental: the per-phase network model, three-phase backward/forward sweep, node-voltage three-phase WLS (with an optional WLAV reweighting loop), branch-current DSSE, sequence-network fault study, transformer connection model, pseudo-measurements and a strict JSON feeder loader exist, reachable from the
dist3phCLI verb. The four-wire neutral model, Carson line construction, three-phase Y-bus, converter-loss and virtual-reference-bus devices, the sparse-l1ExtendedWlavestimator and the embedded IEEE 13/34/123 feeders are not implemented. Implemented in:src/GridSim.Core/Distribution/MultiPhaseModel.cs(MultiPhaseModel,PhaseBus,PhaseBranch,PhaseLoad,PhaseImpedance,Phase),ThreePhaseSweep.cs(ThreePhaseSweep,IMultiPhaseSolver,MultiPhaseResult,ThreePhaseSweepOptions),ThreePhaseWlsEstimator.cs(ThreePhaseWlsEstimator,ThreePhaseVMag,ThreePhaseCurrent,ThreePhaseWlsResult),BranchCurrentDsse.cs(BranchCurrentDsse,BranchCurrentEstimate),SequenceFaultStudy.cs,SymmetricalComponents.cs,TransformerConnectionModel.cs,DistributionPseudoMeasurements.cs,MultiPhaseJsonLoader.cs(MultiPhaseFeeder,MultiPhaseMeasurementSet). Design-to-code name map:
Design name Implemented as Phase.cs([Flags] enum Phase,PhaseSet)Phaseenum inMultiPhaseModel.cs; noPhaseSet; the sweep treats every bus and branch as three-phasePhaseImpedance.cswithFromCarson, Kron reductionPhaseImpedanceinMultiPhaseModel.cs(3x3 matrix,Diagonal,Invert); no Carson or Kron code, line codes are loaded as matricesMultiPhaseYBus.cs,PhaseSource, four-wire neutral modelnot implemented; PhaseBus.IsSourceandSourceVcarry the sourceConverterLossModel.cs,VirtualReferenceBus.csnot implemented MultiPhaseResult.cs,IMultiPhaseSolver.csboth in ThreePhaseSweep.csThreePhaseMeasurement.cs(PhaseMeasurement,PhaseMeasurementKind)ThreePhaseVMagandThreePhaseCurrentinThreePhaseWlsEstimator.cs; file form isMultiPhaseMeasurementSetThreePhaseMeasurementModel.cs,IThreePhaseStep.csnot implemented as types; the model is inside ThreePhaseWlsEstimator.EstimateExtendedWlav.cs(sparse-l1 LP)not implemented; ThreePhaseWlsEstimator.Estimate(..., robust: true)runs an IRLS WLAV loopDsseEstimationResult.csThreePhaseWlsResultandBranchCurrentEstimate(no shared type)FaultAnalysis.csadditiveSingleLineToGround,LineLine,LineLineGroundnot added; FaultAnalysiskeepsFaultType.SingleLineToGroundreported as not representable; the unbalanced faults areSequenceFaultStudyinDistribution/(notProtection/)PowerFlow/BackwardForwardSweep.csextensionThreePhaseSweepis a separate class;PowerFlow/BackwardForwardSweep.csis unchangeddata/distribution/ieee13.json,ieee34.json,ieee123.jsonnot present; data/dist3ph/holds the IEEE 4-node feeder cases onlyThis is a design record. The current behaviour is documented in the technical manual chapter 18. Real-time, distribution and dynamic estimation (section 18.5).
Implementation-ready design for the unbalanced, per-phase distribution state-estimation subsystem: the four-wire phase-impedance network model, a three-phase backward/forward-sweep power flow, two distribution estimation paths (node-voltage WLS and branch-current DSSE), an Extended-WLAV robust estimator, sequence-network fault support, and the pseudo-measurement layer that makes an under-metered feeder observable.
This is a design an implementer follows; it contains no C#. Equations are exact;
data structures are named to fit the existing code. The whole subsystem is a
separate model path that leaves the positive-sequence engine
(GridModel, YBus, NewtonRaphsonPowerFlow, WlsStateEstimator) untouched.
Audience: an engineer extending src/GridSim.Core. Read
UnderstandingRestrictions.md and RestrictionReview.Reviewed (src/GridSim.Core/Diagnostics/RestrictionReview.cs) first - the
no-forward-inference rule governs every seam here.
1. Purpose and scope¶
Transmission state estimation (GridSim's WlsStateEstimator) assumes a
balanced, transposed, positive-sequence network: one complex voltage per bus,
one pi-model 2x2 stamp per branch. Distribution feeders break every one of those
assumptions:
- Unbalanced. Phase loads differ; single- and two-phase laterals exist; the
three phase voltages are genuinely distinct, not one phasor times
1<0/-120/+120. - Untransposed. Overhead lines and cables carry real mutual coupling between phases (and to the neutral/earth). The series impedance is a full 3x3 (or 4x4) matrix, not a scalar.
- High R/X. Newton-Raphson conditions badly; the sweep methods are standard.
- Radically under-metered. A feeder may have telemetry only at the primary substation, plus sparse AMI. Observability comes from pseudo-measurements.
- Increasingly inverter-dominated. DER changes injections and, in islanded operation, removes the transmission slack that anchors the reference.
This subsystem adds:
- A full per-phase 3x3 phase-impedance network model (self/mutual, untransposed, missing phases) with four-wire neutral-inclusive (LV) form.
- Sequence networks (zero / positive / negative) derived from the phase
model, enabling
Protection/FaultAnalysis.csto represent single-line-to- ground (and line-line, line-line-ground) faults it currently declines. - A three-phase backward/forward-sweep power flow extending
PowerFlow/BackwardForwardSweep.cs. - A three-phase node-voltage WLS estimator and a distinct branch-current DSSE estimator that cross-validate each other.
- A balanced virtual-bus reference for islanded / high-DER unbalanced grids.
- Converter-loss modelling for DER/inverter injections.
- An Extended-WLAV (sparse-l1) robust estimator.
- Pseudo-measurement generation (load allocation + AMI/profile priors).
- Validation to machine epsilon against an independent in-house reference, cross-checked to OpenDSS and pandapower-3ph on the IEEE 13/34/123 unbalanced test feeders.
Out of scope here: harmonics, electromagnetic transients, time-series/quasi-
static simulation, and any recursive dynamic estimator (those are the gated
Dynamics/->Estimation/ seam, not this).
2. Restriction posture: DSSE is a filter, not a predictor¶
Every estimator specified here is a static, measurement-only filter: given
z = h(x) + e over a fixed network, resolve the present/past state x. None
integrates a process model forward. The review gate (RestrictionReview) already
permits these static estimators:
Static, measurement-only estimators (WLS, Huber, LAV, constrained, feasible, QR, linear-PMU, branch-current, EWLAV) are filters, not predictors - they need no entry and are unconditionally permitted.
So the two new estimation methods named in this design - branch-current and
EWLAV - are already on the permitted list. No new RestrictionReview
entry is required for anything in this document, because nothing here consumes
a Dynamics/ process model. If a future revision fuses a load-dynamics process
model into a recursive three-phase filter, that - and only that - trips
invariant #5 and must be reviewed and logged first.
The rules that DO bind this subsystem:
- Invariant #1 (the 169h floor). Every new dated input seam calls
ForwardInferenceGuard.RejectFuturebefore touching data:MultiPhaseMeasurementSet.Load, and any AMI / smart-meter / load-profile ingester feeding the pseudo-measurement layer. A three-phase measurement set is a dated ingester exactly likeMeasurementSet(Section 16.4). - Invariant #2 (no future-dated output). The estimators stamp their result at the measurement window time. There is no code path that emits a state dated later than its newest input.
- Solving vs forward inference. The three-phase power flow (Section 10) is
solving - a statement about the model. Seeded from guard-permitted GB data
it is reconstruction; it never nowcasts the real feeder's unpublished present
or future. Pseudo-measurements (Section 16) are historical/allocation
priors, not forecasts: they derive from load-research profiles, nameplate
allocation, or AMI reads that are themselves >=169h old or replayed. The
generator never reads the wall clock (deterministic
TakenUtc), mirroringSyntheticMeasurements. - Balanced virtual bus (Section 11) is a numerical reference device inside the model; it invents no observation of reality.
A dedicated ThreePhaseRestrictionInvariantTests fixture asserts the guard
fires on a fresh three-phase set and on a fresh AMI pseudo-source, next to the
machine-epsilon checks.
3. Design principles and non-regression of the positive-sequence path¶
-
Separate model path. The unbalanced world lives in a new namespace
GridSim.Core.Distributionundersrc/GridSim.Core/Distribution/. The positive-sequence types (GridModel,Bus,Branch,YBus,NewtonRaphsonPowerFlow,BackwardForwardSweep,WlsStateEstimator,MeasurementModel) are not modified except for two purely additive things: (a) theProtection/FaultAnalysis.csgains a newSingleLineToGroundentry point (the existingThreePhasemethod is byte-for-byte unchanged), and (b)Generator/Branchgain optional, defaulted sequence/grounding fields that every existing case leaves at their defaults (Section 9.4). No existing test can change behaviour. -
Reuse the conventions, not the code. The three-phase types mirror the existing style -
sealed recorddata,ComplexfromSystem.Numerics,internalimplementation behind public seams,PowerFlowResult-shaped results, deterministic date-literal seeds - so a reader of the transmission code is immediately at home. Where the physics forces a different choice (rectangular state, 3x3 stamps) the divergence is called out. -
Bridges both ways, tested to machine epsilon.
GridModel.ToBalancedThreePhase()lifts a positive-sequence case into a perfectly balancedMultiPhaseModel(phase impedance = diag with the positive-sequence value, zero mutuals). Running the three-phase BFS on it and collapsing back must reproduce the positive-sequenceBackwardForward Sweep/Newton result to machine epsilon. This is the master non- regression and correctness gate (Section 18.3).-
MultiPhaseModel.ToPositiveSequence()projects an unbalanced model onto its positive-sequence single-line equivalent (average self minus average mutual ->z1), so all existing positive-sequence tooling (contingency, boundary, export) still runs on distribution cases at reduced fidelity. -
Fail loud, never silently degrade. Same doctrine as the rest of the engine: an unsupported connection, a singular grounding configuration, a feeder too meshed for the sweep, or a non-finite result is reported as a typed failure status, never as a NaN dressed up as converged.
4. File and namespace layout¶
All new code under src/GridSim.Core/Distribution/ in namespace
GridSim.Core.Distribution, except the fault additions in
GridSim.Core.Protection.
src/GridSim.Core/Distribution/
Phase.cs [Flags] enum Phase; PhaseSet helpers; phase masks
PhaseImpedance.cs NxN complex matrix value type; Carson; Kron reduction
SymmetricalComponents.cs A / A^-1 transform; z_abc <-> z_012
MultiPhaseModel.cs MultiPhaseModel, PhaseBus, PhaseBranch, PhaseLoad,
PhaseSource, WindingConnection, LoadConnection enums
ConverterLossModel.cs quadratic converter-loss record + evaluation
VirtualReferenceBus.cs balanced virtual-bus reference device
MultiPhaseYBus.cs 3n x 3n (or 4-wire) sparse admittance; sequence Ybus
MultiPhaseResult.cs MultiPhaseResult (per-phase Vm/Va, flows, losses, status)
IMultiPhaseSolver.cs solver seam (parallel to IPowerFlowSolver)
ThreePhaseSweep.cs three-phase BFS (extends the BackwardForwardSweep idea)
ThreePhaseMeasurement.cs PhaseMeasurement, PhaseMeasurementKind, PhaseMeasurementSet
ThreePhaseMeasurementModel.cs rectangular-state h(x) and Jacobian
IThreePhaseStep.cs estimation-step seam (parallel to IWlsStep)
ThreePhaseWlsEstimator.cs node-voltage 3-phase WLS + loop
BranchCurrentDsse.cs distinct branch-current formulation + estimator
ExtendedWlav.cs sparse-l1 (LP) robust estimator
DistributionPseudoMeasurements.cs load-allocation + AMI/profile priors
DsseEstimationResult.cs result type shared by the three estimators
src/GridSim.Core/Protection/
FaultAnalysis.cs + SingleLineToGround / LineLine / LineLineGround (additive)
SequenceFaultStudy.cs sequence-network Thevenin build (new file)
data/distribution/ embedded IEEE feeders (Section 18.1)
ieee13.json ieee34.json ieee123.json
Public surface: MultiPhaseModel, PhaseBus/Branch/Load/Source, Phase,
ThreePhaseSweep, ThreePhaseWlsEstimator, BranchCurrentDsse,
ExtendedWlav, DsseEstimationResult, MultiPhaseResult,
DistributionPseudoMeasurements, the fault entry points. Implementation
(step backends, LP kernels, Carson helpers) stays internal with
InternalsVisibleTo("GridSim.Tests") (already granted in the csproj).
5. Phasing and the primitive/phase-impedance model¶
5.1 Phase identity and masks¶
A PhaseBus, PhaseBranch, PhaseLoad and PhaseSource each carry a Phase
Present mask. A single-phase lateral is A; a two-phase is A | C; a full
three-phase is A | B | C; the four-wire LV form adds N. All per-phase arrays
and matrices are indexed by the ordered present phases (a stable ascending order
A,B,C,N). PhaseSet provides Count, IndexOf(Phase), and the ordered
enumeration so a 2x2 submatrix on {A,C} is handled uniformly with a 3x3 on
{A,B,C}.
Missing phases are first-class: a branch's series matrix is sized to
Present.Count, and a bus's state carries only its present phases. A load or
source on a phase the bus does not present is a validation Error.
5.2 The primitive impedance matrix (modified Carson)¶
For an overhead segment with conductors indexed by {a,b,c,n,...} the primitive
series impedance (ohms per unit length, per Kersting's modified Carson's
equations at line frequency f, earth resistivity rho) is:
Self (conductor i):
z_ii = (r_i + r_earth) + j * x_factor * ( ln(1/GMR_i) + earth_term )
Mutual (i != j):
z_ij = r_earth + j * x_factor * ( ln(1/D_ij) + earth_term )
with, at 50 Hz (SI, ohms/km) :
r_earth = pi^2 * f * 1e-4 (earth return resistance)
x_factor = 4 * pi * f * 1e-4 (= omega * mu0 / (2 pi))
earth_term= 0.5 * ln( rho / f ) + C_earth (Carson earth-return const)
GMR_i is the conductor geometric mean radius; D_ij the centre-to-centre
distance from geometry. PhaseImpedance.FromCarson(conductors, geometry, f, rho)
computes the primitive z_prim (an N x N complex matrix) and, from the
potential-coefficient matrix P (same geometry, image charges), the primitive
shunt y_prim = j * omega * C_prim with C_prim = 2*pi*eps0 * P^-1.
Because published feeders (IEEE 13/34/123, OpenDSS LineGeometry) most often
provide the already-reduced phase matrices directly, the primary authoring
path is to supply z_abc and y_abc per segment as data; FromCarson is the
constructive path for cases given as geometry + conductor tables. Both must
agree to machine epsilon on a shared fixture (Section 18.5).
5.3 Kron reduction of the neutral¶
The four-wire primitive partitions into phase block p and neutral block n:
[ z_pp z_pn ]
z_prim= [ z_np z_nn ]
Kron reduction (grounded neutral folded to earth):
z_abc = z_pp - z_pn * z_nn^-1 * z_np (3x3 phase impedance)
PhaseImpedance.KronReduce(neutralPhases) performs the block solve (dense
complex LU on the small z_nn). The 3-wire model uses z_abc; the 4-wire
model keeps z_prim un-reduced (Section 6). A PhaseBranch records which form
it is in (NeutralModel = Reduced | Explicit).
5.4 Per-unit conversion¶
Distribution data is in physical units (ohms, siemens, kV, kVA). The model
carries a per-segment length and converts to per-unit on the segment's voltage
base and the system BaseMva, exactly analogous to the transmission per-unit
convention (technical ch. 2). Transformer segments define a voltage-base change;
the per-unit z_abc is referred to the segment's own base. A
MultiPhaseModel.Validate() pass checks base continuity across each transformer.
6. Four-wire neutral-inclusive admittance (LV-LSE) with neutral coupling¶
For LV feeders (and MV cases where neutral/earth current matters) the neutral is kept explicit rather than Kron-folded. This is the "LV low-voltage state-estimation" (LV-LSE) form: the state carries the neutral conductor voltage, and neutral-return / earthing currents are observable.
- A four-wire
PhaseBranchhasPresent = A|B|C|N, a4x4seriesz_prim, a4x4shunty_prim, andNeutralModel = Explicit. - The neutral bus voltage is a genuine state at each four-wire node (it is NOT
assumed zero); multi-grounded neutrals are modelled by a shunt earthing
admittance
y_gfrom the neutral node to the ideal ground (the reference for all phase voltages).PhaseBus.NeutralGroundingSiemensgivesy_g(0 = ungrounded, infinite = solidly bonded to the reference). - Neutral coupling enters through the off-diagonal
z_prim[a..c, n]/y_prim[..., n]terms exactly as for any other conductor; nothing special is needed in the sweep or the Y-bus once N is treated as a fourth phase index.
The 4-wire Y-bus is 4-present x 4-present per node. Grounding admittances stamp
the neutral diagonal. Kron reduction (Section 5.3) is exactly the operation that
maps a solved four-wire result onto the three-wire equivalent, so the two forms
are cross-checked: solving the explicit four-wire model and Kron-reducing the
voltages must match the reduced-model solve to machine epsilon on a common
grounded case (Section 18.5).
The estimator treats the neutral phase identically to A/B/C: a four-wire node
contributes 2 * Present.Count rectangular state components. Voltage/current
measurements can address the neutral (Phase.N) - a neutral-current meter is a
strong LV-LSE observation of unbalance.
7. The multi-phase network model¶
MultiPhaseModel (sealed class, parallels GridModel):
double BaseMva
double FrequencyHz (default 50)
IReadOnlyList<PhaseBus> Buses
IReadOnlyList<PhaseBranch> Branches
IReadOnlyList<PhaseLoad> Loads
IReadOnlyList<PhaseSource> Sources (slack / DER / grid-forming)
VirtualReferenceBus? VirtualReference (Section 11; null on grid-tied)
string Name
IReadOnlyList<GridDiagnostic> Validate() (same diagnostic type as GridModel)
PhaseBus (sealed record):
int Id
Phase Present
double BaseKv (line-to-line nominal)
Complex NeutralGroundingSiemens (per-unit; 4-wire only)
double[] VmInitPu, VaInitDeg (per present phase; flat-start default)
PhaseBranch (sealed record):
int From, To
Phase Present
NeutralModelKind NeutralModel (Reduced | Explicit)
PhaseImpedance Zseries (present x present, per-unit)
PhaseImpedance Yshunt (present x present, per-unit; split half/half)
BranchKind Kind (Line | Cable | Transformer | Switch | Regulator)
WindingConnection FromConn, ToConn (transformer only; else Wye by default)
double TapRatio (regulators/transformers; per phase optional)
double[]? PhaseTapRatio (independent per-phase regulator taps)
string? Name
bool InService
PhaseLoad (sealed record):
int Bus
LoadConnection Connection (Wye | Delta)
Phase Phases
LoadModel Model (ConstantPower | ConstantCurrent | ConstantImpedance | ZIP)
double[] Pkw, Qkvar (per phase, or per phase-pair for Delta)
ZipCoefficients? Zip
PhaseSource (sealed record):
int Bus
SourceRole Role (Slack | GridForming | GridFollowingPV | GridFollowingPQ)
Phase Phases
double[] Vsetpu, Vsetdeg (per phase; Slack/GridForming)
double[] Pkw, Qkvar (GridFollowing)
ConverterLossModel? Converter (Section 12)
Complex SequenceZ1, SequenceZ2, SequenceZ0 (machine/inverter sequence
impedances for fault studies)
Complex NeutralGroundingZPu (source neutral earthing; 0 = solidly grounded)
WindingConnection { Wye, WyeGrounded, Delta } and LoadConnection { Wye,
Delta } capture the connection algebra that drives both the sweep transformer
matrices (Section 10.4) and the zero-sequence network (Section 8.3).
Validate() reuses ValidationSeverity/GridDiagnostic and reports: a load or
source on an absent phase (Error), no slack and no virtual reference (Error), a
non-finite matrix entry (Error), a transformer base discontinuity (Error), an
unsupported connection pair (Error), a delta load on a bus missing a required
phase (Error), and an ungrounded four-wire island (Warning - it will float).
8. Multi-phase Y-bus and the sequence networks¶
8.1 Phase-domain Y-bus¶
MultiPhaseYBus.BuildSparse(model) produces a sparse per-phase admittance keyed
by (busIndex, phase) compound indices, mirroring YBus.BuildSparse. Each
in-service branch stamps its 2x2 block-of-blocks:
For a line/cable segment with series admittance Ys = Zseries^-1 (present x present)
and half shunt Bsh = 0.5 * Yshunt:
Y[f,f] += Ys + Bsh
Y[t,t] += Ys + Bsh
Y[f,t] += -Ys
Y[t,f] += -Ys
where each entry is itself a present x present complex matrix. For missing
phases the submatrices are sized to the branch's present set and scattered into
the matching phase slots of the two buses. Transformers stamp their connection-
dependent nodal blocks (Section 10.4 gives the primitive per-connection
admittance Yprim used identically here). Bus shunts and neutral-grounding
admittances land on the diagonal. Determinism: stamps are visited in model order
with a fixed accumulation order so each cell is bit-reproducible (same doctrine
as YBus.BuildSparse).
8.2 Symmetrical-component transform¶
a = exp(j*2*pi/3)
[ 1 1 1 ] 1 [ 1 1 1 ]
A = [ 1 a^2 a ] A^-1 = ----- * [ 1 a a^2]
[ 1 a a^2] 3 [ 1 a^2 a ]
Sequence impedance of a 3x3 phase matrix:
z_012 = A^-1 * z_abc * A
SymmetricalComponents provides A, Ainv, ToSequence(z_abc) and
ToPhase(z_012). For a transposed line z_abc has equal diagonals and equal
off-diagonals, so z_012 = diag(z0, z1, z2) exactly with
For an untransposed line z_012 is full (inter-sequence coupling); the
sequence networks are then coupled. The fault study (Section 9) uses the exact
z_012 blocks; the common decoupled approximation (diagonal only) is available
behind a flag for comparison to hand calculations, and the design records that
the exact coupled form is the default so untransposed feeders are not silently
approximated.
8.3 Sequence Y-bus / Z-bus¶
SequenceFaultStudy.Build(model) assembles three nodal networks:
- Positive (1) and negative (2). For passive plant
Y1 = Y2; they are the balanced single-phase network already produced by the positive-sequence path, reusable directly. Sources contribute1/Z1and1/Z2shunts (machine or inverter sequence impedance fromPhaseSource). - Zero (0). Assembled from the zero-sequence line impedance
z0and, per transformer, the connection-dependent zero-sequence coupling: WyeGrounded - WyeGrounded: passes zero-sequence through (seriesz0).WyeGrounded - Delta: zero-sequence sees the grounded-wye winding to ground but is blocked to the delta side (shunt on the grounded side, open across).Delta - Delta/Wye(ungrounded): blocks zero-sequence (open).- A grounded source neutral adds
3 * Znin the zero-sequence path (3 * NeutralGroundingZPu).
The zero-sequence network topology is therefore genuinely different from the
positive-sequence one - which is exactly why single-line-to-ground currents
cannot be obtained from the positive-sequence model alone, and why the current
FaultAnalysis reports SLG as not-representable.
9. Single-line-to-ground faults via sequence networks¶
This is the additive extension to Protection/FaultAnalysis.cs that lets the
protection overlay operate on earth faults.
9.1 Thevenin sequence impedances at the fault bus¶
For fault bus k, solve one column of each sequence Z-bus (one complex linear
solve per sequence, reusing the dense SolveComplex already in
FaultAnalysis.cs):
with prefault (positive-sequence) voltage E_k = V0_k at the fault bus taken
from the prefault solved state (the same prefault-voltage source the existing
ThreePhase uses).
9.2 Fault-type connection of the sequence networks¶
Single-line-to-ground (phase a), fault impedance Zf:
I_a0 = I_a1 = I_a2 = E_k / (Z0_kk + Z1_kk + Z2_kk + 3*Zf)
Phase currents: I_abc = A * [I_a0; I_a1; I_a2]
(=> I_b = I_c = 0 for a pure a-g fault, the physical signature)
Line-to-line (b-c), impedance Zf:
I_a1 = -I_a2 = E_k / (Z1_kk + Z2_kk + Zf) ; I_a0 = 0
Double line-to-ground (b-c-g):
standard parallel combination of Z2 and (Z0 + 3*Zf) fed from Z1.
Faulted sequence voltages V_012(i) = V0_012(i) - Z_seq[:,k] * I_seq per network,
then per-bus phase voltages V_abc = A * V_012; faulted phase branch currents
follow from the phase-domain stamps (Section 8.1) at the faulted voltages,
driving the protection elements exactly as ThreePhase does today.
9.3 Result and API¶
FaultAnalysis.SingleLineToGround(MultiPhaseModel model, MultiPhaseResult prefault,
int faultBusId, Phase faultedPhase = Phase.A, double faultReactancePu = 0.0)
-> MultiPhaseFaultResult
FaultAnalysis.LineLine(...) FaultAnalysis.LineLineGround(...)
MultiPhaseFaultResult carries the faulted MultiPhaseResult, the per-phase
fault current (pu and kA when BaseKv known), and the sequence currents for
provenance. The existing balanced ThreePhase(GridModel, ...) overload is
untouched; a new ThreePhase(MultiPhaseModel, ...) overload is the three-phase
sibling (three-phase-to-ground = the balanced limit, a consistency check:
sequence-network SLG with all three phases faulted must equal the direct
phase-domain three-phase fault to machine epsilon).
9.4 Additive model fields¶
Generator and Branch in Model/Model.cs gain optional, defaulted fields so a
positive-sequence case can still be fault-studied for SLG by supplying sequence
data without a full phase model:
Generator:SequenceZ2Pu,SequenceZ0Pu,NeutralGroundingZPu(defaults:Z2 = Z1synthesised,Z0open,Zn = 0).Branch:WindingConnection FromConn, ToConn(defaultWyeGrounded),SequenceZ0Pu(default:3*Z1overhead rule-of-thumb, provenance-noted).
Every existing case leaves these at defaults, so no positive-sequence test moves.
10. Three-phase backward/forward-sweep power flow¶
ThreePhaseSweep : IMultiPhaseSolver extends the algorithm in
PowerFlow/BackwardForwardSweep.cs from scalars to present x present complex
matrices/vectors. It is the reference truth for the estimators and the seed for
warm starts.
10.1 State and structure¶
Voltages and currents become per-bus complex phase vectors V[bus] in C^{present},
I[bus] in C^{present}. Series impedances are the PhaseImpedance matrices. The
spanning-tree / breakpoint machinery, PV-bus reactive correction, and weak-mesh
compensation from the scalar sweep carry over unchanged in structure; only the
per-edge algebra becomes matricial. The MaxCompensatedLoops gate and the
"disconnected / too meshed -> non-radial report" fallbacks are preserved.
10.2 Backward sweep (leaves -> source)¶
For each bus in reverse tree order, accumulate the nodal injection current (load + shunt + DER) plus the accumulated child-branch currents, transformed across each child branch's connection matrix:
I_node[bus] is the per-phase current the bus draws (Section 10.5). T_i is the
branch current-transformation matrix (identity for a plain line; connection-
dependent for transformers/regulators, Section 10.4).
10.3 Forward sweep (source -> leaves)¶
T_v is the branch voltage-transformation matrix (identity for a plain line;
the ideal-transformer ratio/connection for transformers). Convergence is on
max over (bus,phase) |V - V_prev| < Tolerance, matching the scalar sweep's
maxDelta contract, with the same NonFinite guard and status doctrine.
10.4 Transformer / regulator connection matrices¶
Each supported connection contributes a 6x6 primitive [Yprim] (equivalently
the (T_v, T_i, Zseries) triple) per Kersting's generalized matrices
[a],[b],[c],[d],[A],[B]:
V_abc(from) = a * V_abc(to) + b * I_abc(to)
I_abc(from) = c * V_abc(to) + d * I_abc(to)
V_abc(to) = A * V_abc(from) - B * I_abc(to)
Connections supported at v1: WyeGrounded-WyeGrounded, Delta-WyeGrounded
(the ubiquitous distribution step-down, with its 30-degree/1/sqrt3 shift),
WyeGrounded-Delta, Delta-Delta, and the step/voltage regulator (per-phase
tap on a wye or closed-delta). Each connection's [a..d,A,B] matrices are
tabulated constants times the per-unit turns ratio; the design lists them
explicitly in the implementation notes and each is unit-tested against a
single-transformer hand calculation.
10.5 Load and source current models¶
Wye constant-power (phase p): I_p = conj( S_p / V_p )
Wye constant-impedance: I_p = V_p / Z_p (Z_p from nameplate S,V)
Wye constant-current: I_p = |I_p0| at fixed pf, angle tracks V_p
Delta constant-power (pair pq): I_pq = conj( S_pq / (V_p - V_q) ),
nodal current = I_pq - I_(previous pair into p)
ZIP: convex combination of the three above by (Zip.z, Zip.i, Zip.p) with z+i+p=1
DER GridFollowingPQ sources inject -I of the constant-power form at their
setpoint (converter loss applied, Section 12); GridFollowingPV sources hold
per-phase (or positive-sequence) voltage magnitude via the reactive correction
already in the scalar sweep, generalized per phase.
10.6 Result¶
MultiPhaseResult mirrors PowerFlowResult: per-(bus,phase) Vm/VaDeg,
per-phase net injections, per-branch per-phase flows and losses, total loss, and
a MultiPhaseStatus { Converged, MaxIterations, NonFinite, NonRadial }. A
ToPositiveSequence() collapses it to a PowerFlowResult for the existing UI /
export plumbing (positive-sequence component of the solved phase voltages).
11. Balanced virtual-bus reference for islanded / high-DER grids¶
A grid-tied feeder inherits its angle/voltage reference from the transmission
slack (the primary substation PhaseSource with Role = Slack). An islanded
microgrid (grid-forming inverters, no transmission connection) has no such
anchor, and its unbalance means there is no single "correct" reference phasor.
VirtualReferenceBus provides a balanced virtual bus: an internal,
perfectly-balanced positive-sequence source E * [1, a^2, a] behind a small
virtual series impedance Zv, attached to a nominated physical bus (usually the
dominant grid-forming unit's terminal). It:
- Fixes the angle datum. The virtual bus's phase-a angle is 0 by definition, so the sweep and the estimator have a well-defined reference even when every physical source is grid-forming and imbalanced.
- Absorbs residual imbalance during the solve (it is the slack of the
islanded network), while
Zvkeeps its influence local and physically interpretable rather than clamping the nearby physical bus to perfect balance. - Grounds the sequence decomposition. Because it is balanced,
V_012at the virtual bus is pure positive-sequence, giving the fault study a clean reference and the estimator a stable positive-sequence datum.
Design points:
- Zv is a design parameter (default: a few percent on the unit's base) chosen
large enough not to distort the local unbalance yet small enough to anchor the
solve; the sensitivity is documented and tested.
- In the estimator, the virtual bus's three voltages are fixed reference
states (removed from the free state, exactly as slack angles are removed in
the transmission estimator), so it adds a datum, not free unknowns.
- It is a model device, not an observation: it asserts nothing about the real
grid and therefore raises no restriction concern. It appears in provenance as a
synthetic reference so no consumer mistakes it for a metered bus.
Selection rule: if Sources contains a Slack, the virtual bus is off by
default; if the model is islanded (no Slack), a VirtualReferenceBus is
required or Validate() reports "no reference (Error)".
12. Converter-loss modelling¶
Inverter-interfaced DER (and any power-electronic converter) dissipates a current-dependent loss that a naive constant-power injection ignores, biasing both the power flow and the estimated DER output. Model it as the standard quadratic in converter current magnitude:
ConverterLossModel (sealed record):
double A0Kw (no-load / switching loss)
double A1 (conduction, linear, kW per pu-amp)
double A2 (resistive, quadratic, kW per pu-amp^2)
double Evaluate(double iConvMagPu) -> P_loss (kW)
Placement:
- Power flow. In the source current model (Section 10.5), the AC-side real
injection is reduced by P_loss (the loss appears on the DC/prime-mover side);
|I_conv| is the AC current the sweep already computes, so the correction is a
fixed-point term evaluated each sweep - it converges with the outer iteration.
- Estimation. When a measurement observes the DC-side or nameplate power and
the state carries AC-side voltage/current, the loss enters h(x) as the same
quadratic; its Jacobian contribution is dP_loss/d|I| = a1 + 2*a2*|I| chained
through d|I|/dx. This keeps an estimated DER injection physically consistent
with its metered AC current instead of over-crediting output by the loss.
The loss model is optional per source (null = ideal converter); coefficients are
authored from datasheet efficiency curves and provenance-noted. A converter-loss
consistency test checks that the power-flow AC/DC balance closes to machine
epsilon for a chosen (a0,a1,a2).
13. The three-phase node-voltage WLS estimator¶
ThreePhaseWlsEstimator is the direct three-phase analogue of
WlsStateEstimator, and its structure deliberately mirrors it (loop owns start
point, damping, convergence, chi-square, bad-data removal; representation lives
behind IThreePhaseStep). Two deliberate divergences from the transmission
estimator, both standard DSSE practice:
13.1 Rectangular state¶
The state is rectangular voltage per present phase:
x = [ Vre(bus,phase), Vim(bus,phase) ] over all buses and present phases,
minus the reference bus/virtual-bus phases (fixed datum)
Rectangular coordinates are chosen because (a) current and power measurement
Jacobians are lower-order and better-conditioned on stiff, high-R/X feeders, (b)
there is no per-phase angle-wrap or reference-degeneracy pathology, and (c) the
branch-current path (Section 14) shares the rectangular convention so the two
estimators compare directly. The divergence from the transmission estimator's
polar (Va, Vm) state is intentional and documented; the balanced-case bridge
(Section 18.3) reconciles the two to machine epsilon.
The reference datum (slack PhaseSource phases, or the VirtualReferenceBus
phases) is fixed and removed from the free state, exactly as slack angles are
removed in the transmission MeasurementModel.
13.2 Measurement kinds and h(x)¶
PhaseMeasurementKind:
VmPu (per bus,phase) h = sqrt(Vre^2 + Vim^2)
VaRad (per bus,phase; PMU) h = atan2(Vim, Vre) - ref
VreVim (PMU rectangular pair) h = Vre ; h = Vim (linear)
PInjKw (per bus,phase) h = Re( V_p * conj( sum_k Y_pk V_k ) )
QInjKvar (per bus,phase) h = Im( V_p * conj( sum_k Y_pk V_k ) )
PFlowKw (per branch end,phase) h = Re( V_p * conj( I_flow_p ) )
QFlowKvar (per branch end,phase) h = Im( V_p * conj( I_flow_p ) )
IMagPu (per branch end,phase) h = |I_flow_p|
IReIm (PMU branch current pair) h = Re/Im( I_flow_p ) (linear)
IneutralPu (four-wire neutral current) (LV-LSE)
I_flow uses the phase-domain branch stamps from Section 8.1. Injection sums run
over the bus's electrical neighbourhood only (the same O(nnz) sparse-injection
discipline as MeasurementModel.ComputeInjectionsSparse), and there is a single
Jacobian evaluation path writing into a dense or sparse sink, mirroring
EvaluateJacobian<TSink> so the dense and sparse backends cannot drift.
13.3 Jacobians (rectangular)¶
With E_p = Vre_p + j Vim_p and nodal current Ii_p = sum_k Y_pk E_k (each Y_pk
a scalar phase-to-phase admittance from the block matrix), the exact partials
are the standard rectangular DSSE forms, e.g. for the linear/near-linear kinds:
VreVim: dVre/dVre = 1, all else 0 (constant, linear)
VmPu: d|V|/dVre = Vre/|V| , d|V|/dVim = Vim/|V|
Pinj_p: dP/dVre_p , dP/dVim_p , dP/dVre_k , dP/dVim_k
from d/dx of Re( E_p * conj(sum_k Y_pk E_k) ) (bilinear -> linear-in-x partials)
Because P/Q injections and flows are bilinear in the rectangular state, the
partials are affine in x; |V| and |I| add the only genuinely nonlinear rows.
This is why rectangular DSSE converges in very few Gauss-Newton steps and why the
gain is nearly constant - exploited in the branch-current path (Section 14) and
noted as a conditioning advantage here.
13.4 Loop, damping, observability, bad data¶
Reused wholesale from the transmission estimator's doctrine:
- Gauss-Newton on the normal equations G = H^T W H, G dx = H^T W (z - h),
with W = diag(1/sigma^2).
- Trust-region damping on ||dx|| (rectangular components share one cap; the
angle/Vm split is not meaningful in rectangular coordinates, so a single
per-component cap is used).
- Observability by gain rank (dense) / unmeasured-island sweep (sparse), reusing
the ObservabilityReport type and doctrine (Rank = -1 on the sparse backend).
- Chi-square consistency (ChiSquare.Quantile, reused unchanged) and largest-
normalised-residual bad-data removal with critical-measurement protection.
- Zero-injection buses (no load, no source) are exact equality constraints via
the same Hachtel/KKT mechanism as ConstrainedDenseWlsStep
(Source == "virtual-zero-injection"), which is heavily used on feeders (every
branchless junction is a zero-injection node).
DsseEstimationResult mirrors EstimationResult (status, converged, iterations,
per-phase Vm/VaDeg, objective, dof, chi-square verdict, residual table,
removals, observability), plus a ToMultiPhaseResult(model) projector analogous
to EstimationResult.ToPowerFlowResult (pure evaluation, no solve - so
no-forward-inference holds).
14. Branch-current DSSE (distinct estimation path)¶
A structurally different estimator that cross-validates the node-voltage WLS - the classic branch-current-based DSSE (Baran/Kelley), which is the distribution workhorse because its gain is nearly constant on radial feeders.
14.1 State¶
i.e. the complex series current of every branch per phase (rectangular). For a
radial feeder with b branches and 3 phases the state is 6b; bus voltages are
recovered from the branch currents by a forward sweep from the reference,
not carried as state.
14.2 Measurement equations¶
- Branch current (magnitude or PMU rectangular). Linear in the state -
h = Ire/Iimor|I|directly. This is the natural, best-conditioned distribution measurement and makesHalmost constant. - Branch power flow.
S = V * conj(I); withVobtained from the current state by the forward sweep,P/Qflow rows are bilinear, evaluated at the swept voltages. - Injection. A bus injection is the (connection-resolved) sum of the incident
branch currents times the bus voltage:
S_inj = V_bus * conj( sum incident I ). - Voltage magnitude.
|V_bus|, withV_busfrom the forward sweep - the only rows coupling distant branches, and the reason a small number of voltage measurements is needed for the magnitude datum.
14.3 Why it is a distinct path, not a reformulation¶
The Jacobian topology is fundamentally different (branch-indexed, near-constant),
the gain factorisation can be reused across iterations, and the two estimators
have different numerical failure modes - so agreement between the node-voltage
WLS and the branch-current DSSE on the same measurement set is a strong
cross-check (Section 18.4), the same philosophy as GridSim's dense/sparse and
Newton/BFS cross-validation. BranchCurrentDsse is a separate public estimator
(not an IThreePhaseStep), returning the same DsseEstimationResult so callers
and tests treat the three estimators uniformly.
14.4 Restriction note¶
Branch-current DSSE is explicitly on the permitted whitelist in
the review gate (RestrictionReview): branch-current estimators are filters, not predictors.
It is measurement-only, emits a present/past state, and needs no review entry.
15. Extended-WLAV robust estimator (sparse-l1)¶
ExtendedWlav implements the weighted least-absolute-value estimate as an
exact linear program, "extended" to carry equality and inequality constraints -
the robust sibling of the approximate IRLS-LAV already in the transmission
estimator.
15.1 Formulation¶
WLAV minimises the weighted l1 norm of residuals:
Linearised at the operating point (H = Jacobian, r = z - h(x_bar),
dx = x - x_bar), each Gauss-Newton step is the LP:
min sum_i w_i (u_i + v_i)
s.t. H dx - u + v = r
u, v >= 0 (u,v = pos/neg residual parts)
[extended] C dx = d_c (zero-injection equalities)
x_lo <= x_bar + dx <= x_hi (state box, when Feasible-style guard on)
Solved by a sparse primal-dual interior-point method (or the equivalent
sparse simplex on the dual), reusing the CSR/CSC + Gilbert-Peierls sparse LU
already in GridSim.Core.Numerics for the KKT/normal-step factorisation. The
outer Gauss-Newton loop re-linearises until ||dx|| meets tolerance.
15.2 Why WLAV, and why "extended"¶
- Robustness by construction. The l1 estimate is insensitive to a bounded number of gross errors without an explicit detection-and-removal loop: a single bad measurement in a redundant set is fit with zero weight at the LP vertex. This is valuable on distribution data where pseudo-measurements and AMI reads carry heavy-tailed errors that break the Gaussian assumption behind WLS.
- Extended = the same estimate honours hard zero-injection equalities (ubiquitous on feeders) and optional state boxes (voltage admissibility), reusing the constrained/feasible doctrine rather than the huge-weight trick.
15.3 Determinism and restriction¶
The LP uses a fixed pivot/ordering rule and a fixed starting point, so the vertex selected is deterministic and byte-reproducible (a determinism test pins it). On clean Gaussian data WLAV agrees with the node-voltage WLS to a documented tolerance (they minimise different norms, so not to machine epsilon, but close); on data with one injected gross error, WLAV's estimate is unmoved while WLS requires its removal loop - the discriminating test. EWLAV is on the permitted whitelist; it is measurement-only and needs no review entry.
16. Pseudo-measurement generation¶
Feeders are under-determined by real telemetry alone. DistributionPseudo
Measurements makes a case observable by generating priors - the distribution
analogue of SyntheticMeasurements + MeasuredInjectionPrior.
16.1 Sources of pseudo-measurements¶
- Nameplate / kVA allocation. Distribute a measured feeder-head load onto downstream buses in proportion to connected transformer kVA (or contract capacity), per phase. Sigma is large (typically 30-50% of value) - these are weak priors, not observations.
- Load-research / AMI profile priors. Where historical smart-meter or load-research class profiles exist, the expected per-phase demand at a bus and time-of-day is a pseudo-measurement with a tighter (profile-derived) sigma.
- Zero-injection equalities. Every branchless junction / pure connection
node injects exactly zero on every present phase - authored as
Source == "virtual-zero-injection"rows the constrained/EWLAV paths promote to hard equalities (Sections 13.4, 15). - Voltage-schedule prior. A flat or regulator-scheduled voltage magnitude at
unmetered buses at a loose sigma, the distribution version of the transmission
pseudo-voltage-scheduleprior.
16.2 Sigma model and provenance¶
Each pseudo-measurement carries a Source tag naming its composition
(pseudo-alloc-kva, pseudo-ami-profile, virtual-zero-injection,
pseudo-voltage-schedule), with sigmas combined root-sum-square, exactly the GDA
sigma-model convention documented for the transmission estimator. This lets the
bad-data / robust logic treat weak priors appropriately (never removing a
zero-injection equality; down-weighting a 40%-sigma allocation before a 2%-sigma
metered flow).
16.3 Determinism¶
Same discipline as SyntheticMeasurements: one seeded RNG (date-literal default
seed), buses/branches walked in model order, Box-Muller Gaussians, NoiseScale =
0 draws nothing (exact-recovery fixture). The generator never reads the wall
clock; TakenUtc is an explicit, safely-historical parameter.
16.4 Restriction: pseudo-measurements are historical priors, not forecasts¶
This is the load-bearing restriction point for the pseudo-measurement layer:
- A pseudo-measurement encodes what a load typically was (allocation, historical profile), not what the feeder will do or is secretly doing now. It is a prior over the past/present state the estimator is reconstructing, never a forward projection of the real grid.
- Any real AMI / profile input is a dated ingester and passes
ForwardInferenceGuard.RejectFutureat load: profiles must be >=169h old or replayed.MultiPhaseMeasurementSet.Loadhorizon-checks itsTakenUtclikeMeasurementSet.Loaddoes. - The generated set's timestamp is the reconstruction window's, so invariant #2 (no future-dated output) holds through the estimate.
17. Seams to extend (summary)¶
New seams, each a clean parallel to an existing one - no existing interface is widened:
| New seam | Parallels | Purpose |
|---|---|---|
IMultiPhaseSolver (Solve -> MultiPhaseResult) |
IPowerFlowSolver |
three-phase power flow |
ThreePhaseSweep |
BackwardForwardSweep |
the v1 solver behind that seam |
IThreePhaseStep (Begin/GateObservability/TrySolve/NormalizedResiduals) |
IWlsStep |
dense/sparse three-phase WLS backends |
ThreePhaseEstimationMethod { Wls, BranchCurrent, Ewlav } |
EstimationMethod |
method selection; all three whitelisted |
PhaseMeasurementKind |
MeasurementKind |
per-phase measurement vocabulary |
MultiPhaseYBus / SequenceFaultStudy |
YBus |
phase and sequence admittances |
DistributionPseudoMeasurements |
SyntheticMeasurements + MeasuredInjectionPrior |
observability priors |
Method selection: rather than overloading the transmission EstimationMethod
enum (which is bound to the positive-sequence WlsStateEstimator), the
distribution estimators take a sibling ThreePhaseEstimationMethod. The names
Wls, BranchCurrent, Ewlav map onto the permitted whitelist entries, and a
comment in the enum cross-references RestrictionReview so the
whitelist coverage is discoverable at the call site. Huber/Lav/Constrained/
Feasible are available on the node-voltage path by reusing the IRLS/KKT/box
mechanisms behind IThreePhaseStep.
The RestrictionReview.AssertPermitted call is present at the head of every
estimator (as it is for the transmission estimator); because none of these
methods usesDynamicsModel, the gate passes them unconditionally, and
RestrictionInvariantTests continues to assert the code table and the log agree.
18. Validation and test plan¶
The gate for merge: the full existing suite stays green (no positive-sequence
regression) and the new suite below passes, under TreatWarningsAsErrors.
18.1 Reference feeders¶
Embed the IEEE 13-, 34-, and 123-bus unbalanced test feeders as
data/distribution/ieee{13,34,123}.json (a faithful transcription of the IEEE
PES / OpenDSS feeder data: line geometries or reduced phase matrices, spot and
distributed loads, regulators, transformers, capacitors, phasing). Add them as
EmbeddedResource with stable logical names
(GridSim.Core.cases.dist.ieee13.json, ...), exactly like the MATPOWER case*.m
resources. A DistributionCases factory (parallel to IeeeCases) materialises
them into MultiPhaseModel.
18.2 Machine-epsilon vs an independent in-house reference¶
"Machine epsilon" is asserted against a second, structurally-different in-house
solver, not against OpenDSS (whose reported precision is limited). The
independent reference is a direct nodal-admittance (Y-bus) fixed-point / full
3n-dimension Newton three-phase power flow (a different algorithm from the
BFS): its converged phase voltages must agree with ThreePhaseSweep to < 1e-9
pu on all three feeders. This is the same doctrine as the transmission
Newton-vs-BFS and dense-vs-sparse cross-checks.
18.3 Non-regression / balanced-bridge (the master gate)¶
GridModel.ToBalancedThreePhase()on each MATPOWER case, solved byThreePhaseSweep, collapsed viaToPositiveSequence(), must reproduce the positive-sequenceBackwardForwardSweep(radial cases) and Newton result to< 1e-10pu. This proves the three-phase machinery is correct AND that the positive-sequence path is untouched.- Every pre-existing test (power flow, transmission estimation, fault
ThreePhase, protections) is asserted byte-identical - the additive model fields default to no-ops.
18.4 Estimator correctness and cross-agreement¶
- Exact recovery. A full, exact (
NoiseScale = 0) three-phase measurement set recovers theThreePhaseSweeptruth to< 1e-10for the node-voltage WLS and the branch-current DSSE on all three feeders (mirrorsWlsEstimatorTests.AssertRecoversTruth). - Cross-estimator. Node-voltage WLS, branch-current DSSE, and EWLAV agree on clean noisy data to a documented tolerance (WLS==BCDSSE to ~1e-8; EWLAV close, different norm).
- Bad data. With one injected gross error, WLS's removal loop identifies and removes it; EWLAV's estimate is unmoved without removal. Both restore the truth within the clean-data band.
- Observability. A feeder stripped to below observability reports
Unobservablewith the free directions named; adding the pseudo-measurement layer restores observability - a direct test of Section 16.
18.5 Model-layer checks¶
- Carson vs supplied matrices.
PhaseImpedance.FromCarsonon a geometry with a published reduced matrix matches the suppliedz_abcto machine epsilon. - Kron consistency. Four-wire explicit solve, Kron-reduced, equals the reduced three-wire solve on a common grounded case (Section 6).
- Sequence identities. For a transposed line,
z_012 = diag(z0,z1,z2)to machine epsilon;A * A^-1 = I. - Fault checks. SLG at a bus:
I_a0 = I_a1 = I_a2andI_b = I_c = 0for a pure a-g fault (to machine epsilon); a symmetric three-phase-to-ground SLG-limit equals the direct phase-domain three-phase fault; magnitudes cross-checked to an OpenDSSFaultStudyto its reported precision. - Converter loss. AC/DC balance closes to machine epsilon for a chosen
(a0,a1,a2); a zero-loss model reproduces the ideal-source solve exactly.
18.6 Cross-check to OpenDSS / pandapower-3ph¶
Under tools/crossval/ (parallel to the existing pandapower cross-validation),
scripts export each feeder solve and estimate and compare against OpenDSS
(Solve, Export Voltages) and pandapower three-phase (pandapower.pf.
runpp_3ph, pandapower.estimation where applicable). Agreement is asserted to
each tool's published precision (typically 1e-4..1e-6 pu), with the in-house
machine-epsilon check (18.2) carrying the tight bound. Differences are documented,
not silently tolerated.
18.7 Determinism¶
Byte-identical results across repeated runs and (CI) across platforms: the pseudo-measurement RNG is seeded, the sweep and estimator visit model order, the sparse orderings are deterministic, and the EWLAV LP uses a fixed pivot rule. A determinism fixture hashes the solved/estimated arrays and pins the hash.
18.8 Restriction invariants¶
ThreePhaseRestrictionInvariantTests: MultiPhaseMeasurementSet.Load rejects a
TakenUtc newer than UtcNow - 169h; an AMI pseudo-source rejects a fresh
stamp; the estimators emit a state stamped no later than their newest input; and
RestrictionReview needs (and gains) no new dynamics-fused entry for any method
here.
19. Build-gate and coding-standard conformance¶
The repo builds pedantically: TreatWarningsAsErrors=true,
EnableNETAnalyzers, AnalysisMode=Recommended, Nullable=enable (root
Directory.Build.props). The new code must land at zero warnings:
- Nullable-clean: no
!dammit operators without justification; seams return non-null or typed-empty (Array.Empty<T>()), matching the existing style. sealedon every concrete class not designed for extension (all of them);internalimplementation withInternalsVisibleTo("GridSim.Tests").- No unused parameters/usings/locals;
staticwhere the analyser asks; culture- invariant parsing/formatting for the feeder JSON loader (CultureInfo. InvariantCulture, asMeasurementSet.TryResolveBranchalready does). - Deterministic APIs: any
Randomis seeded from an explicit option, never the default clock-seeded ctor (an analyser/determinism concern both). - Complex/matrix value types (
PhaseImpedance) implement value equality and guard against non-finite entries at construction, consistent with the model's finite-input validation doctrine. - XML doc comments on every public member (the codebase documents densely; the analyser set and the house style both expect it).
CI runs dotnet build -warnaserror and the full dotnet test; both must be
green before the positive-sequence path is considered non-regressed.
20. Restriction invariants honoured (checklist)¶
Invariant (UnderstandingRestrictions.md) |
How this design honours it |
|---|---|
| #1 169h floor at every dated ingester | MultiPhaseMeasurementSet.Load and every AMI/profile source call ForwardInferenceGuard.RejectFuture |
| #2 no future-dated output | estimators stamp at the window time; ToMultiPhaseResult/ToPowerFlowResult are pure evaluation |
| #3 solver forward trajectories are model output | the three-phase power flow is solving; seeded only from guard-permitted data; never presented as a feeder forecast |
| #4 real-time on replay/permitted-age only | DSSE estimates present/past from permitted-age or replayed three-phase sets, never live feeder telemetry |
| #5 Dynamics->Estimation fusion is gated | nothing here consumes a Dynamics/ process model; all methods are static filters; no new review entry needed (branch-current, EWLAV already whitelisted) |
The pseudo-measurement layer is the subtlety: it is explicitly a historical/ allocation prior over the reconstructed state, not a forecast, and it is horizon-guarded like every other dated input. The balanced virtual bus is a model device that asserts nothing about reality.
21. Implementation phasing¶
A suggested build order that keeps the suite green at each step:
- Model + Y-bus (Sections 5-8.1):
Phase,PhaseImpedance(+ Carson/Kron),MultiPhaseModeland records,MultiPhaseYBus,Validate, the balanced- bridgeToBalancedThreePhase/ToPositiveSequence. Gate: 18.3, 18.5. - Three-phase power flow (Section 10):
ThreePhaseSweep,MultiPhaseResult, the independent Newton reference. Gate: 18.2, 18.3. - Sequence networks + faults (Sections 8.2-8.3, 9):
SymmetricalComponents,SequenceFaultStudy,FaultAnalysis.SingleLineToGroundand additive fields. Gate: 18.5 fault checks. - Node-voltage WLS (Section 13): measurement model,
IThreePhaseStepdense backend, loop, chi-square/bad-data reuse. Gate: 18.4 exact recovery. - Pseudo-measurements (Section 16) + observability tests. Gate: 18.4 observability, 18.8.
- Branch-current DSSE (Section 14) and cross-agreement. Gate: 18.4 cross.
- EWLAV (Section 15) sparse-l1. Gate: 18.4 bad-data, 18.7 determinism.
- Converter loss + virtual bus + four-wire LV (Sections 6, 11, 12) as the islanded/DER-heavy hardening pass. Gate: 18.5 Kron/converter, islanded solves.
- Sparse backends + scaling (sparse
IThreePhaseStep, sparse EWLAV) for large feeders. Gate: 18.7 determinism at scale. - Cross-val scripts (Section 18.6) and the technical-manual chapter.
22. Open questions and future work¶
- Time-series / quasi-static three-phase estimation over a day of AMI reads (state carried bus-to-bus as a warm start) - still a static filter per instant, but the chaining deserves its own restriction note (it must stay a sequence of present-state reconstructions, never a forecast).
- Regulator/OLTC discrete-tap estimation (mixed-integer) - deferred; v1 treats taps as known inputs.
- Meter placement / observability design tooling on the pseudo-measurement layer.
- Harmonic / unbalanced-source extensions to the sequence model.
- A GDA distribution feed (LV/MV network models + AMI in the data lake), analogous to the transmission GDA bridge, once such data is authored and horizon-guarded on the GDA side.
See also¶
- State estimation - the positive-sequence estimator this parallels
- Power flow - Newton/BFS engines and the pi-model stamps reused per phase
- Protections and Protection assessment - the overlay the SLG fault feeds
- Provenance and invariants - the horizon guard on dated inputs
UnderstandingRestrictions.md,RestrictionReview.Reviewed(src/GridSim.Core/Diagnostics/RestrictionReview.cs) - the governing rule and the DSE gate