mom_sommerfeld — Galerkin MoM with direct Sommerfeld quadrature¶
Physical context¶
Every other layered backend in the family relies on a closed-form representation of the layered Green's function:
image_2layer— geometric Tagg / Sunde series.image_nlayer— dispatcher to the closed-form variants.cim— matrix-pencil approximation by complex exponentials.bem— collocation on top of the CIM kernel.
mom_sommerfeld is methodologically distinct: it evaluates the
Sommerfeld integral numerically, point by point, with adaptive
Gauss–Kronrod quadrature. The recursive \(\Gamma_1(\lambda)\) enters
the integrand as is — no expansion, no fit. The price is speed (a
single \(N \times N\) reaction matrix can take seconds rather than
milliseconds), but the result is an absolute reference: any
disagreement between the closed-form engines and mom_sommerfeld
indicates a fit / series accuracy issue, not a kernel bug.
In ADR-0002 this is the engine that anchors the cross-validation envelope for the \(n \ge 3\) regime, and provides the independent methodology that the closed-form layered family lacks.
Governing equation: layered Sommerfeld integral¶
For a top-layer source at depth \(z_s\) and a top-layer field point at \((s, z)\), the full layered Green's function is the Sommerfeld integral with multiple-reflection multiplier:
The integrand carries:
- Two direct exponentials — the source and its air mirror.
- Two reflected exponentials — the source's reflection at \(z = h_1\) and the air mirror's reflection at \(z = h_1\).
- A multiplexion factor \(1 / (1 - \Gamma_1(\lambda) e^{-2\lambda h_1})\) that captures the infinite back-and-forth bouncing between the air boundary (\(R_{\text{air}} = +1\)) and the layer interface (\(\Gamma_1\)).
For \(\Gamma_1 \to 0\) (homogeneous) the integral reduces to \(1/r + 1/r_{\text{img}}\). For \(\Gamma_1 \equiv K_1\) (2-layer) the geometric expansion of the multiplier reproduces the Tagg / Sunde series. For \(n \ge 3\) no closed form is available — the engine just integrates.
Numerical strategy¶
Quadrature truncation¶
The integrand decays exponentially in \(\lambda\) at the rate of the fastest decaying exponential \(\min(|z - z_s|, z + z_s, 2 h_1 - z - z_s, 2 h_1 - |z - z_s|)\). We bound the upper limit at \(\lambda_{\max} = \texttt{lambda\_max\_factor} / \bar h\) with \(\bar h = \min(h_1, s + z + z_s + \epsilon)\) — i.e. the characteristic length scale of the geometry, and a default factor of 200. This puts the residual integrand contribution above \(\lambda_{\max}\) at \(e^{-200} \sim 10^{-87}\), well below any practical tolerance.
Inside that bound, scipy.integrate.quad (Gauss–Kronrod adaptive
quadrature with a 21-point rule) handles the integration to
absolute / relative tolerances \(10^{-9} / 10^{-7}\). The Bessel
function \(J_0(\lambda s)\) oscillates with period \(2\pi/s\); the
adaptive rule subdivides until the per-subinterval rule and a
higher-order rule agree to within tolerance, so the oscillation is
captured automatically.
Why not the Zou et al. complex contour?¶
Zou, Du & Zhou (2015) proposed a deformed integration contour in the complex \(\lambda\)-plane that breaks the Bessel oscillation and makes the integrand non-oscillatory along the contour. The current implementation uses the simpler real-axis adaptive quadrature. Pragmatic justification:
- For \(s \lesssim 100\,\text{m}\) (the typical range) the real-axis adaptive rule converges in 200–400 kernel evaluations per pair, which is acceptable for the engine's role as cross-check reference.
- The Zou contour requires careful handling of the branch points in \(\Gamma_1\) for \(n \ge 3\), which would make the implementation significantly larger.
- The cross-validation tests show that the real-axis rule already agrees with the closed-form engines to better than 1 % across the typical contrast range.
The module's docstring still cites Zou et al. as the reference for the idea; switching to a complex contour is a documented future optimisation (ADR-0002 action items).
Reaction matrix assembly¶
mom_sommerfeld builds the \(N \times N\) reaction matrix in three
modes:
- Homogeneous (\(n = 1\)). Falls back on the closed-form homogeneous self-kernel — quadrature is unnecessary and would introduce numerical noise.
- Two-layer (\(n = 2\)). Uses the Tagg / Sunde self-kernel for the diagonal (the closed-form multi-image self-action) and the direct Sommerfeld quadrature for the off-diagonals. The two are consistent — each off-diagonal entry is the same physics evaluated by quadrature, and the diagonal is the closed-form geometric series for the same physics.
- Multilayer (\(n \ge 3\)). Uses the homogeneous line self-potential plus a layered self-correction obtained from the reflection-only Sommerfeld integral evaluated at \(s = 0\), \(|z - z_s| = 0\) (the integrand has no \(1/r\) singularity in this reflection-only form, so it can be integrated point-on-source).
Galerkin solve¶
The actual linear-system solve reuses the _galerkin_solve helper
from mom — the cluster augmenting rows are identical;
only the kernel changes.
Validity envelope¶
| Property | Range / value |
|---|---|
| Soil model | HomogeneousSoil, TwoLayerSoil, MultiLayerSoil |
| Frequency | quasi-static, \(f < 1\,\text{kHz}\) |
| Electrode placement | every segment in the upper layer |
| Quadrature tolerances | \(\text{epsabs} = 10^{-9}\), \(\text{epsrel} = 10^{-7}\) |
| Quadrature limit | 400 subdivisions |
| \(\lambda_{\max}\) factor | 200 / characteristic length (default) |
| Mesh size \(N\) | practical limit \(\sim 200\) segments at acceptable runtime |
The runtime cost of the engine grows as \(O(N^2)\) Sommerfeld
quadrature evaluations, each costing a few hundred kernel
evaluations. For \(N = 200\) on a 2-layer world the engine completes
in a few seconds; for \(N = 1000\) it would take of the order of a
minute. This is acceptable for cross-validation but not for
production sweeps — use image_2layer / cim / bem there.
Convergence and cost¶
- Quadrature accuracy. With the default tolerances the per-pair error is \(\sim 10^{-8}\) relative to the analytical closed forms. The cluster-impedance error in the assembled result stays below 1 % of the closed-form reference.
- Per-pair cost. Adaptive Gauss–Kronrod typically converges in 100–400 kernel evaluations per (field, source) pair.
- Reduction. At \(\Gamma_1 \to 0\) the engine short-circuits to
\(1/r + 1/r_{\text{img}}\) — bit-exact match with
image.
Cross-validation notes¶
| Counterpart | Expected agreement | What is checked |
|---|---|---|
image (\(n = 1\)) |
bit-exact | quadrature short-circuits to closed form |
image_2layer (\(n = 2\)) |
\(\le 5\,\%\) | quadrature reproduces the geometric series |
cim (any \(n\)) |
\(\le 5\,\%\) | matrix-pencil fit accuracy |
bem (any \(n\)) |
\(\le 5\,\%\) | collocation on the same physics |
mom (\(n \le 2\)) |
\(\le 2\,\%\) | same Galerkin scheme, different kernel evaluation |
The engine's role is the absolute reference. Whenever the
closed-form layered engines disagree, the disagreement is measured
against mom_sommerfeld. The cross-engine validation harness report agreement tables relative to this engine.
References¶
- Sommerfeld, A. (1909). Über die Ausbreitung der Wellen in der drahtlosen Telegraphie. Annalen der Physik 28. The original paper introducing the integral.
- Zou, J., Du, X. & Zhou, C. (2015). Fast calculation of the Green function of a point current source in a horizontal layered soil with a new complex path. IEEE Trans. Magn. 51(3). The reference for a deformed contour that the current implementation does not use, but which is on the optimisation roadmap.
- Piessens, R. et al. (1983). QUADPACK, Springer. The Gauss–Kronrod adaptive quadrature implementation that scipy wraps.
- Dwight, H. B. (1936). Calculation of resistances to ground. Reference DC resistances used in the cross-validation tests.
Example¶
import groundfield as gf
soil = gf.TwoLayerSoil(rho_1=100.0, rho_2=500.0, h_1=2.0)
world = gf.create_world(soil=soil)
gf.create_electrode(world, "rod", name="g1",
position=(0.0, 0.0, 0.0), length=1.5)
gf.create_source(world, attached_to="g1", magnitude=1.0)
# Reference engine — slow but methodologically independent.
engine = gf.create_engine(backend="mom_sommerfeld",
segment_length=0.1,
frequencies=[50.0])
result = world.solve(engine)
print(result.cluster_impedance("g1")[0])
API reference¶
mom_sommerfeld ¶
Method-of-Moments backend with direct Sommerfeld quadrature (mom_sommerfeld).
Mathematical / physical model
The other layered backends (image_nlayer, cim, bem) rely
on a closed-form representation of the layered Green's function
(real image series or complex images). This backend instead evaluates
the Sommerfeld integral
$$
\varphi(s, z; z_s) \;=\; \frac{\rho_1\, I}{4\pi}
!\int_0^{\infty}! \bigl[ e^{-\lambda |z - z_s|}
+ \Gamma_1(\lambda)\, e^{-\lambda (z + z_s)}\bigr]
J_0(\lambda s)\, d\lambda
$$
numerically, point by point, with adaptive Gauss–Kronrod
quadrature (scipy.integrate.quad). The recursive
\(\Gamma_1(\lambda)\) from
:func:groundfield.solver._layered.reflection_gamma is used as is —
no expansion, no fit. The result is therefore independent of the
expansion choices in image_nlayer / cim and serves as the
absolute reference inside the cross-engine validation.
Following Zou et al. 2015, the integration path is deformed into
the complex plane to break the Bessel oscillation: the real-axis
quadrature is replaced by a contour that combines
(a) a flat real segment \([0, \lambda_0]\),
(b) a deformed complex piece that decays exponentially.
For the radial distances of interest in grounding (s ≲ 100 m)
this contour collapses the integrand to a non-oscillatory shape and
makes scipy.integrate.quad converge in a few hundred kernel
evaluations.
The MoM resolution itself (Galerkin scheme with one constraint per
cluster) is the same as :mod:groundfield.solver.mom; only the
underlying Z-matrix kernel differs.
Validity
- Quasi-static, \(f < 1\,\mathrm{kHz}\).
- Slow but methodologically independent. Use it as the reference in cross-engine tests on layered worlds with hard contrasts; the closed-form backends are usually within a fraction of a per cent of the quadrature result.
References
- Sommerfeld, A. (1909). Über die Ausbreitung der Wellen in der drahtlosen Telegraphie. Annalen der Physik 28.
- Zou, J., Du, X., & Zhou, C. (2015). Fast calculation of the Green function of a point current source in a horizontal layered soil with a new complex path. IEEE Trans. Magn. 51(3).
solve_mom_sommerfeld ¶
solve_mom_sommerfeld(
world: "World",
engine: "Engine",
*,
lambda_max_factor: float = 200.0,
epsabs: float = 1e-09,
epsrel: float = 1e-07
) -> FieldResult
Galerkin MoM with direct Sommerfeld quadrature for layered soil.
This is a methodologically independent backend used as the
reference inside :func:groundfield.compare_engines for layered
worlds with hard contrasts.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
world
|
'World'
|
World to evaluate. |
required |
engine
|
'Engine'
|
Engine configuration. |
required |
lambda_max_factor
|
float
|
Upper bound of the quadrature in units of \(1 / \bar h\). |
200.0
|
epsabs
|
float
|
Quadrature tolerances. |
1e-09
|
epsrel
|
float
|
Quadrature tolerances. |
1e-09
|
Returns:
| Type | Description |
|---|---|
FieldResult
|
|
Source code in src/groundfield/solver/mom_sommerfeld.py
343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 | |
sommerfeld_kernel_value ¶
sommerfeld_kernel_value(
stack: LayerStack,
s: float,
z: float,
z_s: float,
*,
lambda_max_factor: float = 200.0,
epsabs: float = 1e-09,
epsrel: float = 1e-07
) -> float
Evaluate the layered-soil Sommerfeld kernel at one point.
Returns
$$
G(s, z, z_s) \;=\; \int_0^{\infty}!\bigl[
e^{-\lambda |z - z_s|}
+ \Gamma_1(\lambda)\, e^{-\lambda (z + z_s)} \bigr]
J_0(\lambda s)\, d\lambda.
$$
The full potential is then ρ_1 · I · G / (4 π).
For a homogeneous soil (stack.n_layers == 1)
\(\Gamma_1 \equiv 0\) and the integral collapses to
\(1/r + 1/r_{\text{img}}\), with
\(r = \sqrt{s^2 + (z - z_s)^2}\) and
\(r_{\text{img}} = \sqrt{s^2 + (z + z_s)^2}\). We short-circuit
that case to keep the homogeneous limit bit-exact.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
stack
|
LayerStack
|
Layer stack. |
required |
s
|
float
|
Cylindrical radius \(s = \sqrt{(x - x_s)^2 + (y - y_s)^2}\) in metres. |
required |
z
|
float
|
Field-point depth and source depth (positive into the soil). |
required |
z_s
|
float
|
Field-point depth and source depth (positive into the soil). |
required |
lambda_max_factor
|
float
|
Upper bound of the quadrature, given as a multiple of \(1 / \bar h\), where \(\bar h\) is a characteristic length built from the layer thicknesses and the geometry (\(\bar h = \min(\text{layer h}, s + z + z_s + \epsilon)\)). Beyond that bound the integrand is exponentially small. |
200.0
|
epsabs
|
float
|
Tolerances passed to :func: |
1e-09
|
epsrel
|
float
|
Tolerances passed to :func: |
1e-09
|
Returns:
| Name | Type | Description |
|---|---|---|
G |
float
|
|
Source code in src/groundfield/solver/mom_sommerfeld.py
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 | |
Related material¶
- ADR-0002 — engine selection heuristic; this engine is the reference for the layered family.