flowchart TB main["main() parses CLI"] pre["manager.pre_run()"] loop["manager.run(nt)"] step["advance(): push → scatter → solve → gather → push"] main --> pre pre --> loop loop --> step step --> loop
5 ALPINE Manager Hierarchy
ALPINE is IPPL’s application layer: a collection of plasma-physics mini-apps in the alpine/ directory that run full particle-in-cell (PIC) simulations end to end. They are not isolated library demos. Each mini-app wires together particles, fields, interpolation, Poisson solvers, MPI load balancing, and diagnostics the way a real simulation code would.
This chapter explains what those mini-apps are for, how they are organized, and how the manager hierarchy keeps application logic separate from reusable PIC infrastructure. We use Landau damping as the main walkthrough because it is the smallest complete example; the same pattern applies to the other ALPINE applications.
For the underlying PIC model, see Chapter 3. For gather/scatter and load balancing, see Chapter 9. For solver options, see Chapter 11 and the runnable LandauDamping example in Chapter 16.
5.1 What ALPINE mini-apps are
IPPL ships two kinds of executables that look similar from the outside but serve different goals:
Unit tests and examples (test/, examples/) |
ALPINE mini-apps (alpine/) |
|
|---|---|---|
| Goal | Verify one subsystem (a solver, FFT layout, CIC gather/scatter, …) | Run a complete PIC-style plasma simulation |
| Scope | Often a single loop or manufactured solution | Domain setup, particles, time stepping, field solve, diagnostics, scaling |
| Typical user | Developer validating a change | Application author learning how to assemble IPPL, or researcher running a benchmark |
| Code style | Direct calls into IPPL classes | Thin main() + a manager class that owns the simulation |
Mini-apps therefore serve three practical purposes:
- Reference applications — they show how to combine IPPL building blocks into a working PIC code without reimplementing the standard loop.
- Correctness and performance benchmarks — standard plasma problems with known behavior are used to test solvers, preconditioners, and Kokkos/MPI portability on large machines [1].
- Templates for new codes — to add a new simulation, you typically subclass the existing managers and override physics-specific methods rather than writing a PIC driver from scratch.
At a high level, every ALPINE mini-app follows the same skeleton:
The executable stays small; the manager owns containers, solvers, and the time-stepping policy.
5.2 Applications in alpine/
CMake builds three manager-based executables from alpine/CMakeLists.txt. Each has a thin driver (*.cpp) and a problem-specific manager that subclasses AlpineManager:
| Executable | Manager class | Physical problem |
|---|---|---|
LandauDamping |
LandauDampingManager |
Electrostatic Landau damping on a periodic domain |
PenningTrap |
PenningTrapManager |
Magnetized plasma in a Penning-trap geometry with imposed \(\mathbf{E}\) and \(\mathbf{B}\) fields |
BumponTailInstability |
BumponTailInstabilityManager |
Bump-on-tail (or related beam–plasma) velocity instability on a periodic domain |
All three share the same command-line shape:
srun ./<Executable> Nx Ny Nz Np Nt <solver> <lbthres> <stepper> [preconditioner args...] --overallocate <factor> --info <level>
Landau damping is documented in detail below because it is the smallest complete walkthrough. The other managers override the same hooks—pre_run(), initializeParticles(), advance(), and dump()—while reusing AlpineManager for deposition, gather, solver dispatch, and load balancing.
5.2.1 Landau damping in brief
Landau damping is a classic kinetic-plasma test case: a small sinusoidal perturbation in particle density launches an electrostatic wave that decays in time because of resonant particle–wave interaction (no collisions required). LandauDampingManager sets a periodic box with \(k_w=0.5\), perturbation amplitude \(\alpha=0.05\), Maxwellian velocities, and positions sampled from \(1+\alpha\cos(k_w x)\).
srun ./LandauDamping 64 64 64 1048576 20 FFT 0.1 LeapFrog --info 5This constructs the manager, calls pre_run() to build fields and particles, then runs 20 Leapfrog steps with an FFT Poisson solve. ORB load balancing triggers when the particle imbalance exceeds lbthres (here 10%).
5.2.2 Penning trap in brief
PenningTrapManager models particles in a static trap: an external quadrupole-like electric potential plus a uniform axial magnetic field \(B_z = 5\) (in code units). Self-fields from the deposited charge are added to these external fields during the push. The domain is periodic on \([0,20]^3\) with Gaussian spatial and velocity distributions centered in the box. The time integrator is a magnetized Leapfrog variant (Boris-style \(\mathbf{v}\times\mathbf{B}\) coupling in the velocity kicks), not the purely electrostatic push used in Landau damping.
srun ./PenningTrap 128 128 128 10000 300 FFT 0.01 LeapFrog --overallocate 1.0 --info 10Because the trap study often needs many time steps, this mini-app is a common target for iterative and preconditioned Poisson solvers (CG, PCG, and the truncated-Green TG path).
5.2.3 Bump-on-tail instability in brief
BumponTailInstabilityManager initializes a periodic electrostatic setup with a bulk Maxwellian plus a fast beam in velocity space—the classic bump-on-tail configuration that drives a kinetic instability. A sinusoidal density perturbation is applied along the last spatial dimension. The standard electrostatic Leapfrog cycle applies (no external \(\mathbf{B}\) field). Optional phase-space dumps exist behind a compile-time flag (EnablePhaseDump).
srun ./BumponTailInstability 128 128 128 10000 10 FFT 0.01 LeapFrog --overallocate 2.0 --info 105.3 Why a manager hierarchy?
A PIC simulation repeats the same structural steps every time step (see Chapter 3 and Chapter 9): push particles, deposit charge, solve for the field, gather forces, push again, migrate particles across MPI ranks, and optionally repartition. ALPINE factors this into layers so that only the physics-specific pieces change between mini-apps:
| Layer | Responsibility | Changes when you add a new mini-app? |
|---|---|---|
BaseManager |
Simulation lifecycle: pre_run(), run(nt) |
|
PicManager |
Owns particle and field containers, solver, load balancer; requires par2grid() / grid2par() definitions of PIC scatter/gather |
|
AlpineManager |
Contains all common ALPINE functions, such as Cloud-in-Cell scatter/gather, charge conservation checks, and default diagnostics | |
LandauDampingManager (or others) |
Domain, initial conditions, time integrator, problem-specific output - This is where the physics is |
Concrete manager subclasses: LandauDampingManager, PenningTrapManager, and BumponTailInstabilityManager, all derived from AlpineManager.
Source files for this stack:
| File | Role |
|---|---|
alpine/LandauDamping.cpp |
Landau damping driver |
alpine/LandauDampingManager.h |
Landau-specific physics |
alpine/PenningTrap.cpp |
Penning trap driver |
alpine/PenningTrapManager.h |
Trap fields, magnetized push, diagnostics |
alpine/BumponTailInstability.cpp |
Bump-on-tail driver |
alpine/BumponTailInstabilityManager.h |
Beam–plasma ICs and instability diagnostics |
alpine/AlpineManager.h |
Shared Particle-in-Cell functions for ALPINE, inherits from PicManager which inherits from BaseManager |
alpine/FieldSolver.hpp |
Solver name → concrete Poisson backend |
alpine/FieldContainer.hpp, alpine/ParticleContainer.hpp, alpine/LoadBalancer.hpp |
Containers for fields and particles used by ALPINE, as well as the load balancing scheme |
5.4 Class hierarchy
classDiagram
direction TB
class BaseManager {
+pre_run()
+pre_step()
+advance()*
+post_step()
+run(int nt)
}
class PicManager~T, Dim, pc, fc, orb~ {
+par2grid()*
+grid2par()*
+setParticleContainer(pc)
+setFieldContainer(fc)
+setFieldSolver(FieldSolverBase)
+setLoadBalancer(orb)
}
class AlpineManager~T, Dim~ {
+grid2par()
+par2grid()
+gatherCIC()
+scatterCIC()
+gatherFEM()
+scatterFEM()
+post_step()
+dump()
}
class LandauDampingManager~T, Dim~ {
+pre_run()
+initializeParticles()
+advance()
+LeapFrogStep()
+dump()
}
class PenningTrapManager~T, Dim~ {
+pre_run()
+initializeParticles()
+advance()
+LeapFrogStep()
+dump()
}
class BumponTailInstabilityManager~T, Dim~ {
+pre_run()
+initializeParticles()
+advance()
+LeapFrogStep()
+dump()
}
BaseManager <|-- PicManager
PicManager <|-- AlpineManager
AlpineManager <|-- LandauDampingManager
AlpineManager <|-- PenningTrapManager
AlpineManager <|-- BumponTailInstabilityManager
BaseManagerdefines when setup, time stepping, and post-step work run.run(nt)callspre_step(),advance(), andpost_step()each step.PicManagerspecializes that contract for PIC: it holds the particle container, field container, field solver, and load balancer, and requires particle-to-grid and grid-to-particle transfers.AlpineManagerimplements those transfers for ALPINE (CIC for spectral/FD solvers, FEM assembly for finite-element solvers), normalizes deposited charge, and provides default timing and dump hooks.- Problem managers (
LandauDampingManager,PenningTrapManager,BumponTailInstabilityManager, …) fill in domain setup, particle sampling, the time integrator, and diagnostics. Penning trap additionally applies external \(\mathbf{E}\) and \(\mathbf{B}\) during the push; the other two are electrostatic Leapfrog.
Together, this stack connects the abstract PIC loop in Chapter 9 to the performance-portable execution model in Chapter 15.
5.5 Object ownership
classDiagram
direction LR
class LandauDampingManager
class FieldContainer {
E
rho
phi
mesh
FieldLayout
}
class ParticleContainer {
R
P
E
q
ParticleSpatialLayout
}
class FieldSolver {
FFT
CG
PCG
FEM
FEM_PRECON
}
class LoadBalancer {
ORB repartition
balance threshold
}
LandauDampingManager *-- FieldContainer
LandauDampingManager *-- ParticleContainer
LandauDampingManager *-- FieldSolver
LandauDampingManager *-- LoadBalancer
ParticleContainer --> FieldContainer : layout from mesh + field layout
FieldSolver --> FieldContainer : solves rho -> phi,E
LoadBalancer --> FieldContainer
LoadBalancer --> ParticleContainer
LoadBalancer --> FieldSolver
PicManager stores shared pointers to these objects. LandauDampingManager::pre_run() constructs them in dependency order:
| Step | Object or operation | Role |
|---|---|---|
| 1 | Domain, spacing, time step, total charge | Defines the periodic Landau problem and grid resolution |
| 2 | FieldContainer |
Mesh, FieldLayout, electric field E, charge density rho, and potential phi when the solver needs it |
| 3 | ParticleContainer |
Particle layout aligned with the field mesh; registers position R, velocity P, charge q, and gathered field E |
| 4 | FieldSolver |
Dispatches to FFT, CG, PCG, FEM, or preconditioned FEM via FieldSolverBase (see accepted types for each application) |
| 5 | LoadBalancer |
ORB repartitioning of domain - tied to layout of fields and particles |
5.6 Runtime lifecycle
sequenceDiagram
participant main as LandauDamping.cpp
participant mgr as LandauDampingManager
participant alpine as AlpineManager
participant pic as PicManager/BaseManager
participant fs as FieldSolver
participant lb as LoadBalancer
main->>mgr: construct(totalP, nt, nr, lbt, solver, step)
main->>mgr: pre_run()
mgr->>mgr: create containers and initialize particles
mgr->>fs: warm-up solve
mgr->>alpine: par2grid()
mgr->>fs: runSolver()
mgr->>alpine: grid2par()
mgr->>mgr: dump()
main->>pic: run(nt)
loop each time step
pic->>alpine: pre_step()
pic->>mgr: advance()
mgr->>mgr: push velocity half-step
mgr->>mgr: push position
mgr->>mgr: particle update/migration
mgr->>lb: balance() and repartition() if needed
mgr->>alpine: par2grid()
mgr->>fs: runSolver()
mgr->>alpine: grid2par()
mgr->>mgr: push velocity half-step
pic->>alpine: post_step()
end
main() never calls scatter, gather, or migration directly. This lets new mini-apps swap physics inside advance() while reusing the same outer loop.
5.7 Landau-specific physics and time stepping
LandauDampingManager encodes the benchmark setup:
- Periodic domain with (r_), (r_/k_w), perturbation amplitude (), and (k_w=0.5).
- Positions sampled from (1+(k_w x)) via inverse transform; velocities from a normal (Maxwellian) distribution.
- Total charge split uniformly across macro-particles.
- Time step (t = (0.05,, 0.5 (x_d))) from the grid spacing.
- Only the
LeapFrogintegrator is accepted in this mini-app.
Each Leapfrog step is the standard electrostatic PIC cycle:
- Half-step velocity kick from gathered ().
- Full-step position push.
- Particle migration (
ParticleBase::update()). - ORB repartition if load imbalance exceeds the threshold.
- Charge deposition (
par2grid()). - Field solve (
runSolver()). - Field gather (
grid2par()). - Second half-step velocity kick.
Diagnostics (dump(), dumpLandau()) report field and particle quantities used to verify damping behavior.
5.8 Building a new ALPINE-style mini-app
Use Landau damping as the template. Override only what your physics requires:
| If you need to change… | Start in… | Typical change |
|---|---|---|
| Problem geometry and BCs | YourManager::pre_run() |
Domain extents, solver parameters, external fields |
| Initial particles | initializeParticles() |
Position/velocity distributions, particle species |
| Time integration | advance() / stepper method |
New integrator |
| Deposition and gather | AlpineManager::par2grid() / grid2par() |
If you need to change the CIC interpolation |
| Output | dump() and helpers |
Diagnostics and output |