Skip to content

09 — All plots in action

groundfield.postprocess ships a complete plot toolkit on top of the solver result and the world. This page demonstrates the project's plotting surface end-to-end on one solved problem.

Reference solve

The reference world: a substation grounding (5 m ring + 3 driven rods) with a 200 m east Hilfserder, a Spannungssonde halfway in between, and a \(1\;\mathrm{A}\) test current at 50 Hz.

import matplotlib.pyplot as plt
import groundfield as gf
from groundfield.generators import OrtsnetzLayout

layout = OrtsnetzLayout.from_footprints(
    [], substation_xy=(0.0, 0.0),
)
layout.add_auxiliary_electrode(distance_m=200.0, direction_deg=0.0)
layout.add_voltage_probe(inline_fraction=0.5)

world = layout.to_world(
    soil=gf.HomogeneousSoil(resistivity=100.0),
    source_magnitude_A=1.0,
    seed=0,
)
engine = gf.create_engine(
    backend="image",
    segment_length=0.5,
    frequencies=[50.0],
)
result = engine.solve(world)

The same result is the input to every plot below.

1. Layout view (OrtsnetzLayout.plot)

ax = layout.plot(figsize=(11, 7))
plt.show()

The layout plot shows footprints, the substation (orange diamond), the KVS markers (blue squares), the Hilfserder (purple triangle) with a dotted return-current line, and the Spannungssonde (lime star). Pass foundation_mask=... to colour houses by their foundation-electrode penetration.

2. Surface potential — TwoSlopeNorm (default)

fig = layout.plot_surface_potential(
    result, world,
    padding_m=60.0, n=180, levels=41,
)
plt.show()

two_slope=True (default) uses :class:matplotlib.colors.TwoSlopeNorm centred at \(\varphi = 0\) so the deep Hilfserder trough (negative potentials) and the small substation + foundation rise (positive potentials) are both visible at full colour resolution on an RdBu_r diverging colormap. The contour levels are split half-and-half between the negative and positive ranges.

3. Surface potential — symmetric mode

fig = layout.plot_surface_potential(
    result, world,
    padding_m=60.0, n=180, levels=41,
    symmetric=True,        # symmetric range [-|phi|_max, +|phi|_max]
)
plt.show()

Use symmetric=True when the potential extremes really are symmetric (mutual-coupling studies, net-zero injection setups). On a strongly asymmetric range (deep Hilfserder trough vs small foundation rise) the symmetric mode collapses the small side into a single colour step — that's exactly why two_slope is the default.

4. Surface potential — log scale on \(|\varphi|\)

fig = layout.plot_surface_potential(
    result, world,
    padding_m=300.0, n=180, levels=41,
    log=True,             # log |phi|, sequential cmap recommended
    cmap="viridis",
)
plt.show()

log=True switches to a log-magnitude colour scale that resolves the boundary decay across several decades. Useful for verifying that the potential reaches remote earth at the chosen padding_m.

5. Radial-decay profile

from groundfield.postprocess import plot_potential_radial

fig = plot_potential_radial(
    result, around="substation_ring_0", world=world,
    r_max=80.0, depths=(0.0, 0.5, 1.0),
)
plt.show()

The classical trumpet curve \(\varphi(r)\) measured along the \(+x\) ray from the substation centre, at three depths. Cross- checks the solver result against the analytic single-electrode Sunde formula.

6. Cross-section in the \(x\)-\(z\) plane

from groundfield.postprocess import plot_potential_contour

fig = plot_potential_contour(
    result, world=world,
    plane="xz", fixed=0.0,
    extent=(-50.0, 220.0, -1.0, 30.0),
    n=200,
)
plt.show()

Vertical slice at \(y = 0\) — useful for visualising the current path through the soil between substation and Hilfserder. Pass plane="xy" for the horizontal slice or plane="yz" for the cross-section through the cable axis.

7. World-level matplotlib geometry

from groundfield.postprocess import plot_geometry

fig = plot_geometry(world, figsize=(11, 7))
plt.show()

A bare 2-D geometry plot of every electrode and conductor in the world — without any solver result. Useful for sanity-checking the generator output before launching a long solve.

Where to go next