Skip to content

Internal ​

using TestParticle imports the tracing setup and execution APIs, end-to-end VDF tracers, main result accessors, and plotting entry points. Diagnostics, conversions, sampling and distribution APIs, interpolation policies, and storage types are public but not exported. Access them through the module or an explicit import:

julia
using TestParticle
using TestParticle: get_energy, Maxwellian, ClampExtrap

# Alternatively, use TestParticle.get_energy, TestParticle.Maxwellian, etc.

Both exported and public-only names are supported API. If an unqualified name from an earlier release is no longer imported, qualify it with TestParticle. or add an explicit import; its implementation remains available.

Public APIs ​

TestParticle.AdaptiveHybrid Type
julia
AdaptiveHybrid{T, A}

Hybrid algorithm that switches between adaptive Boris (FO) and a guiding-center ODE solver gc_alg (defaulting to Vern7() when OrdinaryDiffEqVerner is loaded), used as solve(TraceProblem(u0, tspan, p), alg) with the full-orbit state u0.

Mode switching uses hysteresis with two adiabaticity thresholds to avoid rapid GC ↔ FO chattering near a single bound:

  • threshold_gc_to_fo (α): switch GC → FO when ε ≥ α.

  • threshold_fo_to_gc (β): switch FO → GC when ε < β.

With α ≥ β the band β ≤ ε < α forms a buffer where the current mode is retained, so a particle hovering near the boundary does not oscillate.

The adiabaticity parameter ε compared against the thresholds is selected by the adiabaticity field, which may be:

  • :curvature (default) → ε_curv = ρ_L / R_c (curvature drift; reproduces the legacy get_adiabaticity behavior exactly),

  • :gradB → ε_gradB = ρ_L / L_B, with L_B = |B| / |∇B|,

  • :both → OR of the two criteria: the solver switches to the full orbit whenever either ε_curv ≥ α or ε_gradB ≥ α (equivalently max(ε_curv, ε_gradB) ≥ α),

  • :jacobian → ε_jac = ρ_L · ‖JB‖_F / |B|, CHIMP's original all-in-one criterion (Bates & Fuentes, 2020), where JB is the Jacobian of B and ‖JB‖_F its Frobenius norm. It captures curvature, grad-B, torsion and shear at once, so it switches to full orbit in non-adiabatic regions that :curvature/:gradB miss (e.g. a field whose direction rotates in space while keeping constant magnitude).

Both modes adapt their step size to the local field. The full-orbit mode uses a Boris step dt = 2π * safety_fo / (|q/m| * |B|) clipped to [dtmin, dtmax] and recomputed after every accepted step; when the step changes, the staggered velocity is re-centered on the new step so that the leapfrog scheme stays time-reversible. The guiding-center mode steps with gc_alg, so its steps may span many gyroperiods.

Because a guiding-center step can be long compared with the scale on which ε varies, the adiabaticity check is triggered by whichever comes first: check_interval accepted steps, or check_dt in simulation time since the previous check. A step is also clipped so that it never crosses the next check deadline, which makes check_dt a hard bound on the simulated time a particle can travel between examinations rather than a bound that a long step could overshoot. check_dt defaults to 100 * dtmax: small enough to catch a transition that a long guiding-center step would otherwise jump over, and large enough that the clamp does not turn check_dt into a step-size cap that costs more than the monitoring saves. Lower it where ε changes on a shorter scale.

While the solver is in the full-orbit mode, check_margin_max > 1 lengthens both bounds by up to that factor in proportion to how far ε sits above threshold_fo_to_gc: ε changes by O(ε) over one curvature radius, so an ε that is a factor k above the exit threshold needs of order k such excursions before the mode can flip, and checking in between only costs time. The interval collapses back to its nominal value as ε approaches the threshold, and the guiding-center mode always uses the nominal interval, so a transition into a non-adiabatic region is detected as early as with a uniform interval.

It is off by default (check_margin_max = 1). Stretching reorders the mode history enough to matter: on the magnetic-bottle benchmark 4 makes the run about 40% faster but the RMS position error 50% larger, because the full-orbit excursions become fewer and longer and the full-orbit step is the coarser one. Raise it only where the full-orbit mode is cheap relative to the guiding-center mode.

On the switch to the guiding center, the state is refined by up to switch_correction fixed-point iterations of X = x - ρ(X) instead of the zeroth-order X = x - ρ(x) map, which removes the O(ε) jump that the switch would otherwise introduce. That map is the exact inverse of the reconstruction back to the full orbit, so the round trip through a switch returns the particle where it was. Set switch_correction = 0 for the legacy behavior.

Each check point records only the adiabaticity value used by the selected adiabaticity mode (a single scalar per check) together with the active mode in sol.stats.adiabaticity. This diagnostic is only saved when save_adiabaticity = true (the default); set it to false to skip the buffers and reduce memory/allocation overhead.

source
TestParticle.ErasedField Type
julia
ErasedField{itd}

Type alias for a Field wrapping an ErasedFunction.

source
TestParticle.ErasedFunction Type
julia
ErasedFunction

Type-erased callable for 3D vector fields wrapping arbitrary user functions or interpolators. Holds precompiled FunctionWrapper handles for both Float64 and Float32 inputs (supporting both 3-component and full 6-component state inputs), along with the raw callable for autodiff (ForwardDiff.Dual) and custom types.

source
TestParticle.Field Type
julia
Field(f; time_dependent=is_time_dependent(f))

Wrap f(x) or f(x, t) as a field. Automatic detection prefers the two-argument signature; set time_dependent explicitly for ambiguous or input-type-specific callables.

source
TestParticle.FullOrbit Type
julia
FullOrbit, RelativisticOrbit, Canonical, RelativisticCanonical, GuidingCenter

Model of a TraceProblem: the equations that its state u0 and parameters p follow, and thereby the solvers and diagnostics that apply to it. RelativisticOrbit states hold γv in place of v.

source
TestParticle.GPUGrid Type
julia
GPUGrid{N, C, X, V, A<:AbstractArray{V, N}} <: AbstractFieldInterpolator

Multilinear interpolator over an N-dimensional grid of V values that can be evaluated inside a device kernel. Each axis is a GPUUniformAxis or a GPUNonUniformAxis carrying its boundary condition; queries outside a filling axis return fill. With CartesianCoords{D}, the grid spans the location components D; with SphericalCoords, it spans (r, θ, ϕ) and rotates a vector result back into the Cartesian basis.

source
TestParticle.GPUNonUniformAxis Type
julia
GPUNonUniformAxis{T, V<:AbstractVector{T}, B}

Non-uniformly spaced grid axis of a GPUGrid; a cell is bracketed with a binary search.

source
TestParticle.GPUUniformAxis Type
julia
GPUUniformAxis{T, B}

Uniformly spaced grid axis of a GPUGrid; a cell is located with a multiply.

source
TestParticle.LazyTimeInterpolator Type
julia
LazyTimeInterpolator{T, F, L}

A callable struct for handling time-dependent fields with lazy loading and linear time interpolation.

Fields

  • times::Vector{T}: Sorted vector of time points.

  • loader::L: Function i -> field that loads the field at index i.

  • buffer::Dict{Int, F}: Cache for loaded fields.

  • lock::ReentrantLock: Lock for thread safety.

source
TestParticle.PotentialField Type
julia
PotentialField{TP, TA, TGP, TGA}

A representation of scalar potential phi and vector potential A, along with optional spatial gradient functions grad_phi and grad_A.

source
TestParticle.Species Type

Type for the particles: Proton, Electron.

source
TestParticle.TraceCanonicalProblem Type
julia
TraceCanonicalProblem(u0, tspan, p; prob_func)

TraceProblem in canonical coordinates: u0 = [x..., p...] and p = (; q, m, c, pf) from prepare_canonical. The relativistic variant is TraceProblem{RelativisticCanonical}.

source
TestParticle.TraceGCProblem Type
julia
TraceGCProblem(u0, tspan, p; prob_func)

TraceProblem of a guiding center: u0 = [X..., vpar] and p = (; q, q2m, μ, E, B) from prepare_gc.

source
TestParticle.TraceProblem Method
julia
TraceProblem(u0, tspan, p; prob_func = DEFAULT_PROB_FUNC)
TraceProblem(f, u0, tspan, p; prob_func = DEFAULT_PROB_FUNC)
TraceProblem{M}(...)

Problem specification for particle tracing in the model M: FullOrbit (the default), RelativisticOrbit, Canonical, RelativisticCanonical or GuidingCenter, also written TraceCanonicalProblem and TraceGCProblem. Supports the native solvers of each model and standard ODE solvers (e.g. Tsit5, Vern7). RelativisticOrbit states hold γv in place of v. Boris and hybrid solvers accept only the default FullOrbit equations without F.

When f is not provided, the model's equations are used: in-place for mutable arrays, out-of-place for StaticArrays. The default equations of a model imply it, and TraceProblem{M} throws on those of another. Custom f keeps M.

Normalized units need no separate equations: prepare p with q = 1, m = 1, and c = 1 for the relativistic case.

source
TestParticle.BiKappa Function
julia
 BiKappa(args...; kw...)

Construct a BiKappa distribution. Forwards to VelocityDistributionFunctions.BiKappa.

julia
 BiKappa(B, u0, ppar, pperp, n, kappa; m=mᵢ)

Construct a BiKappa distribution with magnetic field B, bulk velocity u0, parallel thermal pressure ppar, perpendicular thermal pressure pperp, number density n, and spectral index kappa in SI units. The default particle is proton.

source
TestParticle.BiMaxwellian Function
julia
 BiMaxwellian(args...; kw...)

Construct a BiMaxwellian distribution. Forwards to VelocityDistributionFunctions.BiMaxwellian.

julia
 BiMaxwellian(B, u0, ppar, pperp, n; m=mᵢ)

Construct a BiMaxwellian distribution with magnetic field B, bulk velocity u0, parallel thermal pressure ppar, perpendicular thermal pressure pperp, and number density n in SI units. The default particle is proton.

source
TestParticle.Kappa Function
julia
 Kappa(args...; kw...)

Construct a Kappa distribution. Forwards to VelocityDistributionFunctions.Kappa.

julia
 Kappa(u0, p, n, kappa; m=mᵢ)

Construct a Kappa distribution with bulk velocity u0, thermal pressure p, number density n, and spectral index kappa in SI units. The default particle is proton.

source
TestParticle.Maxwellian Function
julia
 Maxwellian(args...; kw...)

Construct a Maxwellian distribution. Forwards to VelocityDistributionFunctions.Maxwellian.

julia
 Maxwellian(u0, p, n; m=mᵢ)

Construct a Maxwellian distribution with bulk velocity u0, thermal pressure p, and number density n in SI units. The default particle is proton.

source
TestParticle.TerminateOutside Method
julia
TerminateOutside(isoutofdomain)

Create a DiscreteCallback that terminates the simulation if isoutofdomain(u, p, t) is true. The condition follows the same (u, p, t) convention as the isoutside keyword, and is adapted here to the (u, t, integrator) signature that SciML callbacks use.

source
TestParticle.adiabaticity_components Function
julia
adiabaticity_components(x, Bfunc, q, m, μ, t = 0.0)
adiabaticity_components(x, Bfunc, μ, t = 0.0; species = Proton)

Return the tuple (ε_curv, ε_gradB, ε_jac) of first-order adiabaticity parameters at position x and time t:

  • ε_curv = ρ / R_c (curvature drift; the definition used by get_adiabaticity),

  • ε_gradB = ρ / L_B (grad-B drift; CHIMP's eGC), with L_B = |B| / |∇B| the magnetic-field length scale,

  • ε_jac = ρ · ‖JB‖_F / |B| (CHIMP's all-in-one criterion; JB is the Jacobian of B and ‖JB‖_F its Frobenius norm). Because ‖JB‖_F² = |∇B|² + |B|²|κ|² + |B|²τ² + shear, this folds curvature, grad-B, torsion and shear into one number, so it switches to full orbit in non-adiabatic regions that ε_curv/ε_gradB miss (e.g. a field whose direction rotates in space with constant magnitude). It always satisfies ε_jac ≥ max(ε_curv, ε_gradB).

ρ = √(2 μ m / |B|) / |q| is the gyroradius. When |B| = 0 all three components are Inf.

source
TestParticle.analytic_projection Method
julia
analytic_projection(f, k, vi, vj, vk, dvk) -> Matrix

Analytic 2-D projection f(vᵢ, vⱼ) = ∫ f dvₖ in [s²/km⁵] of a 3-D phase-space density, evaluated on the velocity grids vi and vj in [km/s] while the k-th axis is integrated over vk in [km/s] with the midpoint rule at spacing dvk. The two remaining axes follow from k as i < j: k = 3 projects onto Vx–Vy, k = 2 onto Vx–Vz and k = 1 onto Vy–Vz.

f takes a velocity in [m/s] and returns f in [s³/m⁶].

source
TestParticle.bin_centers Method
julia
bin_centers(edges)

Centers of the 1-D bins delimited by edges, in the same unit as edges.

source
TestParticle.bin_velocity_space Method
julia
bin_velocity_space(vs, weights, edges; vx_source = nothing, average = false)

Accumulate velocity-space samples into a 3-D grid.

vs are sample velocities in [m/s] as returned by get_particle_crossings and weights the weight of each sample. edges are the bin edges in [km/s], shared by all three velocity axes. Samples outside the binned range are discarded, and the returned array carries the unit of weights. Empty bins are zero.

Two estimators are available, and they differ in how the samples that land in a bin are combined:

  • average = false (default) sums the weights, which is the histogram estimator of the bin-averaged f: feeding f·ΔV/dv³ with f in [s³/km⁶] returns f in [s³/km⁶], and feeding the constant n/(N·dv³) turns binned counts into a Monte-Carlo estimate of f. Its error carries the counting noise ∝ 1/√n of the samples per bin.

  • average = true takes the mean of the weights, a ratio estimator of the local f that divides out the counting noise. It is the better choice when the sampling spreads only a handful of samples over each bin, e.g. forward Liouville tracking of a uniformly sampled velocity sphere onto a fine detector grid.

Either way project_vdf turns the result into 2-D projections in [s²/km⁵].

If vx_source is given, each sample is rescaled by |vx_source| / |vx|. A trajectory maps a source velocity-space volume onto a detector volume that differs by exactly that ratio, so the rescale turns a source-referred weight into the detector-referred one. This is what makes a flux-weighted launch ensemble consistent with the crossing flux recorded at the detector.

source
TestParticle.build_interpolator Function
julia
build_interpolator(A, grids::Tuple; bc, order=1, gridtype=CartesianGrid, kwargs...)
build_interpolator([gridtype,] A, grids..., order=1, bc=FillExtrap(NaN); kwargs...)

Build a callable field for prepare. Vector data has shape (3, length.(grids)...) or contains SVector{3} values. Orders are 0, 1, and 3. The tuple API requires bc: one policy for all axes or a tuple of per-axis policies. FillExtrap values must agree across axes.

PeriodicBC(endpoint=:inclusive) repeats the endpoint; :exclusive omits it (uniform-grid period NΔx). WrapExtrap() wraps over the grid span without imposing spline periodicity. FillExtrap(NaN) fills outside; ClampExtrap() extends constantly. Field lookup neither wraps particle state nor terminates trajectories.

gridtype=StructuredGrid uses spherical (r, θ, ϕ) grids and (Br, Bθ, Bϕ) components; queries and outputs are Cartesian. Supply fill/clamp for radius and colatitude and PeriodicBC with period 2π for longitude. Its positional default is (FillExtrap(NaN), FillExtrap(NaN), PeriodicBC(check = false)).

coeffs selects cubic coefficient storage. store=StorePolicy(copy=false) may alias inputs, which must remain unchanged. Device conversion requires order 1 and fill, clamp, wrap, or inclusive periodic policies; exclusive periodic fields are CPU-only.

source
TestParticle.cart2sph Method
julia
cart2sph(x, y, z)

Convert from Cartesian to spherical coordinates vector.

source
TestParticle.cart2sphvec Method
julia
cart2sphvec(vx, vy, vz, x, y, z)

Convert a vector from Cartesian to spherical coordinates at the Cartesian position (x, y, z).

source
TestParticle.embed_vdf Method
julia
embed_vdf(g1, g2, full_centers, M) -> Matrix

Embed a 2-D sub-grid projection M onto a canvas defined by full_centers. g1 and g2 are bin centers along the two axes in [km/s], and full_centers is the target full grid of bin centers.

source
TestParticle.energy2velocity Method

Return velocity magnitude from energy in [eV].

source
TestParticle.full_to_gc Function
julia
full_to_gc(xu, param)

Convert full particle state xu to guiding center state state_gc and magnetic moment μ. Returns (state_gc, μ), where state_gc = [R..., vpar].

source
TestParticle.gc_to_full Function
julia
gc_to_full(state_gc, param, μ, phase=0)

Convert guiding center state state_gc to full particle state xu. Returns xu = [x, y, z, vx, vy, vz].

source
TestParticle.generate_sphere Function
julia
generate_sphere(nθ=64, nϕ=64, radius=1.0)

Generate a sphere with radius and matching grid points nθ and nϕ. Returns a tuple of (x, y, z) matrices.

source
TestParticle.get_adiabaticity Function
julia
get_adiabaticity(r, Bfunc, q, m, μ, t=0.0)
get_adiabaticity(r, Bfunc, μ, t=0.0; species = Proton)

Calculate the adiabaticity parameter ϵ = ρ / Rc at position r and time t. ρ is the gyroradius and Rc is the radius of curvature of the magnetic field.

source
TestParticle.get_adiabaticity Method
julia
get_adiabaticity(sol::AbstractODESolution, t)

Calculate the adiabaticity parameter ϵ from a solution sol at time t. t must be a scalar.

source
TestParticle.get_adiabaticity Method
julia
get_adiabaticity(sol::AbstractODESolution)

Calculate the adiabaticity parameter ϵ from a solution sol at all time steps. The model, parameters q, m and Bfunc are extracted from sol.prob.

source
TestParticle.get_curvature_radius Method
julia
get_curvature_radius(x, t, Bfunc)

Calculate the radius of curvature of the magnetic field at position x and time t. Returns Inf if the field is zero or the field lines are straight.

source
TestParticle.get_dv! Method
julia
get_dv!(dv, v, x, p, t)

In-place solver components for DynamicalODEProblem (velocity).

source
TestParticle.get_dx! Method
julia
get_dx!(dx, v, x, p, t)

In-place solver components for DynamicalODEProblem (location).

source
TestParticle.get_energy Method
julia
get_energy(sol::AbstractODESolution) -> Vector

Kinetic energy per unit charge [eV for |q| = e] at every saved state of sol, from the model and the particle of sol.prob.

source
TestParticle.get_fields Method
julia
get_fields(sol::AbstractODESolution)

Return the electric and magnetic fields at the saved states of sol, in the element type of the state.

source
TestParticle.get_first_crossing Function
julia
get_first_crossing(sol, surface) -> SVector{6}
get_first_crossing(sols, surface) -> Vector{SVector{6}}

State [x, y, z, vx, vy, vz] at the first crossing of surface, linearly interpolated between the two bracketing saved points. Returns a state filled with NaN for trajectories that never reach surface, which is how unreachable velocities are flagged when tracing a detector grid backward to a source. Requires Meshes.jl to be loaded.

source
TestParticle.get_gc Method
julia
get_gc(xu, param; use_vE = false)
get_gc(x, y, z, vx, vy, vz, bx, by, bz, q2m)

Calculate the coordinates of the guiding center according to the phase space coordinates of a particle. Reference: wiki

Nonrelativistic definition:

If use_vE=true, it calculates the guiding center relative to the E × B drift frame:

which is essential for correctly identifying the polarization drift in time-varying fields.

source
TestParticle.get_gc_velocity Method
julia
get_gc_velocity(y, p, t)

Get the guiding center velocity.

source
TestParticle.get_gyrofrequency Function
julia
get_gyrofrequency(B=5e-9; q=qᵢ, m=mᵢ)

Return the gyrofrequency [rad/s].

Arguments

  • B: Magnetic field magnitude [T]. Default is 5 nT.

  • q: Charge [C]. Default is proton charge.

  • m: Mass [kg]. Default is proton mass.

source
TestParticle.get_gyroperiod Function
julia
get_gyroperiod(B=5e-9; q=qᵢ, m=mᵢ)

Return the gyroperiod [s].

Arguments

  • B: Magnetic field magnitude [T]. Default is 5 nT.

  • q: Charge [C]. Default is proton charge.

  • m: Mass [kg]. Default is proton mass.

source
TestParticle.get_gyroradius Method
julia
get_gyroradius(V, B; q=qᵢ, m=mᵢ)

Return the gyroradius [m].

Arguments

  • V: Velocity magnitude [m/s] (usually perpendicular to the magnetic field).

  • B: Magnetic field magnitude [T].

  • q: Charge [C]. Default is proton charge.

  • m: Mass [kg]. Default is proton mass.

source
TestParticle.get_gyroradius Method
julia
get_gyroradius(sol::AbstractODESolution, t)

Return the gyroradius [m] from the solution sol at time t. The interpolated magnetic field function is obtained from sol.prob.p.

source
TestParticle.get_magnetic_properties Method
julia
get_magnetic_properties(x, t, Bfunc)

Calculate magnetic field properties at position x and time t. Returns the NamedTuple (; B, ∇B, κ, b̂, Bmag, JB):

  • B: Magnetic field vector

  • ∇B: Gradient of magnetic field magnitude

  • κ: Curvature vector (b̂ ⋅ ∇) b̂

  • b̂: Unit magnetic field vector

  • Bmag: Magnitude of B

  • JB: Jacobian of B, JB[i, j] = ∂Bᵢ/∂xⱼ

∇B, κ and b̂ are zero in a magnetic null.

source
TestParticle.get_particle_crossings Function
julia
get_particle_crossings(sol, surface, weight = 1.0) -> (velocities, weights)
get_particle_crossings(sols, surface, weights = 1.0) -> (velocities, weights)
get_particle_crossings(sols, surfaces, weights = 1.0) -> (velocities, weights)

Detect every crossing of a virtual detector surface and return the velocity [m/s] at each crossing together with the corresponding weight.

A trajectory can cross the same detector more than once, so a crossing carries the weight of its trajectory rather than of the trajectory set: passing a scalar weight applies it to every trajectory of the ensemble. With a vector of surfaces the two returned vectors hold one vector per surface. Requires Meshes.jl to be loaded.

source
TestParticle.get_particle_flux Function
julia
get_particle_flux(sol, surface, weight = 1.0) -> (number_flux, velocity_flux)
get_particle_flux(sols, surface, weights = 1.0) -> (number_flux, velocity_flux)
get_particle_flux(sols, surfaces, weights = 1.0) -> (number_fluxes, velocity_fluxes)

Number flux density and velocity flux density through surface, or through each of surfaces, obtained by summing the crossing weights of a single trajectory or of an ensemble of trajectories sols and dividing by the surface area. Requires Meshes.jl to be loaded.

source
TestParticle.get_work_channels Method
julia
get_work_channels(xv, p, t; kwargs...) -> SVector{3}

Group the work rates into the three physical acceleration channels (P_parallel, P_fermi, P_betatron) [W].

The mapping between the four leading-order terms of get_work_rates and the channels is:

  • P_parallel = P_par: direct acceleration by E∥;

  • P_fermi = P_fermi: work by E on the curvature drift;

  • P_betatron = P_betatron + P_grad: perpendicular heating under conservation of μ. Grad-B drift work is not a separate mechanism; since (μ/B)(b̂×∇B)⋅E = μ v_E⋅∇B, it is the convective (spatial) part of the betatron channel, while μ ∂B/∂t is its local (temporal) part.

The state and the keywords are as in get_work_rates.

source
TestParticle.get_work_rates Method
julia
get_work_rates(xv, p, t; model = _param_model(p), magnetic_properties = nothing, E = nothing)
    -> SVector{4}

Calculate the leading-order adiabatic work rates [W] (P_par, P_fermi, P_grad, P_betatron) acting on a full-orbit particle or a guiding center:

TermFormulaPhysics
P_parq v∥ (E⋅b̂)direct acceleration by E∥
P_fermi(m v∥²/B) (b̂×κ)⋅Ework by E on the curvature drift
P_grad(μ/B) (b̂×∇B)⋅Econvective part of the betatron channel, μ v_E⋅∇B
P_betatronμ ∂B/∂tlocal part of the betatron channel

Here v_E = (E×b̂)/B is the ExB drift, so P_grad is the perpendicular heating a particle picks up by being convected across ∇B at fixed μ, while P_betatron is the heating from the local change of |B|. Together they are μ (∂_t + v_E⋅∇) B. See get_work_channels for the grouping into physical acceleration channels.

model defaults to GuidingCenter() when p carries a magnetic moment μ and to FullOrbit() otherwise. Full-orbit μ uses the gyration velocity v⊥ - v_E. For RelativisticOrbit(), the state holds γv and the drifts use the mass γm.

magnetic_properties and E are optional precomputed values of get_magnetic_properties and of the electric field at (get_x(xv), t), used to avoid recomputing the field Jacobian when the caller already has them.

For a full-orbit state these are gyro-averaged quantities: they do not reproduce the instantaneous q v⋅E, which oscillates at the gyrofrequency. They are therefore best compared against the energy change accumulated over many gyroperiods.

For the guiding-center equations solved by trace_gc!, the four terms sum to dK/dt with K = ½ m v∥² + μB up to O(ε) corrections. The sum is exact when b̂⋅(∇×b̂) = 0, as in axisymmetric fields without a B_φ component. In this case B*_∥ = |B| and the residual twist term vanishes. Planar field lines or translation invariance alone do not imply this condition. When it holds, any remaining mismatch is numerical (e.g. time-integration or quadrature error).

All four terms are zero in a magnetic null.

Reference: Northrop, T. G. (1963), The Adiabatic Motion of Charged Particles.

source
TestParticle.get_work_rates Method
julia
get_work_rates(sol::AbstractODESolution) -> Vector{SVector{4}}

Work rates at every saved state of sol, in the element type of the state.

source
TestParticle.prepare Method
julia
prepare(pf::PotentialField; species = Proton, q = nothing, m = nothing, c = c, type = nothing)

Return parameters (; q, m, c, pf) for TraceCanonicalProblem.

source
TestParticle.prepare Method
julia
prepare(; E=nothing, B=nothing, F=nothing, species=Proton, q, m, c, type, erased=false)

Build particle parameters from callable electric, magnetic, and external force fields. Omitted fields are zero. Each callable accepts x or (x, t) and returns three components. Construct numerical fields with build_interpolator before calling prepare. See Field to select the calling convention. q and m override species; c sets the relativistic speed of light. type converts particle constants.

source
TestParticle.prepare_canonical Method
julia
prepare_canonical(; phi = nothing, A = nothing, grad_phi = nothing, grad_A = nothing, kwargs...)

PotentialField and prepare in one call, for TraceCanonicalProblem.

source
TestParticle.prepare_erased Method
julia
prepare_erased(; E = nothing, B = nothing, F = nothing, kwargs...)

Tracing parameters with type-erased fields for uniform dispatch and static compilation. Equivalent to prepare(; erased = true, kwargs...).

source
TestParticle.prepare_gc Method
julia
prepare_gc(xv; E = nothing, B = nothing, kw...) -> (state_gc, param_gc)

Initial guiding-center state [X..., vpar] from full-orbit state xv and parameters (; q, q2m, μ, E, B) for tracing it. Omitted fields are zero. xv is [x, y, z, vx, vy, vz]. Field arguments and keywords mirror prepare.

TODO: support external forces

source
TestParticle.project_vdf Method
julia
project_vdf(f3d, dv) -> (f_xy, f_xz, f_yz)

Integrate a 3-D phase-space density f3d in [s³/km⁶] over each velocity axis in turn, returning the projections f(vᵢ, vⱼ) = ∫ f dvₖ in [s²/km⁵]. dv is the bin width in [km/s]. The array axes are ordered (v₁, v₂, v₃), so the projections come out as (v₁, v₂), (v₁, v₃) and (v₂, v₃).

source
TestParticle.refine_vdf_window Method
julia
refine_vdf_window(f, vx, vy, vz, v0, dv; margin = 0.0, relthresh = 1.0e-5,
    bounds = nothing)

Refine a coarse velocity grid down to the spacing dv, keeping only the populated part of f. This is the second pass of the adaptive backward Liouville method: a cheap coarse pass locates the region of velocity space that the detector can actually see, and only that region is retraced on the fine grid.

Returns (vx_new, vy_new, vz_new) in the same unit as the inputs: the smallest box enclosing every grid point where f exceeds relthresh times its maximum, expanded by margin and clamped to bounds. The box bounds are snapped onto v0 + k·dv so that the refined grid shares bin centers with the full grid starting at v0. v0, dv and margin may be scalars or 3-tuples for per-axis values.

bounds is a (lo, hi) pair, or one such pair per axis. It defaults to the extent of the coarse grid, which is the range the coarse pass actually probed; pass the extent of the full display grid instead when the refined result has to live on that grid.

source
TestParticle.relative_l2 Method
julia
relative_l2(rec, ref; thresh = 1.0e-4)

Relative L2 difference ‖rec − ref‖ / ‖ref‖, evaluated over the entries where ref is above thresh times its own maximum and rec is finite. Restricting to the populated cells keeps the metric from being dominated by the empty tails of a reconstruction.

source
TestParticle.sample_maxwellian Function
julia
sample_maxwellian([rng], Tn, m; offset=0.0, u0=SA[0.0, 0.0, 0.0])
sample_maxwellian([rng], Tn, species::String; offset=0.0, u0=SA[0.0, 0.0, 0.0])

Sample a velocity [m/s] from a Maxwellian distribution with temperature Tn [K], mass m [kg], energy offset offset [J], and bulk velocity u0 [m/s]. This function requires VelocityDistributionFunctions.jl to be loaded.

source
TestParticle.sample_unit_sphere Function
julia
sample_unit_sphere(rng::AbstractRNG=default_rng())

Sample a unit vector on a sphere uniformly.

source
TestParticle.sample_velocity_ball Method
julia
sample_velocity_ball([rng], radius; center = SA[0.0, 0.0, 0.0]) -> SVector{3}

Sample a velocity [m/s] uniformly from the ball of radius [m/s] centered on center [m/s].

Uniformity is what makes the sample usable as a quadrature rule in velocity space: each draw stands for the same volume 4πR³ / (3N), which forward Liouville tracking needs in order to convert a sampled f into a contribution to a detector bin.

source
TestParticle.sph2cart Method
julia
sph2cart(r, θ, ϕ)

Convert from spherical to Cartesian coordinates vector.

source
TestParticle.sph2cartvec Method
julia
sph2cartvec(vr, vθ, vϕ, θ, ϕ)

Convert a vector from spherical to Cartesian coordinates. θ and ϕ define the position (polar and azimuthal angles in radians) where the spherical components (vr, vθ, vϕ) are defined.

source
TestParticle.trace! Method
julia
trace!(dy, y, p, t)

ODE equations for charged particle moving in EM field and external force field with in-place form.

source
TestParticle.trace Method
julia
trace(y, p, t)::SVector{6}

ODE equations for charged particle moving in EM field and external force field with out-of-place form.

source
TestParticle.trace_gc! Method
julia
trace_gc!(dy, y, p, t)

Guiding center equations for nonrelativistic charged particle moving in EM field with in-place form. Variable y = (x, y, z, u), where u is the velocity along the magnetic field at (x,y,z).

source
TestParticle.trace_gc_drifts! Method
julia
trace_gc_drifts!(dx, x, p, t)

Equations for tracing the guiding center using analytical drifts, including the grad-B drift, curvature drift, and ExB drift. Parallel velocity is also added. This expression requires the full particle trajectory p.sol.

source
TestParticle.trace_gc_exb! Method
julia
trace_gc_exb!(dx, x, p, t)

Equations for tracing the guiding center using the ExB drift and parallel velocity from a reference trajectory.

source
TestParticle.trace_gc_flr! Method
julia
trace_gc_flr!(dx, x, p, t)

Equations for tracing the guiding center using the ExB drift with FLR corrections and parallel velocity.

source
TestParticle.trace_relativistic! Method
julia
trace_relativistic!(dy, y, p, t)

ODE equations for relativistic charged particle (x, γv) moving in EM field with in-place form. The speed of light is p.c when present, c in SI otherwise.

source
TestParticle.trace_relativistic Method
julia
trace_relativistic(y, p, t) -> SVector{6}

ODE equations for relativistic charged particle (x, γv) moving in EM field with out-of-place form.

source
TestParticle.vdf_backward Method
julia
vdf_backward(sols, source, f_src, dims) -> Array{T, 3}

Phase-space density [s³/km⁶] on a velocity grid of size dims = (nx, ny, nz), obtained by tracing that grid backward in time and evaluating the source distribution on the traced-back states, as prescribed by Liouville's theorem.

Each trajectory of sols is followed back to the surface source with get_first_crossing and f_src is evaluated on the velocity there; grid points whose trajectory never reaches source are left at zero. f_src takes a velocity in [m/s] and returns f in [s³/m⁶].

source
TestParticle.vdf_backward_trace Method
julia
vdf_backward_trace(
    param, detector, source, f_src;
    v_range = 1000.0e3, vy_range = v_range, vz_range = v_range,
    dv = 20.0e3, dt, alg, tspan = (0.0, -20.0),
    adaptive = true, dv_coarse = 3 * dv, margin = 3 * dv,
    relthresh = 1.0e-5, bounds = nothing, isoutside = nothing,
    ensemblealg = EnsembleThreads()
) -> (f_3d, (vx_grid, vy_grid, vz_grid))

Reconstruct the 3-D phase-space density [s³/km⁶] at detector by tracing a velocity grid backward in time to source and evaluating f_src on the traced-back states.

source
TestParticle.vdf_forward_trace Method
julia
vdf_forward_trace(
    param, source, detector, f_src;
    nparticles = 50000, vradius, tspan = (0.0, 20.0), dt, alg,
    center = SA[0.0, 0.0, 0.0], dv_km = 20.0,
    v_edges = -1000.0:dv_km:1000.0,
    isoutside = nothing, ensemblealg = EnsembleThreads(),
    seed = 42
) -> (f_3d, (f_xy, f_xz, f_yz))

Reconstruct the 3-D phase-space density [s³/km⁶] at detector using forward Liouville tracking from a uniformly sampled velocity ball at source.

source
TestParticle.vdf_grid_problem Method
julia
vdf_grid_problem(vx, vy, vz, x0, param, tspan) -> TraceProblem

Build a TraceProblem that traces a velocity-space grid: trajectory i starts at position x0 [m] with the grid velocity (vx[ix], vy[iy], vz[iz]) [m/s], the z index varying fastest. This is the setup of the backward Liouville method, where the grid sits at the detector and tspan runs backward in time.

source
TestParticle.velocity_moments Method
julia
velocity_moments(v, A; dv)
velocity_moments(proj::Tuple; dv)

Density n [m⁻³] and momentum density n·V [m⁻³·km/s] of a 2-D velocity distribution A in [s²/km⁵], sampled on the first-axis bin centers v in [km/s] with bin width dv in [km/s]. Since the momentum is taken along the first axis, pass the Vx–Vy or the Vx–Vz projection to obtain n·Vx.

source

Private types and methods ​

TestParticle.AbstractFieldInterpolator Type
julia
AbstractFieldInterpolator

Abstract type for all field interpolators.

source
TestParticle.FieldInterpolator Type
julia
FieldInterpolator{T, S}

A callable struct that wraps a 3D interpolation object.

source
TestParticle.FieldInterpolator1D Type
julia
FieldInterpolator1D{T, S}

A callable struct that wraps a 1D interpolation object.

source
TestParticle.FieldInterpolator2D Type
julia
FieldInterpolator2D{T, S}

A callable struct that wraps a 2D interpolation object.

source
TestParticle.SphericalFieldInterpolator Type
julia
SphericalFieldInterpolator{T, S}

A callable struct for spherical grid interpolation (scalar or combined vector).

source
TestParticle._adiabaticity_components Method
julia
_adiabaticity_components(mp, q, m, μ)

Same as adiabaticity_components but taking the get_magnetic_properties output mp instead of recomputing it, for callers that already have the field derivatives in hand.

source
TestParticle._foreach_chunk Method
julia
_foreach_chunk(f, ensemblealg, n)

Call f(irange) on chunks of the trajectory indices 1:n, all at once for EnsembleSerial and one chunk per thread for EnsembleThreads.

source
TestParticle._gc_adia_parameters Method
julia
_gc_adia_parameters(xv, E, B, q, m, t) -> (X, μ)

Guiding-center position and magnetic moment of the full-orbit state xv — the two quantities an adiabaticity estimate needs. The gyrophase (get_perp_vector and an atan2) is skipped because the estimate never uses it; the values match those of _get_gc_parameters.

source
TestParticle._gc_frame Method
julia
_gc_frame(x, v, E, B, m, t) -> (; b̂, w, vpar, μ)

Field-aligned frame at position x: unit field b̂, gyration velocity w = v - vpar b̂ - v_E, parallel velocity vpar = b̂ ⋅ v and magnetic moment μ = m|w|² / (2|B|).

source
TestParticle._gc_phase Method

Gyrophase of the gyration velocity w in the frame built from b̂.

source
TestParticle._gc_position Method
julia
_gc_position(x, v, B, q, m, t)

Guiding-center position X = x - ρ(x), with the Larmor radius ρ evaluated at the particle: the zeroth-order map.

source
TestParticle._gc_state_at_switch Method
julia
_gc_state_at_switch(xv, E, B, q, m, t, niter) -> (X, vpar, μ, phase)

Guiding-center state of the full-orbit state xv, refined by up to niter fixed-point iterations of X = x - ρ(X): the Larmor radius is evaluated at the guiding center instead of at the particle, which is the first-order correction that removes the O(ε) error of the zeroth-order map. niter = 0 recovers the zeroth-order map.

This map is the exact inverse of _gc_to_full, so a switch to the guiding center and back returns the particle where it was; the reverse conversion needs no correction of its own.

An iteration is kept only if it reduces the residual ‖x - (X + ρ(X))‖, so a strongly non-adiabatic crossing, where the fixed-point map stops contracting, cannot make the conversion worse than the zeroth-order one.

source
TestParticle._gc_to_full Function
julia
_gc_to_full(state_gc, E_field, B_field, q, m, μ, t, phase = 0)

Internal GC → full-orbit conversion taking the field functions and charges directly, so it can be shared by the GC and hybrid solvers. E_field and B_field must be callable as E_field(x, t) / B_field(x, t); static fields simply ignore t.

Returns an SVector{6} so that it can be used in a hot loop and stored in a Vector{SVector{6}} without allocating; gc_to_full converts it to a Vector for the public API.

source
TestParticle._get_gc_parameters Method
julia
_get_gc_parameters(xv, E, B, q, m, t) -> (X, vpar, q, q / m, μ, phase)

Extract the guiding-center position X, parallel velocity vpar, magnetic moment μ, and gyrophase phase from a full-particle state xv at time t. E and B must be callable as E(x, t) / B(x, t); static fields simply ignore t.

#TODO : support external forces

source
TestParticle._lorentz_factor Method
julia
_lorentz_factor(u, model, p)

Lorentz factor of the velocity part u of a full-orbit state; relativistic states store γv in place of v. Other models have no velocity part, so they have no method.

source
TestParticle._model Method
julia
_model(prob)

Model of prob. A plain ODEProblem has none of its own, so it follows from the equations and the parameters.

source
TestParticle._param_model Method
julia
_param_model(p)

Model that the parameters p are written for: the default where neither a problem nor the equations name the model.

source
TestParticle._prepare_potential_scalar Method
julia
PotentialField(;
    phi = nothing, A = nothing, grad_phi = nothing, grad_A = nothing, erased = false
)

Scalar potential phi(x[, t]) and vector potential A(x[, t]) for canonical tracing; nothing is an absent potential. Gradients that are not given are computed with ForwardDiff. grad_A(x[, t])[i, j] is , so that grad_A * v is   at fixed .

source
TestParticle._rng_seed Method
julia
_rng_seed(seed::Integer, i::Integer)

Combine the ensemble seed and the trajectory index i into a well-mixed UInt64 used to seed a per-trajectory RNG.

A naive seed + i couples the two linearly: incrementing seed merely rotates which RNG each trajectory index draws from, so two nearby seeds (e.g. 1 and 2) produce ensembles that share almost all trajectories and are therefore not independent. The SplitMix64-style finalizer below is a bijection on UInt64, so any distinct seed decorrelates every trajectory while remaining deterministic and reproducible (no reliance on hash, which is salted per Julia session).

Note: Julia's multi-argument Xoshiro(seed, i, …) constructor does not mix — it embeds the integers directly into the 4×UInt64 state (Xoshiro(s0,s1,s2,s3) → initstate!). Because xoshiro256++'s first output depends only on s0 and s3, seeding with Xoshiro(seed, i, 0, 0) yields the same first draw for every trajectory index i and does not decorrelate adjacent seeds. Robust mixing in Xoshiro only happens via the single-argument constructor (SHA2-256 hash_seed), so seed and i must be folded into one well-mixed integer first — which is what this function does.

source
TestParticle._split_xv Method

Split a full-orbit state into position and velocity.

source
TestParticle.get_cell_centers Function
julia
get_cell_centers(grid::Union{CartesianGrid, RectilinearGrid})

Return the cell center coordinates of a Meshes.jl grid, one range per dimension. Requires Meshes.jl to be loaded.

source
TestParticle.get_mean_magnitude Method
julia
get_mean_magnitude(B)

Calculate the Root Mean Square (RMS) magnitude of a vector field B. It is assumed that the first dimension of B represents the vector components. This function is compatible with any spatial dimension.

source
TestParticle.get_perp_vector Method
julia
get_perp_vector(b::AbstractVector)

Obtain two unit vectors e1 and e2 such that (e1, e2, b) form a right-handed orthonormal system.

source
TestParticle.get_velocity Method
julia
get_velocity(sol) -> Matrix

Velocity of a full-orbit sol at every saved state, as a 3 × n matrix.

source
TestParticle.is_time_dependent Method
julia
is_time_dependent(f::Function)

Judge whether the field function is time dependent.

source
TestParticle.makegrid Function
julia
makegrid(grid::Union{CartesianGrid, RectilinearGrid})

Return the coordinate ranges of a Meshes.jl grid, one range per dimension, e.g. (x, y, z) for a 3-D grid. Requires Meshes.jl to be loaded.

source
TestParticle.trace_gc Method
julia
trace_gc(y, p, t)

Guiding center equations for nonrelativistic charged particle moving in EM field with out-of-place form.

source