Interpolations
The interpolation routines in JustPIC target rectangular 2D and 3D grids and support both uniform and coordinate-vector refined grids.
The two standard transfer paths are documented separately:
Vertex interpolation: vertex → particle → vertex.
Centroid interpolation: centroid → particle → centroid.
Both pages show ghosted and physical-only array layouts.
Grid to particle
Information is transferred from nodal or staggered grids to particle-carried fields with linear interpolation. The one-dimensional linear interpolation kernel is
where the

Numerically, it is more appropriately implemented as a double fma as it is slightly more accurate than a naive implementation:
v_p = fma(t, v1, fma(-t, v0, v0))Bi- and tri-linear interpolation over a rectangular or cubic cells is thus nothing else than a chain of lerp kernels along the different dimensions of the cell. For example, the bilinear interpolation requires two lerps along the left and right sides of the cell, followed by a lerp on the horizontal direction; and trilinear interpolation requires two bilinear kernels and one lerp.
N-linear interpolation is implemented recursively to keep the code dimension agnostic while staying friendly to compiler specialization.
We can interpolate an arbitrary field F onto particles with grid2particle!:
using JustPIC
# define model domain
nxcell, max_xcell, min_xcell = 24, 30, 12
n = 9
Lx = Ly = 1.0
xvi = xv, yv = range(0, Lx, length=n), range(0, Ly, length=n)
dx, dy = step(xv), step(yv)
xc = range(dx / 2, Lx - dx / 2, length=n - 1)
yc = range(dy / 2, Ly - dy / 2, length=n - 1)
grid_vx = xv, range(first(yc) - dy, step=dy, length=n + 1)
grid_vy = range(first(xc) - dx, step=dx, length=n + 1), yv
particles = init_particles(
JustPIC.CPU, nxcell, max_xcell, min_xcell, grid_vx, grid_vy
)
# field F at the grid
F = [y for x in particles.xvi[1], y in particles.xvi[2]]
# instantiate empty `CellArray`
Fp, = init_cell_arrays(particles, Val(1));
# interpolate F onto Fp
grid2particle!(Fp, F, particles);
# F is linear in y, so each active particle recovers its own y coordinate
active = particles.index.data
all(Fp.data[active] .≈ particles.coords[2].data[active])
# output
trueparticles.xvi includes one ghost node on each side, so the compact call above expects F to use that same layout. For a field stored only on physical nodes, disable the shift in each unpadded direction:
F_physical = [y for x in xv, y in yv]
grid2particle!(Fp, F_physical, particles; ghost_1=false, ghost_2=false)Particle to grid
Information on particles can be accumulated back to grid nodes with inverse distance weighting:
where the weight is
On shared-memory hardware this typically requires atomics. JustPIC avoids that by looping over grid nodes and scanning only the neighboring particle cells that can contribute to each node.
This interpolation is handled by particle2grid!:
julia> particle2grid!(F, Fp, particles)The same ghost_1, ghost_2, and ghost_3 keywords select whether each destination direction includes particle ghost nodes. They default to true.
particle2centroid! and the PIC/FLIP transfers take the same keywords; for the centroid routines they refer to the ghosted centroid grid particles.xci. centroid2particle! accepts them too (and ghosted as a shorthand for all three): with ghost centroids, particles sitting between a domain boundary and the first centroid are interpolated from them; without, those particles use the nearest physical cell.
Related high-level helpers in this workflow are particle2centroid!, centroid2particle!, update_phase_ratios!, subgrid_diffusion!, and subgrid_diffusion_centroid!. The two subgrid-diffusion routines read their T_grid through grid2particle!/centroid2particle! and so expect the ghosted vertex and centroid layouts respectively, while ΔT_grid carries one ghost node per side, matching size(particles.index).
API
JustPIC.grid2particle! Function
grid2particle!(Fp, xvi, F, particles::PassiveMarkers)Interpolate a nodal field F to passive-marker values Fp, updated in place.
The vertex grid xvi must be supplied explicitly, since PassiveMarkers stores only marker coordinates and no grid metadata.
Arguments
Fp: destination marker field, or tuple of marker fields.xvi: vertex coordinates of the grid on whichFis defined.F: source nodal field, or tuple of nodal fields matchingFp.particles:PassiveMarkerscontainer supplying marker coordinates.
JustPIC.particle2grid! Function
particle2grid!(F, Fp, buffer, xi, particles::PassiveMarkers)Interpolate passive-marker values Fp onto the grid nodes F, overwriting F in place.
Because passive markers scatter to arbitrary nodes, weights are accumulated with atomic updates into F and buffer and normalized in a final pass; buffer must be a scratch array with the same size as F. The vertex grid xi is supplied explicitly.
Arguments
F: destination nodal array.Fp: marker field stored with the same layout asparticles.coords.buffer: scratch nodal array (same size asF) used to accumulate weights.xi: vertex coordinates of the target grid.particles:PassiveMarkerscontainer supplying marker coordinates.
JustPIC.centroid2particle! Function
centroid2particle!(Fp, F, particles; ghosted = true)
centroid2particle!(Fp, xci, F, particles, di; ghosted = true)Interpolate cell-centered field values F to particle values Fp.
The destination Fp is mutated in place and may be either a single particle field or a tuple of particle fields.
Arguments
xci,di: optional explicit centroid coordinates and spacing of the grid carryingF, for lower-level/internal use.ghosted: whetherFuses the ghostedparticles.xcilayout. Particles lying between a domain boundary and the first centroid are then interpolated from the ghost centroids.ghost_1,ghost_2,ghost_3: per-direction override ofghosted(centroid2particle!(Fp, F, particles)only). When a direction is not ghosted, particles outside the first/last centroid use the nearest physical cell.
JustPIC.particle2centroid! Function
particle2centroid!(F, Fp, particles::Particles)
particle2centroid!(F, Fp, xci::NTuple, particles::Particles, di)Interpolate particle-centered values Fp to cell centers F.
xci contains the 1D coordinate arrays of the cell centers. This is the cell-centered counterpart to particle2grid! and mutates F in place.
Arguments
F: destination centroid array, or tuple of centroid arrays.Fp: particle field stored with the same cell layout asparticles.particles: theParticlescontainer supplying particle coordinates. Its storedxcicoordinates define the target centroid grid.ghost_1,ghost_2,ghost_3: whetherFincludes ghost nodes in each coordinate direction. Disable a keyword for a physical-only direction.