Acoustic Helmholtz solver with PML

SimpleFWI solves the 2D constant-density acoustic Helmholtz equation in PyTorch, using a conservative second-order spatial discretization:

(∇² + ω² m) p = q
m = 1 / v²,    ω = 2πf,    Fourier convention: exp(-iωt)

Here v is velocity in m/s, m is squared slowness in s²/m², p is the complex wavefield and q is the source. The current API does not model elasticity, variable density, 3D propagation or topography.

How is PML implemented?

SimpleFWI adds PML cells outside the physical grid and applies cubic complex coordinate stretching inside that padding. Padding and coordinate stretching work together: the physical model stays on its declared node grid, and the additional cells absorb outgoing waves. Outer pressure-boundary degrees of freedom are eliminated. The code uses PML, not a replacement local absorbing boundary condition.

BoundaryConfig(top='pml', pml_cells=10) requests PML on all four sides. BoundaryConfig(top='free_surface', pml_cells=10) removes top padding and imposes zero pressure at the physical top; side and bottom PML remain. Sources and receivers cannot lie on eliminated zero-pressure nodes.

The velocity extension into PML participates consistently in physical-model derivatives. PML thickness and damping still require calibration: a chosen cell count alone does not establish a reflection-error bound. When refining a grid, double the PML cell count if spacing halves to preserve physical width.

CPU reference and experimental GPU solver

Solver Formulation Current evidence
CPU sparse direct Assemble a sparse Helmholtz system; reuse bounded factor caches Reference path for numerical comparisons
CPU/CUDA FGMRES Matrix-free operator actions with a shifted-Helmholtz multigrid preconditioner Ten CUDA tests passed on an NVIDIA A40; the iterative path remains experimental

The solver checks the true unpreconditioned residual against explicit tolerances. A failed solve raises before its gradient can be accepted. Requested devices and precision are explicit; unavailable CUDA does not silently select CPU. Matrix-free iterative actions do not imply that CPU direct factorization or stored optimizer state consumes no memory.

The small recorded Marmousi continuation took 1.04 s on CPU direct and 13.08 s on GPU FGMRES. Those single measurements establish no GPU speedup or large-survey scaling. Higher-frequency and harder iterative cases can fail convergence. Read the measured validation limits.

Sources, receivers and adjoint gradients

Grid2D declares (nz, nx) nodes, (dz, dx) spacing and a first-node origin. Acquisition uses (z, x) source and receiver coordinates in metres. Bilinear receiver interpolation and its transpose scatter use identical weights. Source amplitudes are normalized by cell area; recorded pressure is in numerical source_unit, not calibrated pascals.

AcousticFWI.simulate returns complex sampled data in [frequency, source, receiver] order. Implicit adjoint differentiation solves the adjoint equation rather than retaining a differentiation graph through all factorization or Krylov steps. Source batches limit live wavefields, while stored observations, solver workspace and native optimizer state remain separate memory costs.

Does dispersion screening correct the physics automatically?

No. The accuracy screen checks grid spacing, frequency and minimum allowed velocity against the discretization's phase-error criterion. It rejects unsafe requested combinations; it does not secretly lower the frequency, change precision or apply an unvalidated correction. Passing the screen does not prove accuracy in a heterogeneous medium or calibrate PML reflections.

Before a physical accuracy study, compare grid refinement, PML thickness and independent reference solutions. See the accuracy guide and operator architecture.

Try the Marmousi wavefield tutorial

Open the CPU/GPU Helmholtz notebook. It models the complete bundled Marmousi grid on CPU, plots wavefields and residuals, then uses a smaller crop for bounded CPU/CUDA solver comparisons. It explicitly identifies whether an actual CUDA device is available. The low-frequency full-grid run and crop comparison do not establish calibrated full-model accuracy, high-frequency performance or inversion recovery.