Skip to content

[WIP] Expose PETSc as a solver for the Poisson equation - #7214

Closed
RemiLehe wants to merge 5 commits into
BLAST-WarpX:developmentfrom
RemiLehe:petsc_poisson_solver
Closed

[WIP] Expose PETSc as a solver for the Poisson equation#7214
RemiLehe wants to merge 5 commits into
BLAST-WarpX:developmentfrom
RemiLehe:petsc_poisson_solver

Conversation

@RemiLehe

Copy link
Copy Markdown
Member

No description provided.

RemiLehe and others added 5 commits August 27, 2026 15:12
WarpX already exposes PETSc as an interface to solve the curl-curl
equation of the implicit electromagnetic solvers (`newton.linear_solver =
petsc_ksp`, `jacobian.pc_type = pc_petsc`). This adds the electrostatic
counterpart: with `warpx.poisson_solver = petsc`, the Poisson equation of
`computePhi` is solved by one of PETSc's Krylov solvers, preconditioned
by the AMReX geometric multigrid.

As in `amrex::GMRESMLMG`, the linear system is not assembled into a
sparse matrix. It is handed to PETSc as a `MatShell` whose action is
computed by the very same `amrex::MLNodeLinOp` that MLMG would use, and
the multigrid V-cycles are handed to PETSc as a `PCShell`. The Krylov
solver iterates on a correction, whose residual is computed with the
inhomogeneous operator, so that non-zero Dirichlet boundary values are
accounted for. The degrees of freedom of the PETSc vectors are the nodes
that a box owns (nodes shared with a neighboring box, or with a periodic
image, appear only once) and that are not Dirichlet nodes.

Since `petsc` discretizes Poisson's equation exactly like `multigrid`
does and accepts the same boundary conditions, the sites that used to
test `poisson_solver_id == Multigrid` (the Poisson boundary handler and
the initial div(B) cleaning) now test `!= IntegratedGreenFunction`, which
is what they actually mean. The tuning knobs of the Krylov solver live in
the new `petsc_poisson` namespace.

All of the new code is guarded by `AMREX_USE_PETSC`. Embedded boundaries
are not supported yet, since the calculation of the E field that follows
the solve needs the internal state of a completed MLMG solve; the
effective-potential solver is not supported either, since it does not go
through the same Poisson solve.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
With embedded boundaries, WarpX does not compute E from phi itself but
asks AMReX to do it, through the `EBCalcEfromPhiPerLevel` functor that
`computePhi` calls after the solve. That functor used
`amrex::MLMG::getGradSolution`, which reads the potential from the state
that `amrex::MLMG::solve` leaves behind, so it could not be used after a
solve that bypasses MLMG.

`EBCalcEfromPhiPerLevel` gains an overload that computes the gradient
directly from the linear operator and `phi`, via the public
`MLNodeLinOp::applyBC` and `MLLinOp::compGrad`. The MLMG path keeps
calling the original overload, so its behaviour is unchanged. The PETSc
solver additionally calls `MLLinOp::postSolve`, which is what writes the
prescribed potential into the nodes covered by the embedded boundary at
the end of an MLMG solve.

No change was needed for the degrees of freedom: `MLEBNodeFDLaplacian`
marks the EB-covered nodes as Dirichlet (with a negative value) in its
Dirichlet mask, so the `setDirichletNodesToZero` probe that builds the
DOF map already excludes them.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Review pass to reduce the amount of code and make it easier to digest:

- Collapse the PETScPoissonSolver class into the petscPoissonSolve free
  function, with a file-local context struct for the PETSc callbacks.
  The class was created and destroyed inside petscPoissonSolve at every
  call anyway, so nothing persisted; the public header shrinks to the
  options struct and one function declaration.
- Keep Dirichlet nodes as degrees of freedom instead of excluding them
  from the DOF map, which is also how amrex::GMRESMLMG treats them. Both
  the right-hand side and the operator output are zeroed on those nodes,
  and GMRES only forms linear combinations, so they remain exactly zero
  throughout the solve. This removes the probe that extracted the
  Dirichlet mask through setDirichletNodesToZero, the subtlest part of
  the previous version.
- Hardcode GMRES with right preconditioning, and drop the
  petsc_poisson.ksp_type and petsc_poisson.restart_length parameters
  (and the fragile list of GMRES variants that supported them). These
  duplicated PETSc's own runtime options (-ksp_type,
  -ksp_gmres_restart), which already take precedence since
  KSPSetFromOptions is called last.
- Share two work MultiFabs between the operator and preconditioner
  callbacks and the residual/correction computations, instead of
  allocating six; the phases never overlap.
- Replace the untested GPU code paths (VECCUDA/VECHIP) with an abort;
  no CI compiles PETSc with CUDA/HIP, and the VecGetArray semantics on
  device vectors deserve a dedicated, tested implementation.

Also enable PETSc in the 3D CPU job of the Azure pipeline (the 1D, 2D
and RZ jobs already build with it), so that the two new tests actually
run in CI.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
All four Azure jobs failed to compile PETScPoissonSolver.cpp:
`amrex::MLLinOpT::make` is a protected method, which amrex::GMRESMLMG can
only call because it is declared a friend of MLLinOpT. Create the work
arrays directly from the layout (and factory) of `phi` instead, and
assert that `phi` carries the ghost layer that the AMReX operators need.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Select the PETSc vector type per compute backend (VECCUDA, VECHIP, or
VECSTANDARD) and remove the abort that disabled the solver on GPUs. This
mirrors what KSP_impl::createObjects does for the curl-curl solver, down
to calling VecSetFromOptions after VecSetSizes so that -vec_type can
still override the choice.

The copy kernels are unchanged: as in WarpXSolverVec::copyTo/copyFrom,
they use VecGetArray and write the resulting array from amrex::ParallelFor.
Note that on a GPU this array is the host-side array of the PETSc vector,
so this assumes the allocation is addressable from the device; a comment
in copyToArray now records that assumption.

Compile-tested for CUDA (sm_86) against PETSc 3.24.0 built with
--with-cuda=1, in a 3D build configured with -DWarpX_COMPUTE=CUDA
-DWarpX_PETSC=ON -DWarpX_EB=ON. Not run: the build machine has no GPU.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@RemiLehe RemiLehe closed this Aug 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant