Skip to content

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:

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 , , and are graphically described below.

Numerically, it is more appropriately implemented as a double fma as it is slightly more accurate than a naive implementation:

julia
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!:

julia
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

true

particles.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:

julia
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  , with being the distance between the particle and the node, and a integer number.

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
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
julia
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 which F is defined.

  • F: source nodal field, or tuple of nodal fields matching Fp.

  • particles: PassiveMarkers container supplying marker coordinates.

source
JustPIC.particle2grid! Function
julia
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 as particles.coords.

  • buffer: scratch nodal array (same size as F) used to accumulate weights.

  • xi: vertex coordinates of the target grid.

  • particles: PassiveMarkers container supplying marker coordinates.

source
JustPIC.centroid2particle! Function
julia
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 carrying F, for lower-level/internal use.

  • ghosted: whether F uses the ghosted particles.xci layout. 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 of ghosted (centroid2particle!(Fp, F, particles) only). When a direction is not ghosted, particles outside the first/last centroid use the nearest physical cell.

source
JustPIC.particle2centroid! Function
julia
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 as particles.

  • particles: the Particles container supplying particle coordinates. Its stored xci coordinates define the target centroid grid.

  • ghost_1, ghost_2, ghost_3: whether F includes ghost nodes in each coordinate direction. Disable a keyword for a physical-only direction.

source