Skip to content

The isolated Poisson solver ​

IDGroupMilestoneBackendsFigureRegression
SV-6Self-gravityM8 (#81), P4CPU, FNLyesselfgravity-star (through the star it solves for)

Case and scope ​

On a periodic domain a self-gravitating object feels its own images, and the mean density must be removed. Isolated boundaries give the free-space potential, Φ→0 at infinity, of the mass on the grid: what a star, a collapsing cloud or a binary needs. P4 adds them to the FFT solver and verifies the solver (SV-6, this page), a star in equilibrium (SV-7) and a collapsing one (SV-8, both on the n = 1 polytrope).

The method (poisson_bc = isolated). Hockney and Eastwood's zero padding: the density sits in an array of m=2(N+ngc) cells per direction, zero outside the domain, and is convolved, by one r2c transform, a product and one c2r, with the lattice Green's function of Lh, h2G(n). The circular convolution equals the free-space one on every cell the scatter reads, ghosts included, because m exceeds twice the largest offset. Using the lattice kernel of Lh rather than the sampled continuum −1/(4πr) makes Φ satisfy the discrete Poisson equation, not just approximate it.

The kernel. G is generated once, offline, by scripts/lattice_green.py into adam_lattice_green_library (6545 values, the wedge n1≥n2≥n3≥0):

  • near field, max|nd|≤32: the Dirichlet problem L1G=δ on the cube [−32,32]3, solved exactly by a type-I sine transform, with the far-field series on its faces;
  • far field: the series G0+G1+G2+G3, G0=−1/(4πr), G1=−5(n14+n24+n34−35r4)/(32πr7), G2=P8/r13, G3=P12/r19, each term solving its order of the Taylor series of L1 (checked symbolically). The series leaves one coefficient free in G2 and one in G3 (the cubic harmonics of degree 4 and 6); the lattice fixes them, and they are fitted to the table (least squares over 12≤r≤26).

What the case catches:

  • a kernel that is not the lattice Green's function of Lh (a sampled continuum kernel, a bad table/series seam): the residual of Lh leaves the kernel's level;
  • a padding too small for the offsets the scatter reads: the error against the exact potential on the ghosts and the padded far field;
  • a wrong mass normalisation or sign: the error against the exact potentials outside the source (−GM/r);
  • a solver that accepts a configuration it cannot solve: the scope is fatal otherwise (3-D, every direction non-periodic, cubic cells: the kernel is the unit lattice's).

Models and numerics ​

ItemChoiceCode
EquationLhΦ=4πGρ, Φ→0 at infinitymodels
Solver, CPUFFTW, zero padding to m=2(N+ngc), convolution with the kernel's spectruminitialize_blocks, make_kernel, transform, transform_kernel, adam_poisson_fft_object.F90
Solver, FNLcuFFT on rank 0's GPUtransform_kernel_fnl, spectral_convolve_dev, kernel_spectrum_dev, adam_fnl_poisson_fft_object.F90
Kernelthe lattice Green's function: table for max|nd|≤32 (LGF_RADIUS), series G0+G1+G2+G3 beyondadam_lattice_green_library.F90, generated by scripts/lattice_green.py
Selection[gravity] poisson_bc = isolatedload_from_file, adam_flume_gravity_object.F90

Initial and boundary conditions ​

ItemValue
Domainthe cube [−1,1]3, non-periodic
Gridoctree levels 1–3 (163 to 643 cells), 8 cells per block side, 3 ghosts
RanksCPU: 1, 2, 3; FNL: 1, 2
Factor4πG with G=1
Source 1: the n = 1 polytropeρ=sin⁡(kr)/(kr), k=π/a, a=1/2 (smooth; its derivative jumps at r=a); exact Φ=−(4π/k2)sin⁡(kr)/(kr)−M/a inside, −M/r outside, M=4a3/π
Source 2: the uniform sphereρ=1, r<a, as cell averages (83 samples per cell); exact −2π(a2−r2/3) inside, −M/r outside, M=4πa3/3
Refusals--refuse-isolated-periodic (a periodic grid), --refuse-isolated-anisotropic (cells 1/8×2/8×3/8), --refuse-isolated-2d (null z): each must stop with exit 1 and its message

Generator checks (deterministic, scripts/lattice_green.py):

CheckBoundMeasured
residual of L1 on the table10−132.2⋅10−16
residual of L1 on the composite kernel (table and series, as the Fortran evaluates it) on [−64,64]3, across the faces where they meet10−121.0⋅10−13
G(0) against Watson's closed form for the simple cubic lattice, relative10−122.0⋅10−13
G(1,0,0)−G(0)−1/610−142.8⋅10−17

Input files ​

FileRole
src/tests/poisson/test_poisson_fft.F90the unit test, --isolated (and the three --refuse-isolated-* flags)
src/tests/flume/verification/selfgravity/poisson.shleg sv6: the test on every rank count of --np-list, and the refusals; --dump DIR covers SV-6 too
src/tests/flume/verification/selfgravity/poisson_oracle.pycompare DIR_CPU DIR_FNL on the padded arrays
scripts/lattice_green.pygenerates adam_lattice_green_library.F90 and runs the generator checks
src/tests/flume/verification/make_doc_runs.sh, group isolatedsv6-poisson: the test with its log and dumps
src/tests/flume/verification/make_doc_figures.py, fig_selfgravity_isolateddraws the figure (--only selfgravity-isolated)
bash
python3 scripts/lattice_green.py --check            # generator checks; the committed module against a fresh emission
cd src/tests/flume/verification/selfgravity
./poisson.sh --legs sv6                                                                   # CPU (FFTW)
POISSON_EXE=$PWD/../../../../../exe/test_poisson_fft_fnl ./poisson.sh --legs sv6 --np-list "1 2"  # FNL (cuFFT)

Results ​

SV-6: the errors against N, the polytrope's potential across the padded array, its error

Left: the error against the exact potentials falls as Δx2 (the sphere less regularly); the residual sits at the kernel's level. Middle: the polytrope's potential along the line through the centre over the whole padded array, on the exact curve, −GM/r outside the star. Right: the error on the plane through the centre, 6.1⋅10−4 at most, at the centre; smooth, with no trace of the domain's edge or of the padding.

AssertedBoundCPU (FFTW)FNL (cuFFT, A30 and the WSL GPU)
residual of Lh, relative to max|4πGρ|10−10≤2.1⋅10−11≤2.1⋅10−11
least-squares order against the exact Φ, every cell and ghost, polytrope≥1.92.032.03
the same, uniform sphere≥1.21.351.35
ghost cells left unfilled000
isolated on a periodic grid, with non-cubic cells, in 2-Drefused, exit 1refusedrefused
CPU against FNL (poisson_oracle.py compare, the padded arrays)10−13≤1.6⋅10−15≤1.6⋅10−15

Related cases: the periodic solver is SV-2; the stars it holds and collapses are SV-7 and SV-8; isolated boundaries on AMR are the multigrid's James step (#87, SV-13, SV-14).

Findings ​

  • The kernel series needs G2 and G3. With G0+G1 alone the composite residual was 7⋅10−10, concentrated on the cube's faces, and summed over a 643 source it showed as a residual of 3.4⋅10−10 in Φ; with G2 and G3 they are 1.0⋅10−13 and 2.1⋅10−11.
  • The residual is the kernel's, not round-off. It is 10−14 while every offset lies inside the table (163, 323) and 2⋅10−11 at 643, where the offsets reach the series: about 10−13 per offset, summed over the source cells. At 1283 (measured once, 6 ranks) it is 5⋅10−11.
  • The polytrope converges at second order (2.14, 1.92, 2.02 per pair from 163 to 1283; the dip is the kink of its density at the surface). The uniform sphere converges irregularly (0.6, 2.1, 1.75): its surface cuts cells, and the bound only asserts better than first order.
  • Conservation needs symmetry, not a round-off residual. The convolution with an even kernel is a symmetric linear map, which is what the exact exchange of E and W needs (models): SV-8 measures it.
  • The error is largest at the centre, where the potential is deepest.