Skip to content

The FFT Poisson solver ​

IDGroupMilestoneBackendsFigureRegression
SV-2Self-gravityM8 (#81), P2CPU, FNLyes—

Case and scope ​

Self-gravity needs a Poisson solver that is exact for the discrete equation LhΦ=4πG(ρ−ρ¯), with Lh the 7-point Laplacian the gravity stencils telescope against, so that the scheme is the one P0 analysed. P2 wrote this as the condition of exact conservation; P4 found the condition weaker: any linear, symmetric solve conserves E+12ρΦ and momentum exactly in the semi-discrete form (see SV-6). The round-off residual remains a property of this solver, verified here.

What the case catches (the unit test test_poisson_fft):

  • a wrong eigenvalue of Lh (a missing factor, a swapped axis, a wrong spacing): the error against the discrete solution leaves round-off. The domain is anisotropic, [0,1]×[0,2]×[0,3], so an axis mix-up cannot cancel;
  • a misplaced block in the gather, or a wrong ghost wrap in the scatter: the error over every cell and ghost, and the residual from the scattered ghosts;
  • a ghost layer the scatter leaves unfilled (a sentinel value);
  • a solver that accepts a grid it cannot solve: a non-periodic grid must stop the run.

Models and numerics ​

ItemChoiceCode
EquationLhΦ=4πG(ρ−ρ¯), Lh the 7-point Laplacian (5-point in 2-D)models
Solver, CPUFFTW. Rank 0 gathers the block interiors and transforms; each Fourier mode is divided by the eigenvalue of Lh itself, λk=∑d(2cos⁡(2πkd/Nd)−2)/Δxd2; the mean (λk=0) is set to zero; the inverse transform follows, then the scatter fills every block with all its ghost layers by the periodic wrapinitialize, solve, transform, adam_poisson_fft_object.F90
Solver, FNLcuFFT on rank 0's GPU, the same gather and scattertransform_fnl, spectral_solve_dev, adam_fnl_poisson_fft_object.F90
Scopeuniform, periodic grids; anything else stops the run. The gather makes the solve serial on rank 0: correct for any rank count, not scalable. Isolated boundaries come in P4 (SV-6), AMR in P5 (SV-9)initialize, adam_poisson_fft_object.F90

Initial and boundary conditions ​

ItemValue
Domain[0,1]×[0,2]×[0,3], periodic on every face
Sourceρ=1.5+sin⁡(k1x)cos⁡(k1y)cos⁡(k1z)+0.5cos⁡(k2⋅x), k1=2π(1,1,13), k2=2π(2,12,13); in 2-D the z wave numbers are 0
Factor4πG with G=1
Grids3-D octree at levels 1–3 (163 to 643 cells); 2-D quadtree (null_z) at levels 1–3 (162 to 642); a single 83 block (level 0: the wrap inside one block; on 2+ ranks, ranks without blocks). 8 cells per block side, 3 ghosts
RanksCPU: 1, 2, 3 (the poisson.sh default list), and 4; FNL: 1, 2
Exact solutionseach mode is an eigenfunction of Lh: the discrete solution divides it by its λk, the continuum one by −|k|2
Refusal--no-periodic: one 3-D configuration without periodic boundaries; the run must stop with exit 1, naming the direction (direction 1 is not periodic)

Input files ​

FileRole
src/tests/poisson/test_poisson_fft.F90the unit test (modes test-poisson-fft-gnu, test-poisson-fft-fnl-nvf); --dump DIR writes rank 0's potential of every level
src/tests/flume/verification/selfgravity/poisson.shruns the test on every rank count of --np-list (leg sv2), and the refusal; POISSON_EXE selects the build
src/tests/flume/verification/selfgravity/poisson_oracle.pycompare DIR_CPU DIR_FNL: the CPU = FNL check on the dumped potentials, and the figure's reader
src/tests/flume/verification/make_doc_runs.sh, group selfgravitysv2-poisson: the test with its log and dumps
src/tests/flume/verification/make_doc_figures.py, fig_selfgravity_poissondraws the figure (--only selfgravity-poisson)
bash
cd src/tests/flume/verification/selfgravity
./poisson.sh --legs sv2 --dump <dir-cpu>                                    # CPU (FFTW)
POISSON_EXE=$PWD/../../../../../exe/test_poisson_fft_fnl ./poisson.sh --legs sv2 --np-list "1 2" --dump <dir-fnl>
python3 poisson_oracle.py compare <dir-cpu> <dir-fnl>                       # CPU = FNL

Results ​

SV-2: the errors against N, the 2-D potential and its error against the continuum solution

Left: against N, the error against the continuum solution falls as Δx2, while the error against the discrete solution and the residual stay at round-off. Middle and right: the 2-D potential at N=64 and its error against the continuum solution, 4.3⋅10−4, largest where |Φ| is.

AssertedBoundCPU (FFTW)FNL (cuFFT, A30 and the WSL GPU)
max|Φ−Φh|/max|Φh|, every cell and ghost10−12≤2.8⋅10−15≤2.8⋅10−15
residual of Lh from the scattered ghosts, relative10−10≤3.7⋅10−14≤4.3⋅10−14
order against the continuum solution, N=32→64≥1.91.98 (3-D), 1.98 (2-D)1.98, 1.98
ghost cells the scatter left unfilled000
a non-periodic gridrefused, exit 1refusedrefused
CPU against FNL, max|ΦCPU−ΦFNL|/max|Φ| (poisson_oracle.py compare)10−13≤9.8⋅10−15≤9.8⋅10−15

Related cases: the isolated variant of the same solver is SV-6; the multigrid solver on AMR grids, checked against this one on uniform grids, is SV-9; the coupling to the scheme is SV-3 to SV-5.

Findings ​

  • The printed numbers do not depend on the rank count (np 1 and np 4 agree to every digit), and the A30 potential is bitwise that of the WSL GPU.
  • The solver is exact for Lh, and the scheme is second order through Lh.
  • The residual grows from 10−15 to 4⋅10−14 between N=16 and 64: Lh amplifies the round-off of Φ by (kΔx)−2. That is the floor the conservation of P3 inherits.
  • The solve is serial on rank 0 (the gather): correct on any rank count, not scalable.