Skip to content

Analytical reference formulas

The :mod:groundfield.references subpackage contains analytical reference formulas used in plausibility tests and as closed-form sanity checks against the numerical backends.

Dwight 1936 — straight rod in homogeneous soil

groundfield.references.dwight1936 implements the classical formula for the spreading resistance of a single vertical rod electrode in homogeneous soil:

\[ R = \frac{\rho}{2\pi L}\,\left[\ln\!\frac{4 L}{a} - 1\right] \]

with \(\rho\) the soil resistivity, \(L\) the rod length and \(a\) the rod radius. The formula is exercised by tests/test_dwight_references.py against the image-method backend.

Carson 1926 — overhead-line earth-return impedance

groundfield.references.carson implements Carson's series expansion for the earth-return self/mutual impedance of an overhead conductor over a homogeneous lossy half-space.

Oeding — inductive coupling between buried conductors

groundfield.references.oeding implements the closed-form expressions for the mutual inductance between buried conductors in homogeneous soil from Oeding & Oswald, Elektrische Kraftwerke und Netze.

IEEE Std 80 — substation grounding-grid resistance

groundfield.references.ieee80 implements Sverak's equation (IEEE Std 80-2000 Eq. 57 / 2013 Eq. 53) for the grounding resistance of a horizontal grid,

\[ R_g = \rho\left[\frac{1}{L_T} + \frac{1}{\sqrt{20\,A}} \left(1 + \frac{1}{1 + h\sqrt{20/A}}\right)\right], \]

with total conductor length \(L_T\), grid area \(A\) and burial depth \(h\).

Beyond the closed-form resistance, tests/test_ieee80_benchmark.py reproduces the standard's Annex H benchmark (IEEE Std 80-2013) for all three benchmark grids: the uniform-soil Grid 1 (bare grid, Table H.5) and Grid 2 (grid + twenty 7.5 m rods, Table H.6), and the two-layer Grid 3 (Grid 2 geometry in a soil with \(\rho_1 = 300\) Ω·m, \(\rho_2 = 100\) Ω·m and interface \(h = 6.096\) m, so the rods cross the layer boundary; Table H.7). Solved with the mom backend — which recovers the current distribution as the reference programs CDEGS / ETAP / WinIGS do — groundfield matches the published grid resistance, GPR and touch voltage to about 1 % on every grid and the step voltage to a few percent. The worked notebooks/43_ieee80_benchmark.ipynb lays the results side by side for all three grids, tabulates the relative error and plots the grid-resistance comparison and the touch-voltage surface.

Carson — earth-return line impedance

groundfield.references.earth_return implements Carson's closed-form series impedance per unit length of a conductor with earth return — the engineering-level (\(\Omega/\text{km}\)) companion to the dimensionless Carson integral in groundfield.references.carson:

\[ Z'_\text{self} = \frac{\omega\mu_0}{8} + j\,\frac{\omega\mu_0}{2\pi}\,\ln\frac{D_e}{\mathrm{GMR}}, \qquad D_e = 658.87\,\sqrt{\rho/f}\ \text{[m]}, \]

with the soil-independent earth-return resistance \(\omega\mu_0/8\) and the equivalent-depth reactance (\(D_e\) is the depth of the fictitious return conductor). The mutual form replaces \(\mathrm{GMR}\) by the conductor–conductor separation.

tests/test_earth_return_benchmark.py validates the assembled Sommerfeld/Pollaczek earth-return stack (earth_inductive_model="sommerfeld") against it: the reactance \(X'\) — carrying the frequency- and soil-dependent depth \(D_e\) rather than the naive geometric image at \(2h\) — matches Carson to about 1 %, while the resistance \(R'\) approaches \(\omega\mu_0/8\) from below as the modelled line lengthens (the return current closes over roughly one skin depth). The worked notebooks/45_earth_return_line_impedance.ipynb plots \(Z'(f)\) for the self and mutual terms and the \(R'\) convergence, including the normal-soil power-frequency corner (100 Ω·m, 50 Hz). A two-layer section validates the layered earth-return reactance against the rigorous closed form of Tsiamitros et al. (IEEE Trans. PWRD 20(3), 2005, Eq. 8) across an explicit 50 Hz – 1 MHz frequency sweep — matching to a few percent in \(X'\) and to \(\approx 0.003\) in the layer attribution, with the low-frequency (\(\le 1\) kHz) regime, where the inductive return is deep-layer dominated, highlighted for low-frequency analysis. Since 0.13.0 "sommerfeld" is the default earth_inductive_model, so a plain inductive solve already carries this earth-return physics; "perfect_mirror" (the former default, a fast electrostatic-image baseline) is now opt-in for a quick frequency-independent estimate.

Pollaczek/Sunde — layered buried earth-return line impedance

groundfield.references.pollaczek implements the rigorous layered, buried-conductor earth-return self impedance per unit length — the two-layer companion to the homogeneous Carson closed form above. Where Carson gives the equivalent-depth reactance of an overhead conductor, this gives the exact spectral integral for a conductor buried at depth \(h\) in the upper layer of a two-layer earth (\(\rho_1\) over \(\rho_2\), interface at \(h_1\)):

\[ Z'_\text{self} = j\,\frac{\omega\mu_0}{2\pi}\left[ \ln\!\frac{2h}{\mathrm{GMR}} + 2\int_0^\infty \frac{e^{-2h\lambda}}{\lambda + u_\text{eff}(\lambda)} \,d\lambda \right], \]

with the effective vertical wavenumber of the two-layer earth from the Tagg/Sunde/Wait tanh-recursion (exposed as layered_effective_wavenumber):

\[ u_\text{eff}(\lambda) = u_1\, \frac{u_2 + u_1\tanh(u_1 h_1)}{u_1 + u_2\tanh(u_1 h_1)}, \qquad u_i = \sqrt{\lambda^2 + j\omega\mu_0/\rho_i}. \]

Mathematical / physical model

The formulation is quasi-static (displacement current neglected, \(u_i = \sqrt{\lambda^2 + j\omega\mu_0/\rho_i}\) carries only the conduction term), valid for the power- to audio-frequency range that grounding analysis works in. It returns the earth-return series impedance only (external inductance; no internal-conductor \(R'\) or internal inductance — the same convention as carson_self_impedance), so a caller adds the internal conductor term separately. The \(\ln(2h/\mathrm{GMR})\) term is the small-argument form of the perfect-mirror image \(K_0(\gamma\,\mathrm{GMR}) - K_0(2\gamma h)\); the integral is the finite-conductivity earth-return correction, with the layering entering through \(u_\text{eff}\) alone. Limiting cases: \(\rho_1 = \rho_2 \Rightarrow u_\text{eff} = u_1\) (homogeneous Pollaczek, matching carson_self_impedance in the reactance to \(< 0.1\,\%\), the earth-return resistance sitting just below \(\omega\mu_0/8\) — Carson's small-argument limit, which the exact Pollaczek integral recovers only as \(f \to 0\), the deficit growing with frequency); \(h_1 \to \infty \Rightarrow\) upper layer only; \(h_1 \to 0 \Rightarrow\) lower layer only. In between, the reactance lies within the \([\text{Carson}(\rho_1), \text{Carson}(\rho_2)]\) bracket and is deep-layer dominated at low frequency — a thin resistive topsoil is inductively "seen through".

Unlike the assembled Sommerfeld/Pollaczek stack (earth_inductive_model="sommerfeld"), which builds a PEEC segment inductance matrix from which a single per-unit-length impedance cannot be read back without an ill-defined return-path assumption, this returns one scalar series impedance \(z'(f) = R' + jX'\) that a reduced nodal network of an earthing system consumes directly, and that serves as an independent analytic reference for the assembled stack's earth-return reactance. tests/test_pollaczek_reference.py (50 tests) pins the homogeneous Carson limit, the resistance-from-below behaviour, both \(h_1\) limits, the Carson bracket across 50 Hz – 1 kHz, the low-frequency deep-layer regime, the wavenumber recursion and the input guards; a loop-closure test in tests/test_earth_return_benchmark.py ties the assembled stack to this reference and to the Tsiamitros et al. (2005, Eq. 8) integral (all three within \(\sim 1\,\%\) on the Case V two-layer reactance at 1 kHz). The worked notebooks/46_pollaczek_earth_return.ipynb plots \(z'(f)\) against Carson, the homogeneous convergence and the two-layer bracket across layer thickness. References: Pollaczek (1926); Sunde (1949, Earth Conduction Effects in Transmission Systems, §§ 3–4); Wait (1970); Tsiamitros et al. (2005).

Conductor–earth loop impedance

loop_impedance_conductor_earth builds the full conductor–earth loop impedance on top of the earth-return term:

\[ z'_\text{loop}(f) = R'_\text{internal} + Z'_\text{earth-return}, \]

the series impedance of a current that flows out along a buried earthing conductor — a cable PEN, a cable screen — and returns through the soil, with the earthing electrodes at both ends idealised as \(\approx 0\,\Omega\). The earth-return term (pollaczek_self_impedance) carries the external and, through the \(\mathrm{GMR}\), the internal inductance of the conductor; r_internal is its own IEC 60228 series resistance per unit length. For a solid round conductor pass \(\mathrm{GMR} = 0.7788\,a = a\,e^{-1/4}\) (internal inductance \(\mu_0/8\) included); for a thin tubular / wire screen pass the screen radius (no internal inductance). With the ideal termination the loop impedance of a line of length \(\ell\) is \(z'_\text{loop}\,\ell\); a real termination earthing resistance \(R_E\) adds in series on top. This is the loop that governs earth-fault current sharing and the reach of an earthing system tied together through NAYY PENs or NA2XS2Y screens — which the bare earth-return impedance does not give on its own. Earth-return path only: no mutual coupling to a parallel conductor and no internal skin-effect model (pass a skin-corrected r_internal near the top of the band). The notebook works it for both a 150 mm² Al PEN and a 25 mm² Cu screen.

Coupling between two conductor–earth loops

pollaczek_mutual_impedance is the off-diagonal counterpart of the self impedance — the earth-return coupling between two parallel buried conductor–earth loops:

\[ Z'_\text{M} = j\,\frac{\omega\mu_0}{2\pi}\left[ \ln\frac{D}{d} + 2\int_0^\infty \frac{e^{-(h_i+h_j)\lambda}}{\lambda + u_\text{eff}(\lambda)} \cos(\lambda x)\,d\lambda\right], \]

with the horizontal separation \(x\), the direct distance \(d=\sqrt{x^2+(h_i-h_j)^2}\) and the mirror distance \(D=\sqrt{x^2+(h_i+h_j)^2}\); the \(\ln(D/d)\) term is the perfect-mirror image pair (small-argument \(K_0(\gamma d)-K_0(\gamma D)\)) and the integral the finite-conductivity correction, the layering again entering only through \(u_\text{eff}\). Unlike the self, it carries no internal-conductor term — two galvanically separate loops couple purely inductively through the soil — so with the ideal termination the coupling of a parallel run of length \(\ell\) is \(Z'_\text{M}\,\ell\), and together with two self impedances it forms the \(2\times2\) loop impedance matrix of the coupled pair. It reduces to carson_mutual_impedance (and to the exact homogeneous Pollaczek mutual \(K_0\) form) when \(\rho_1=\rho_2\), its magnitude lies below the self and falls off logarithmically with separation, and its two-layer reactance stays within the Carson bracket, deep-layer dominated at low frequency. This is exactly the coupling term an independent-loop reduction of a networked earthing system drops; the notebook works it as a separation sweep and a \(2\times2\) matrix for two parallel PENs.

API reference

references

Analytical reference formulas for solver validation.

This subpackage collects closed-form expressions from the classical literature on grounding-resistance computation in homogeneous soil. They serve two purposes:

  1. Plausibility checks for the numerical backends — every new electrode geometry should ship with at least one comparison test against a closed-form formula.
  2. Sanity checks on 2-layer models that are nearly homogeneous (\(\rho_1 \approx \rho_2\)): the numerical result must converge to the homogeneous limit of the corresponding reference formula.

Modules:

Name Description
dwight1936

Formulas from Dwight, H. B.: Calculation of Resistances to Ground, Electrical Engineering / AIEE Transactions, December 1936, pp. 1319-1328. Table I covers driven rod, rod pair (close / far), buried horizontal wire, right-angle, 3/4/6/8 point stars, ring, strip, round and vertical plate.

oeding

Per-unit-length loop self- and mutual-inductance formulas from Oeding & Oswald (2016) Elektrische Kraftwerke und Netze (Springer), chapter 9 — used as an analytical reference for the segment-based Neumann implementation in :mod:groundfield.coupling.inductance.

carson

Worked examples and tabulated values from Carson 1926 (Bell STJ 5(4)) plus modern textbook reference points (Tleis 2008). Used to validate the Carson earth-return correction in :mod:groundfield.coupling.carson (ADR-0005).

earth_return

Carson's engineering-level (Ω/km) earth-return line impedance — self and mutual series impedance with the equivalent-depth :math:D_e reactance and the soil-independent :math:\omega\mu_0/8 resistance. Benchmarks the assembled Sommerfeld/Pollaczek inductive stack (earth_inductive_model="sommerfeld").

pollaczek

Rigorous layered (Pollaczek/Sunde) earth-return line impedance — the two-layer, buried-conductor self impedance per unit length :math:z'(f) as a spectral integral with the Tagg/Sunde/Wait effective vertical wavenumber :math:u_\text{eff}(\lambda). The layered, buried companion to earth_return (Carson): it reduces to :func:~groundfield.references.earth_return.carson_self_impedance when :math:\rho_1 = \rho_2, brackets between the two homogeneous Carson bounds otherwise, and supplies the scalar per-unit-length series impedance that the assembled Sommerfeld/Pollaczek PEEC stack (a segment inductance matrix) does not expose directly. loop_impedance_conductor_earth adds the earthing conductor's own series resistance on top, giving the full conductor–earth loop impedance (cable PEN, cable screen) with ideal (:math:\approx 0\,\Omega) far-end earthing. pollaczek_mutual_impedance is the off-diagonal counterpart — the earth-return coupling between two parallel buried conductors — which together with two self impedances forms the :math:2\times2 loop impedance matrix of the coupled pair.

ieee80

Sverak's grid grounding-resistance formula from IEEE Std 80 (IEEE Guide for Safety in AC Substation Grounding, 2000 Eq. 57 / 2013 Eq. 53). Benchmarks the image backend's grid resistance against the industry-standard engineering estimate.

layered_effective_wavenumber

layered_effective_wavenumber(
    lam: float,
    rho_1: float,
    rho_2: float,
    h_1: float,
    omega: float,
) -> complex

Effective vertical wavenumber :math:u_\text{eff}(\lambda) of a two-layer earth.

The Tagg/Sunde/Wait tanh-recursion surface term seen by a buried conductor. Reduces to the homogeneous half-space value :math:u_1 when rho_1 == rho_2, to :math:u_1 as h_1 → ∞ and to :math:u_2 as h_1 → 0. Units: 1/m.

Parameters:

Name Type Description Default
lam float

Spectral (Hankel) variable, 1/m.

required
rho_1 float

Upper / lower layer resistivity in Ω·m.

required
rho_2 float

Upper / lower layer resistivity in Ω·m.

required
h_1 float

Upper-layer thickness in m.

required
omega float

Angular frequency in rad/s.

required
Source code in src/groundfield/references/pollaczek.py
def layered_effective_wavenumber(
    lam: float, rho_1: float, rho_2: float, h_1: float, omega: float
) -> complex:
    r"""Effective vertical wavenumber :math:`u_\text{eff}(\lambda)` of a two-layer earth.

    The Tagg/Sunde/Wait tanh-recursion surface term seen by a buried
    conductor. Reduces to the homogeneous half-space value :math:`u_1` when
    ``rho_1 == rho_2``, to :math:`u_1` as ``h_1 → ∞`` and to :math:`u_2`
    as ``h_1 → 0``. Units: 1/m.

    Parameters
    ----------
    lam
        Spectral (Hankel) variable, 1/m.
    rho_1, rho_2
        Upper / lower layer resistivity in Ω·m.
    h_1
        Upper-layer thickness in m.
    omega
        Angular frequency in rad/s.
    """
    u1 = cmath.sqrt(lam * lam + 1j * omega * MU_0 / rho_1)
    if rho_1 == rho_2:
        return u1
    u2 = cmath.sqrt(lam * lam + 1j * omega * MU_0 / rho_2)
    t = cmath.tanh(u1 * h_1)
    return u1 * (u2 + u1 * t) / (u1 + u2 * t)

loop_impedance_conductor_earth

loop_impedance_conductor_earth(
    rho_1: float,
    rho_2: float,
    h_1: float,
    frequency: float,
    *,
    depth: float,
    gmr: float,
    r_internal: float
) -> complex

Conductor–earth loop impedance per unit length (Ω/m), ideal termination.

The series impedance seen by a current that flows out along a buried earthing conductor — a cable PEN, a cable screen — and returns through the earth, with the earthing electrodes at both ends idealised as :math:\approx 0\,\Omega:

.. math::

z'_\text{loop}
= \underbrace{R'_\text{internal}}_{\text{conductor}}
  + \underbrace{Z'_\text{earth-return}}_{\text{Pollaczek self}}
= R'_\text{internal}
  + Z'_\text{self}(\rho_1, \rho_2, h_1, f;\, \text{depth}, \text{gmr}).

This is the "go along the conductor, return through the soil" loop that governs earth-fault current sharing and the reach of an earthing system connected through cable PENs / screens — the quantity a bare earth-return impedance (:func:pollaczek_self_impedance) does not give on its own, because it omits the conductor's own series resistance.

Convention. The earth-return term carries the external inductance and, through gmr, the conductor's internal inductance: for a solid round conductor pass gmr = 0.7788·a = a·e^{-1/4} (a the physical radius), which already includes the internal inductance :math:\mu_0/(8\pi); for a thin tubular / wire screen pass gmr equal to the screen radius (negligible internal inductance). r_internal is the conductor's series resistance per unit length — its DC / IEC 60228 value, or a skin-corrected AC value near the top of the band. With the ideal (:math:\approx 0\,\Omega) earthing at both ends the loop impedance of a line of length :math:\ell is simply :math:z'_\text{loop}\,\ell; a real earthing resistance :math:R_E at the terminations adds in series on top of that.

Parameters:

Name Type Description Default
rho_1 float

Two-layer earth resistivities (Ω·m), upper-layer thickness (m) and frequency (Hz), exactly as in :func:pollaczek_self_impedance. Set rho_1 == rho_2 for a homogeneous earth.

required
rho_2 float

Two-layer earth resistivities (Ω·m), upper-layer thickness (m) and frequency (Hz), exactly as in :func:pollaczek_self_impedance. Set rho_1 == rho_2 for a homogeneous earth.

required
h_1 float

Two-layer earth resistivities (Ω·m), upper-layer thickness (m) and frequency (Hz), exactly as in :func:pollaczek_self_impedance. Set rho_1 == rho_2 for a homogeneous earth.

required
frequency float

Two-layer earth resistivities (Ω·m), upper-layer thickness (m) and frequency (Hz), exactly as in :func:pollaczek_self_impedance. Set rho_1 == rho_2 for a homogeneous earth.

required
depth float

Burial depth of the conductor in m. Must be positive.

required
gmr float

Geometric mean radius in m — 0.7788·a for a solid round conductor, the screen radius for a tubular / wire screen. Must be positive.

required
r_internal float

Conductor series resistance per unit length in Ω/m. Must be non-negative.

required

Returns:

Type Description
complex

Loop series impedance :math:R'_\text{loop} + jX'_\text{loop} in Ω/m. Multiply by the line length for the terminated loop impedance.

Notes

Earth-return path only: no mutual coupling to a parallel conductor, and no internal skin-effect model (pass a skin-corrected r_internal if that matters at the top of the band). A conductor–conductor loop (e.g. line–PEN at a fixed spacing) has its return-path reactance set by that spacing, not by the earth-return depth, and is much smaller — this function is specifically the conductor–earth loop.

Examples:

>>> import math
>>> a = math.sqrt(150e-6 / math.pi)          # 150 mm^2 solid-round radius
>>> z = loop_impedance_conductor_earth(       # NAYY PEN, 100 Ohm.m, 50 Hz
...     100.0, 100.0, 10.0, 50.0,
...     depth=0.7, gmr=0.7788 * a, r_internal=0.206e-3)
>>> round(z.real * 1e3, 3), round(z.imag * 1e3, 3)  # mOhm/m -> Ohm/km
(0.255, 0.758)
Source code in src/groundfield/references/pollaczek.py
def loop_impedance_conductor_earth(
    rho_1: float,
    rho_2: float,
    h_1: float,
    frequency: float,
    *,
    depth: float,
    gmr: float,
    r_internal: float,
) -> complex:
    r"""Conductor–earth loop impedance per unit length (Ω/m), ideal termination.

    The series impedance seen by a current that flows out along a buried
    earthing conductor — a cable PEN, a cable screen — and returns through
    the earth, with the earthing electrodes at both ends idealised as
    :math:`\approx 0\,\Omega`:

    .. math::

        z'_\text{loop}
        = \underbrace{R'_\text{internal}}_{\text{conductor}}
          + \underbrace{Z'_\text{earth-return}}_{\text{Pollaczek self}}
        = R'_\text{internal}
          + Z'_\text{self}(\rho_1, \rho_2, h_1, f;\, \text{depth}, \text{gmr}).

    This is the "go along the conductor, return through the soil" loop that
    governs earth-fault current sharing and the reach of an earthing system
    connected through cable PENs / screens — the quantity a bare
    earth-return impedance (:func:`pollaczek_self_impedance`) does not give
    on its own, because it omits the conductor's own series resistance.

    Convention. The earth-return term carries the external inductance and,
    **through** ``gmr``, the conductor's internal inductance: for a solid
    round conductor pass ``gmr = 0.7788·a = a·e^{-1/4}`` (``a`` the physical
    radius), which already includes the internal inductance
    :math:`\mu_0/(8\pi)`; for a thin tubular / wire screen pass ``gmr`` equal
    to the screen radius (negligible internal inductance). ``r_internal`` is
    the conductor's series resistance per unit length — its DC / IEC 60228
    value, or a skin-corrected AC value near the top of the band. With the
    ideal (:math:`\approx 0\,\Omega`) earthing at both ends the loop
    impedance of a line of length :math:`\ell` is simply
    :math:`z'_\text{loop}\,\ell`; a real earthing resistance :math:`R_E` at
    the terminations adds in series on top of that.

    Parameters
    ----------
    rho_1, rho_2, h_1, frequency
        Two-layer earth resistivities (Ω·m), upper-layer thickness (m) and
        frequency (Hz), exactly as in :func:`pollaczek_self_impedance`.
        Set ``rho_1 == rho_2`` for a homogeneous earth.
    depth
        Burial depth of the conductor in m. Must be positive.
    gmr
        Geometric mean radius in m — ``0.7788·a`` for a solid round
        conductor, the screen radius for a tubular / wire screen. Must be
        positive.
    r_internal
        Conductor series resistance per unit length in Ω/m. Must be
        non-negative.

    Returns
    -------
    complex
        Loop series impedance :math:`R'_\text{loop} + jX'_\text{loop}` in
        Ω/m. Multiply by the line length for the terminated loop impedance.

    Notes
    -----
    Earth-return path only: no mutual coupling to a parallel conductor, and
    no internal skin-effect model (pass a skin-corrected ``r_internal`` if
    that matters at the top of the band). A conductor–conductor loop (e.g.
    line–PEN at a fixed spacing) has its return-path reactance set by that
    spacing, not by the earth-return depth, and is much smaller — this
    function is specifically the conductor–*earth* loop.

    Examples
    --------
    >>> import math
    >>> a = math.sqrt(150e-6 / math.pi)          # 150 mm^2 solid-round radius
    >>> z = loop_impedance_conductor_earth(       # NAYY PEN, 100 Ohm.m, 50 Hz
    ...     100.0, 100.0, 10.0, 50.0,
    ...     depth=0.7, gmr=0.7788 * a, r_internal=0.206e-3)
    >>> round(z.real * 1e3, 3), round(z.imag * 1e3, 3)  # mOhm/m -> Ohm/km
    (0.255, 0.758)
    """
    _check_positive(
        rho_1=rho_1, rho_2=rho_2, h_1=h_1, frequency=frequency,
        depth=depth, gmr=gmr,
    )
    if r_internal < 0.0:
        raise ValueError(f"r_internal must be non-negative, got {r_internal}")
    z_earth = pollaczek_self_impedance(
        rho_1, rho_2, h_1, frequency, depth=depth, gmr=gmr,
    )
    return complex(r_internal, 0.0) + z_earth

pollaczek_mutual_impedance

pollaczek_mutual_impedance(
    rho_1: float,
    rho_2: float,
    h_1: float,
    frequency: float,
    *,
    depth_i: float,
    depth_j: float,
    separation: float
) -> complex

Layered Pollaczek earth-return mutual impedance per unit length (Ω/m).

The per-unit-length coupling impedance between two parallel buried conductors — each closing its current through the earth — over a two-layer earth, in the same Pollaczek/Sunde spectral-integral form as :func:pollaczek_self_impedance:

.. math::

Z'_\text{M} = j\,\frac{\omega\mu_0}{2\pi}\left[
    \ln\!\frac{D}{d}
    + 2\int_0^\infty
      \frac{e^{-(h_i+h_j)\lambda}}{\lambda + u_\text{eff}(\lambda)}
      \cos(\lambda x)\,d\lambda \right],

with the horizontal separation :math:x, the direct and mirror distances

.. math::

d = \sqrt{x^2 + (h_i - h_j)^2}, \qquad
D = \sqrt{x^2 + (h_i + h_j)^2},

and the two-layer effective vertical wavenumber :math:u_\text{eff}(\lambda) from :func:layered_effective_wavenumber. The :math:\ln(D/d) term is the perfect-mirror image pair (the small-argument form of :math:K_0(\gamma d) - K_0(\gamma D)); the integral is the finite-conductivity earth-return correction.

This is the mutual (off-diagonal) counterpart of :func:pollaczek_self_impedance: the coupling that induces a voltage on a parallel earthing conductor / cable through the shared earth-return path — the term a self impedance alone omits. It carries no internal-conductor term (the two conductors are galvanically separate; the coupling is purely inductive through the soil), so with ideal (:math:\approx 0\,\Omega) earthing at both ends the coupling impedance of a parallel run of length :math:\ell is :math:Z'_\text{M}\,\ell. Together with two self impedances it forms the :math:2\times2 loop impedance matrix of the coupled pair.

Limiting behaviour (covered by the tests): it reduces to the homogeneous Carson mutual impedance (:func:~groundfield.references.earth_return.carson_mutual_impedance) when :math:\rho_1 = \rho_2 (reactance to well under 1 % at low frequency), its magnitude is below the self impedance and decays monotonically with the separation :math:x, and its two-layer reactance lies within the two homogeneous Carson bounds, deep-layer dominated at low frequency.

Parameters:

Name Type Description Default
rho_1 float

Two-layer earth resistivities (Ω·m), upper-layer thickness (m) and frequency (Hz), as in :func:pollaczek_self_impedance. Set rho_1 == rho_2 for a homogeneous earth.

required
rho_2 float

Two-layer earth resistivities (Ω·m), upper-layer thickness (m) and frequency (Hz), as in :func:pollaczek_self_impedance. Set rho_1 == rho_2 for a homogeneous earth.

required
h_1 float

Two-layer earth resistivities (Ω·m), upper-layer thickness (m) and frequency (Hz), as in :func:pollaczek_self_impedance. Set rho_1 == rho_2 for a homogeneous earth.

required
frequency float

Two-layer earth resistivities (Ω·m), upper-layer thickness (m) and frequency (Hz), as in :func:pollaczek_self_impedance. Set rho_1 == rho_2 for a homogeneous earth.

required
depth_i float

Burial depths of the two conductors in m. Must be positive.

required
depth_j float

Burial depths of the two conductors in m. Must be positive.

required
separation float

Horizontal centre-to-centre distance :math:x between the conductors in m. Must be non-negative; separation == 0 is allowed only when depth_i != depth_j (one conductor directly above the other). Coincident conductors (separation == 0 and depth_i == depth_j) are the self case — use :func:pollaczek_self_impedance.

required

Returns:

Type Description
complex

Mutual series impedance :math:R'_\text{M} + jX'_\text{M} in Ω/m.

Notes

Earth-return coupling only: quasi-static, no displacement current, and (like the self) the layering enters solely through :math:u_\text{eff}. As :math:x \to \mathrm{GMR} with :math:depth_i = depth_j it recovers :func:pollaczek_self_impedance.

Source code in src/groundfield/references/pollaczek.py
def pollaczek_mutual_impedance(
    rho_1: float,
    rho_2: float,
    h_1: float,
    frequency: float,
    *,
    depth_i: float,
    depth_j: float,
    separation: float,
) -> complex:
    r"""Layered Pollaczek earth-return **mutual** impedance per unit length (Ω/m).

    The per-unit-length coupling impedance between two parallel buried
    conductors — each closing its current through the earth — over a
    two-layer earth, in the same Pollaczek/Sunde spectral-integral form as
    :func:`pollaczek_self_impedance`:

    .. math::

        Z'_\text{M} = j\,\frac{\omega\mu_0}{2\pi}\left[
            \ln\!\frac{D}{d}
            + 2\int_0^\infty
              \frac{e^{-(h_i+h_j)\lambda}}{\lambda + u_\text{eff}(\lambda)}
              \cos(\lambda x)\,d\lambda \right],

    with the horizontal separation :math:`x`, the direct and mirror
    distances

    .. math::

        d = \sqrt{x^2 + (h_i - h_j)^2}, \qquad
        D = \sqrt{x^2 + (h_i + h_j)^2},

    and the two-layer effective vertical wavenumber
    :math:`u_\text{eff}(\lambda)` from
    :func:`layered_effective_wavenumber`. The :math:`\ln(D/d)` term is the
    perfect-mirror image pair (the small-argument form of
    :math:`K_0(\gamma d) - K_0(\gamma D)`); the integral is the
    finite-conductivity earth-return correction.

    This is the mutual (off-diagonal) counterpart of
    :func:`pollaczek_self_impedance`: the coupling that induces a voltage on
    a parallel earthing conductor / cable through the shared earth-return
    path — the term a self impedance alone omits. It carries **no
    internal-conductor term** (the two conductors are galvanically separate;
    the coupling is purely inductive through the soil), so with ideal
    (:math:`\approx 0\,\Omega`) earthing at both ends the coupling impedance
    of a parallel run of length :math:`\ell` is :math:`Z'_\text{M}\,\ell`.
    Together with two self impedances it forms the :math:`2\times2` loop
    impedance matrix of the coupled pair.

    Limiting behaviour (covered by the tests): it reduces to the homogeneous
    Carson mutual impedance
    (:func:`~groundfield.references.earth_return.carson_mutual_impedance`)
    when :math:`\rho_1 = \rho_2` (reactance to well under 1 % at low
    frequency), its magnitude is below the self impedance and decays
    monotonically with the separation :math:`x`, and its two-layer reactance
    lies within the two homogeneous Carson bounds, deep-layer dominated at
    low frequency.

    Parameters
    ----------
    rho_1, rho_2, h_1, frequency
        Two-layer earth resistivities (Ω·m), upper-layer thickness (m) and
        frequency (Hz), as in :func:`pollaczek_self_impedance`. Set
        ``rho_1 == rho_2`` for a homogeneous earth.
    depth_i, depth_j
        Burial depths of the two conductors in m. Must be positive.
    separation
        Horizontal centre-to-centre distance :math:`x` between the
        conductors in m. Must be non-negative; ``separation == 0`` is
        allowed only when ``depth_i != depth_j`` (one conductor directly
        above the other). Coincident conductors (``separation == 0`` and
        ``depth_i == depth_j``) are the self case — use
        :func:`pollaczek_self_impedance`.

    Returns
    -------
    complex
        Mutual series impedance :math:`R'_\text{M} + jX'_\text{M}` in Ω/m.

    Notes
    -----
    Earth-return coupling only: quasi-static, no displacement current, and
    (like the self) the layering enters solely through
    :math:`u_\text{eff}`. As :math:`x \to \mathrm{GMR}` with
    :math:`depth_i = depth_j` it recovers :func:`pollaczek_self_impedance`.
    """
    _check_positive(
        rho_1=rho_1, rho_2=rho_2, h_1=h_1, frequency=frequency,
        depth_i=depth_i, depth_j=depth_j,
    )
    if separation < 0.0:
        raise ValueError(f"separation must be non-negative, got {separation}")
    omega = 2.0 * math.pi * frequency
    x = separation
    d = math.hypot(x, depth_i - depth_j)
    if d <= 0.0:
        raise ValueError(
            "coincident conductors (separation == 0 and depth_i == depth_j); "
            "use pollaczek_self_impedance for the self case"
        )
    big_d = math.hypot(x, depth_i + depth_j)

    def integrand(lam: float, part: int) -> float:
        ue = layered_effective_wavenumber(lam, rho_1, rho_2, h_1, omega)
        v = math.exp(-(depth_i + depth_j) * lam) / (lam + ue)
        return v.real if part == 0 else v.imag

    # Oscillatory cos(lam*x) tail. Use the weighted quadrature when the
    # oscillation is resolved over the exponential decay length
    # L = depth_i + depth_j (x >~ 0.3 L); for small x (cos ~ 1 over the
    # decay, where the weighted rule is ill-conditioned) fold the cosine
    # into the integrand and integrate plainly.
    decay_len = depth_i + depth_j
    if x > 0.3 * decay_len:
        re, _ = quad(integrand, 0.0, math.inf, args=(0,), weight="cos",
                     wvar=x, limit=200)
        im, _ = quad(integrand, 0.0, math.inf, args=(1,), weight="cos",
                     wvar=x, limit=200)
    else:
        def integrand_cos(lam: float, part: int) -> float:
            return integrand(lam, part) * math.cos(lam * x)

        re, _ = quad(integrand_cos, 0.0, math.inf, args=(0,), limit=200)
        im, _ = quad(integrand_cos, 0.0, math.inf, args=(1,), limit=200)
    return (
        1j * omega * MU_0 / (2.0 * math.pi)
        * (math.log(big_d / d) + 2.0 * (re + 1j * im))
    )

pollaczek_self_impedance

pollaczek_self_impedance(
    rho_1: float,
    rho_2: float,
    h_1: float,
    frequency: float,
    *,
    depth: float,
    gmr: float
) -> complex

Layered Pollaczek earth-return self impedance per unit length (Ω/m).

Rigorous two-layer, buried-conductor series impedance :math:Z' = R' + jX'; earth-return only (no internal-conductor resistance or inductance — see the module docstring). Reduces to :func:carson_self_impedance in the homogeneous limit.

Parameters:

Name Type Description Default
rho_1 float

Upper / lower layer resistivity in Ω·m. Must be positive. Set equal for a homogeneous earth.

required
rho_2 float

Upper / lower layer resistivity in Ω·m. Must be positive. Set equal for a homogeneous earth.

required
h_1 float

Upper-layer thickness in m. Must be positive (ignored when rho_1 == rho_2).

required
frequency float

Frequency in Hz. Must be positive.

required
depth float

Burial depth of the conductor in m. Must be positive.

required
gmr float

Geometric mean radius of the conductor in m (the physical wire radius for a thin external-inductance model). Must be positive.

required

Returns:

Type Description
complex

Earth-return series impedance :math:R' + jX' in Ω/m.

Source code in src/groundfield/references/pollaczek.py
def pollaczek_self_impedance(
    rho_1: float,
    rho_2: float,
    h_1: float,
    frequency: float,
    *,
    depth: float,
    gmr: float,
) -> complex:
    r"""Layered Pollaczek earth-return **self** impedance per unit length (Ω/m).

    Rigorous two-layer, buried-conductor series impedance
    :math:`Z' = R' + jX'`; earth-return only (no internal-conductor
    resistance or inductance — see the module docstring). Reduces to
    :func:`carson_self_impedance` in the homogeneous limit.

    Parameters
    ----------
    rho_1, rho_2
        Upper / lower layer resistivity in Ω·m. Must be positive. Set
        equal for a homogeneous earth.
    h_1
        Upper-layer thickness in m. Must be positive (ignored when
        ``rho_1 == rho_2``).
    frequency
        Frequency in Hz. Must be positive.
    depth
        Burial depth of the conductor in m. Must be positive.
    gmr
        Geometric mean radius of the conductor in m (the physical wire
        radius for a thin external-inductance model). Must be positive.

    Returns
    -------
    complex
        Earth-return series impedance :math:`R' + jX'` in Ω/m.
    """
    _check_positive(
        rho_1=rho_1, rho_2=rho_2, h_1=h_1, frequency=frequency,
        depth=depth, gmr=gmr,
    )
    omega = 2.0 * math.pi * frequency

    def integrand(lam: float, part: int) -> float:
        ue = layered_effective_wavenumber(lam, rho_1, rho_2, h_1, omega)
        v = math.exp(-2.0 * depth * lam) / (lam + ue)
        return v.real if part == 0 else v.imag

    re, _ = quad(integrand, 0.0, math.inf, args=(0,), limit=200)
    im, _ = quad(integrand, 0.0, math.inf, args=(1,), limit=200)
    return (
        1j * omega * MU_0 / (2.0 * math.pi)
        * (math.log(2.0 * depth / gmr) + 2.0 * (re + 1j * im))
    )