Appearance
The FFT Poisson solver
| ID | Group | Milestone | Backends | Figure | Regression |
|---|---|---|---|---|---|
| SV-2 | Self-gravity | M8 (#81), P2 | CPU, FNL | yes | — |
Case and scope
Self-gravity needs a Poisson solver that is exact for the discrete equation
What the case catches (the unit test test_poisson_fft):
- a wrong eigenvalue of
(a missing factor, a swapped axis, a wrong spacing): the error against the discrete solution leaves round-off. The domain is anisotropic, , 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
| Item | Choice | Code |
|---|---|---|
| Equation | models | |
| Solver, CPU | FFTW. Rank 0 gathers the block interiors and transforms; each Fourier mode is divided by the eigenvalue of | initialize, solve, transform, adam_poisson_fft_object.F90 |
| Solver, FNL | cuFFT on rank 0's GPU, the same gather and scatter | transform_fnl, spectral_solve_dev, adam_fnl_poisson_fft_object.F90 |
| Scope | uniform, 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
| Item | Value |
|---|---|
| Domain | |
| Source | |
| Factor | |
| Grids | 3-D octree at levels 1–3 (null_z) at levels 1–3 ( |
| Ranks | CPU: 1, 2, 3 (the poisson.sh default list), and 4; FNL: 1, 2 |
| Exact solutions | each mode is an eigenfunction of |
| 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
| File | Role |
|---|---|
src/tests/poisson/test_poisson_fft.F90 | the 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.sh | runs 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.py | compare 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 selfgravity | sv2-poisson: the test with its log and dumps |
src/tests/flume/verification/make_doc_figures.py, fig_selfgravity_poisson | draws 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 = FNLResults

Left: against N, the error against the continuum solution falls as
| Asserted | Bound | CPU (FFTW) | FNL (cuFFT, A30 and the WSL GPU) |
|---|---|---|---|
| residual of | |||
| order against the continuum solution, | 1.98 (3-D), 1.98 (2-D) | 1.98, 1.98 | |
| ghost cells the scatter left unfilled | 0 | 0 | 0 |
| a non-periodic grid | refused, exit 1 | refused | refused |
CPU against FNL, poisson_oracle.py compare) |
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
, and the scheme is second order through . - The residual grows from
to between and 64: amplifies the round-off of by . 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.