A Particle-in-Cell Push-and-Weight Step
Advance one macro-particle with the Boris rotation, deposit its charge to the grid, and see the core loop of a PIC plasma code.
Problem
Particle-in-cell (PIC) codes track many macro-particles moving in fields defined on a grid. Each step interpolates fields to particle positions, pushes velocities and positions, then weights (deposits) charge and current back to the grid to update the fields. We work one push-and-weight cycle.
Boris push
The Boris algorithm advances velocity in a magnetic field by a half electric kick, a magnetic rotation, and a second half kick. It conserves energy in a static B field exactly, which naive integrators do not, making it the standard PIC pusher.
import numpy as np
q,m,dt=1.0,1.0,0.1
x=np.array([0.3]); v=np.array([1.0,0.5,0.0])
E=np.array([0.1,0.0,0.0]); B=np.array([0.0,0.0,1.0])
vm=v+ (q*E/m)*(dt/2) # half E kick
t=(q*B/m)*(dt/2); s=2*t/(1+t@t)
vp=vm+np.cross(vm+np.cross(vm,t),s) # rotation
v=vp+(q*E/m)*(dt/2) # half E kick
x=x+v[0]*dt
# linear weighting to two nearest grid nodes (grid spacing 1)
i=int(np.floor(x)); f=x-i
rho={i:q*(1-f)[0], i+1:q*f[0]}
print('new v',np.round(v,3),'charge to nodes',{k:round(val,3) for k,val in rho.items()})
Result
The magnetic term rotates the velocity vector without changing its magnitude, while the electric kicks add energy. Linear weighting splits the particle's charge between the two nearest grid nodes in proportion to distance, so a particle at fraction f from node i deposits (1-f) to node i and f to node i+1. Summing over all particles gives the charge density that feeds the field solve.
- Consistent weighting for deposition and field interpolation is required to avoid self-forces on particles.
- The time step must resolve the cyclotron and plasma periods for stability and accuracy.
- Kronos uses PIC and gyrokinetic tools to study edge and scrape-off-layer behavior in the Hyperion spherical tokamak design.