classDiagram
class Poisson {
+setLhs(lhs)
+setRhs(rhs)
+setGradient(grad)
+updateParameter(key, value)
+mergeParameters(params)
+solve()
}
class FFTPeriodicPoissonSolver {
+setRhs(rhs)
+solve()
}
class FFTOpenPoissonSolver {
+setRhs(rhs)
+solve()
+greensFunction()
}
class FFTTruncatedGreenPeriodicPoissonSolver {
+setRhs(rhs)
+solve()
+greensFunction()
}
class PoissonCG {
+setRhs(rhs)
+setSolver(lhs)
+solve()
}
class FEMPoissonSolver {
+setRhs(rhs)
+solve()
}
class PreconditionedFEMPoissonSolver {
+setRhs(rhs)
+solve()
}
Poisson <|-- FFTPeriodicPoissonSolver
Poisson <|-- FFTOpenPoissonSolver
Poisson <|-- FFTTruncatedGreenPeriodicPoissonSolver
Poisson <|-- PoissonCG
Poisson <|-- FEMPoissonSolver
Poisson <|-- PreconditionedFEMPoissonSolver
11 Poisson Solvers
IPPL provides many different methods for solving the Poisson equation: \[ \Delta \phi = - \rho \text{, with appropriate boundary conditions,} \] These solvers are needed for solving electrostatic/gravitational problems, either in an isolated manner (solving for fields on a grid), or in the context of a Particle-in-Cell scheme (see Chapter 3).
The available solvers are: FFT-based solvers for both periodic and free-space boundary conditions, a finite differences solver using a (Preconditioned) Conjugate Gradient iterative solver, and a Finite Element Method (FEM) based solver.
11.1 Main classes
| Class | Role |
|---|---|
Poisson |
Common Poisson solver interface (base class). |
FFTPeriodicPoissonSolver |
FFT-based periodic solver for Poisson. |
FFTOpenPoissonSolver |
FFT-based open-boundary solver based on Hockney-Eastwood and a new Vico-Greengard spectral solver [1]. |
FFTTruncatedGreenPeriodicPoissonSolver |
An FFT-based solver for long-range interaction only, using a truncated Green function. This is needed in the P3M approach to include collisions. |
PoissonCG |
(Preconditioned) Conjugate Gradient solver based on a finite differences discretization of the Poisson equation. |
FEMPoissonSolver |
Matrix-free FEM-based Poisson solver (using Conjugate Gradient as the iterative solver). |
PreconditionedFEMPoissonSolver |
FEM Poisson solver with preconditioning (using PCG). |
For more information on Poisson solvers in IPPL, see [2].
11.2 Solver hierarchy
The base Poisson<FieldLHS, FieldRHS> class stores LHS, RHS, and solver parameters. One can also pass a solver output type as a parameter, which is an enum:
| Output type | Meaning |
|---|---|
SOL |
Get the solution of the Poisson equation, in-place (replaces the rhs with the solution). |
GRAD |
Get the gradient of the potential only i.e. the electric field (in the electrostatic case). Note that this returns the negative gradient (\(\mathbf{E} = -\nabla\phi\)). |
SOL_AND_GRAD |
Return both solution and gradient. |
11.3 FFT solver parameters
FFT-based Poisson solvers inherit the FFT parameter set (see Chapter 10):
params.add("use_heffte_defaults", false);
params.add("use_pencils", true);
params.add("use_reorder", false);
params.add("use_gpu_aware", true);
params.add("comm", ippl::a2av);
params.add("r2c_direction", 0);For the FFTOpenPoissonSolver, one can also choose the algorithm type:
| Solver | Parameter | Values |
|---|---|---|
FFTOpenPoissonSolver |
algorithm |
HOCKNEY, VICO, BIHARMONIC, or DCT_VICO. |
FFTOpenPoissonSolver |
greens_function |
STANDARD or INTEGRATED; only used by HOCKNEY. |
The HOCKNEY algorithm is the standard Hockney-Eastwood method for solving the Poisson equation with open boundary conditions [3]. The VICO algorithm is a novel method with spectral accuracy properties, described in [1], and the DCT_VICO is its memory-optimized counterpart. We suggest the user to use DCT_VICO as the algorithm of choice, as it has similar performance and memory footprint as the Hockney-Eastwood method, with the added benefit of higher-accuracy.
11.3.1 Hockney Green-function models
The HOCKNEY open-boundary solver can build either a point-sampled or a cell-integrated real-space Green function before transforming the doubled-grid convolution kernel. The selection is made with the integer greens_function parameter:
| Value | Meaning |
|---|---|
FFTOpenPoissonSolver::STANDARD |
Point-sampled free-space kernel. This is the default and preserves historical IPPL behavior. |
FFTOpenPoissonSolver::INTEGRATED |
Cell-averaged kernel following Qiang et al. [4], [5]. This is currently implemented for 3D HOCKNEY only. |
The standard kernel samples \[ G(\mathbf{r}) = -\frac{1}{4\pi |\mathbf{r}|}. \]
The integrated kernel assumes the charge density is uniform over each source cell and uses the cell average \[ \overline{G}(\mathbf{r}) = -\frac{1}{4\pi h_x h_y h_z} \int_{-h_x/2}^{h_x/2} \int_{-h_y/2}^{h_y/2} \int_{-h_z/2}^{h_z/2} \frac{dx' dy' dz'} {\sqrt{(x-x')^2 + (y-y')^2 + (z-z')^2}}. \]
IPPL evaluates this integral by the closed-form eight-corner difference of the antiderivative \[ \begin{aligned} F(x,y,z) ={}& -\frac{z^2}{2}\tan^{-1}\!\left(\frac{xy}{zr}\right) -\frac{y^2}{2}\tan^{-1}\!\left(\frac{xz}{yr}\right) -\frac{x^2}{2}\tan^{-1}\!\left(\frac{yz}{xr}\right) \\ &+ yz\ln(x+r) + xz\ln(y+r) + xy\ln(z+r), \end{aligned} \] where \(r=\sqrt{x^2+y^2+z^2}\). This removes the point singularity from the cell containing the source and is useful for high-aspect-ratio beams where point sampling of \(1/r\) can be inefficient near narrow dimensions.
A minimal setup is:
using Solver_t = ippl::FFTOpenPoissonSolver<VField_t, Field_t>;
ippl::ParameterList params;
params.add("algorithm", Solver_t::HOCKNEY);
params.add("greens_function", Solver_t::INTEGRATED);
Solver_t solver;
solver.mergeParameters(params);
solver.setRhs(rho);
solver.solve();Calling shiftedGreensFunction(shift) replaces the cached Hockney kernel by the shifted kernel \(G(\mathbf{r}-\mathbf{s})\). With greens_function == INTEGRATED, the same shift is applied before the cell average is evaluated. This is intended for workflows such as image-charge corrections, where the caller constructs the image contribution around an IPPL solve with a shifted kernel. The shifted kernel remains active until greensFunction() is called again or the mesh spacing changes.
11.4 (Preconditioned) Conjugate Gradient solver
PoissonCG solves the Poisson problem with a finite-difference Laplace operator. The Poisson equation is discretized using a second-order centered finite differences scheme, which in the 1D case looks like:
\[ \frac{\phi_{i+1} - 2\phi_i + \phi_{i-1}}{\Delta x^2} = - \rho_i \hspace{5mm} \forall i \in \{0,..., n-1\}. \] This is also known as the 3-point stencil. In the 2D case, this becomes a 5 point stencil, while in the 3D case, it becomes a 7D stencil.
When applying this to every grid point of the field, we obtain a linear system of equations of type \(A \mathbf{\phi} = \mathbf{\rho}\), where \(A\) is an \(n\times n\) sized matrix: \[ \underbrace{\frac{1}{\Delta x^2}\begin{bmatrix} 2 & -1 & \\ -1 & 2 & -1 & \\ & -1 & \ddots & \ddots &\\ & & \ddots & \ddots & -1 \\ & & & -1 & 2 \\ \end{bmatrix}}_\text{$A$} \cdot \underbrace{\begin{bmatrix} \phi_0 \\ \phi_1 \\ \vdots \\ \phi_{n-2}\\ \phi_{n-1} \\ \end{bmatrix}}_\text{$\vec{\phi}$} = \underbrace{\begin{bmatrix} \rho_0 \\ \rho_1 \\ \vdots \\ \rho_{n-2}\\ \rho_{n-1} \\ \end{bmatrix}}_\text{$\vec{\rho}$}. \]
Instead of building the matrix, one can solve this linear system of equations in a manner by defining an operator for the action of the matrix \(A\) on a vector \(\vec{x}\): \[ [F_A(\vec{x})]_j = \frac{\vec{x}_{j+1} - 2\vec{x}_j + \vec{x}_{j-1}}{h_x^2}. \]
This is what is done in PoissonCG, as building the matrix is an expensive task, especially when the number ofgrid points in the simulation is large. By defining a Laplace operator, one can simply pass the action of the matrix on a vector to the CG iterative solver, which uses this operator to simply compute any needed matrix-vector products on the fly.
This solver can either be run using plain CG, or with a preconditioner to reduce the condition number of the matrix and therefore make the iterative solver converge faster (PCG).
The preconditioners which are currently available are: Jacobi, Richardson, Gauss-Seidel, and SSOR (see [6], [7], [8] for more information on these preconditioners and their implementation).
The preconditioner can be set using the solver ParameterList. A list of the input parameters which one can pass are:
| Key | Meaning |
|---|---|
solver |
non-preconditioned for CG or preconditioned for PCG. |
preconditioner_type |
jacobi, richardson, gauss-seidel, ssor |
richardson_iterations |
Number of Richardson preconditioner iterations. |
gauss_seidel_inner_iterations |
Inner Gauss-Seidel or SSOR iterations. |
gauss_seidel_outer_iterations |
Outer Gauss-Seidel or SSOR iterations. |
ssor_omega |
SSOR relaxation parameter. |
communication |
This is a bool (0 or 1) which when enabled turns on halo communication in the preconditioners. |
max_iterations |
Iteration cap for the linear solve. |
tolerance |
Residual tolerance. |
TestCGSolver verifies convergence against a manufactured periodic solution. Its command-line parser accepts a grid-size exponent and optional scaling/preconditioner selectors; for example j selects Jacobi, r selects Richardson, g selects Gauss-Seidel, and s selects SSOR. The test prints relative error, residual, and iteration count.
11.5 FEMPoissonSolver
The FEM-based Poisson solver is based on the FEM framework in IPPL (src/FEM). It employs a first-order Lagrange space to discretize the Poisson equation in its variational form:
\[ \int_{\Omega} \nabla \phi \cdot \nabla v = \int_{\Omega} \rho \cdot v, \hspace{4mm} \forall v\in V. \] where \(v\) is known as the test function, and \(\phi\) the trial function.
When applying a finite element discretization to this variational form, one obtains a linear system of equations \(A\mathbf{x} = \mathbf{b}\), where \(A\) is known as the stiffness matrix and \(\mathbf{b}\) as the load vector. Assembling the stifness matrix is very expensive, therefore we choose to employ a matrix-free approach, where the product \(A\mathbf{x}\) is computed on-the-fly. More information on this approach can be found in [2].
11.6 Minimal working example of a solver
FFTPeriodicPoissonSolver solves periodic Poisson problems using real-to-complex FFTs. test/solver/TestFFTPeriodicPoissonSolver.cpp verifies the solver against a smooth manufactured solution on [-1,1]^3 over multiple grid sizes.
Minimal pattern:
using Field_t = ippl::Field<double, Dim, Mesh_t, Centering_t>;
using VField_t = ippl::Field<ippl::Vector<double, Dim>, Dim, Mesh_t, Centering_t>;
using Solver_t = ippl::FFTPeriodicPoissonSolver<VField_t, Field_t>;
Field_t rhs(mesh, layout);
ippl::ParameterList params;
params.add("output_type", Solver_t::SOL);
params.add("use_heffte_defaults", false);
params.add("use_pencils", true);
params.add("use_gpu_aware", true);
params.add("comm", ippl::a2av);
params.add("r2c_direction", 0);
Solver_t solver;
solver.mergeParameters(params);
solver.setRhs(rhs);
solver.solve();
# rhs should now contain the solution of the Poisson equation.With output_type == SOL, the RHS field is overwritten with the potential in the current test pattern.
11.7 Verification material
The correctness and convergence of all solvers are tested in test/solver and test/solver/fem(for the FEM-based solver). All test files have instructions on how to run them at the beginning of the file.