Concepts¶
This page explains the physical and numerical model that groundinsight
implements. It is aimed at readers who want to understand why the code
returns the numbers it returns — for example to validate results against
measurements or analytical expressions.
Problem statement¶
Consider a power grid composed of substations (buses) and the cables and overhead lines (branches) that connect them. Each bus possesses a grounding grid tying it to remote earth; each branch carries a grounding conductor (cable shield, overhead-line earth wire) that is bonded to the grids of its two terminals. A single-phase-to-ground fault at one bus drives a fault current back towards the source. That current splits into two parallel paths:
- the local earth path through the grounding grid at the fault bus, and
- the metallic return path through the grounding conductor(s) of the connecting branches.
The split depends on the impedances of both paths and on the mutual coupling between the faulted phase and the grounding conductor that runs alongside it. The resulting EPR, the reduction factor and the grounding impedance are the quantities of interest.
Objects¶
groundinsight represents the grid with four Pydantic models:
Bus— a node. Carries a grounding impedance \(Z_{\text{B}}(\rho_E, f)\) that couples the bus to remote earth.Branch— an edge between two buses with a self impedance \(Z_{\text{self}}(\rho_E, f, l)\) (series impedance of the grounding conductor) and a mutual impedance \(Z_{\text{mutual}}(\rho_E, f, l)\) (coupling between faulted phase and grounding conductor).Source— a current source anchored at a bus. Holds a dictionary that maps each frequency to the injected phasor current.Fault— a marker at a bus. Holds a dictionary of frequency-dependent scaling factors that reduce the source current for each harmonic.
Bus and branch properties are derived from reusable types (BusType,
BranchType) whose impedances are parameterised by formula strings. The
formulas are parsed with SymPy, turned into lambdify callables and evaluated
for every network frequency.
Impedance formulas¶
Formula strings may contain the symbols
| Symbol | Meaning |
|---|---|
rho |
specific earth resistance \(\rho_E\) in \(\Omega\,\text{m}\) |
f |
frequency \(f\) in Hz |
l |
branch length \(l\) in km |
j |
imaginary unit (internally 1j) |
Expressions are evaluated symbolically, so any analytic expression supported
by SymPy is admissible. The literal string "nan" maps to an effectively
infinite impedance and can be used to model open ends (broken shields,
isolators etc.).
Nodal-admittance formulation¶
All computations take place per frequency \(f\) in the phasor domain.
groundinsight assembles an admittance matrix \(Y(f)\) of size \(N\times N\)
(where \(N\) is the number of buses):
Each entry can be switched off by setting grounding_conductor=False on the
corresponding branch, which is useful for modelling insulated shields.
The right-hand-side vector \(\underline{i}(f)\) holds the source injections scaled by the active fault's frequency scaling and — crucially — the mutual-coupling contributions. For every branch on a path from source to fault a Norton equivalent is added: the phase current \(I_{\text{phase}}\) driving the branch induces a shield current of magnitude \(I_{\text{mut}} = I_{\text{phase}}\,Z_{\text{mutual}}/Z_{\text{self}}\), injected as \(-I_{\text{mut}}\) at the from bus and \(+I_{\text{mut}}\) at the to bus (signs follow the direction source → fault).
The EPR vector is then
Numerically the system is solved with SciPy's sparse LU decomposition
(scipy.sparse.csc_matrix + splu) — this scales well to meshed
low-voltage networks with thousands of buses.
Path finding¶
Mutual-coupling injections require a direction. groundinsight derives that
direction by enumerating every simple path from each source bus to the active
fault bus via a depth-first search (PathFinder). Each path is stored as an
ordered list of Branch objects; its injection signs follow the traversal
order.
In ring or meshed topologies a single source–fault pair yields multiple
paths. By default every path carries the full source current. The optional
parallel_coefficient on a branch lets you pre-scale the current share of
individual parallel legs; if you set auto_parallel_coefficients=True on
run_fault, groundinsight solves a reduced phase-only network first and
uses its current distribution as the per-path scaling.
Derived quantities¶
Once \(\underline{u}(f)\) is known for every frequency, three result families are computed:
Earth potential rise (EPR)¶
The per-frequency bus voltages are stored as ComplexNumber entries on
ResultBus objects. RMS values across all frequencies are computed via
Branch currents¶
For every branch and frequency the current through the grounding conductor is
where the second term accounts for the Norton source representing the
mutual coupling (compute_branch_currents).
Reduction factor¶
The reduction factor \(r\) at the fault bus is defined as
groundinsight obtains the denominator by re-solving the same network with
all mutual-coupling Norton sources removed. For a single shielded line
directly between source and fault with identical impedances the expression
collapses to the familiar analytical form
Grounding impedance¶
The effective grounding impedance seen at the fault bus is
It is exposed per frequency and as RMS-scalar through
net.res_all_impedances().
Summary of the calculation pipeline¶
run_fault(network, fault_name) executes the following steps (see
network_operations.run_fault and ElectricalNetwork for details):
set_active_fault— select the target fault.define_paths— if no paths are set yet, callPathFinderto enumerate them with a DFS.build_electrical_network— create anElectricalNetworkhelper that holds the numerical arrays.solve_network— build \(Y(f)\) and \(\underline{i}(f)\) and solve the linear system per frequency (sparse LU).compute_branch_currents— derive branch currents from \(\Delta u\) plus Norton contributions.compute_reduction_factors— re-solve without mutual Norton sources and take the EPR ratio at the fault bus.compute_grounding_impedance— evaluate \(Z_G\) per frequency and as RMS.
The persistent state of the calculation (sparse matrices, vectors) lives on
a private ElectricalNetwork attribute of the Network instance, so
re-solving after topology changes only requires a fresh run_fault call.