Simulation classes

class lambdapic.simulation.Simulation(nx: int, ny: int, dx: float, dy: float, npatch_x: int = 0, npatch_y: int = 0, nsteps: int | None = None, sim_time: float | None = None, dt_cfl: float = 0.95, n_guard: int = 3, boundary_conditions: ~typing.Dict[~typing.Literal['xmin', 'xmax', 'ymin', 'ymax', 'zmin', 'zmax'], ~typing.Literal['pml', 'periodic']] = <factory>, cpml_thickness: int = 6, log_file: str | None = None, truncate_log: bool = True, random_seed: int | None = None, comm: ~mpi4py.MPI.Comm | None = None)[source]

Main simulation class for 2D Particle-In-Cell (PIC) simulations.

Parameters:
  • nx (int) – Number of grid cells in x direction. Must be divisible by npatch_x.

  • ny (int) – Number of grid cells in y direction. Must be divisible by npatch_y.

  • dx (float) – Grid cell size in x direction (meters).

  • dy (float) – Grid cell size in y direction (meters).

  • npatch_x (int) – Number of patches to divide the domain into along x direction. Default 0 for auto patch number.

  • npatch_y (int) – Number of patches to divide the domain into along y direction. Default 0 for auto patch number.

  • nsteps (int, optional) – Number of simulation steps. Mutually exclusive with sim_time.

  • sim_time (float, optional) – Total simulation time in seconds. Mutually exclusive with nsteps.

  • dt_cfl (float, optional) – CFL (Courant-Friedrichs-Lewy) stability factor. Must be ≤ 1.0. The actual time step is calculated as dt = dt_cfl / (c * sqrt(1/dx² + 1/dy²)). Defaults to 0.95.

  • n_guard (int, optional) – Number of guard cells used for field synchronization between patches. Defaults to 3.

  • boundary_conditions (Dict[Literal['xmin', 'xmax', 'ymin', 'ymax'], Literal['pml', 'periodic']], optional) – Dictionary mapping boundary names to their conditions. Supported boundaries: ‘xmin’, ‘xmax’, ‘ymin’, ‘ymax’. Supported conditions: ‘pml’ (Perfectly Matched Layer) or ‘periodic’. Defaults to all boundaries set to ‘pml’.

  • cpml_thickness (int, optional) – Thickness of CPML (Convolutional PML) absorbing boundary layers in grid cells. Defaults to 6.

  • log_file (str, optional) – Path to log file. If None, generates timestamp-based filename. Defaults to None.

  • truncate_log (bool, optional) – Whether to truncate existing log file or append to it. Defaults to True.

  • random_seed (int, optional) – Random seed for reproducible particle initialization (default: None)

  • comm (mpi4py.MPI.Comm, optional) – MPI communicator. If None, uses MPI.COMM_WORLD. Defaults to None.

add_collision(collision_groups: Sequence[Sequence[Species]])[source]

Register particle collision groups for the simulation.

Parameters:

collision_groups – A sequence of groups, where each group is a sequence of Species. All unique pairs within a group (including intra-species) are considered for collisions.

Example

  • [[e1, e1, e2, e3], [ion, ion]] will perform e1<->e1, e1<->e2, e1<->e3, e2<->e3 collisions, and ion<->ion collisions.

  • [[e1, e1], [e1, ion], [e2, ion], [ion, ion]]. This is manually specifying all collision pairs.

Notes

  • Species in the groups must already be added to the simulation via add_species().

  • If called after the simulation has already been initialized, this will immediately construct the Collision object.

add_species(species: Sequence[Species])[source]

Add particle species to the simulation.

Parameters:

species – One or more species to add to the simulation

Note

  • Automatically ensures unique species names by renaming: electron -> electron.1

  • Assigns ispec indices to each species

  • Species must be added before initialization

create_patches() Patches[source]

Create and initialize all patches for the simulation domain.

Returns:

Collection of all patches with initialized neighbor relationships

Note

  • Creates a 2D grid of patches based on npatch_x and npatch_y

  • Initializes neighbor indices for patch communication

  • Only called on rank 0 during initialization

generate_lists()[source]

Generate particle lists for all modules.

Creates particle lists needed by: - Pushers - Radiation modules - Field interpolator - Current depositor

initialize()[source]

Initialize the simulation components.

This method: 1. Creates and distributes patches across MPI ranks 2. Initializes fields, boundaries, and MPI communication 3. Adds species and particles 4. Sets up solvers, interpolators, and pushers 5. Configures QED modules if needed

Note

Must be called before running the simulation. Performs collective MPI operations and should be called on all ranks.

maxwell_stage()[source]

Perform a single Maxwell solver stage (half time step).

Updates electromagnetic fields using the FDTD method: 1. Updates E field by 0.5*dt 2. Synchronizes E field across patches 3. Updates B field by 0.5*dt 4. Synchronizes B field across patches

run(nsteps: int | None = None, sim_time: float | None = None, callbacks: ~typing.Sequence[~typing.Callable[[~lambdapic.simulation.Simulation], None]] | None = None, stop_callback: ~typing.Callable[[...], bool] = <function Simulation.<lambda>>)[source]

Run the simulation for a specified number of steps or time duration.

Parameters:
  • nsteps – Number of time steps to run (mutually exclusive with sim_time)

  • sim_time – Total simulation time in seconds (mutually exclusive with nsteps)

  • callbacks – Callbacks to execute at different simulation stages

Note

The main simulation loop performs: 1. Field updates (Maxwell solver) 2. Particle pushing (position and momentum) 3. Current deposition 4. QED processes (radiation and pair production) 5. Particle synchronization between patches 6. Callback execution at defined stages

property time: float

Get the current simulation time in seconds.

Returns:

Current simulation time (itime * dt)

update_lists()[source]

Update particle lists after particle creation/destruction.

Updates particle lists for all modules when particles have been: - Created (e.g., through QED processes) - Destroyed - Moved between patches

Also resets the ‘extended’ flag for all particles.

class lambdapic.simulation.Simulation3D(nx: int, ny: int, nz: int, dx: float, dy: float, dz: float, npatch_x: int = 0, npatch_y: int = 0, npatch_z: int = 0, nsteps: int | None = None, sim_time: float | None = None, dt_cfl: float = 0.95, n_guard: int = 3, boundary_conditions: ~typing.Dict[~typing.Literal['xmin', 'xmax', 'ymin', 'ymax', 'zmin', 'zmax'], ~typing.Literal['pml', 'periodic']] = <factory>, cpml_thickness: int = 6, log_file: str | None = None, truncate_log: bool = True, random_seed: int | None = None, comm: ~mpi4py.MPI.Comm | None = None)[source]

Bases: Simulation

Main class for 3D PIC simulation.

Parameters:
  • nx (int) – Number of grid cells in x direction. Must be divisible by npatch_x.

  • ny (int) – Number of grid cells in y direction. Must be divisible by npatch_y.

  • nz (int) – Number of grid cells in z direction. Must be divisible by npatch_z.

  • dx (float) – Grid cell size in x direction (meters).

  • dy (float) – Grid cell size in y direction (meters).

  • dz (float) – Grid cell size in z direction (meters).

  • npatch_x (int) – Number of patches to divide the domain into along x direction. Default 0 for auto patch number.

  • npatch_y (int) – Number of patches to divide the domain into along y direction. Default 0 for auto patch number.

  • npatch_z (int) – Number of patches to divide the domain into along z direction. Default 0 for auto patch number.

  • nsteps (int, optional) – Number of simulation steps. Mutually exclusive with sim_time.

  • sim_time (float, optional) – Total simulation time in seconds. Mutually exclusive with nsteps.

  • dt_cfl (float, optional) – CFL (Courant-Friedrichs-Lewy) stability factor. Must be ≤ 1.0. The actual time step is calculated as dt = dt_cfl / (c * sqrt(1/dx² + 1/dy² + 1/dz²)). Defaults to 0.95.

  • n_guard (int, optional) – Number of guard cells used for field synchronization between patches. Defaults to 3.

  • boundary_conditions (Dict[Literal['xmin', 'xmax', 'ymin', 'ymax', 'zmin', 'zmax'], Literal['pml', 'periodic']], optional) – Dictionary mapping boundary names to their conditions. Supported boundaries: ‘xmin’, ‘xmax’, ‘ymin’, ‘ymax’, ‘zmin’, ‘zmax’. Supported conditions: ‘pml’ (Perfectly Matched Layer) or ‘periodic’. Defaults to all boundaries set to ‘pml’.

  • cpml_thickness (int, optional) – Thickness of CPML (Convolutional PML) absorbing boundary layers in grid cells. Defaults to 6.

  • log_file (Optional[str], optional) – Path to log file. If None, generates timestamp-based filename. Defaults to None.

  • truncate_log (bool, optional) – Whether to truncate existing log file or append to it. Defaults to True.

  • random_seed (int, optional) – Random seed for reproducible particle initialization (default: None)

  • comm (Optional[mpi4py.MPI.Comm], optional) – MPI communicator. If None, uses MPI.COMM_WORLD. Defaults to None.

create_patches()[source]

Create and initialize all 3D patches for the simulation domain.

Returns:

Collection of all 3D patches with initialized neighbor relationships

Note

  • Creates a 3D grid of patches based on npatch_x, npatch_y, and npatch_z

  • Initializes neighbor indices for patch communication

  • Only called on rank 0 during initialization

class lambdapic.simulation.SimulationCallbacks(callbacks: Sequence[Callable[[Simulation], None]], simulation: Simulation)[source]

Manages the execution of callbacks at different simulation stages.

Initialize the callback manager.

Parameters:
  • callbacks – List of callback objects

  • simulation – The simulation instance to pass to callbacks

non_empty_stages()[source]

Get stages that have registered callbacks.

Returns:

List of simulation stages that have at least one callback registered

Note

Useful for checking which stages will trigger callback execution.

run(stage: str)[source]

Execute all callbacks registered for a given simulation stage.

Parameters:

stage – The simulation stage to run callbacks for (e.g., ‘start’, ‘maxwell_1’)

Note

Calls each callback function in sequence, passing the simulation instance.

Integrated Features

The Simulation class includes several features that are activated automatically during run():

Progress Bar

When calling run(), a ProgressBar is created automatically. The progress bar detects whether the output is a terminal or a log file and adapts its display accordingly: in terminals it uses tqdm for an interactive progress bar, and in non-terminal environments it emits structured log messages at regular intervals.

Dynamic Load Balancing

During run(), a LoadBalancer monitors the computational load across MPI ranks. When the load imbalance exceeds a configurable threshold, the load balancer triggers automatic patch rebalancing to redistribute work evenly. After rebalancing, update_patches() is called automatically to regenerate field and particle lists for all simulation modules.

The load of each patch is calculated as:

load = npart + (nx * ny [* nz]) / 2

where npart is the number of alive particles and the grid term accounts for field computation cost. This calculation is performed internally by _calculate_patch_loads().