Off-Surface Evaluation

This section describes the off-surface evaluation paths for B and GradB in virtual_casing_jax, including the adaptive refinement logic and validation strategy. The implementation mirrors the reference virtual-casing/BIEST flow and is designed to be both accurate and efficient for small to moderate target sets.

Equations

Let \Gamma be the surface with outward normal n. Define the surface densities:

\[\sigma = \mathbf{B}\cdot\mathbf{n}, \qquad \mathbf{K} = \mathbf{n}\times\mathbf{B}.\]

For off-surface targets x, the jump term is absent. The external and internal fields are

\[\mathbf{B}_{\mathrm{ext}}(x) = \nabla G[\sigma](x) - \mathrm{BiotSavart}[\mathbf{K}](x),\]
\[\mathbf{B}_{\mathrm{int}}(x) = -\nabla G[\sigma](x) + \mathrm{BiotSavart}[\mathbf{K}](x),\]

which matches the reference BIEST implementation [MCO2019] and virtual-casing C++ API.

The off-surface gradient is

\[(\nabla \mathbf{B}_{\mathrm{ext}})_{k i} = \varepsilon_{k \ell m}\,\partial_i\partial_\ell G[K_m] + \partial_i\partial_k G[\sigma],\]

and \nabla \mathbf{B}_{\mathrm{int}} = - \nabla \mathbf{B}_{\mathrm{ext}}.

Algorithm

Off-surface evaluation is performed in two stages:

  1. Base resampling: the surface and field are resampled to a quadrature grid that is at least the maximum of:

    • NFP * src_nt (toroidal modes from the source field)

    • surface resolution from setup

    • a minimum patch size (13x13) to match the BIEST off-surface singular-correction requirements.

  2. Evaluation:

    • ComputeBOffSurf uses a combination of LaplaceFxdU and BiotSavartFxU.

    • ComputeGradBOffSurf uses LaplaceFxd2U (second derivatives) for both \mathbf{K} and \sigma and assembles the curl term.

Adaptive Refinement

The off-surface field evaluator supports adaptive refinement, matching the strategy in BIEST:

  • Evaluate the Laplace double-layer DxU on the target set using a constant density.

  • Define an error metric:

    \[\epsilon = \max_i \min(|1-U_i|, |U_i|),\]

    where U_i is the double-layer potential at target i.

  • Refine the source grid by doubling (Nt, Np) until \epsilon \le 10^{-digits} or the optional max_Nt/max_Np caps are reached.

This logic is implemented in virtual_casing_jax.integrals.computeB_offsurface_adaptive and _offsurface_adapt_grid, mirroring ExtVacuumField::EvalOffSurface in the C++ code.

JIT-Friendly Schedule

The Python adaptive loop changes grid sizes dynamically, which is not compatible with JIT compilation. For JIT-friendly workflows, a fixed refinement schedule can be provided:

computeB_offsurface_adaptive_schedule and computeGradB_offsurface_adaptive_schedule accept a static tuple of (Nt, Np) pairs (e.g. ((24, 24), (48, 48), (96, 96))). The implementation evaluates each level and updates the result only when the double-layer error is above the tolerance. This preserves the adaptive decision while keeping the shapes static, so the function is JIT-safe.

High-level wrappers are exposed as:

  • VirtualCasingJAX.compute_external_B_offsurf_schedule

  • VirtualCasingJAX.compute_internal_B_offsurf_schedule

  • VirtualCasingJAX.compute_external_gradB_offsurf_schedule

  • VirtualCasingJAX.compute_internal_gradB_offsurf_schedule

These wrappers reuse the same density construction as the base off-surface path, and support JIT when levels is passed as a static argument.

Off-Surface GradB (Base vs Adaptive)

The reference C++ code evaluates off-surface GradB on the base grid (no adaptive refinement). The default JAX path follows this behavior to maintain parity with the C++ dumps.

An optional adaptive GradB path is available via adaptive=True in compute_external_gradB_offsurf. This uses the same adaptive grid selection as the off-surface field evaluator, and is validated against a finite-difference gradient of compute_external_B_offsurf.

Validation

Off-surface validation includes:

  • Parity tests against the C++ dumps for: - ComputeBOffSurf (external/internal) - ComputeGradBOffSurf (external)

  • A finite-difference test comparing adaptive GradB against the gradient of compute_external_B_offsurf.

See the tests:

  • tests/test_virtual_casing_offsurf_parity.py

  • tests/test_virtual_casing_gradb_offsurf_fd.py

Performance Notes

  • The direct quadrature path is memory-intensive; use chunk_size to control memory.

  • Adaptive refinement can upsample aggressively for small surfaces; use max_Nt and max_Np to cap grid sizes if needed.

  • Off-surface GradB is more expensive than off-surface B because it evaluates second-derivative kernels.

Implementation Mapping

JAX entry points and their reference equivalents:

  • VirtualCasingJAX.compute_external_B_offsurfVirtualCasing::ComputeBextOffSurf (C++)

  • VirtualCasingJAX.compute_internal_B_offsurfVirtualCasing::ComputeBintOffSurf (C++)

  • VirtualCasingJAX.compute_external_gradB_offsurfVirtualCasing::ComputeGradBOffSurf (C++ local parity extension)

All kernel normalizations and sign conventions follow BIEST [BIEST] and the formulas in [MCO2019].