Python Interface
This document lists all Python-exposed functions from the pipic module with their arguments and descriptions. (See src/pipic.h for C++ implementation.)
Initialization
pipic.init(solver, nx, xmin, xmax, ny=1, ymin=-0.5, ymax=0.5, nz=1, zmin=-0.5, zmax=0.5)
Initialize a PIC simulation container.
Parameters:
solver(str): Solver name. Available options:"fourier_boris","ec","ec2","electrostatic_1d","emc2".nx(int): Number of grid cells in x-direction (must be power of 2 for FFT-based solvers).xmin,xmax(float): Spatial domain boundaries in x-direction (cm).ny(int, optional): Number of grid cells in y-direction. Default: 1 (for 2D/1D).ymin,ymax(float, optional): Spatial domain boundaries in y-direction (cm). Default: -0.5, 0.5.nz(int, optional): Number of grid cells in z-direction. Default: 1 (for 1D).zmin,zmax(float, optional): Spatial domain boundaries in z-direction (cm). Default: -0.5, 0.5.
Returns: Simulation object.
Example:
import pipic
sim = pipic.init(solver='fourier_boris', nx=256, xmin=0, xmax=1e-3)
Read-Only Attributes:
After initialization, the simulation object exposes grid parameters:
sim.nx,sim.ny,sim.nz: Grid cell counts.sim.xmin,sim.xmax,sim.ymin,sim.ymax,sim.zmin,sim.zmax: Domain boundaries.
Advance
sim.advance(time_step, number_of_iterations=1, use_omp=True)
Advance the simulation by one or more time steps.
Parameters:
time_step(float): Duration of each time step in seconds.number_of_iterations(int, optional): Number of time steps to advance. Default: 1.use_omp(bool, optional): Enable OpenMP parallelization. Default: True.
Example:
dt = 1e-16 # seconds
sim.advance(time_step=dt, number_of_iterations=100)
Field Callbacks
sim.field_loop(handler, data_double=0, data_int=0, use_omp=False)
Iterate over grid nodes to initialize or read field values.
Parameters:
handler(int): Address of field loop callback.data_double(int, optional): Address of double-precision data array.data_int(int, optional): Address of integer data array.use_omp(bool, optional): Enable OpenMP parallelization. Default: False.
Handler signature:
from numba import cfunc
from pipic import types
@cfunc(types.field_loop_callback)
def field_handler(ind, r, E, B, data_double, data_int):
# ind: grid indices [ix, iy, iz, component_code]
# r: position [x, y, z]
# E: electric field [Ex, Ey, Ez] - can be modified
# B: magnetic field [Bx, By, Bz] - can be modified
Example (initialize electric field):
@cfunc(types.field_loop_callback)
def set_field(ind, r, E, B, data_double, data_int):
E[0] = 1e4 * np.sin(2*np.pi*r[0]/L)
sim.field_loop(handler=set_field.address)
Particle Callbacks
sim.add_particles(name, number, charge, mass, temperature, density, data_double=0, data_int=0)
Add particles to the simulation.
Parameters:
name(str): Particle species identifier (e.g.,"electron","positron","photon").number(int): Total number of macroparticles to add.charge(float): Particle charge in esu (usepipic.consts.electron_chargefor electrons).mass(float): Particle mass in grams (usepipic.consts.electron_massfor electrons).temperature(float): Initial thermal energy in erg, defined as \(\\frac{2}{3}\\langle E_k \\rangle\).density(int): Address of density callback function (see below).data_double(int, optional): Address of double-precision data array. Default: 0.data_int(int, optional): Address of integer data array. Default: 0.
Example:
from numba import cfunc
from pipic import types
@cfunc(types.add_particles_callback)
def uniform_density(r, data_double, data_int):
return 1e18 # cm⁻³
sim.add_particles(name='electron',
number=10000,
charge=pipic.consts.electron_charge,
mass=pipic.consts.electron_mass,
temperature=1e-6 * pipic.consts.electron_mass * pipic.consts.light_velocity**2,
density=uniform_density.address)
Density Callback (for add_particles)
from numba import cfunc
from pipic import types
@cfunc(types.add_particles_callback)
def density_profile(r, data_double, data_int):
# r[0] = x, r[1] = y, r[2] = z
return density_value # particles per cm³
sim.particle_loop(name, handler, data_double=0, data_int=0)
Iterate over particles of a specific type (single-threaded).
Parameters:
name(str): Particle species to loop over.handler(int): Address of particle loop callback.data_double(int, optional): Address of double-precision data array.data_int(int, optional): Address of integer data array.
Handler signature:
from numba import cfunc, carray
from pipic import types
@cfunc(types.particle_loop_callback)
def particle_handler(r, p, w, id, data_double, data_int):
# r: position array [x, y, z]
# p: momentum array [px, py, pz]
# w: weight array [weight]
# id: particle ID array [id]
# Read but DO NOT modify r, p, w, id directly
# Use data arrays for output
Add Handler
sim.add_handler(name, subject="", handler=0, field_handler=0, data_double=0, data_int=0)
Register a custom extension handler.
Parameters:
name(str): Handler identifier.subject(str, optional): Target particle types (e.g.,"electron, positron"),"all_types", or"cells". Default: empty.handler(int, optional): Address of particle/cell handler callback. Default: 0.field_handler(int, optional): Address of field handler callback. Default: 0.data_double(int, optional): Address of double-precision shared data.data_int(int, optional): Address of integer shared data.
Example:
import pipic.extensions.qed_gonoskov2015 as qed
handler_ptr = qed.handler()
sim.add_handler(name='qed_gonoskov2015',
subject='electron, positron',
handler=handler_ptr)
Constants and Utilities
Constants Module
Access physical constants via pipic.consts:
from pipic import consts
consts.light_velocity # c = 2.998×10¹⁰ cm/s
consts.electron_charge # e = -4.803×10⁻¹⁰ esu
consts.electron_mass # mₑ = 9.109×10⁻²⁸ g
consts.proton_mass # mₚ = 1.673×10⁻²⁴ g
consts.pi # π = 3.14159...
consts.hbar # ℏ = 1.055×10⁻²⁷ erg·s
Callback Types
Callback type signatures via pipic.types:
types.add_particles_callback: For density profilestypes.particle_loop_callback: For particle iterationtypes.field_loop_callback: For field iteration
Utilities
pipic.addressof(array)
Get memory address of NumPy array for passing to callbacks.
Parameters:
array: NumPy array
Returns: int (memory address)
Example:
import numpy as np
data = np.zeros(100, dtype=np.double)
sim.field_loop(handler=callback.address,
data_double=pipic.addressof(data))
Advanced Usage
Particle Queries
sim.get_number_of_particles()
Return total number of macroparticles across all species.
Returns: int
sim.get_type_index(type_name)
Get internal index for a particle type.
Parameters:
type_name(str): Particle species name.
Returns: int (type index)
Custom Field Sampling
sim.custom_field_loop(number_of_iterations, it2r, field2data, data_double=0, data_int=0)
Sample fields at arbitrary coordinates.
Parameters:
number_of_iterations(int): Number of sample points.it2r(int): Address of coordinate callback.field2data(int): Address of field-to-data callback.data_double(int, optional): Address of double-precision data array.data_int(int, optional): Address of integer data array.
Solver-Specific Settings
sim.fourier_solver_settings(divergence_cleaning=-1, sin2_kfilter=-1)
Configure Fourier-based solver options (only for fourier_boris and related solvers).
Parameters:
divergence_cleaning(int, optional): Enable (1) or disable (0) divergence cleaning. Default: -1 (no change).sin2_kfilter(int, optional): Enable (1) or disable (0) high-frequency filter. Default: -1 (no change).
Example:
sim.fourier_solver_settings(divergence_cleaning=1, sin2_kfilter=0)
sim.en_corr_type(correction_type=2)
Set energy correction type for EC/EC2 solvers.
Parameters:
correction_type(int, optional): Correction algorithm variant. Default: 2.
Note: Only applicable to ec and ec2 solvers.
Logging and Diagnostics
sim.log_policy(log_to_file=True, log_to_screen=False)
Control logging output destinations.
Parameters:
log_to_file(bool, optional): Write log messages topipic_log.txt. Default: True.log_to_screen(bool, optional): Print log messages to console. Default: False.
sim.set_rng_seed(seed)
Set random number generator seed for reproducibility.
Parameters:
seed(int): RNG seed value.
Example:
sim.set_rng_seed(42) # Deterministic runs
Advanced C++ Integration
sim.ensemble_data()
Return pointer to internal ensemble data structure (for advanced C++ integration).
Returns: int (memory address)
sim.simulation_box()
Return pointer to simulation box structure (for advanced C++ integration).
Returns: int (memory address)