Python Interface =========================== This document lists all Python-exposed functions from the `pipic` module with their arguments and descriptions. (See `src/pipic.h <../../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:** .. code-block:: python 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:** .. code-block:: python 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:** .. code-block:: python 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):** .. code-block:: python @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 (use ``pipic.consts.electron_charge`` for electrons). - ``mass`` (float): Particle mass in grams (use ``pipic.consts.electron_mass`` for electrons). - ``temperature`` (float): Initial thermal energy in erg, defined as :math:`\\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:** .. code-block:: python 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)** .. code-block:: python 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:** .. code-block:: python 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:** .. code-block:: python 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``: .. code-block:: python 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 profiles - ``types.particle_loop_callback``: For particle iteration - ``types.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:** .. code-block:: python 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:** .. code-block:: python 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 to ``pipic_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:** .. code-block:: python 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)