diff --git a/dev/.documenter-siteinfo.json b/dev/.documenter-siteinfo.json index 7f9540b0..8ac3e0dd 100644 --- a/dev/.documenter-siteinfo.json +++ b/dev/.documenter-siteinfo.json @@ -1 +1 @@ -{"documenter":{"julia_version":"1.10.2","generation_timestamp":"2024-03-19T16:24:42","documenter_version":"1.3.0"}} \ No newline at end of file +{"documenter":{"julia_version":"1.10.2","generation_timestamp":"2024-03-19T16:37:39","documenter_version":"1.3.0"}} \ No newline at end of file diff --git a/dev/api/index.html b/dev/api/index.html index d9d1c4fb..916180d2 100644 --- a/dev/api/index.html +++ b/dev/api/index.html @@ -1,9 +1,9 @@ -API · TestParticle.jl

Internal

Public APIs

TestParticle.BiMaxwellianMethod
BiMaxwellian(B::Vector{U}, u0::Vector{T}, ppar::T, pperp::T, 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.MaxwellianMethod
Maxwellian(u0::Vector{T}, p::T, 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.prepareMethod
prepare(grid::CartesianGrid, E, B; kwargs...) -> (q2m, E, B)

Return a tuple consists of particle charge-mass ratio for a prescribed species and interpolated EM field functions.

keywords

  • order::Int=1: order of interpolation in [1,2,3].

  • bc::Int=1: type of boundary conditions, 1 -> NaN, 2 -> periodic.

  • species::Species=Proton: particle species.

  • q::AbstractFloat=1.0: particle charge. Only works when Species=User.

  • m::AbstractFloat=1.0: particle mass. Only works when Species=User.

    prepare(grid::CartesianGrid, E, B, F; species=Proton, q=1.0, m=1.0) -> (q, m, E, B, F)

Return a tuple consists of particle charge, mass for a prescribed species of charge q and mass m, interpolated EM field functions, and external force F.

prepare(x::AbstractRange, y::AbstractRange, z::AbstractRange, E, B; kwargs...) -> (q2m, E, B)
+API · TestParticle.jl

Internal

Public APIs

TestParticle.BiMaxwellianMethod
BiMaxwellian(B::Vector{U}, u0::Vector{T}, ppar::T, pperp::T, 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.MaxwellianMethod
Maxwellian(u0::Vector{T}, p::T, 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.prepareMethod
prepare(grid::CartesianGrid, E, B; kwargs...) -> (q2m, E, B)

Return a tuple consists of particle charge-mass ratio for a prescribed species and interpolated EM field functions.

keywords

  • order::Int=1: order of interpolation in [1,2,3].

  • bc::Int=1: type of boundary conditions, 1 -> NaN, 2 -> periodic.

  • species::Species=Proton: particle species.

  • q::AbstractFloat=1.0: particle charge. Only works when Species=User.

  • m::AbstractFloat=1.0: particle mass. Only works when Species=User.

    prepare(grid::CartesianGrid, E, B, F; species=Proton, q=1.0, m=1.0) -> (q, m, E, B, F)

Return a tuple consists of particle charge, mass for a prescribed species of charge q and mass m, interpolated EM field functions, and external force F.

prepare(x::AbstractRange, y::AbstractRange, z::AbstractRange, E, B; kwargs...) -> (q2m, E, B)
 prepare(x, y, E, B; kwargs...) -> (q2m, E, B)
-prepare(x::AbstractRange, E, B; kwargs...) -> (q2m, E, B)

Direct range input for uniform grid in 2/3D is also accepted.

prepare(E, B; kwargs...) -> (q2m, E, B)

Return a tuple consists of particle charge-mass ratio for a prescribed species of charge q and mass m and analytic EM field functions. Prescribed species are Electron and Proton; other species can be manually specified with species=Ion/User, q and m.

prepare(E, B, F; kwargs...) -> (q, m, E, B, F)

Return a tuple consists of particle charge, mass for a prescribed species of charge q and mass m, analytic EM field functions, and external force F.

source
TestParticle.sampleMethod
sample(vdf::Maxwellian)

Sample a 3D velocity from a Maxwellian distribution vdf using the Box-Muller method.

sample(vdf::BiMaxwellian)

Sample a 3D velocity from a BiMaxwellian distribution vdf using the Box-Muller method.

source
TestParticle.trace!Method
trace!(dy, y, p::TPTuple, t)
-trace!(dy, y, p::FullTPTuple, t)

ODE equations for charged particle moving in static EM field with in-place form.

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

source
TestParticle.traceMethod
trace(y, p::TPTuple, t) -> SVector{6, Float64}
-trace(y, p::FullTPTuple, t) -> SVector{6, Float64}

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

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

source
TestParticle.trace_normalized!Method
trace_normalized!(dy, y, p::TPNormalizedTuple, t)

Normalized ODE equations for charged particle moving in static EM field with in-place form. If the field is in 2D X-Y plane, periodic boundary should be applied for the field in z via the extrapolation function provided by Interpolations.jl.

source
TestParticle.trace_relativisticMethod
trace_relativistic(y, p::TPTuple, t) -> SVector{6, Float64}

ODE equations for relativistic charged particle moving in static EM field with out-of-place form.

source

Private types and methods

TestParticle.FieldType
Field{itd, F} <: AbstractField{itd}

A representation of a field function f, defined by:

time-independent field

\[\mathbf{F} = F(\mathbf{x}),\]

time-dependent field

\[\mathbf{F} = F(\mathbf{x}, t).\]

Arguments

  • field_function::Function: the function of field.
  • itd::Bool: whether the field function is time dependent.
  • F: the type of field_function.
source
TestParticle.dipole_fieldlineFunction
dipole_fieldline(L, ϕ, nP)

Creates nP points on one field line of the magnetic field from a dipole. In a centered dipole magnetic field model, the path along a given L shell can be described as r = L*cos²λ, where r is the radial distance (in planetary radii) to a point on the line, λ is its co-latitude, and L is the L-shell of interest.

source
TestParticle.getB_CS_harrisMethod
getB_CS_harris(B₀, L)

Return the magnetic field at location r near a current sheet with magnetic strength B₀ and sheet length L.

source
TestParticle.getB_bottleMethod
getB_bottle(x, y, z, distance, a, b, I1, I2) -> Vector{Float}

Get magnetic field from a magnetic bottle. Reference: https://en.wikipedia.org/wiki/Magneticmirror#Magneticbottles

Arguments

  • x,y,z::Float: particle coordinates in [m].
  • distance::Float: distance between solenoids in [m].
  • a::Float: radius of each side coil in [m].
  • b::Float: radius of central coil in [m].
  • I1::Float: current in the solenoid times number of windings in side coils.
  • I2::Float: current in the central solenoid times number of windings in the

central loop.

source
TestParticle.getB_mirrorMethod
getB_mirror(x, y, z, distance, a, I1) -> Vector{Float}

Get magnetic field from a magnetic mirror generated from two coils.

Arguments

  • x,y,z::Float: particle coordinates in [m].
  • distance::Float: distance between solenoids in [m].
  • a::Float: radius of each side coil in [m].
  • I1::Float: current in the solenoid times number of windings in side coils.
source
TestParticle.getB_tokamak_coilMethod
getB_tokamak_coil(x, y, z, a, b, ICoils, IPlasma)

Get the magnetic field from a Tokamak topology consists of 16 coils. Original: https://github.com/BoschSamuel/Simulation-of-a-Tokamak-Fusion-Reactor/blob/master/Simulation2.m

Arguments

  • x,y,z::Float: location in [m].
  • a::Float: radius of each coil in [m].
  • b::Float: radius of central region in [m].
  • ICoil::Float: current in the coil times number of windings.
  • IPlasma::Float: current of the plasma?
source
TestParticle.getB_tokamak_profileMethod
getB_tokamak_profile(x, y, z, q_profile, a, R₀, Bζ0)

Reconstruct the magnetic field distribution from a safe factor(q) profile. The formulations are from the book "Tokamak 4th Edition" by John Wesson.

Arguments

  • x,y,z::Float: location in [m].
  • q_profile::Function: profile of q. The variable of this function must be the normalized radius.
  • a::Float: minor radius [m].
  • R₀::Float: major radius [m].
  • Bζ0::Float: toroidal magnetic field on axis [T].
source
TestParticle.get_gcMethod
get_gc(param::Union{TPTuple, FullTPTuple})

Get three functions for plotting the orbit of guiding center.

For example:

param = prepare(E, B; species=Proton)
+prepare(x::AbstractRange, E, B; kwargs...) -> (q2m, E, B)

Direct range input for uniform grid in 2/3D is also accepted.

prepare(E, B; kwargs...) -> (q2m, E, B)

Return a tuple consists of particle charge-mass ratio for a prescribed species of charge q and mass m and analytic EM field functions. Prescribed species are Electron and Proton; other species can be manually specified with species=Ion/User, q and m.

prepare(E, B, F; kwargs...) -> (q, m, E, B, F)

Return a tuple consists of particle charge, mass for a prescribed species of charge q and mass m, analytic EM field functions, and external force F.

source
TestParticle.sampleMethod
sample(vdf::Maxwellian)

Sample a 3D velocity from a Maxwellian distribution vdf using the Box-Muller method.

sample(vdf::BiMaxwellian)

Sample a 3D velocity from a BiMaxwellian distribution vdf using the Box-Muller method.

source
TestParticle.trace!Method
trace!(dy, y, p::TPTuple, t)
+trace!(dy, y, p::FullTPTuple, t)

ODE equations for charged particle moving in static EM field with in-place form.

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

source
TestParticle.traceMethod
trace(y, p::TPTuple, t) -> SVector{6, Float64}
+trace(y, p::FullTPTuple, t) -> SVector{6, Float64}

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

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

source
TestParticle.trace_normalized!Method
trace_normalized!(dy, y, p::TPNormalizedTuple, t)

Normalized ODE equations for charged particle moving in static EM field with in-place form. If the field is in 2D X-Y plane, periodic boundary should be applied for the field in z via the extrapolation function provided by Interpolations.jl.

source
TestParticle.trace_relativisticMethod
trace_relativistic(y, p::TPTuple, t) -> SVector{6, Float64}

ODE equations for relativistic charged particle moving in static EM field with out-of-place form.

source

Private types and methods

TestParticle.FieldType
Field{itd, F} <: AbstractField{itd}

A representation of a field function f, defined by:

time-independent field

\[\mathbf{F} = F(\mathbf{x}),\]

time-dependent field

\[\mathbf{F} = F(\mathbf{x}, t).\]

Arguments

  • field_function::Function: the function of field.
  • itd::Bool: whether the field function is time dependent.
  • F: the type of field_function.
source
TestParticle.dipole_fieldlineFunction
dipole_fieldline(L, ϕ, nP)

Creates nP points on one field line of the magnetic field from a dipole. In a centered dipole magnetic field model, the path along a given L shell can be described as r = L*cos²λ, where r is the radial distance (in planetary radii) to a point on the line, λ is its co-latitude, and L is the L-shell of interest.

source
TestParticle.getB_CS_harrisMethod
getB_CS_harris(B₀, L)

Return the magnetic field at location r near a current sheet with magnetic strength B₀ and sheet length L.

source
TestParticle.getB_bottleMethod
getB_bottle(x, y, z, distance, a, b, I1, I2) -> Vector{Float}

Get magnetic field from a magnetic bottle. Reference: https://en.wikipedia.org/wiki/Magneticmirror#Magneticbottles

Arguments

  • x,y,z::Float: particle coordinates in [m].
  • distance::Float: distance between solenoids in [m].
  • a::Float: radius of each side coil in [m].
  • b::Float: radius of central coil in [m].
  • I1::Float: current in the solenoid times number of windings in side coils.
  • I2::Float: current in the central solenoid times number of windings in the

central loop.

source
TestParticle.getB_mirrorMethod
getB_mirror(x, y, z, distance, a, I1) -> Vector{Float}

Get magnetic field from a magnetic mirror generated from two coils.

Arguments

  • x,y,z::Float: particle coordinates in [m].
  • distance::Float: distance between solenoids in [m].
  • a::Float: radius of each side coil in [m].
  • I1::Float: current in the solenoid times number of windings in side coils.
source
TestParticle.getB_tokamak_coilMethod
getB_tokamak_coil(x, y, z, a, b, ICoils, IPlasma)

Get the magnetic field from a Tokamak topology consists of 16 coils. Original: https://github.com/BoschSamuel/Simulation-of-a-Tokamak-Fusion-Reactor/blob/master/Simulation2.m

Arguments

  • x,y,z::Float: location in [m].
  • a::Float: radius of each coil in [m].
  • b::Float: radius of central region in [m].
  • ICoil::Float: current in the coil times number of windings.
  • IPlasma::Float: current of the plasma?
source
TestParticle.getB_tokamak_profileMethod
getB_tokamak_profile(x, y, z, q_profile, a, R₀, Bζ0)

Reconstruct the magnetic field distribution from a safe factor(q) profile. The formulations are from the book "Tokamak 4th Edition" by John Wesson.

Arguments

  • x,y,z::Float: location in [m].
  • q_profile::Function: profile of q. The variable of this function must be the normalized radius.
  • a::Float: minor radius [m].
  • R₀::Float: major radius [m].
  • Bζ0::Float: toroidal magnetic field on axis [T].
source
TestParticle.get_gcMethod
get_gc(param::Union{TPTuple, FullTPTuple})

Get three functions for plotting the orbit of guiding center.

For example:

param = prepare(E, B; species=Proton)
 gc = get_gc(params)
 # The definitions of stateinit, tspan, E and B are ignored.
 prob = ODEProblem(trace!, stateinit, tspan, param)
@@ -12,10 +12,10 @@
 f = Figure(fontsize=18)
 ax = Axis3(f[1, 1], aspect = :data)
 gc_plot(x,y,z,vx,vy,vz) = (gc([x,y,z,vx,vy,vz])...,)
-lines!(ax, sol, idxs=(gc_plot, 1, 2, 3, 4, 5, 6))
source
TestParticle.get_rotation_matrixMethod
get_rotation_matrix(axis::AbstractVector, angle::Real) --> SMatrix{3,3}

Create a rotation matrix for rotating a 3D vector around a unit axis by an angle in radians. Reference: Rotation matrix from axis and angle

Example

using LinearAlgebra
 v = [-0.5, 1.0, 1.0]
 v̂ = normalize(v)
 θ = deg2rad(-74)
-R = get_rotation_matrix(v̂, θ)
source
TestParticle.getchargemassMethod
getchargemass(species::Species, q::AbstractFloat, m::AbstractFloat)

Return charge and mass for species. if species = User, input q and m are returned.

source
TestParticle.getinterpFunction
getinterp(A, gridx, gridy, gridz, order::Int=1, bc::Int=1)
-getinterp(A, gridx, gridy, order::Int=1, bc::Int=1)

Return a function for interpolating array A on the grid given by gridx, gridy, and gridz.

Arguments

  • order::Int=1: order of interpolation in [1,2,3].
  • bc::Int=1: type of boundary conditions, 1 -> NaN, 2 -> periodic, 3 -> Flat.
source
TestParticle.guiding_centerMethod
guiding_center(xu, param::Union{TPTuple, FullTPTuple})

Calculate the coordinates of the guiding center according to the phase space coordinates of a particle. Reference: https://en.wikipedia.org/wiki/Guiding_center

A simple definition:

\[\mathbf{X}=\mathbf{x}-m\frac{\mathbf{v}\times\mathbf{B}}{qB}\]

source
TestParticle.set_axes_equalMethod
set_axes_equal(ax)

Set 3D plot axes to equal scale for Matplotlib. Make axes of 3D plot have equal scale so that spheres appear as spheres and cubes as cubes. Required since ax.axis('equal') and ax.set_aspect('equal') don't work on 3D.

source
TestParticle.solveFunction
solve(prob::TraceProblem; trajectories::Int=1,
-   savestepinterval::Int=1, isoutofdomain::Function=ODE_DEFAULT_ISOUTOFDOMAIN)

Trace particles using the Boris method with specified prob.

keywords

  • trajectories::Int: number of trajectories to trace.
  • savestepinterval::Int: saving output interval.
  • isoutofdomain::Function: a function with input of position and velocity vector xv that determines whether to stop tracing.
source
TestParticle.update_velocity!Method
update_velocity!(xv, paramBoris, param, dt)

Update velocity using the Boris method, Birdsall, Plasma Physics via Computer Simulation. Reference: https://apps.dtic.mil/sti/citations/ADA023511

source
+R = get_rotation_matrix(v̂, θ)
source
TestParticle.getchargemassMethod
getchargemass(species::Species, q::AbstractFloat, m::AbstractFloat)

Return charge and mass for species. if species = User, input q and m are returned.

source
TestParticle.getinterpFunction
getinterp(A, gridx, gridy, gridz, order::Int=1, bc::Int=1)
+getinterp(A, gridx, gridy, order::Int=1, bc::Int=1)

Return a function for interpolating array A on the grid given by gridx, gridy, and gridz.

Arguments

  • order::Int=1: order of interpolation in [1,2,3].
  • bc::Int=1: type of boundary conditions, 1 -> NaN, 2 -> periodic, 3 -> Flat.
source
TestParticle.guiding_centerMethod
guiding_center(xu, param::Union{TPTuple, FullTPTuple})

Calculate the coordinates of the guiding center according to the phase space coordinates of a particle. Reference: https://en.wikipedia.org/wiki/Guiding_center

A simple definition:

\[\mathbf{X}=\mathbf{x}-m\frac{\mathbf{v}\times\mathbf{B}}{qB}\]

source
TestParticle.set_axes_equalMethod
set_axes_equal(ax)

Set 3D plot axes to equal scale for Matplotlib. Make axes of 3D plot have equal scale so that spheres appear as spheres and cubes as cubes. Required since ax.axis('equal') and ax.set_aspect('equal') don't work on 3D.

source
TestParticle.solveFunction
solve(prob::TraceProblem; trajectories::Int=1,
+   savestepinterval::Int=1, isoutofdomain::Function=ODE_DEFAULT_ISOUTOFDOMAIN)

Trace particles using the Boris method with specified prob.

keywords

  • trajectories::Int: number of trajectories to trace.
  • savestepinterval::Int: saving output interval.
  • isoutofdomain::Function: a function with input of position and velocity vector xv that determines whether to stop tracing.
source
TestParticle.update_velocity!Method
update_velocity!(xv, paramBoris, param, dt)

Update velocity using the Boris method, Birdsall, Plasma Physics via Computer Simulation. Reference: https://apps.dtic.mil/sti/citations/ADA023511

source
diff --git a/dev/examples/advanced/demo_analytic_magnetosphere/index.html b/dev/examples/advanced/demo_analytic_magnetosphere/index.html index 2efbb334..8dc712f7 100644 --- a/dev/examples/advanced/demo_analytic_magnetosphere/index.html +++ b/dev/examples/advanced/demo_analytic_magnetosphere/index.html @@ -103,4 +103,4 @@ z = range(-18Rₑ, 18Rₑ, length=50) trace_field!(ax, x, y, z, invRE) -


This page was generated using DemoCards.jl and Literate.jl.

+


This page was generated using DemoCards.jl and Literate.jl.

diff --git a/dev/examples/advanced/demo_batsrus_3dstructured/index.html b/dev/examples/advanced/demo_batsrus_3dstructured/index.html index 8decb388..d3ad1d0a 100644 --- a/dev/examples/advanced/demo_batsrus_3dstructured/index.html +++ b/dev/examples/advanced/demo_batsrus_3dstructured/index.html @@ -105,4 +105,4 @@ zlabel("z [Rg]") ax.set_box_aspect([1.17,4,4]) -TestParticle.set_axes_equal(ax)


This page was generated using DemoCards.jl.

+TestParticle.set_axes_equal(ax)


This page was generated using DemoCards.jl.

diff --git a/dev/examples/advanced/demo_boris_outofdomain/index.html b/dev/examples/advanced/demo_boris_outofdomain/index.html index ccc8dbad..0d6ed166 100644 --- a/dev/examples/advanced/demo_boris_outofdomain/index.html +++ b/dev/examples/advanced/demo_boris_outofdomain/index.html @@ -70,4 +70,4 @@ end axislegend(position=:lt, framevisible=false) -


This page was generated using DemoCards.jl and Literate.jl.

+


This page was generated using DemoCards.jl and Literate.jl.

diff --git a/dev/examples/advanced/demo_currentsheet/index.html b/dev/examples/advanced/demo_currentsheet/index.html index 41d157bd..276d1244 100644 --- a/dev/examples/advanced/demo_currentsheet/index.html +++ b/dev/examples/advanced/demo_currentsheet/index.html @@ -83,4 +83,4 @@ color=:black, alpha=0.6, lengthscale=100.0) @. Y -= 15 end -


This page was generated using DemoCards.jl and Literate.jl.

+


This page was generated using DemoCards.jl and Literate.jl.

diff --git a/dev/examples/advanced/demo_ensemble/index.html b/dev/examples/advanced/demo_ensemble/index.html index 383eefab..d69b7f38 100644 --- a/dev/examples/advanced/demo_ensemble/index.html +++ b/dev/examples/advanced/demo_ensemble/index.html @@ -69,4 +69,4 @@ ##TODO: wait for https://github.com/MakieOrg/Makie.jl/issues/3623 to be fixed! ax.scene.plots[9+2*i-1].color = Makie.wong_colors()[i] end -

Note that by default linear interpolation is applied when plotting the trajectories from the Boris method.


This page was generated using DemoCards.jl and Literate.jl.

+

Note that by default linear interpolation is applied when plotting the trajectories from the Boris method.


This page was generated using DemoCards.jl and Literate.jl.

diff --git a/dev/examples/advanced/demo_magneticbottle/index.html b/dev/examples/advanced/demo_magneticbottle/index.html index fbf90f61..8d68590f 100644 --- a/dev/examples/advanced/demo_magneticbottle/index.html +++ b/dev/examples/advanced/demo_magneticbottle/index.html @@ -123,4 +123,4 @@ lines!(ax, x1, y1, z1, color=:black) end end -


This page was generated using DemoCards.jl and Literate.jl.

+


This page was generated using DemoCards.jl and Literate.jl.

diff --git a/dev/examples/advanced/demo_magneticmirror/index.html b/dev/examples/advanced/demo_magneticmirror/index.html index 5dc6d1e3..9054d309 100644 --- a/dev/examples/advanced/demo_magneticmirror/index.html +++ b/dev/examples/advanced/demo_magneticmirror/index.html @@ -103,4 +103,4 @@ # Ba = Bx.(x) # # lines(z, Ba, color=:red) # lines(x, Ba, color=:red) -


This page was generated using DemoCards.jl and Literate.jl.

+


This page was generated using DemoCards.jl and Literate.jl.

diff --git a/dev/examples/advanced/demo_output_func/index.html b/dev/examples/advanced/demo_output_func/index.html index 73a2d8b4..f411882e 100644 --- a/dev/examples/advanced/demo_output_func/index.html +++ b/dev/examples/advanced/demo_output_func/index.html @@ -104,4 +104,4 @@ zp = [s[3] for s in sols[i][1]] lines!(ax, xp, yp, zp, label="$i") end -


This page was generated using DemoCards.jl and Literate.jl.

+


This page was generated using DemoCards.jl and Literate.jl.

diff --git a/dev/examples/advanced/demo_proton_dipole/index.html b/dev/examples/advanced/demo_proton_dipole/index.html index 95efa873..aff5124f 100644 --- a/dev/examples/advanced/demo_proton_dipole/index.html +++ b/dev/examples/advanced/demo_proton_dipole/index.html @@ -71,4 +71,4 @@ get_energy_ratio(sol)
-5.399366794067525e-7

This is much more accurate, at the cost of more iterations. In terms of accuracy, this is roughly equivalent to solve(prob, Vern9(); reltol=1e-7); in terms of performance, it is 2x slower (0.04s v.s. 0.02s) and consumes about the same amount of memory 42 MiB. We can also use the classical Boris method implemented within the package:

dt = 1e-4
 prob = TraceProblem(stateinit, tspan, param)
 sol = TestParticle.solve(prob; dt)[1]
-get_energy_ratio(sol)
-1.0158504769154868e-15

The Boris method requires a fixed time step. It takes about 0.05s and consumes 53 MiB memory. In this specific case, the time step is determined empirically. If we increase the time step to 1e-2 seconds, the trajectory becomes completely off (but the energy is still conserved). Therefore, as a rule of thumb, we should not use the default Tsit5() scheme without decreasing reltol. Use adaptive Vern9() for an unfamiliar field configuration, then switch to more accurate schemes if needed. A more thorough test can be found here.


This page was generated using DemoCards.jl and Literate.jl.

+get_energy_ratio(sol)
-1.0158504769154868e-15

The Boris method requires a fixed time step. It takes about 0.05s and consumes 53 MiB memory. In this specific case, the time step is determined empirically. If we increase the time step to 1e-2 seconds, the trajectory becomes completely off (but the energy is still conserved). Therefore, as a rule of thumb, we should not use the default Tsit5() scheme without decreasing reltol. Use adaptive Vern9() for an unfamiliar field configuration, then switch to more accurate schemes if needed. A more thorough test can be found here.


This page was generated using DemoCards.jl and Literate.jl.

diff --git a/dev/examples/advanced/demo_savingcallback/index.html b/dev/examples/advanced/demo_savingcallback/index.html index 691f0b6a..bc733433 100644 --- a/dev/examples/advanced/demo_savingcallback/index.html +++ b/dev/examples/advanced/demo_savingcallback/index.html @@ -79,4 +79,4 @@ t: [0.0, 0.0007064003808057418, 0.006151342961409062, 0.03990852562861322, 0.24729664360869114, 0.8655117969873891, 1.8106352974346311, 2.900697481526176, 3.141592653589793] saveval: -Tuple{StaticArraysCore.SVector{3, Float64}, Float64}[([0.0, 0.0, 1.0], 6.123233995736766e-17), ([0.0, 0.0, 1.0], 6.123233995736766e-17), ([0.0, 0.0, 1.0], 6.123233995736766e-17), ([0.0, 0.0, 1.0], 6.123233995736766e-17), ([0.0, 0.0, 1.0], 6.123233995736766e-17), ([0.0, 0.0, 1.0], 6.123233995736766e-17), ([0.0, 0.0, 1.0], 6.123233995736766e-17), ([0.0, 0.0, 1.0], 6.123233995736766e-17), ([0.0, 0.0, 1.0], 6.123233995736766e-17)]

This page was generated using DemoCards.jl and Literate.jl.

+Tuple{StaticArraysCore.SVector{3, Float64}, Float64}[([0.0, 0.0, 1.0], 6.123233995736766e-17), ([0.0, 0.0, 1.0], 6.123233995736766e-17), ([0.0, 0.0, 1.0], 6.123233995736766e-17), ([0.0, 0.0, 1.0], 6.123233995736766e-17), ([0.0, 0.0, 1.0], 6.123233995736766e-17), ([0.0, 0.0, 1.0], 6.123233995736766e-17), ([0.0, 0.0, 1.0], 6.123233995736766e-17), ([0.0, 0.0, 1.0], 6.123233995736766e-17), ([0.0, 0.0, 1.0], 6.123233995736766e-17)]

This page was generated using DemoCards.jl and Literate.jl.

diff --git a/dev/examples/advanced/demo_shock/index.html b/dev/examples/advanced/demo_shock/index.html index 5cda61c5..e71bd1aa 100644 --- a/dev/examples/advanced/demo_shock/index.html +++ b/dev/examples/advanced/demo_shock/index.html @@ -241,4 +241,4 @@ sols = solve(ensemble_prob, Vern9(), EnsembleSerial(); trajectories); -f = plot_traj(sols; azimuth=1.08π, elevation=pi/16)

Clearly, test particle tracing in MHD parallel shocks fails to recover physics. MHD parallel shocks are essentially hydrodynamic shocks where magnetic field plays no role. Due to the lack of collision and other diffusion processes, we are unable to capture the correct microscopic scenario here.


This page was generated using DemoCards.jl and Literate.jl.

+f = plot_traj(sols; azimuth=1.08π, elevation=pi/16)

Clearly, test particle tracing in MHD parallel shocks fails to recover physics. MHD parallel shocks are essentially hydrodynamic shocks where magnetic field plays no role. Due to the lack of collision and other diffusion processes, we are unable to capture the correct microscopic scenario here.


This page was generated using DemoCards.jl and Literate.jl.

diff --git a/dev/examples/advanced/demo_tokamak_coil/index.html b/dev/examples/advanced/demo_tokamak_coil/index.html index 826f041d..16b3f1bd 100644 --- a/dev/examples/advanced/demo_tokamak_coil/index.html +++ b/dev/examples/advanced/demo_tokamak_coil/index.html @@ -82,4 +82,4 @@ Z = @. (a - 0.05) * sin(U) wireframe!(ax, X, Y, Z, color=(:blue, 0.1), linewidth=0.5, transparency=true) -


This page was generated using DemoCards.jl and Literate.jl.

+


This page was generated using DemoCards.jl and Literate.jl.

diff --git a/dev/examples/advanced/demo_tokamak_profile/index.html b/dev/examples/advanced/demo_tokamak_profile/index.html index 9cab6f19..3efddbe4 100644 --- a/dev/examples/advanced/demo_tokamak_profile/index.html +++ b/dev/examples/advanced/demo_tokamak_profile/index.html @@ -84,4 +84,4 @@ lines!(ax, sol; idxs=(1,2,3)) wireframe!(fig2[1, 1], tor_mesh, color=(:blue, 0.1), linewidth=0.5, transparency=true) -

The trajectory of the trapped particle is sometimes called the "banana orbit".


This page was generated using DemoCards.jl and Literate.jl.

+

The trajectory of the trapped particle is sometimes called the "banana orbit".


This page was generated using DemoCards.jl and Literate.jl.

diff --git a/dev/examples/basics/demo_Buniform_Ezero/index.html b/dev/examples/basics/demo_Buniform_Ezero/index.html index e93fba2d..63439850 100644 --- a/dev/examples/basics/demo_Buniform_Ezero/index.html +++ b/dev/examples/basics/demo_Buniform_Ezero/index.html @@ -31,4 +31,4 @@ ) plot!(ax, sol, idxs=(1, 2, 3)) -


This page was generated using DemoCards.jl and Literate.jl.

+


This page was generated using DemoCards.jl and Literate.jl.

diff --git a/dev/examples/basics/demo_ExB_drift/index.html b/dev/examples/basics/demo_ExB_drift/index.html index 22ae299c..456affaa 100644 --- a/dev/examples/basics/demo_ExB_drift/index.html +++ b/dev/examples/basics/demo_ExB_drift/index.html @@ -59,4 +59,4 @@ ##TODO: wait for https://github.com/MakieOrg/Makie.jl/issues/3623 to be fixed! ax.scene.plots[9+2*i-1].color = Makie.wong_colors()[i] end -


This page was generated using DemoCards.jl and Literate.jl.

+


This page was generated using DemoCards.jl and Literate.jl.

diff --git a/dev/examples/basics/demo_FLR/index.html b/dev/examples/basics/demo_FLR/index.html index 66f6befb..624956a9 100644 --- a/dev/examples/basics/demo_FLR/index.html +++ b/dev/examples/basics/demo_FLR/index.html @@ -71,4 +71,4 @@ ##TODO: wait for https://github.com/MakieOrg/Makie.jl/issues/3623 to be fixed! ax.scene.plots[9+2*i-1].color = Makie.wong_colors()[i] end -


This page was generated using DemoCards.jl and Literate.jl.

+


This page was generated using DemoCards.jl and Literate.jl.

diff --git a/dev/examples/basics/demo_boris/index.html b/dev/examples/basics/demo_boris/index.html index 537854e5..e04a5d5a 100644 --- a/dev/examples/basics/demo_boris/index.html +++ b/dev/examples/basics/demo_boris/index.html @@ -76,9 +76,9 @@ # Visualization f = plot_trajectory(sol_boris, sol1, sol2)

Fixed time step Tsit5() is ok, but adaptive Tsit5() is pretty bad for long time evolutions. The change in radius indicates change in energy, which is sometimes known as numerical heating.

Another aspect to compare is performance:

@time sol_boris = TestParticle.solve(prob_boris; dt, savestepinterval=10)[1];
 @time sol1 = solve(prob, Tsit5(); adaptive=false, dt, dense=false, saveat=dt);
-@time sol2 = solve(prob, Tsit5());
  0.000117 seconds (257 allocations: 21.609 KiB)
-  0.000832 seconds (2.46 k allocations: 377.031 KiB)
-  0.000732 seconds (11.86 k allocations: 1.329 MiB)
+@time sol2 = solve(prob, Tsit5());
  0.000091 seconds (257 allocations: 21.609 KiB)
+  0.000789 seconds (2.46 k allocations: 377.031 KiB)
+  0.000738 seconds (11.86 k allocations: 1.329 MiB)
 

We can extract the solution (x, y, z, vx, vy, vz) at any given time by performing a linear interpolation:

t = tspan[2] / 2
 sol_boris(t)
6-element StaticArraysCore.MVector{6, Float64} with indices SOneTo(6):
     -3.911568318655031e-5
@@ -86,4 +86,4 @@
      0.0
  99639.26547015733
   8486.269885387323
-     0.0

The Boris method is faster and consumes less memory. However, in practice, it is pretty hard to find an optimal algorithm. When calling OrdinaryDiffEq.jl, we recommend using Vern9() as a starting point instead of Tsit5(), especially combined with adaptive timestepping.


This page was generated using DemoCards.jl and Literate.jl.

+ 0.0

The Boris method is faster and consumes less memory. However, in practice, it is pretty hard to find an optimal algorithm. When calling OrdinaryDiffEq.jl, we recommend using Vern9() as a starting point instead of Tsit5(), especially combined with adaptive timestepping.


This page was generated using DemoCards.jl and Literate.jl.

diff --git a/dev/examples/basics/demo_curvature_B/index.html b/dev/examples/basics/demo_curvature_B/index.html index 39ef4274..50c6dfb6 100644 --- a/dev/examples/basics/demo_curvature_B/index.html +++ b/dev/examples/basics/demo_curvature_B/index.html @@ -74,4 +74,4 @@ ##TODO: wait for https://github.com/MakieOrg/Makie.jl/issues/3623 to be fixed! ax.scene.plots[9+2*i-1].color = Makie.wong_colors()[i] end -


This page was generated using DemoCards.jl and Literate.jl.

+


This page was generated using DemoCards.jl and Literate.jl.

diff --git a/dev/examples/basics/demo_dimensionless/index.html b/dev/examples/basics/demo_dimensionless/index.html index c8098435..4a355217 100644 --- a/dev/examples/basics/demo_dimensionless/index.html +++ b/dev/examples/basics/demo_dimensionless/index.html @@ -48,4 +48,4 @@ ) lines!(ax, sol, vars=(1,2)) -


This page was generated using DemoCards.jl and Literate.jl.

+


This page was generated using DemoCards.jl and Literate.jl.

diff --git a/dev/examples/basics/demo_dimensionless_dimensional/index.html b/dev/examples/basics/demo_dimensionless_dimensional/index.html index 20d113de..27f36f39 100644 --- a/dev/examples/basics/demo_dimensionless_dimensional/index.html +++ b/dev/examples/basics/demo_dimensionless_dimensional/index.html @@ -61,4 +61,4 @@ sol2.(trange, idxs=1) .* l₀, sol2.(trange, idxs=2) .* l₀ end lines!(ax, xp, yp, linestyle=:dashdot, linewidth=5) -

We see that the results are almost identical, with only floating point numerical errors. Tracing in dimensionless units usually allows larger timesteps, which leads to faster computation.


This page was generated using DemoCards.jl and Literate.jl.

+

We see that the results are almost identical, with only floating point numerical errors. Tracing in dimensionless units usually allows larger timesteps, which leads to faster computation.


This page was generated using DemoCards.jl and Literate.jl.

diff --git a/dev/examples/basics/demo_dimensionless_periodic/index.html b/dev/examples/basics/demo_dimensionless_periodic/index.html index 458b31a5..22efa4a4 100644 --- a/dev/examples/basics/demo_dimensionless_periodic/index.html +++ b/dev/examples/basics/demo_dimensionless_periodic/index.html @@ -45,4 +45,4 @@ ) lines!(ax, sol, vars=(1,2)) -


This page was generated using DemoCards.jl and Literate.jl.

+


This page was generated using DemoCards.jl and Literate.jl.

diff --git a/dev/examples/basics/demo_electron_proton/index.html b/dev/examples/basics/demo_electron_proton/index.html index f3e8a0c7..c08b477f 100644 --- a/dev/examples/basics/demo_electron_proton/index.html +++ b/dev/examples/basics/demo_electron_proton/index.html @@ -49,4 +49,4 @@ ax.scene.plots[9+2*2-1].color = :deepskyblue3 axislegend() -


This page was generated using DemoCards.jl and Literate.jl.

+


This page was generated using DemoCards.jl and Literate.jl.

diff --git a/dev/examples/basics/demo_energy_conservation/index.html b/dev/examples/basics/demo_energy_conservation/index.html index d41f0183..128464a9 100644 --- a/dev/examples/basics/demo_energy_conservation/index.html +++ b/dev/examples/basics/demo_energy_conservation/index.html @@ -87,4 +87,4 @@

Simulated travel distance

simulated travel distance: 5.744086746768604e12 [m]
 

Predicted final energy

predicted energy gain: 1.723226024042558e11 [eV]
 

Simulated final energy

simulated final energy: 1.7232260240426102e11 [eV]
-

This page was generated using DemoCards.jl and Literate.jl.

+

This page was generated using DemoCards.jl and Literate.jl.

diff --git a/dev/examples/basics/demo_gradient_B/index.html b/dev/examples/basics/demo_gradient_B/index.html index 63b1a102..d2dcfc1a 100644 --- a/dev/examples/basics/demo_gradient_B/index.html +++ b/dev/examples/basics/demo_gradient_B/index.html @@ -66,4 +66,4 @@ ##TODO: wait for https://github.com/MakieOrg/Makie.jl/issues/3623 to be fixed! ax.scene.plots[9+2*i-1].color = Makie.wong_colors()[i] end -

Note that in this grad-B drift case, the analytic and numeric guiding centers have different trajectories.


This page was generated using DemoCards.jl and Literate.jl.

+

Note that in this grad-B drift case, the analytic and numeric guiding centers have different trajectories.


This page was generated using DemoCards.jl and Literate.jl.

diff --git a/dev/examples/basics/demo_gravity_drift/index.html b/dev/examples/basics/demo_gravity_drift/index.html index 55d875c3..593ba316 100644 --- a/dev/examples/basics/demo_gravity_drift/index.html +++ b/dev/examples/basics/demo_gravity_drift/index.html @@ -27,4 +27,4 @@ figure = (; size = (800, 400), fontsize=18), axis = (; title="ExF Drift", xlabel="Z [m]", ylabel="X [m]", aspect = DataAspect()) ) -


This page was generated using DemoCards.jl and Literate.jl.

+


This page was generated using DemoCards.jl and Literate.jl.

diff --git a/dev/examples/basics/demo_multiple/index.html b/dev/examples/basics/demo_multiple/index.html index 7e862519..13fdad26 100644 --- a/dev/examples/basics/demo_multiple/index.html +++ b/dev/examples/basics/demo_multiple/index.html @@ -63,4 +63,4 @@ for i in eachindex(sols) lines!(ax, sols[i], idxs=(1, 2, 3), label="$i") end -


This page was generated using DemoCards.jl and Literate.jl.

+


This page was generated using DemoCards.jl and Literate.jl.

diff --git a/dev/examples/basics/demo_polarization_drift/index.html b/dev/examples/basics/demo_polarization_drift/index.html index 5b0ec9ec..20876e46 100644 --- a/dev/examples/basics/demo_polarization_drift/index.html +++ b/dev/examples/basics/demo_polarization_drift/index.html @@ -49,4 +49,4 @@ lines!(ax2, sol, idxs=(v_perp, 0, 5, 6)) lines!(ax3, sol, idxs=2) lines!(ax4, sol, idxs=(gc_y, 0, 1, 2, 3, 4, 5, 6)) -


This page was generated using DemoCards.jl and Literate.jl.

+


This page was generated using DemoCards.jl and Literate.jl.

diff --git a/dev/examples/index.html b/dev/examples/index.html index 3944c19b..8ccaa0f8 100644 --- a/dev/examples/index.html +++ b/dev/examples/index.html @@ -161,4 +161,4 @@

This demo shows how to trace particles from structured SWMF outputs

-
+
diff --git a/dev/index.html b/dev/index.html index cc18b8d8..4c3c6ad1 100644 --- a/dev/index.html +++ b/dev/index.html @@ -1,3 +1,3 @@ Home · TestParticle.jl

TestParticle.jl

TestParticle.jl is a test particle tracer in a static electromagnetic field.

This package supports charged particle tracing in analytic/numerical relativistic/non-relativistic

  • electric and magnetic field;
  • body force field.

All tracing are performed in 3D, as is the nature for the fields. For a numerical field, the mesh is constructed with Meshes.jl, and the field is interpolated with the aid of Interpolations.jl. For an analytical field, the user is responsible for providing the function for calculating the field at a given spatial location. The actual tracing is done through DifferentialEquations.jl, thanks to the ODE system of the equations of motion.

For all the tracing methods, we provide both an inplace version (with ! at the end of the function name) and a non-inplace version using StaticArrays. The non-inplace version requires the initial conditions to be static a static vector. Use them at your convenience.

The single particle motions are the basics in understanding the test particle method. Check out Single-Particle Motions for more complete maths.

Installation

julia> ]
-pkg> add TestParticle

Usage

It would be better to understand the basic workflow of DifferentialEquations.jl before digging into TestParticle.jl. All we are doing here can be concluded as contructing the ODE system from Newton's 2nd law and preparing the field/particle data. Check more in examples.

Additionally, we have a native Boris solver with a similar interface as DifferentialEquations.jl. Check out the details in later sections.

Acknowledgement

Nothing can be done such easily without the support of the Julia community. We appreciate all the contributions from developers around the world.

+pkg> add TestParticle

Usage

It would be better to understand the basic workflow of DifferentialEquations.jl before digging into TestParticle.jl. All we are doing here can be concluded as contructing the ODE system from Newton's 2nd law and preparing the field/particle data. Check more in examples.

Additionally, we have a native Boris solver with a similar interface as DifferentialEquations.jl. Check out the details in later sections.

Acknowledgement

Nothing can be done such easily without the support of the Julia community. We appreciate all the contributions from developers around the world.

diff --git a/dev/objects.inv b/dev/objects.inv index 36222625..a9a2441b 100644 Binary files a/dev/objects.inv and b/dev/objects.inv differ diff --git a/dev/plotfunctions/index.html b/dev/plotfunctions/index.html index cf5955b2..2c252700 100644 --- a/dev/plotfunctions/index.html +++ b/dev/plotfunctions/index.html @@ -10,4 +10,4 @@ plot!(sol, idxs=(1, 2, 3))

Multiple particle trajectories saved as the type EnsembleSolution is also supported by the Makie recipe.

Advanced recipe

Note

We currently rely on the Makie recipe implemented in DiffEqBase.jl. This recipe depends on an experimental API introduced in Makie v0.20, which may be unstable and contains bugs. Once it becomes more stabilized with bug fixes, we will recover the interactive widgets support, e.g. orbit and monitor.

orbit

The slider can control the time span, and the maximum of time span will be displayed on the right of the slider.

The reset button can reset the scale of lines when the axis limits change. When you drag the slider, clicking reset button will reset the axis limits to fit the data.

monitor

After first click of the run button, the evolution of the orbit will be displayed from the beginning. For other times, it will start from the time set by the time slider. The functionality of reset button is the same as above.

The time slider controls the time span. The speed slider controls the speed of the animation.

+

After first click of the run button, the evolution of the orbit will be displayed from the beginning. For other times, it will start from the time set by the time slider. The functionality of reset button is the same as above.

The time slider controls the time span. The speed slider controls the speed of the animation.

diff --git a/dev/search_index.js b/dev/search_index.js index 4904ad5c..f7424f44 100644 --- a/dev/search_index.js +++ b/dev/search_index.js @@ -1,3 +1,3 @@ var documenterSearchIndex = {"docs": -[{"location":"examples/basics/demo_dimensionless/","page":"Dimensionless Units","title":"Dimensionless Units","text":"EditURL = \"/home/runner/work/TestParticle.jl/TestParticle.jl/docs/examples/basics/demo_dimensionless.jl\"","category":"page"},{"location":"examples/basics/demo_dimensionless/#demo_dimensionless","page":"Dimensionless Units","title":"Dimensionless Units","text":"","category":"section"},{"location":"examples/basics/demo_dimensionless/","page":"Dimensionless Units","title":"Dimensionless Units","text":"(Image: Source code) (Image: compat) (Image: Author) (Image: Update time)","category":"page"},{"location":"examples/basics/demo_dimensionless/","page":"Dimensionless Units","title":"Dimensionless Units","text":"This example shows how to trace charged particles in dimensionless units. After normalization, q=1 B=1 m=1 so that the gyroradius r_L = mv_perpqB = v_perp. All the quantities are given in dimensionless units. If the magnetic field is homogeneous and the initial perpendicular velocity is 4, then the gyroradius is 4. To convert them to the original units, v_perp = 4*U_0 and r_L = 4*l_0. Check Demo: single tracing with additional diagnostics for explaining the unit conversion.","category":"page"},{"location":"examples/basics/demo_dimensionless/","page":"Dimensionless Units","title":"Dimensionless Units","text":"Tracing in dimensionless units is beneficial for many scenarios. For example, MHD simulations do not have intrinsic scales. Therefore, we can do dimensionless particle tracing in MHD fields, and then convert to any scale we would like.","category":"page"},{"location":"examples/basics/demo_dimensionless/","page":"Dimensionless Units","title":"Dimensionless Units","text":"Now let's demonstrate this with trace_normalized!.","category":"page"},{"location":"examples/basics/demo_dimensionless/","page":"Dimensionless Units","title":"Dimensionless Units","text":"using TestParticle\nusing TestParticle: qᵢ, mᵢ\nusing OrdinaryDiffEq\n\nusing CairoMakie\n\n# Number of cells for the field along each dimension\nnx, ny, nz = 4, 6, 8\n# Unit conversion factors between SI and dimensionless units\nB₀ = 10e-9 # [T]\nΩ = abs(qᵢ) * B₀ / mᵢ # [1/s]\nt₀ = 1 / Ω # [s]\nU₀ = 1.0 # [m/s]\nl₀ = U₀ * t₀ # [m]\nE₀ = U₀*B₀ # [V/m]\n# All quantities are in dimensionless units\nx = range(-10, 10, length=nx) # [l₀]\ny = range(-10, 10, length=ny) # [l₀]\nz = range(-10, 10, length=nz) # [l₀]\n\nB = fill(0.0, 3, nx, ny, nz) # [B₀]\nB[3,:,:,:] .= 1.0\nE = fill(0.0, 3, nx, ny, nz) # [E₀]\n\nparam = prepare(x, y, z, E, B; species=User)\n\n# Initial condition\nstateinit = let\n x0 = [0.0, 0.0, 0.0] # initial position [l₀]\n u0 = [4.0, 0.0, 0.0] # initial velocity [v₀]\n [x0..., u0...]\nend\n# Time span\ntspan = (0.0, π) # half gyroperiod\n\nprob = ODEProblem(trace_normalized!, stateinit, tspan, param)\nsol = solve(prob, Vern9())\n\n### Visualization\nf = Figure(fontsize = 18)\nax = Axis(f[1, 1],\n title = \"Proton trajectory\",\n xlabel = \"X\",\n ylabel = \"Y\",\n limits = (-4.1, 4.1, -8.1, 0.1),\n aspect = DataAspect()\n)\n\nlines!(ax, sol, vars=(1,2))\n","category":"page"},{"location":"examples/basics/demo_dimensionless/","page":"Dimensionless Units","title":"Dimensionless Units","text":"(Image: )","category":"page"},{"location":"examples/basics/demo_dimensionless/","page":"Dimensionless Units","title":"Dimensionless Units","text":"","category":"page"},{"location":"examples/basics/demo_dimensionless/","page":"Dimensionless Units","title":"Dimensionless Units","text":"This page was generated using DemoCards.jl and Literate.jl.","category":"page"},{"location":"examples/advanced/demo_currentsheet/","page":"Current sheet","title":"Current sheet","text":"EditURL = \"/home/runner/work/TestParticle.jl/TestParticle.jl/docs/examples/advanced/demo_currentsheet.jl\"","category":"page"},{"location":"examples/advanced/demo_currentsheet/#demo_currentsheet","page":"Current sheet","title":"Current sheet","text":"","category":"section"},{"location":"examples/advanced/demo_currentsheet/","page":"Current sheet","title":"Current sheet","text":"(Image: Source code) (Image: compat) (Image: Author) (Image: Update time)","category":"page"},{"location":"examples/advanced/demo_currentsheet/","page":"Current sheet","title":"Current sheet","text":"This example shows how to trace protons in a stationary magnetic field that corresponds to the 1D Harris current sheet defined by a reference strength and width. Reference: https://en.wikipedia.org/wiki/Current_sheet","category":"page"},{"location":"examples/advanced/demo_currentsheet/","page":"Current sheet","title":"Current sheet","text":"using TestParticle\nusing TestParticle: getB_CS_harris\nusing OrdinaryDiffEq\nusing StaticArrays\nusing CairoMakie\n\n### Obtain field\n\n# Harris current sheet parameters in SI units\nconst B₀, L = 20e-9, 0.4TestParticle.Rₑ\n\nfunction getB(xu)\n SVector{3}(getB_CS_harris(xu[1:3], B₀, L))\nend\n\nfunction getE(xu)\n SA[0.0, 0.0, 0.0]\nend\n\n### Initialize particles\n\nm = TestParticle.mᵢ\nq = TestParticle.qᵢ\nc = TestParticle.c\nRₑ = TestParticle.Rₑ\n# Initial condition\nstateinit = let\n # initial particle energy, [eV]\n Ek = 5e7\n # initial velocity, [m/s]\n v₀ = [c*√(1-1/(1+Ek*q/(m*c^2))^2), 0.0, 0.0]\n # initial position, [m]\n r₀ = [-5.0Rₑ, 0.0, 0.0]\n\n [r₀..., v₀...]\nend\n\nparam = prepare(getE, getB)\ntspan = (0.0, 10.0)\n\nprob = ODEProblem(trace!, stateinit, tspan, param)\n\nsol = solve(prob, Tsit5(); save_idxs=[1,2,3])\n\n### Visualization\n\nf = Figure(fontsize=18)\nax = Axis3(f[1, 1],\n title = \"Particle trajectory near the Harris current sheet\",\n xlabel = \"x [Re]\",\n ylabel = \"y [Re]\",\n zlabel = \"z [Re]\",\n aspect = :data,\n)\n\nn = 100 # number of timepoints\nts = range(tspan..., length=n)\nx = sol(ts, idxs=1)./Rₑ |> Vector\ny = sol(ts, idxs=2)./Rₑ |> Vector\nz = sol(ts, idxs=3)./Rₑ |> Vector\n\nl = lines!(ax, x, y, z, label=\"50 MeV proton, B0 = 20 nT\")\naxislegend()\n\nX, Y, Z = let xrange = range(-8, 8, length=20)\n X = collect(Float32, xrange)\n Y = zeros(Float32, size(X)...)\n Z = zeros(Float32, size(X)...)\n X, Y, Z\nend\n\nB = zeros(Float32, 3, size(X)...)\n\ni = 1\nfor (x,y) in zip(X, Y)\n B[1+3*(i-1):3*i] = getB_CS_harris([x,0.0,0.0], 4e-2, 1.0)\n global i += 1\nend\n\nfor s = 1:3\n quiver!(ax, X, Y, Z, vec(B[1,:,:]), vec(B[2,:,:]), vec(B[3,:,:]),\n color=:black, alpha=0.6, lengthscale=100.0)\n @. Y -= 15\nend\n","category":"page"},{"location":"examples/advanced/demo_currentsheet/","page":"Current sheet","title":"Current sheet","text":"(Image: )","category":"page"},{"location":"examples/advanced/demo_currentsheet/","page":"Current sheet","title":"Current sheet","text":"","category":"page"},{"location":"examples/advanced/demo_currentsheet/","page":"Current sheet","title":"Current sheet","text":"This page was generated using DemoCards.jl and Literate.jl.","category":"page"},{"location":"examples/advanced/demo_savingcallback/","page":"Single tracing with additional diagnostics","title":"Single tracing with additional diagnostics","text":"EditURL = \"/home/runner/work/TestParticle.jl/TestParticle.jl/docs/examples/advanced/demo_savingcallback.jl\"","category":"page"},{"location":"examples/advanced/demo_savingcallback/#demo_savingcallback","page":"Single tracing with additional diagnostics","title":"Single tracing with additional diagnostics","text":"","category":"section"},{"location":"examples/advanced/demo_savingcallback/","page":"Single tracing with additional diagnostics","title":"Single tracing with additional diagnostics","text":"(Image: Source code) (Image: compat) (Image: Author) (Image: Update time)","category":"page"},{"location":"examples/advanced/demo_savingcallback/","page":"Single tracing with additional diagnostics","title":"Single tracing with additional diagnostics","text":"This example demonstrates tracing one proton in an analytic E field and numerical B field. It also combines one type of normalization using a reference velocity U₀, a reference magnetic field B₀, and a reference time 1/Ω, where Ω is the gyrofrequency. This indicates that in the dimensionless units, a proton with initial perpendicular velocity 1 under magnetic field magnitude 1 will possess a gyro-radius of 1. In the dimensionless spatial coordinates, we can zoom in/out the EM field to control the number of discrete points encountered in a gyroperiod. For example, if dx=dy=dz=1, it means that a particle with perpendicular velocity 1 will \"see\" one discrete point along a certain direction oriented from the gyro-center within the gyro-radius; if dx=dy=dz=0.5, then the particle will \"see\" two discrete points. MHD models, for instance, are dimensionless by nature. There will be customized (dimensionless) units for (x,y,z,E,B) that we needs to convert the dimensionless units for computing. If we simulate a turbulence field with MHD, we want to include more discrete points within a gyro-radius for the effect of small scale perturbations to take place. (Otherwise within one gyro-period all you will see is a nice-looking helix!) However, we cannot simply shrink the spatial coordinates as we wish, otherwise we will quickly encounter the boundary of our simulation.","category":"page"},{"location":"examples/advanced/demo_savingcallback/","page":"Single tracing with additional diagnostics","title":"Single tracing with additional diagnostics","text":"The SavingCallback from DiffEqCallbacks.jl can be used to save additional outputs for diagnosis. Here we save the magnetic field along the trajectory, together with the parallel velocity. Note that SavingCallback is currently not compatible with ensemble problems; for multiple particle tracing with customized outputs, see Demo: ensemble tracing with extra saving.","category":"page"},{"location":"examples/advanced/demo_savingcallback/","page":"Single tracing with additional diagnostics","title":"Single tracing with additional diagnostics","text":"using TestParticle\nusing TestParticle: qᵢ, mᵢ\nusing OrdinaryDiffEq\nusing StaticArrays\nusing Statistics\nusing LinearAlgebra: normalize, ×, ⋅\nusing DiffEqCallbacks\n\n# Number of cells for the field along each dimension\nnx, ny, nz = 4, 6, 8\n# Spatial coordinates given in customized units\nx = range(-0.5, 0.5, length=nx)\ny = range(-0.5, 0.5, length=ny)\nz = range(-0.5, 0.5, length=nz)\n# Numerical magnetic field given in customized units\nB = Array{Float32, 4}(undef, 3, nx, ny, nz)\n\nB[1,:,:,:] .= 0.0\nB[2,:,:,:] .= 0.0\nB[3,:,:,:] .= 2.0\n\n# Reference values for unit conversions between the customized and dimensionless units\nconst B₀ = let Bmag = @views hypot.(B[1,:,:,:], B[2,:,:,:], B[3,:,:,:])\n sqrt(mean(vec(Bmag) .^ 2))\nend\nconst U₀ = 1.0\nconst l₀ = 4*nx\nconst t₀ = l₀ / U₀\nconst E₀ = U₀ * B₀\n\n### Convert from customized to default dimensionless units\n# Dimensionless spatial extents [l₀]\nx /= l₀\ny /= l₀\nz /= l₀\n# For full EM problems, the normalization of E and B should be done separately.\nB ./= B₀\nE(x) = SA[0.0/E₀, 0.0/E₀, 0.0/E₀]\n\n# By default User type assumes q=1, m=1; bc=2 uses periodic boundary conditions\nparam = prepare(x, y, z, E, B; species=User, bc=2)\n\ntspan = (0.0, π) # half averaged gyroperiod based on B₀\n\n# Dummy initial state; positions have units l₀; velocities have units U₀\nstateinit = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]\n\nprob = ODEProblem(trace_normalized!, stateinit, tspan, param)\n\nprob.u0[4:6] = let\n B0 = prob.p[3](prob.u0)\n B0 = normalize(B0)\n\n Bperp1 = SA[0.0, -B0[3], B0[2]] |> normalize\n Bperp2 = B0 × Bperp1 |> normalize\n\n # initial velocity azimuthal angle\n ϕ = 2π*0\n # initial velocity pitch angle w.r.t. B\n θ = acos(0.0)\n\n sinϕ, cosϕ = sincos(ϕ)\n @. (B0*cos(θ) + Bperp1*(sin(θ)*cosϕ) + Bperp2*(sin(θ)*sinϕ)) * U₀\nend\n\nsaved_values = SavedValues(Float64, Tuple{SVector{3, Float64}, Float64})\n\nfunction save_B_mu(u, t, integrator)\n b = integrator.p[3](u)\n μ = @views b ⋅ u[4:6] / hypot(b...)\n\n b, μ\nend\n\ncb = SavingCallback(save_B_mu, saved_values)\n\nsol = solve(prob, Vern9(); callback=cb);","category":"page"},{"location":"examples/advanced/demo_savingcallback/","page":"Single tracing with additional diagnostics","title":"Single tracing with additional diagnostics","text":"The extra values are saved in saved_values:","category":"page"},{"location":"examples/advanced/demo_savingcallback/","page":"Single tracing with additional diagnostics","title":"Single tracing with additional diagnostics","text":"saved_values","category":"page"},{"location":"examples/advanced/demo_savingcallback/","page":"Single tracing with additional diagnostics","title":"Single tracing with additional diagnostics","text":"SavedValues{tType=Float64, savevalType=Tuple{StaticArraysCore.SVector{3, Float64}, Float64}}\nt:\n[0.0, 0.0007064003808057418, 0.006151342961409062, 0.03990852562861322, 0.24729664360869114, 0.8655117969873891, 1.8106352974346311, 2.900697481526176, 3.141592653589793]\nsaveval:\nTuple{StaticArraysCore.SVector{3, Float64}, Float64}[([0.0, 0.0, 1.0], 6.123233995736766e-17), ([0.0, 0.0, 1.0], 6.123233995736766e-17), ([0.0, 0.0, 1.0], 6.123233995736766e-17), ([0.0, 0.0, 1.0], 6.123233995736766e-17), ([0.0, 0.0, 1.0], 6.123233995736766e-17), ([0.0, 0.0, 1.0], 6.123233995736766e-17), ([0.0, 0.0, 1.0], 6.123233995736766e-17), ([0.0, 0.0, 1.0], 6.123233995736766e-17), ([0.0, 0.0, 1.0], 6.123233995736766e-17)]","category":"page"},{"location":"examples/advanced/demo_savingcallback/","page":"Single tracing with additional diagnostics","title":"Single tracing with additional diagnostics","text":"","category":"page"},{"location":"examples/advanced/demo_savingcallback/","page":"Single tracing with additional diagnostics","title":"Single tracing with additional diagnostics","text":"This page was generated using DemoCards.jl and Literate.jl.","category":"page"},{"location":"examples/basics/demo_polarization_drift/","page":"Polarization drift","title":"Polarization drift","text":"EditURL = \"/home/runner/work/TestParticle.jl/TestParticle.jl/docs/examples/basics/demo_polarization_drift.jl\"","category":"page"},{"location":"examples/basics/demo_polarization_drift/#demo_polarization_drift","page":"Polarization drift","title":"Polarization drift","text":"","category":"section"},{"location":"examples/basics/demo_polarization_drift/","page":"Polarization drift","title":"Polarization drift","text":"(Image: Source code) (Image: compat) (Image: Author) (Image: Author) (Image: Update time)","category":"page"},{"location":"examples/basics/demo_polarization_drift/","page":"Polarization drift","title":"Polarization drift","text":"This example demonstrates a single proton motion under time-varying E field. More theoretical details can be found in Time-Varying E Drift, and Fundamentals of Plasma Physics by Paul Bellan.","category":"page"},{"location":"examples/basics/demo_polarization_drift/","page":"Polarization drift","title":"Polarization drift","text":"using TestParticle\nusing TestParticle: get_gc\nusing OrdinaryDiffEq\nusing StaticArrays\nusing CairoMakie\n\nfunction uniform_B(x)\n return SA[0, 0, 1e-8]\nend\n\nfunction time_varying_E(x, t)\n # return SA[0, 1e-9*cos(0.1*t), 0]\n return SA[0, 1e-9*0.1*t, 0]\nend\n\n# Initial condition\nstateinit = let x0 = [1.0, 0, 0], v0 = [0.0, 1.0, 0.1]\n [x0..., v0...]\nend\n# Time span\ntspan = (0, 100)\nparam = prepare(time_varying_E, uniform_B, species=Proton)\nprob = ODEProblem(trace!, stateinit, tspan, param)\nsol = solve(prob, Vern9())\n# Functions for obtaining the guiding center from actual trajectory\ngc = get_gc(param)\nv_perp(xu) = hypot(xu[4], xu[5])\n\n# Visualization\nf = Figure(size=(800, 600), fontsize=18)\nax1 = Axis3(f[1:3, 1],\n title = \"Polarization Drift\",\n xlabel = \"x [m]\",\n ylabel = \"y [m]\",\n zlabel = \"z [m]\",\n aspect = :data,\n azimuth = 0.9π,\n elevation = 0.1π\n)\nax2 = Axis(f[1,2], xlabel = \"time [s]\", ylabel = \"v_perp [m/s]\")\nax3 = Axis(f[2,2], xlabel = \"time [s]\", ylabel = \"y [m]\")\nax4 = Axis(f[3,2], xlabel = \"time [s]\", ylabel = \"gc_y [m]\")\n\ngc_y(t, x, y, z, vx, vy, vz) = (t, gc(SA[x, y, z, vx, vy, vz])[2])\nv_perp(t, vy, vz) = (t, sqrt(vy^2 + vz^2))\n\nlines!(ax1, sol, idxs=(1, 2, 3))\nlines!(ax2, sol, idxs=(v_perp, 0, 5, 6))\nlines!(ax3, sol, idxs=2)\nlines!(ax4, sol, idxs=(gc_y, 0, 1, 2, 3, 4, 5, 6))\n","category":"page"},{"location":"examples/basics/demo_polarization_drift/","page":"Polarization drift","title":"Polarization drift","text":"(Image: )","category":"page"},{"location":"examples/basics/demo_polarization_drift/","page":"Polarization drift","title":"Polarization drift","text":"","category":"page"},{"location":"examples/basics/demo_polarization_drift/","page":"Polarization drift","title":"Polarization drift","text":"This page was generated using DemoCards.jl and Literate.jl.","category":"page"},{"location":"examples/basics/demo_gradient_B/","page":"Grad-B drift","title":"Grad-B drift","text":"EditURL = \"/home/runner/work/TestParticle.jl/TestParticle.jl/docs/examples/basics/demo_gradient_B.jl\"","category":"page"},{"location":"examples/basics/demo_gradient_B/#demo_gradB","page":"Grad-B drift","title":"Grad-B drift","text":"","category":"section"},{"location":"examples/basics/demo_gradient_B/","page":"Grad-B drift","title":"Grad-B drift","text":"(Image: Source code) (Image: compat) (Image: Author) (Image: Author) (Image: Update time)","category":"page"},{"location":"examples/basics/demo_gradient_B/","page":"Grad-B drift","title":"Grad-B drift","text":"This example demonstrates a single proton motion under a non-uniform B field with gradient ∇B ⊥ B. The orbit of guiding center includes some high order terms, it is different from the formula of magnetic field gradient drift of some textbooks which just preserves the first order term. It is more complex than the simpler ExB drift. More theoretical details can be found in Grad-B Drift, and Fundamentals of Plasma Physics by Paul Bellan.","category":"page"},{"location":"examples/basics/demo_gradient_B/","page":"Grad-B drift","title":"Grad-B drift","text":"using TestParticle\nusing TestParticle: get_gc\nusing OrdinaryDiffEq\nusing StaticArrays\nusing LinearAlgebra: ×, ⋅, normalize, norm\nusing ForwardDiff: gradient\nusing CairoMakie\n\nfunction grad_B(x)\n return SA[0, 0, 1e-8 + 1e-9*x[2]]\nend\n\nfunction uniform_E(x)\n return SA[1e-9, 0, 0]\nend\n\nabs_B(x) = norm(grad_B(x))\n\n# Trace the orbit of the guiding center using analytical drifts\nfunction trace_gc!(dx, x, p, t)\n q2m, E, B, sol = p\n xu = sol(t)\n gradient_B = gradient(abs_B, x)\n Bv = B(x)\n b = normalize(Bv)\n v_par = (xu[4:6] ⋅ b) .* b\n v_perp = xu[4:6] - v_par\n dx[1:3] = norm(v_perp)^2*(Bv × gradient_B)/(2*q2m*norm(Bv)^3) +\n (E(x) × Bv) / norm(Bv)^2 + v_par\nend\n\n# Initial condition\nstateinit = let x0 = [1.0, 0, 0], v0 = [0.0, 1.0, 0.1]\n [x0..., v0...]\nend\n# Time span\ntspan = (0, 20)\nparam = prepare(uniform_E, grad_B, species=Proton)\nprob = ODEProblem(trace!, stateinit, tspan, param)\nsol = solve(prob, Vern9())\n# Functions for obtaining the guiding center from actual trajectory\ngc = get_gc(param)\ngc_x0 = gc(stateinit)\nprob_gc = ODEProblem(trace_gc!, gc_x0, tspan, (param..., sol))\nsol_gc = solve(prob_gc, Tsit5(); save_idxs=[1,2,3])\n\n# Numeric and analytic results\nf = Figure(fontsize=18)\nax = Axis3(f[1, 1],\n title = \"Grad-B Drift\",\n xlabel = \"x [m]\",\n ylabel = \"y [m]\",\n zlabel = \"z [m]\",\n aspect = :data,\n azimuth = 0.3π,\n)\n\ngc_plot(x, y, z, vx, vy, vz) = (gc(SA[x, y, z, vx, vy, vz])...,)\n\nlines!(ax, sol, idxs=(1, 2, 3))\nlines!(ax, sol, idxs=(gc_plot, 1, 2, 3, 4, 5, 6))\nlines!(ax, sol_gc, idxs=(1, 2, 3))\n\nfor i in 1:3\n ##TODO: wait for https://github.com/MakieOrg/Makie.jl/issues/3623 to be fixed!\n ax.scene.plots[9+2*i-1].color = Makie.wong_colors()[i]\nend\n","category":"page"},{"location":"examples/basics/demo_gradient_B/","page":"Grad-B drift","title":"Grad-B drift","text":"(Image: )","category":"page"},{"location":"examples/basics/demo_gradient_B/","page":"Grad-B drift","title":"Grad-B drift","text":"Note that in this grad-B drift case, the analytic and numeric guiding centers have different trajectories.","category":"page"},{"location":"examples/basics/demo_gradient_B/","page":"Grad-B drift","title":"Grad-B drift","text":"","category":"page"},{"location":"examples/basics/demo_gradient_B/","page":"Grad-B drift","title":"Grad-B drift","text":"This page was generated using DemoCards.jl and Literate.jl.","category":"page"},{"location":"examples/basics/demo_dimensionless_dimensional/","page":"Dimensionless and Dimensional Tracing","title":"Dimensionless and Dimensional Tracing","text":"EditURL = \"/home/runner/work/TestParticle.jl/TestParticle.jl/docs/examples/basics/demo_dimensionless_dimensional.jl\"","category":"page"},{"location":"examples/basics/demo_dimensionless_dimensional/#demo_dimensionless_dimensional","page":"Dimensionless and Dimensional Tracing","title":"Dimensionless and Dimensional Tracing","text":"","category":"section"},{"location":"examples/basics/demo_dimensionless_dimensional/","page":"Dimensionless and Dimensional Tracing","title":"Dimensionless and Dimensional Tracing","text":"(Image: Source code) (Image: compat) (Image: Author) (Image: Update time)","category":"page"},{"location":"examples/basics/demo_dimensionless_dimensional/","page":"Dimensionless and Dimensional Tracing","title":"Dimensionless and Dimensional Tracing","text":"This example shows how to trace charged particles in both dimensional and dimensionless units. We first solve the Lorentz equation in SI units, and then convert the quantities to normalized units and solve it again in dimensionless units.","category":"page"},{"location":"examples/basics/demo_dimensionless_dimensional/","page":"Dimensionless and Dimensional Tracing","title":"Dimensionless and Dimensional Tracing","text":"The Lorentz equation in SI units is written as","category":"page"},{"location":"examples/basics/demo_dimensionless_dimensional/","page":"Dimensionless and Dimensional Tracing","title":"Dimensionless and Dimensional Tracing","text":"fracmathrmdmathbfvmathrmdt = fracqmleft( mathbfvtimesmathbfB + mathbfE right)","category":"page"},{"location":"examples/basics/demo_dimensionless_dimensional/","page":"Dimensionless and Dimensional Tracing","title":"Dimensionless and Dimensional Tracing","text":"It can be normalized to","category":"page"},{"location":"examples/basics/demo_dimensionless_dimensional/","page":"Dimensionless and Dimensional Tracing","title":"Dimensionless and Dimensional Tracing","text":"fracmathrmdmathbfv^primemathrmdt^prime = mathbfv^primetimesmathbfB^prime + mathbfE^prime","category":"page"},{"location":"examples/basics/demo_dimensionless_dimensional/","page":"Dimensionless and Dimensional Tracing","title":"Dimensionless and Dimensional Tracing","text":"with the following transformation","category":"page"},{"location":"examples/basics/demo_dimensionless_dimensional/","page":"Dimensionless and Dimensional Tracing","title":"Dimensionless and Dimensional Tracing","text":"beginaligned\nmathbfv = mathbfv^prime V_0 \nt = t^prime t_0 = t^prime Omega^-1 = t^prime fracmqB_0 \nmathbfB = mathbfB^prime B_0 \nmathbfE = mathbfE^prime E_0 = mathbfE^prime V_0 B_0\nendaligned","category":"page"},{"location":"examples/basics/demo_dimensionless_dimensional/","page":"Dimensionless and Dimensional Tracing","title":"Dimensionless and Dimensional Tracing","text":"where all the coefficients with subscript 0 are expressed in SI units. All the variables with a prime are written in the dimensionless units.","category":"page"},{"location":"examples/basics/demo_dimensionless_dimensional/","page":"Dimensionless and Dimensional Tracing","title":"Dimensionless and Dimensional Tracing","text":"using TestParticle\nusing TestParticle: c, qᵢ, mᵢ\nusing OrdinaryDiffEq\nusing StaticArrays\nusing CairoMakie\n\n# Unit conversion factors between SI and dimensionless units\nconst B₀ = 1e-8 # [T]\nconst U₀ = c # [m/s]\nconst E₀ = U₀*B₀ # [V/m]\nconst Ω = abs(qᵢ) * B₀ / mᵢ # [1/s]\nconst t₀ = 1 / Ω # [s]\nconst l₀ = U₀ * t₀ # [m]\n# Electric field magnitude in SI units\nconst Emag = 1e-8 # [V/m]\n### Solving in SI units\nB(x) = SA[0, 0, B₀]\nE(x) = SA[Emag, 0.0, 0.0]\n\n# Initial conditions\nx0 = [0.0, 0.0, 0.0] # [m]\nv0 = [0.0, 0.01c, 0.0] # [m/s]\nstateinit1 = [x0..., v0...]\ntspan1 = (0, 2π*t₀) # [s]\n\nparam1 = prepare(E, B, species=Proton)\nprob1 = ODEProblem(trace!, stateinit1, tspan1, param1)\nsol1 = solve(prob1, Vern9(); reltol=1e-4, abstol=1e-6)\n\n### Solving in dimensionless units\nB_normalize(x) = SA[0, 0, B₀/B₀]\nE_normalize(x) = SA[Emag/E₀, 0.0, 0.0]\n# For full EM problems, the normalization of E and B should be done separately.\nparam2 = prepare(E_normalize, B_normalize; species=User)\n# Scale initial conditions by the conversion factors\nx0 ./= l₀\nv0 ./= U₀\ntspan2 = (0, 2π)\nstateinit2 = [x0..., v0...]\n\nprob2 = ODEProblem(trace_normalized!, stateinit2, tspan2, param2)\nsol2 = solve(prob2, Vern9(); reltol=1e-4, abstol=1e-6)\n\n### Visualization\nf = Figure(fontsize=18)\nax = Axis(f[1, 1],\n xlabel = \"x\",\n ylabel = \"y\",\n aspect = DataAspect(),\n)\n\nlines!(ax, sol1, idxs=(1, 2))\n# Interpolate dimensionless solutions and map back to SI units\nxp, yp = let trange = range(tspan2..., length=40)\n sol2.(trange, idxs=1) .* l₀, sol2.(trange, idxs=2) .* l₀\nend\nlines!(ax, xp, yp, linestyle=:dashdot, linewidth=5)\n","category":"page"},{"location":"examples/basics/demo_dimensionless_dimensional/","page":"Dimensionless and Dimensional Tracing","title":"Dimensionless and Dimensional Tracing","text":"(Image: )","category":"page"},{"location":"examples/basics/demo_dimensionless_dimensional/","page":"Dimensionless and Dimensional Tracing","title":"Dimensionless and Dimensional Tracing","text":"We see that the results are almost identical, with only floating point numerical errors. Tracing in dimensionless units usually allows larger timesteps, which leads to faster computation.","category":"page"},{"location":"examples/basics/demo_dimensionless_dimensional/","page":"Dimensionless and Dimensional Tracing","title":"Dimensionless and Dimensional Tracing","text":"","category":"page"},{"location":"examples/basics/demo_dimensionless_dimensional/","page":"Dimensionless and Dimensional Tracing","title":"Dimensionless and Dimensional Tracing","text":"This page was generated using DemoCards.jl and Literate.jl.","category":"page"},{"location":"examples/basics/demo_Buniform_Ezero/","page":"Helix motion","title":"Helix motion","text":"EditURL = \"/home/runner/work/TestParticle.jl/TestParticle.jl/docs/examples/basics/demo_Buniform_Ezero.jl\"","category":"page"},{"location":"examples/basics/demo_Buniform_Ezero/#demo_uniformB_zeroE","page":"Helix motion","title":"Helix motion","text":"","category":"section"},{"location":"examples/basics/demo_Buniform_Ezero/","page":"Helix motion","title":"Helix motion","text":"(Image: Source code) (Image: compat) (Image: Author) (Image: Author) (Image: Update time)","category":"page"},{"location":"examples/basics/demo_Buniform_Ezero/","page":"Helix motion","title":"Helix motion","text":"This example demonstrates a single proton motion under a uniform B field. The E field is assumed to be zero such that there is no particle acceleration.","category":"page"},{"location":"examples/basics/demo_Buniform_Ezero/","page":"Helix motion","title":"Helix motion","text":"using TestParticle\nusing TestParticle: get_gc\nusing OrdinaryDiffEq\nusing StaticArrays\nusing LinearAlgebra\nusing CairoMakie\n\nuniform_B(x) = SA[0.0, 0.0, 1e-8]\nuniform_E(x) = SA[0.0, 0.0, 0.0]\n\n# Initial condition\nstateinit = let x0 = [1.0, 0, 0], v0 = [0.0, 1.0, 0.1]\n [x0..., v0...]\nend\n# Time span\ntspan = (0, 18)\n\nparam = prepare(uniform_E, uniform_B, species=Proton)\nprob = ODEProblem(trace!, stateinit, tspan, param)\nsol = solve(prob, Vern9())\n\n# Visualization\nf = Figure(fontsize=18)\nax = Axis3(f[1, 1],\n title = \"Helix Trajectory\",\n xlabel = \"x [m]\",\n ylabel = \"y [m]\",\n zlabel = \"z [m]\",\n aspect = :data,\n)\n\nplot!(ax, sol, idxs=(1, 2, 3))\n","category":"page"},{"location":"examples/basics/demo_Buniform_Ezero/","page":"Helix motion","title":"Helix motion","text":"(Image: )","category":"page"},{"location":"examples/basics/demo_Buniform_Ezero/","page":"Helix motion","title":"Helix motion","text":"","category":"page"},{"location":"examples/basics/demo_Buniform_Ezero/","page":"Helix motion","title":"Helix motion","text":"This page was generated using DemoCards.jl and Literate.jl.","category":"page"},{"location":"plotfunctions/#Plot-Functions","page":"Plot Functions","title":"Plot Functions","text":"","category":"section"},{"location":"plotfunctions/","page":"Plot Functions","title":"Plot Functions","text":"TestParticle.jl wraps types of AbstractODESolution from DifferentialEquations.jl. The plotting recipes for Plots.jl and Makie.jl work automatically for the particle tracing solutions.","category":"page"},{"location":"plotfunctions/","page":"Plot Functions","title":"Plot Functions","text":"Before using the plot recipes of Testparticle.jl, you need to import Makie package via using GLMakie or using CairoMakie, which depends on your choice for the backend. All plot recipes depend on Makie.jl.","category":"page"},{"location":"plotfunctions/#Convention","page":"Plot Functions","title":"Convention","text":"","category":"section"},{"location":"plotfunctions/","page":"Plot Functions","title":"Plot Functions","text":"By convention, we use integers to represent the 7 dimensions in the input argument vars for all plotting methods:","category":"page"},{"location":"plotfunctions/","page":"Plot Functions","title":"Plot Functions","text":"Component Meaning\n0 time\n1 x\n2 y\n3 z\n4 vx\n5 vy\n6 vz","category":"page"},{"location":"plotfunctions/#Basic-recipe","page":"Plot Functions","title":"Basic recipe","text":"","category":"section"},{"location":"plotfunctions/","page":"Plot Functions","title":"Plot Functions","text":"The simplest usage is directly calling the plot or lines function provided by Makie. For example,","category":"page"},{"location":"plotfunctions/","page":"Plot Functions","title":"Plot Functions","text":"plot(sol, idxs=(1, 2, 3))","category":"page"},{"location":"plotfunctions/","page":"Plot Functions","title":"Plot Functions","text":"The keyword argument idxs can be used to select the variables to be plotted; if not specified, it will plot the timeseries of x, y, z, vx, vy, vz. Please refer to Choose Variables for details.","category":"page"},{"location":"plotfunctions/","page":"Plot Functions","title":"Plot Functions","text":"If you want to plot a function of time, position or velocity, you can first define the function. For example,","category":"page"},{"location":"plotfunctions/","page":"Plot Functions","title":"Plot Functions","text":"Eₖ(t, vx, vy, vz) = (t, mₑ*(vx^2 + vy^2 + vz^2)/2)\nlines(sol, idxs=(Eₖ, 0, 4, 5, 6))","category":"page"},{"location":"plotfunctions/","page":"Plot Functions","title":"Plot Functions","text":"will plot the kinetic energy as a function of x.","category":"page"},{"location":"plotfunctions/","page":"Plot Functions","title":"Plot Functions","text":"You can choose the plotting time span via the keyword argument tspan. For example,","category":"page"},{"location":"plotfunctions/","page":"Plot Functions","title":"Plot Functions","text":"lines(sol, tspan=(0, 1))","category":"page"},{"location":"plotfunctions/","page":"Plot Functions","title":"Plot Functions","text":"The plots can be customized further:","category":"page"},{"location":"plotfunctions/","page":"Plot Functions","title":"Plot Functions","text":"f = Figure(size=(1200, 800), fontsize=18)\nax = Axis3(f[1,1],\n title = \"Particle trajectory\",\n xlabel = \"X\",\n ylabel = \"Y\",\n zlabel = \"Z\",\n)\n\nplot!(sol, idxs=(1, 2, 3))","category":"page"},{"location":"plotfunctions/","page":"Plot Functions","title":"Plot Functions","text":"Multiple particle trajectories saved as the type EnsembleSolution is also supported by the Makie recipe.","category":"page"},{"location":"plotfunctions/#Advanced-recipe","page":"Plot Functions","title":"Advanced recipe","text":"","category":"section"},{"location":"plotfunctions/","page":"Plot Functions","title":"Plot Functions","text":"note: Note\nWe currently rely on the Makie recipe implemented in DiffEqBase.jl. This recipe depends on an experimental API introduced in Makie v0.20, which may be unstable and contains bugs. Once it becomes more stabilized with bug fixes, we will recover the interactive widgets support, e.g. orbit and monitor.","category":"page"},{"location":"plotfunctions/#orbit","page":"Plot Functions","title":"orbit","text":"","category":"section"},{"location":"plotfunctions/","page":"Plot Functions","title":"Plot Functions","text":"(Image: )","category":"page"},{"location":"plotfunctions/","page":"Plot Functions","title":"Plot Functions","text":"The slider can control the time span, and the maximum of time span will be displayed on the right of the slider.","category":"page"},{"location":"plotfunctions/","page":"Plot Functions","title":"Plot Functions","text":"The reset button can reset the scale of lines when the axis limits change. When you drag the slider, clicking reset button will reset the axis limits to fit the data.","category":"page"},{"location":"plotfunctions/#monitor","page":"Plot Functions","title":"monitor","text":"","category":"section"},{"location":"plotfunctions/","page":"Plot Functions","title":"Plot Functions","text":"","category":"page"},{"location":"plotfunctions/","page":"Plot Functions","title":"Plot Functions","text":"(Image: )","category":"page"},{"location":"plotfunctions/","page":"Plot Functions","title":"Plot Functions","text":"After first click of the run button, the evolution of the orbit will be displayed from the beginning. For other times, it will start from the time set by the time slider. The functionality of reset button is the same as above.","category":"page"},{"location":"plotfunctions/","page":"Plot Functions","title":"Plot Functions","text":"The time slider controls the time span. The speed slider controls the speed of the animation.","category":"page"},{"location":"examples/advanced/demo_output_func/","page":"Ensemble tracing with extra saving","title":"Ensemble tracing with extra saving","text":"EditURL = \"/home/runner/work/TestParticle.jl/TestParticle.jl/docs/examples/advanced/demo_output_func.jl\"","category":"page"},{"location":"examples/advanced/demo_output_func/#demo_output_func","page":"Ensemble tracing with extra saving","title":"Ensemble tracing with extra saving","text":"","category":"section"},{"location":"examples/advanced/demo_output_func/","page":"Ensemble tracing with extra saving","title":"Ensemble tracing with extra saving","text":"(Image: Source code) (Image: compat) (Image: Author) (Image: Update time)","category":"page"},{"location":"examples/advanced/demo_output_func/","page":"Ensemble tracing with extra saving","title":"Ensemble tracing with extra saving","text":"This example demonstrates tracing multiple protons in an analytic E field and numerical B field. See Demo: single tracing with additional diagnostics for explaining the unit conversion. Also check Demo: Ensemble for basic usages of the ensemble problem.","category":"page"},{"location":"examples/advanced/demo_output_func/","page":"Ensemble tracing with extra saving","title":"Ensemble tracing with extra saving","text":"The output_func argument can be used to change saving outputs. It works as a reduction function, but here we demonstrate how to add additional outputs. Besides the regular outputs, we also save the magnetic field along the trajectory, together with the parallel velocity.","category":"page"},{"location":"examples/advanced/demo_output_func/","page":"Ensemble tracing with extra saving","title":"Ensemble tracing with extra saving","text":"using TestParticle\nusing TestParticle: qᵢ, mᵢ\nusing OrdinaryDiffEq\nusing StaticArrays\nusing Statistics\nusing LinearAlgebra: normalize, ×, ⋅\nusing Random\nusing CairoMakie\n\nseed = 1 # seed for random number\nRandom.seed!(seed)\n\n\"Set initial state for EnsembleProblem.\"\nfunction prob_func(prob, i, repeat)\n B0 = prob.p[3](prob.u0)\n B0 = normalize(B0)\n\n Bperp1 = SA[0.0, -B0[3], B0[2]] |> normalize\n Bperp2 = B0 × Bperp1 |> normalize\n\n # initial azimuthal angle\n ϕ = 2π*rand()\n # initial pitch angle\n θ = acos(0.5)\n\n sinϕ, cosϕ = sincos(ϕ)\n u = @. (B0*cos(θ) + Bperp1*(sin(θ)*cosϕ) + Bperp2*(sin(θ)*sinϕ)) * U₀\n\n prob = @views remake(prob; u0=[prob.u0[1:3]..., u...])\nend\n\n# Number of cells for the field along each dimension\nnx, ny, nz = 4, 6, 8\n# Spatial coordinates given in customized units\nx = range(0, 1, length=nx)\ny = range(0, 1, length=ny)\nz = range(0, 1, length=nz)\n# Numerical magnetic field given in customized units\nB = Array{Float32, 4}(undef, 3, nx, ny, nz)\n\nB[1,:,:,:] .= 0.0\nB[2,:,:,:] .= 0.0\nB[3,:,:,:] .= 2.0\n\n# Reference values for unit conversions between the customized and dimensionless units\nconst B₀ = let Bmag = @views hypot.(B[1,:,:,:], B[2,:,:,:], B[3,:,:,:])\n sqrt(mean(vec(Bmag) .^ 2))\nend\nconst U₀ = 1.0\nconst l₀ = 2*nx\nconst t₀ = l₀ / U₀\nconst E₀ = U₀ * B₀;","category":"page"},{"location":"examples/advanced/demo_output_func/","page":"Ensemble tracing with extra saving","title":"Ensemble tracing with extra saving","text":"Convert from customized to default dimensionless units","category":"page"},{"location":"examples/advanced/demo_output_func/","page":"Ensemble tracing with extra saving","title":"Ensemble tracing with extra saving","text":"# Dimensionless spatial extents [l₀]\nx /= l₀\ny /= l₀\nz /= l₀\n# For full EM problems, the normalization of E and B should be done separately.\nB ./= B₀\nE(x) = SA[0.0/E₀, 0.0/E₀, 0.0/E₀]\n\n# By default User type assumes q=1, m=1\n# bc=2 uses periodic boundary conditions\nparam = prepare(x, y, z, E, B; species=User, bc=2)\n\n# Initial condition\nstateinit = let\n x0 = [0.0, 0.0, 0.0] # initial position [l₀]\n u0 = [1.0, 0.0, 0.0] # initial velocity [v₀]\n [x0..., u0...]\nend\n# Time span\ntspan = (0.0, 2π) # one averaged gyroperiod based on B₀\n\nsaveat = tspan[2] / 40 # save interval\n\nprob = ODEProblem(trace_normalized!, stateinit, tspan, param)\n\n\"Set customized outputs for the ensemble problem.\"\nfunction output_func(sol, i)\n Bfunc = sol.prob.p[3]\n b = Bfunc.(sol.u)\n\n μ = [@views b[i] ⋅ sol[4:6, i] / hypot(b[i]...) for i in eachindex(sol)]\n\n (sol.u, b, μ), false\nend\n\n# Number of trajectories\ntrajectories = 2\n\nensemble_prob = EnsembleProblem(prob; prob_func, output_func, safetycopy=false)\nsols = solve(ensemble_prob, Vern9(), EnsembleThreads(); trajectories, saveat);","category":"page"},{"location":"examples/advanced/demo_output_func/","page":"Ensemble tracing with extra saving","title":"Ensemble tracing with extra saving","text":"Visualization","category":"page"},{"location":"examples/advanced/demo_output_func/","page":"Ensemble tracing with extra saving","title":"Ensemble tracing with extra saving","text":"f = Figure(fontsize = 18)\nax = Axis3(f[1, 1],\n title = \"Proton trajectories\",\n xlabel = \"X\",\n ylabel = \"Y\",\n zlabel = \"Z\",\n aspect = :data,\n)\n\nfor i in eachindex(sols)\n xp = [s[1] for s in sols[i][1]]\n yp = [s[2] for s in sols[i][1]]\n zp = [s[3] for s in sols[i][1]]\n lines!(ax, xp, yp, zp, label=\"$i\")\nend\n","category":"page"},{"location":"examples/advanced/demo_output_func/","page":"Ensemble tracing with extra saving","title":"Ensemble tracing with extra saving","text":"(Image: )","category":"page"},{"location":"examples/advanced/demo_output_func/","page":"Ensemble tracing with extra saving","title":"Ensemble tracing with extra saving","text":"","category":"page"},{"location":"examples/advanced/demo_output_func/","page":"Ensemble tracing with extra saving","title":"Ensemble tracing with extra saving","text":"This page was generated using DemoCards.jl and Literate.jl.","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"EditURL = \"/home/runner/work/TestParticle.jl/TestParticle.jl/docs/examples/index.md\"","category":"page"},{"location":"examples/#examples","page":"Examples","title":"Examples","text":"","category":"section"},{"location":"examples/","page":"Examples","title":"Examples","text":"This section contains thorough examples for using TestParticle.jl. If you prefer to run the demos on your local computer with interactive support, we suggest switching from using CairoMakie (which is a Makie frontend for static 2D rendering) to using GLMakie (which is another Makie frontend for interactive 3D rendering).","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"","category":"page"},{"location":"examples/#Basics","page":"Examples","title":"Basics","text":"","category":"section"},{"location":"examples/","page":"Examples","title":"Examples","text":"
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n\n \n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Energy Conservation","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Demonstrate energy conservation in uniform fields.","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n\n \n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Boris method","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Simple electron trajectory under uniform B and zero E","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n\n \n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Helix motion","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Simple proton trajectory under uniform B and zero E","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n\n \n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Dimensionless Units","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Tracing charged particle in dimensionless units","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n\n \n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Tracing with Dimensionless Units and Periodic Boundary","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Tracing charged particle in dimensionless units and periodic boundary","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n\n \n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Dimensionless and Dimensional Tracing","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Tracing charged particle in both dimensional and dimensionless units.","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n\n \n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Proton and electron in a static EM field","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Simple particle trajectories under uniform B and zero E","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n\n \n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Multiple particles","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Tracing multiple charged particles in a static EM field","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n\n \n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"E×B drift","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Simple ExB drift demonstration","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n\n \n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"ExF drift","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Simple proton trajectory under uniform B and gravity","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n\n \n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Grad-B drift","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Simple magnetic field gradient drift demonstration","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n\n \n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Curl-B drift","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Simple magnetic field curvature drift demonstration","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n\n \n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Finite-Larmor-Radius effect","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Finite Larmor radius effect demonstration using Makie","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n\n \n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Polarization drift","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Simple polarization drift under time-varying E field","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
","category":"page"},{"location":"examples/#Advanced","page":"Examples","title":"Advanced","text":"","category":"section"},{"location":"examples/","page":"Examples","title":"Examples","text":"
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n\n \n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Advanced Boris tracing","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Boris ensemble tracing with field out-of-domain check","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n\n \n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Ensemble tracing","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Tracing multiple charged particles in a static EM field","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n\n \n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Single tracing with additional diagnostics","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Tracing one charged particle with additional diagnostics","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n\n \n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Ensemble tracing with extra saving","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Tracing multiple charged particles in a static EM field","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n\n \n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Current sheet","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Tracing charged particle in the Harris current sheet","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n\n \n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Magnetic mirror","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Charged particle in the magnetic mirror","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n\n \n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Electron in a magnetic bottle","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Tracing charged particle in a magnetic bottle","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n\n \n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Magnetic dipole","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Tracing charged particle in a static analytic dipole magnetic field","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n\n \n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Analytical magnetosphere","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Tracing charged particle in an analytical magnetosphere","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n\n \n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Shock","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Tracing charged particle in an MHD plane shock","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n\n \n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Coil Tokamak","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Tracing protons in a Tokamak","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n\n \n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Tokamak profile","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Tracing passing and trapped proton in a Tokamak","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n\n \n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Tracing particle from PIC","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"This demo shows how to trace particles from structured SWMF outputs","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"","category":"page"},{"location":"examples/advanced/demo_batsrus_3dstructured/","page":"Tracing particle from PIC","title":"Tracing particle from PIC","text":"EditURL = \"/home/runner/work/TestParticle.jl/TestParticle.jl/docs/examples/advanced/demo_batsrus_3dstructured.md\"","category":"page"},{"location":"examples/advanced/demo_batsrus_3dstructured/#demo_pic","page":"Tracing particle from PIC","title":"Tracing particle from PIC","text":"","category":"section"},{"location":"examples/advanced/demo_batsrus_3dstructured/","page":"Tracing particle from PIC","title":"Tracing particle from PIC","text":"(Image: Author) (Image: Update time)","category":"page"},{"location":"examples/advanced/demo_batsrus_3dstructured/","page":"Tracing particle from PIC","title":"Tracing particle from PIC","text":"This example shows how to trace charged particles in the structured SWMF outputs from an MHD-EPIC simulation of Ganymede. For more details about the field file format, please checkout Batsrus.jl.","category":"page"},{"location":"examples/advanced/demo_batsrus_3dstructured/","page":"Tracing particle from PIC","title":"Tracing particle from PIC","text":"using Statistics: mean\nusing Batsrus\nusing TestParticle\nusing OrdinaryDiffEq\nusing FieldTracer\nusing PyPlot\n\n## Utility functions\n\nfunction initial_conditions(i)\n j = i - 1\n [x[xc_], y[yc_+j], z[zc_], Uix[xc_,yc_+j,zc_], Uiy[xc_,yc_+j,zc_], Uiz[xc_,yc_+j,zc_]]\nend\n\n\"Set initial conditions.\"\nfunction prob_func(prob, i, repeat)\n remake(prob, u0=initial_conditions(i))\nend\n\n## Data processing\n\nfilename = \"3d_var_region0_0_t00002500_n00106468.out\"\ndata = readdata(filename)\n\nvar = getvars(data, [\"Bx\", \"By\", \"Bz\", \"Ex\", \"Ey\", \"Ez\", \"uxs0\", \"uys0\", \"uzs0\", \"uxs1\", \"uys1\", \"uzs1\"])\n\nconst RG = 2634e3 # [m]\n\nx = range(extrema(data.x[:,:,:,1])..., length=size(data.x, 1)) .* RG\ny = range(extrema(data.x[:,:,:,2])..., length=size(data.x, 2)) .* RG\nz = range(extrema(data.x[:,:,:,3])..., length=size(data.x, 3)) .* RG\nB = zeros(Float32, 3, length(x), length(y), length(z)) # [T]\nE = zeros(Float32, 3, length(x), length(y), length(z)) # [V/m]\n\n# Convert into SI units\nB[1,:,:,:] .= var[\"Bx\"] .* 1e-9\nB[2,:,:,:] .= var[\"By\"] .* 1e-9\nB[3,:,:,:] .= var[\"Bz\"] .* 1e-9\nE[1,:,:,:] .= var[\"Ex\"] .* 1e-6\nE[2,:,:,:] .= var[\"Ey\"] .* 1e-6\nE[3,:,:,:] .= var[\"Ez\"] .* 1e-6\n\n## Initial conditions\n\nUex, Uey, Uez = var[\"uxs0\"] .* 1e3, var[\"uys0\"] .* 1e3, var[\"uzs0\"] .* 1e3\nUix, Uiy, Uiz = var[\"uxs1\"] .* 1e3, var[\"uys1\"] .* 1e3, var[\"uzs1\"] .* 1e3\n\nxc_ = floor(Int, length(x)/2) + 1\nyc_ = floor(Int, length(y)/2) + 1\nzc_ = floor(Int, length(z)/2) + 1\n\nstateinit_e = [x[xc_], y[yc_], z[zc_], Uex[xc_,yc_,zc_], Uey[xc_,yc_,zc_], Uez[xc_,yc_,zc_]]\nstateinit_p = [x[xc_], y[yc_], z[zc_], Uix[xc_,yc_,zc_], Uiy[xc_,yc_,zc_], Uiz[xc_,yc_,zc_]]\n\nparam_electron = prepare(x, y, z, E, B, species=Electron)\ntspan_electron = (0.0, 0.1)\n\nparam_proton = prepare(x, y, z, E, B, species=Proton)\ntspan_proton = (0.0, 10.0)\ntrajectories = 5\n\nprob_p = ODEProblem(trace!, stateinit_p, tspan_proton, param_proton)\nensemble_prob = EnsembleProblem(prob_p, prob_func=prob_func)\nsol_p = solve(ensemble_prob, Vern9(), EnsembleThreads(); trajectories)\n\n## Visualization\n\nusing3D()\nfig = plt.figure(figsize=(10,6))\nax = fig.gca(projection=\"3d\")\n\n## Field tracing\n\nfor i in 1:10:length(x)\n xs, ys, zs = x[i], 0.0, 0.0\n x1, y1, z1 = trace(B[1,:,:,:], B[2,:,:,:], B[3,:,:,:], xs, ys, zs, x, y, z, ds=0.2, maxstep=1000)\n line = ax.plot(x1 ./ RG, y1 ./RG, z1 ./ RG, \"k-\", alpha=0.3)\nend\n\nfor i in 1:10:length(y)\n xs, ys, zs = x[xc_], y[i], z[zc_]\n x1, y1, z1 = trace(B[1,:,:,:], B[2,:,:,:], B[3,:,:,:], xs, ys, zs, x, y, z, ds=0.2, maxstep=1000)\n line = ax.plot(x1 ./ RG, y1 ./RG, z1 ./ RG, \"k-\", alpha=0.3)\nend\n\nn = 200 # number of timepoints\n\nts = range(0, stop=tspan_proton[2], length=n)\nfor i = 1:trajectories\n if sol_p[i].t[end] < tspan_proton[2]\n ts⁺ = range(0, stop=sol_p[i].t[end], length=n)\n ax.plot(sol_p[i](ts⁺,idxs=1) ./ RG, sol_p[i](ts⁺,idxs=2) ./ RG, sol_p[i](ts⁺,idxs=3) ./ RG, label=\"proton $i\", lw=1.5)\n else\n ax.plot(sol_p[i](ts,idxs=1) ./ RG, sol_p[i](ts,idxs=2) ./ RG, sol_p[i](ts,idxs=3) ./ RG, label=\"proton $i\", lw=1.5)\n end\nend\n\n#ax.plot(sol_p[1,:], sol_p[2,:], sol_p[3,:], label=\"proton\")\n\nax.legend()\ntitle(\"Particle trajectory near Ganymede's magnetopause from PIC\")\nxlabel(\"x [Rg]\")\nylabel(\"y [Rg]\")\nzlabel(\"z [Rg]\")\n\nax.set_box_aspect([1.17,4,4])\nTestParticle.set_axes_equal(ax)","category":"page"},{"location":"examples/advanced/demo_batsrus_3dstructured/","page":"Tracing particle from PIC","title":"Tracing particle from PIC","text":"(Image: )","category":"page"},{"location":"examples/advanced/demo_batsrus_3dstructured/","page":"Tracing particle from PIC","title":"Tracing particle from PIC","text":"","category":"page"},{"location":"examples/advanced/demo_batsrus_3dstructured/","page":"Tracing particle from PIC","title":"Tracing particle from PIC","text":"This page was generated using DemoCards.jl.","category":"page"},{"location":"examples/basics/demo_boris/","page":"Boris method","title":"Boris method","text":"EditURL = \"/home/runner/work/TestParticle.jl/TestParticle.jl/docs/examples/basics/demo_boris.jl\"","category":"page"},{"location":"examples/basics/demo_boris/#demo_boris","page":"Boris method","title":"Boris method","text":"","category":"section"},{"location":"examples/basics/demo_boris/","page":"Boris method","title":"Boris method","text":"(Image: Source code) (Image: compat) (Image: Author) (Image: Update time)","category":"page"},{"location":"examples/basics/demo_boris/","page":"Boris method","title":"Boris method","text":"This example demonstrates a single electron motion under a uniform B field. The E field is assumed to be zero such that there is no particle acceleration. We use the Boris method for phase space conservation under a fixed time step. This is compared against other ODE general algorithms for performance and accuracy.","category":"page"},{"location":"examples/basics/demo_boris/","page":"Boris method","title":"Boris method","text":"using TestParticle\nusing StaticArrays\nusing OrdinaryDiffEq\nusing CairoMakie\n\nfunction plot_trajectory(sol_boris, sol1, sol2)\n f = Figure(size=(700, 600), fontsize=18)\n ax = Axis(f[1, 1], aspect=1, limits = (-3, 1, -2, 2),\n xlabel = \"X\",\n ylabel = \"Y\")\n idxs = (1, 2)\n ##TODO: wait for https://github.com/MakieOrg/Makie.jl/issues/3623 to be fixed!\n l0 = lines!(ax, sol_boris[1]; idxs, linewidth=2, label=\"Boris\")\n l1 = lines!(ax, sol1; idxs, linewidth=2, linestyle=:dashdot, label=\"Tsit5 fixed\")\n l2 = linesegments!(ax, sol2; idxs, linewidth=2, linestyle=:dot, label=\"Tsit5 adaptive\")\n\n ax.scene.plots[1].linewidth = 2\n ax.scene.plots[5].linewidth = 2\n\n ax.scene.plots[3].color = Makie.wong_colors()[2]\n ax.scene.plots[5].color = Makie.wong_colors()[3]\n\n scale!(ax.scene.plots[1], invrL, invrL)\n scale!(ax.scene.plots[3], invrL, invrL)\n scale!(ax.scene.plots[5], invrL, invrL)\n\n axislegend(position=:rt, framevisible=false)\n\n f\nend\n\nconst Bmag = 0.01\nuniform_B(x) = SA[0.0, 0.0, Bmag]\nuniform_E(x) = SA[0.0, 0.0, 0.0]\n\nx0 = [0.0, 0.0, 0.0]\nv0 = [0.0, 1e5, 0.0]\nstateinit = [x0..., v0...]\n\nparam = prepare(uniform_E, uniform_B, species=Electron)\n\n# Reference parameters\nconst tperiod = 2π / (abs(param[1]) * hypot(param[3]([0.0,0.0,0.0], 0.0)...))\nconst rL = hypot(v0...) / (abs(param[1]) * Bmag)\nconst invrL = 1 / rL;","category":"page"},{"location":"examples/basics/demo_boris/","page":"Boris method","title":"Boris method","text":"We first trace the particle for one period with a discrete time step of a quarter period.","category":"page"},{"location":"examples/basics/demo_boris/","page":"Boris method","title":"Boris method","text":"tspan = (0.0, tperiod)\ndt = tperiod / 4\n\nprob = TraceProblem(stateinit, tspan, param)\n\nsol_boris = TestParticle.solve(prob; dt, savestepinterval=1);","category":"page"},{"location":"examples/basics/demo_boris/","page":"Boris method","title":"Boris method","text":"Let's compare against the default ODE solver Tsit5 from DifferentialEquations.jl, in both fixed time step mode and adaptive mode:","category":"page"},{"location":"examples/basics/demo_boris/","page":"Boris method","title":"Boris method","text":"prob = ODEProblem(trace!, stateinit, tspan, param)\nsol1 = solve(prob, Tsit5(); adaptive=false, dt, dense=false, saveat=dt);\nsol2 = solve(prob, Tsit5());\n\n# Visualization\nf = plot_trajectory(sol_boris, sol1, sol2)","category":"page"},{"location":"examples/basics/demo_boris/","page":"Boris method","title":"Boris method","text":"(Image: )","category":"page"},{"location":"examples/basics/demo_boris/","page":"Boris method","title":"Boris method","text":"It is clear that the Boris method comes with larger phase errors (mathcalO(Delta t)) compared with Tsit5. The phase error gets smaller using a smaller dt:","category":"page"},{"location":"examples/basics/demo_boris/","page":"Boris method","title":"Boris method","text":"dt = tperiod / 8\n\nprob = TraceProblem(stateinit, tspan, param)\n\nsol_boris = TestParticle.solve(prob; dt, savestepinterval=1);\n\nprob = ODEProblem(trace!, stateinit, tspan, param)\nsol1 = solve(prob, Tsit5(); adaptive=false, dt, dense=false, saveat=dt);\n\n# Visualization\nf = plot_trajectory(sol_boris, sol1, sol2)","category":"page"},{"location":"examples/basics/demo_boris/","page":"Boris method","title":"Boris method","text":"(Image: )","category":"page"},{"location":"examples/basics/demo_boris/","page":"Boris method","title":"Boris method","text":"The Boris pusher shines when we do long time tracing, which is fast and conserves energy:","category":"page"},{"location":"examples/basics/demo_boris/","page":"Boris method","title":"Boris method","text":"tspan = (0.0, 200*tperiod)\ndt = tperiod / 12\n\nprob_boris = TraceProblem(stateinit, tspan, param)\nprob = ODEProblem(trace!, stateinit, tspan, param)\n\nsol_boris = TestParticle.solve(prob_boris; dt, savestepinterval=10);\nsol1 = solve(prob, Tsit5(); adaptive=false, dt, dense=false, saveat=dt);\nsol2 = solve(prob, Tsit5());\n\n# Visualization\nf = plot_trajectory(sol_boris, sol1, sol2)","category":"page"},{"location":"examples/basics/demo_boris/","page":"Boris method","title":"Boris method","text":"(Image: )","category":"page"},{"location":"examples/basics/demo_boris/","page":"Boris method","title":"Boris method","text":"Fixed time step Tsit5() is ok, but adaptive Tsit5() is pretty bad for long time evolutions. The change in radius indicates change in energy, which is sometimes known as numerical heating.","category":"page"},{"location":"examples/basics/demo_boris/","page":"Boris method","title":"Boris method","text":"Another aspect to compare is performance:","category":"page"},{"location":"examples/basics/demo_boris/","page":"Boris method","title":"Boris method","text":"@time sol_boris = TestParticle.solve(prob_boris; dt, savestepinterval=10)[1];\n@time sol1 = solve(prob, Tsit5(); adaptive=false, dt, dense=false, saveat=dt);\n@time sol2 = solve(prob, Tsit5());","category":"page"},{"location":"examples/basics/demo_boris/","page":"Boris method","title":"Boris method","text":" 0.000117 seconds (257 allocations: 21.609 KiB)\n 0.000832 seconds (2.46 k allocations: 377.031 KiB)\n 0.000732 seconds (11.86 k allocations: 1.329 MiB)\n","category":"page"},{"location":"examples/basics/demo_boris/","page":"Boris method","title":"Boris method","text":"We can extract the solution (x, y, z, vx, vy, vz) at any given time by performing a linear interpolation:","category":"page"},{"location":"examples/basics/demo_boris/","page":"Boris method","title":"Boris method","text":"t = tspan[2] / 2\nsol_boris(t)","category":"page"},{"location":"examples/basics/demo_boris/","page":"Boris method","title":"Boris method","text":"6-element StaticArraysCore.MVector{6, Float64} with indices SOneTo(6):\n -3.911568318655031e-5\n -5.5137270837814596e-5\n 0.0\n 99639.26547015733\n 8486.269885387323\n 0.0","category":"page"},{"location":"examples/basics/demo_boris/","page":"Boris method","title":"Boris method","text":"The Boris method is faster and consumes less memory. However, in practice, it is pretty hard to find an optimal algorithm. When calling OrdinaryDiffEq.jl, we recommend using Vern9() as a starting point instead of Tsit5(), especially combined with adaptive timestepping.","category":"page"},{"location":"examples/basics/demo_boris/","page":"Boris method","title":"Boris method","text":"","category":"page"},{"location":"examples/basics/demo_boris/","page":"Boris method","title":"Boris method","text":"This page was generated using DemoCards.jl and Literate.jl.","category":"page"},{"location":"examples/basics/demo_FLR/","page":"Finite-Larmor-Radius effect","title":"Finite-Larmor-Radius effect","text":"EditURL = \"/home/runner/work/TestParticle.jl/TestParticle.jl/docs/examples/basics/demo_FLR.jl\"","category":"page"},{"location":"examples/basics/demo_FLR/#demo_FLR","page":"Finite-Larmor-Radius effect","title":"Finite-Larmor-Radius effect","text":"","category":"section"},{"location":"examples/basics/demo_FLR/","page":"Finite-Larmor-Radius effect","title":"Finite-Larmor-Radius effect","text":"(Image: Source code) (Image: compat) (Image: Author) (Image: Update time)","category":"page"},{"location":"examples/basics/demo_FLR/","page":"Finite-Larmor-Radius effect","title":"Finite-Larmor-Radius effect","text":"More theoretical details can be found in Non-uniform E Field.","category":"page"},{"location":"examples/basics/demo_FLR/","page":"Finite-Larmor-Radius effect","title":"Finite-Larmor-Radius effect","text":"using TestParticle\nusing TestParticle: get_gc\nusing OrdinaryDiffEq\nusing StaticArrays\nusing LinearAlgebra: ×, ⋅, norm, normalize\nusing Tensors: laplace\nimport Tensors: Vec as Vec3\n# using SpecialFunctions\nusing CairoMakie\n\nfunction uniform_B(x)\n return SA[0, 0, 1e-8]\nend\n\nfunction nonuniform_E(x)\n return SA[1e-9*cos(0.3*x[1]), 0, 0]\nend\n\n# trace the orbit of the guiding center\nfunction trace_gc!(dx, x, p, t)\n q2m, E, B, sol = p\n xu = sol(t)\n xp = @view xu[1:3]\n Bv = B(xp)\n b = normalize(Bv)\n v_par = (xu[4:6] ⋅ b) .* b # (v ⋅ b)b\n v_perp = xu[4:6] - v_par\n r4 = (norm(v_perp) / q2m / norm(Bv))^2 / 4\n EB(x) = (E(x) × B(x)) / norm(B(x))^2\n # dx[1:3] = EB(xp) + v_par\n dx[1:3] = EB(x) + r4*laplace.(EB, Vec3(x...)) + v_par\n\n # more accurate\n # dx[1:3] = besselj0(0.3*norm(v_perp)/q2m/norm(Bv))*EB(x) + v_par\nend\n\n# Initial condition\nstateinit = let x0 = [1.0, 0, 0], v0 = [0.0, 1.0, 0.1]\n [x0..., v0...]\nend\n# Time span\ntspan = (0, 20)\nparam = prepare(nonuniform_E, uniform_B, species=Proton)\nprob = ODEProblem(trace!, stateinit, tspan, param)\nsol = solve(prob, Vern9())\n\ngc = get_gc(param)\ngc_x0 = gc(stateinit)\nprob_gc = ODEProblem(trace_gc!, gc_x0, tspan, (param..., sol))\nsol_gc = solve(prob_gc, Tsit5(); save_idxs=[1,2,3])\n\n# numeric result and analytic result\nf = Figure(fontsize=18)\nax = Axis3(f[1, 1],\n title = \"Finite Larmor Radius Effect\",\n xlabel = \"x [m]\",\n ylabel = \"y [m]\",\n zlabel = \"z [m]\",\n aspect = :data,\n azimuth = 0.3π,\n)\n\ngc_plot(x, y, z, vx, vy, vz) = (gc(SA[x, y, z, vx, vy, vz])...,)\n\nlines!(ax, sol, idxs=(1, 2, 3))\nlines!(ax, sol, idxs=(gc_plot, 1, 2, 3, 4, 5, 6))\nlines!(ax, sol_gc, idxs=(1, 2, 3))\n\nfor i in 1:3\n ##TODO: wait for https://github.com/MakieOrg/Makie.jl/issues/3623 to be fixed!\n ax.scene.plots[9+2*i-1].color = Makie.wong_colors()[i]\nend\n","category":"page"},{"location":"examples/basics/demo_FLR/","page":"Finite-Larmor-Radius effect","title":"Finite-Larmor-Radius effect","text":"(Image: )","category":"page"},{"location":"examples/basics/demo_FLR/","page":"Finite-Larmor-Radius effect","title":"Finite-Larmor-Radius effect","text":"","category":"page"},{"location":"examples/basics/demo_FLR/","page":"Finite-Larmor-Radius effect","title":"Finite-Larmor-Radius effect","text":"This page was generated using DemoCards.jl and Literate.jl.","category":"page"},{"location":"examples/basics/demo_energy_conservation/","page":"Energy Conservation","title":"Energy Conservation","text":"EditURL = \"/home/runner/work/TestParticle.jl/TestParticle.jl/docs/examples/basics/demo_energy_conservation.jl\"","category":"page"},{"location":"examples/basics/demo_energy_conservation/#demo_energy_conservation","page":"Energy Conservation","title":"Energy Conservation","text":"","category":"section"},{"location":"examples/basics/demo_energy_conservation/","page":"Energy Conservation","title":"Energy Conservation","text":"(Image: Source code) (Image: compat) (Image: Author) (Image: Update time)","category":"page"},{"location":"examples/basics/demo_energy_conservation/","page":"Energy Conservation","title":"Energy Conservation","text":"This example demonstrates the energy conservation of a single proton motion in two cases. The first one is under a uniform B field and zero E field. The second on is under a zero B field and uniform E field.","category":"page"},{"location":"examples/basics/demo_energy_conservation/","page":"Energy Conservation","title":"Energy Conservation","text":"using TestParticle\nusing OrdinaryDiffEq\nusing StaticArrays\nusing LinearAlgebra: ×\nusing CairoMakie\n\nconst B₀ = 1e-8 # [T]\nconst E₀ = 3e-2 # [V/m]\n\n\"f2\"\nfunction location!(dx, v, x, p::TestParticle.TPTuple, t)\n dx .= v\nend\n\n\"f1\"\nfunction lorentz!(dv, v, x, p::TestParticle.TPTuple, t)\n q2m, E, B = p\n dv .= q2m*(E(x, t) + v × (B(x, t)))\nend\n\n### Initialize field\n\nfunction uniform_B(x)\n return SA[0, 0, B₀]\nend\n\nfunction uniform_E(x)\n return SA[E₀, 0.0, 0.0]\nend\n\nfunction zero_B(x)\n return SA[0.0, 0.0, 0.0]\nend\n\nfunction zero_E(x)\n return SA[0.0, 0.0, 0.0]\nend\n\n\"Check energy conservation.\"\nE(dx, dy, dz) = 1 // 2 * (dx^2 + dy^2 + dz^2)\n\n### Initialize particles\n\nx0 = [0.0, 0, 0]\nv0 = [0.0, 1e2, 0.0]\nstateinit = [x0..., v0...]\ntspan_proton = (0.0, 2000.0);","category":"page"},{"location":"examples/basics/demo_energy_conservation/","page":"Energy Conservation","title":"Energy Conservation","text":"Uniform B field and zero E field","category":"page"},{"location":"examples/basics/demo_energy_conservation/","page":"Energy Conservation","title":"Energy Conservation","text":"param_proton = prepare(zero_E, uniform_B, species=Proton)\n\n### Solve for the trajectories\n\nprob_p = DynamicalODEProblem(lorentz!, location!, v0, x0, tspan_proton, param_proton)\n\nΩᵢ = TestParticle.qᵢ * B₀ / TestParticle.mᵢ\nTᵢ = 2π / Ωᵢ\nprintln(\"Number of gyrations: \", tspan_proton[2] / Tᵢ)\n\nsol = solve(prob_p, ImplicitMidpoint(), dt = Tᵢ/15)\n\nf = Figure(fontsize=18)\nax = Axis(f[1, 1],\n title = \"Proton in a uniform B field and zero E field\",\n xlabel = \"x\",\n ylabel = \"y\",\n aspect = 1,\n)\n\nlines!(ax, sol, idxs=(1, 2))\n","category":"page"},{"location":"examples/basics/demo_energy_conservation/","page":"Energy Conservation","title":"Energy Conservation","text":"(Image: )","category":"page"},{"location":"examples/basics/demo_energy_conservation/","page":"Energy Conservation","title":"Energy Conservation","text":"Zero B field and uniform E field","category":"page"},{"location":"examples/basics/demo_energy_conservation/","page":"Energy Conservation","title":"Energy Conservation","text":"param_proton = prepare(uniform_E, zero_B, species=Proton)\n\n# acceleration, [m/s²]\na = param_proton[1] * E₀\n# predicted final speed, [m/s]\nv_final_predict = a * tspan_proton[2]\n# predicted travel distance, [m/s]\nd_final_predict = 0.5 * tspan_proton[2] * v_final_predict\n# predicted energy gain, [eV]\nE_predict = E₀ * d_final_predict\n\nprob_p = DynamicalODEProblem(lorentz!, location!, v0, x0, tspan_proton, param_proton)\n\nsol = solve(prob_p, Vern6())\n\nenergy = map(x -> E(x[1:3]...), sol.u) .* TestParticle.mᵢ;","category":"page"},{"location":"examples/basics/demo_energy_conservation/","page":"Energy Conservation","title":"Energy Conservation","text":"Predicted final speed","category":"page"},{"location":"examples/basics/demo_energy_conservation/","page":"Energy Conservation","title":"Energy Conservation","text":"predicted final speed: 5.744086746808526e9 [m/s]\n","category":"page"},{"location":"examples/basics/demo_energy_conservation/","page":"Energy Conservation","title":"Energy Conservation","text":"Simulated final speed","category":"page"},{"location":"examples/basics/demo_energy_conservation/","page":"Energy Conservation","title":"Energy Conservation","text":"simulated final speed: 5.744086746808613e9 [m/s]\n","category":"page"},{"location":"examples/basics/demo_energy_conservation/","page":"Energy Conservation","title":"Energy Conservation","text":"Predicted travel distance","category":"page"},{"location":"examples/basics/demo_energy_conservation/","page":"Energy Conservation","title":"Energy Conservation","text":"predicted travel distance: 5.744086746808526e12 [m]\n","category":"page"},{"location":"examples/basics/demo_energy_conservation/","page":"Energy Conservation","title":"Energy Conservation","text":"Simulated travel distance","category":"page"},{"location":"examples/basics/demo_energy_conservation/","page":"Energy Conservation","title":"Energy Conservation","text":"simulated travel distance: 5.744086746768604e12 [m]\n","category":"page"},{"location":"examples/basics/demo_energy_conservation/","page":"Energy Conservation","title":"Energy Conservation","text":"Predicted final energy","category":"page"},{"location":"examples/basics/demo_energy_conservation/","page":"Energy Conservation","title":"Energy Conservation","text":"predicted energy gain: 1.723226024042558e11 [eV]\n","category":"page"},{"location":"examples/basics/demo_energy_conservation/","page":"Energy Conservation","title":"Energy Conservation","text":"Simulated final energy","category":"page"},{"location":"examples/basics/demo_energy_conservation/","page":"Energy Conservation","title":"Energy Conservation","text":"simulated final energy: 1.7232260240426102e11 [eV]\n","category":"page"},{"location":"examples/basics/demo_energy_conservation/","page":"Energy Conservation","title":"Energy Conservation","text":"","category":"page"},{"location":"examples/basics/demo_energy_conservation/","page":"Energy Conservation","title":"Energy Conservation","text":"This page was generated using DemoCards.jl and Literate.jl.","category":"page"},{"location":"examples/advanced/demo_analytic_magnetosphere/","page":"Analytical magnetosphere","title":"Analytical magnetosphere","text":"EditURL = \"/home/runner/work/TestParticle.jl/TestParticle.jl/docs/examples/advanced/demo_analytic_magnetosphere.jl\"","category":"page"},{"location":"examples/advanced/demo_analytic_magnetosphere/#demo_analytic_magnetosphere","page":"Analytical magnetosphere","title":"Analytical magnetosphere","text":"","category":"section"},{"location":"examples/advanced/demo_analytic_magnetosphere/","page":"Analytical magnetosphere","title":"Analytical magnetosphere","text":"(Image: Source code) (Image: compat) (Image: Author) (Image: Update time)","category":"page"},{"location":"examples/advanced/demo_analytic_magnetosphere/","page":"Analytical magnetosphere","title":"Analytical magnetosphere","text":"This demo shows how to trace particles in a vacuum superposition of a dipolar magnetic field mathbfB_D with a uniform background magnetic field mathbfB_mathrmIMF. In this slightly modified dipole field, magnetic null points appear near 14 Earth's radii, and the particle orbits are also distorted from the idealized motions in Demo: magnetic dipole.","category":"page"},{"location":"examples/advanced/demo_analytic_magnetosphere/","page":"Analytical magnetosphere","title":"Analytical magnetosphere","text":"using TestParticle\nusing TestParticle: getB_dipole, getE_dipole, sph2cart, mᵢ, qᵢ, c, Rₑ\nusing OrdinaryDiffEq\nusing StaticArrays\nusing FieldTracer\nusing CairoMakie\n\nfunction getB_superposition(xu)\n getB_dipole(xu) + SA[0.0, 0.0, -10e-9]\nend\n\n\"Boundary condition check.\"\nfunction isoutofdomain(u, p, t)\n rout = 18Rₑ\n if hypot(u[1], u[2], u[3]) < 1.1Rₑ ||\n abs(u[1]) > rout || abs(u[2]) > rout || abs(u[3]) > rout\n return true\n else\n return false\n end\nend\n\n\"Set initial conditions.\"\nfunction prob_func(prob, i, repeat)\n # initial particle energy\n Ek = 5e3 # [eV]\n # initial velocity, [m/s]\n v₀ = sph2cart(c*sqrt(1-1/(1+Ek*qᵢ/(mᵢ*c^2))^2), 0.0, π/4)\n # initial position, [m]\n r₀ = sph2cart(13*Rₑ, π*i, π/2)\n\n prob = remake(prob; u0 = [r₀..., v₀...])\nend\n\n# obtain field\nparam = prepare(getE_dipole, getB_superposition)\nstateinit = zeros(6) # particle position and velocity to be modified\ntspan = (0.0, 2000.0)\ntrajectories = 2\n\nprob = ODEProblem(trace!, stateinit, tspan, param)\nensemble_prob = EnsembleProblem(prob; prob_func, safetycopy=false)\n\n# See https://docs.sciml.ai/DiffEqDocs/stable/basics/common_solver_opts/\n# for the solver options\nsols = solve(ensemble_prob, Vern9(), EnsembleSerial(); reltol=1e-5,\n trajectories, isoutofdomain, dense=true, save_on=true)\n\n### Visualization\n\nf = Figure(fontsize=18)\nax = Axis3(f[1, 1],\n title = \"5 keV Protons in a vacuum superposition magnetosphere\",\n xlabel = \"x [Re]\",\n ylabel = \"y [Re]\",\n zlabel = \"z [Re]\",\n aspect = :data,\n limits = (-14, 14, -14, 14, -5, 5)\n)\n\ninvRE = 1 / Rₑ\n\nfor (i, sol) in enumerate(sols)\n l = lines!(ax, sol, idxs=(1, 2, 3))\n ##TODO: wait for https://github.com/MakieOrg/Makie.jl/issues/3623 to be fixed!\n scale!(ax.scene.plots[9+2*i-1], invRE, invRE, invRE)\n ax.scene.plots[9+2*i-1].color = Makie.wong_colors()[i]\nend\n\n# Field lines\nfunction get_numerical_field(x, y, z)\n bx = zeros(length(x), length(y), length(z))\n by = similar(bx)\n bz = similar(bx)\n\n for i in CartesianIndices(bx)\n pos = [x[i[1]], y[i[2]], z[i[3]]]\n bx[i], by[i], bz[i] = getB_superposition(pos)\n end\n\n bx, by, bz\nend\n\nfunction trace_field!(ax, x, y, z, unitscale)\n bx, by, bz = get_numerical_field(x, y, z)\n\n zs = 0.0\n nr, nϕ = 8, 4\n dϕ = 2π / nϕ\n for r in range(8Rₑ, 16Rₑ, length=nr), ϕ in range(0, 2π-dϕ, length=nϕ)\n xs = r * cos(ϕ)\n ys = r * sin(ϕ)\n\n x1, y1, z1 = FieldTracer.trace(bx, by, bz, xs, ys, zs, x, y, z; ds=0.1, maxstep=10000)\n\n lines!(ax, x1.*unitscale, y1.*unitscale, z1.*unitscale, color=:gray)\n end\nend\n\nx = range(-18Rₑ, 18Rₑ, length=50)\ny = range(-18Rₑ, 18Rₑ, length=50)\nz = range(-18Rₑ, 18Rₑ, length=50)\n\ntrace_field!(ax, x, y, z, invRE)\n","category":"page"},{"location":"examples/advanced/demo_analytic_magnetosphere/","page":"Analytical magnetosphere","title":"Analytical magnetosphere","text":"(Image: )","category":"page"},{"location":"examples/advanced/demo_analytic_magnetosphere/","page":"Analytical magnetosphere","title":"Analytical magnetosphere","text":"","category":"page"},{"location":"examples/advanced/demo_analytic_magnetosphere/","page":"Analytical magnetosphere","title":"Analytical magnetosphere","text":"This page was generated using DemoCards.jl and Literate.jl.","category":"page"},{"location":"api/#Internal","page":"API","title":"Internal","text":"","category":"section"},{"location":"api/#Public-APIs","page":"API","title":"Public APIs","text":"","category":"section"},{"location":"api/","page":"API","title":"API","text":"Modules = [TestParticle]\nPrivate = false","category":"page"},{"location":"api/#TestParticle.BiMaxwellian","page":"API","title":"TestParticle.BiMaxwellian","text":"Type for BiMaxwellian velocity distributions with respect to the magnetic field.\n\n\n\n\n\n","category":"type"},{"location":"api/#TestParticle.BiMaxwellian-Union{Tuple{U}, Tuple{T}, Tuple{Vector{U}, Vector{T}, T, T, Any}} where {T<:AbstractFloat, U<:AbstractFloat}","page":"API","title":"TestParticle.BiMaxwellian","text":"BiMaxwellian(B::Vector{U}, u0::Vector{T}, ppar::T, pperp::T, n; m=mᵢ)\n\nConstruct 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.\n\n\n\n\n\n","category":"method"},{"location":"api/#TestParticle.Maxwellian","page":"API","title":"TestParticle.Maxwellian","text":"Type for Maxwellian velocity distributions. \n\n\n\n\n\n","category":"type"},{"location":"api/#TestParticle.Maxwellian-Union{Tuple{T}, Tuple{Vector{T}, T, Any}} where T","page":"API","title":"TestParticle.Maxwellian","text":"Maxwellian(u0::Vector{T}, p::T, n; m=mᵢ)\n\nConstruct a Maxwellian distribution with bulk velocity u0, thermal pressure p, and number density n in SI units. The default particle is proton.\n\n\n\n\n\n","category":"method"},{"location":"api/#TestParticle.prepare-Union{Tuple{TB}, Tuple{TE}, Tuple{Meshes.CartesianGrid, TE, TB}} where {TE, TB}","page":"API","title":"TestParticle.prepare","text":"prepare(grid::CartesianGrid, E, B; kwargs...) -> (q2m, E, B)\n\nReturn a tuple consists of particle charge-mass ratio for a prescribed species and interpolated EM field functions.\n\nkeywords\n\norder::Int=1: order of interpolation in [1,2,3].\nbc::Int=1: type of boundary conditions, 1 -> NaN, 2 -> periodic.\nspecies::Species=Proton: particle species.\nq::AbstractFloat=1.0: particle charge. Only works when Species=User.\nm::AbstractFloat=1.0: particle mass. Only works when Species=User.\nprepare(grid::CartesianGrid, E, B, F; species=Proton, q=1.0, m=1.0) -> (q, m, E, B, F)\n\nReturn a tuple consists of particle charge, mass for a prescribed species of charge q and mass m, interpolated EM field functions, and external force F.\n\nprepare(x::AbstractRange, y::AbstractRange, z::AbstractRange, E, B; kwargs...) -> (q2m, E, B)\nprepare(x, y, E, B; kwargs...) -> (q2m, E, B)\nprepare(x::AbstractRange, E, B; kwargs...) -> (q2m, E, B)\n\nDirect range input for uniform grid in 2/3D is also accepted.\n\nprepare(E, B; kwargs...) -> (q2m, E, B)\n\nReturn a tuple consists of particle charge-mass ratio for a prescribed species of charge q and mass m and analytic EM field functions. Prescribed species are Electron and Proton; other species can be manually specified with species=Ion/User, q and m.\n\nprepare(E, B, F; kwargs...) -> (q, m, E, B, F)\n\nReturn a tuple consists of particle charge, mass for a prescribed species of charge q and mass m, analytic EM field functions, and external force F.\n\n\n\n\n\n","category":"method"},{"location":"api/#TestParticle.sample-Union{Tuple{Maxwellian{T}}, Tuple{T}} where T","page":"API","title":"TestParticle.sample","text":"sample(vdf::Maxwellian)\n\nSample a 3D velocity from a Maxwellian distribution vdf using the Box-Muller method.\n\nsample(vdf::BiMaxwellian)\n\nSample a 3D velocity from a BiMaxwellian distribution vdf using the Box-Muller method.\n\n\n\n\n\n","category":"method"},{"location":"api/#TestParticle.trace!-Tuple{Any, Any, Tuple{Float64, TestParticle.AbstractField, TestParticle.AbstractField}, Any}","page":"API","title":"TestParticle.trace!","text":"trace!(dy, y, p::TPTuple, t)\ntrace!(dy, y, p::FullTPTuple, t)\n\nODE equations for charged particle moving in static EM field with in-place form.\n\nODE equations for charged particle moving in static EM field and external force field with in-place form.\n\n\n\n\n\n","category":"method"},{"location":"api/#TestParticle.trace-Tuple{Any, Tuple{Float64, TestParticle.AbstractField, TestParticle.AbstractField}, Any}","page":"API","title":"TestParticle.trace","text":"trace(y, p::TPTuple, t) -> SVector{6, Float64}\ntrace(y, p::FullTPTuple, t) -> SVector{6, Float64}\n\nODE equations for charged particle moving in static EM field with out-of-place form.\n\nODE equations for charged particle moving in static EM field and external force field with out-of-place form.\n\n\n\n\n\n","category":"method"},{"location":"api/#TestParticle.trace_normalized!-Tuple{Any, Any, Tuple{AbstractFloat, TestParticle.AbstractField, TestParticle.AbstractField}, Any}","page":"API","title":"TestParticle.trace_normalized!","text":"trace_normalized!(dy, y, p::TPNormalizedTuple, t)\n\nNormalized ODE equations for charged particle moving in static EM field with in-place form. If the field is in 2D X-Y plane, periodic boundary should be applied for the field in z via the extrapolation function provided by Interpolations.jl.\n\n\n\n\n\n","category":"method"},{"location":"api/#TestParticle.trace_relativistic!-Tuple{Any, Any, Tuple{Float64, TestParticle.AbstractField, TestParticle.AbstractField}, Any}","page":"API","title":"TestParticle.trace_relativistic!","text":"trace_relativistic!(dy, y, p::TPTuple, t)\n\nODE equations for relativistic charged particle moving in static EM field with in-place form.\n\n\n\n\n\n","category":"method"},{"location":"api/#TestParticle.trace_relativistic-Tuple{Any, Tuple{Float64, TestParticle.AbstractField, TestParticle.AbstractField}, Any}","page":"API","title":"TestParticle.trace_relativistic","text":"trace_relativistic(y, p::TPTuple, t) -> SVector{6, Float64}\n\nODE equations for relativistic charged particle moving in static EM field with out-of-place form.\n\n\n\n\n\n","category":"method"},{"location":"api/#TestParticle.trace_relativistic_normalized!-Tuple{Any, Any, Tuple{AbstractFloat, TestParticle.AbstractField, TestParticle.AbstractField}, Any}","page":"API","title":"TestParticle.trace_relativistic_normalized!","text":"trace_relativistic_normalized!(dy, y, p::TPNormalizedTuple, t)\n\nNormalized ODE equations for relativistic charged particle moving in static EM field with in-place form.\n\n\n\n\n\n","category":"method"},{"location":"api/#Private-types-and-methods","page":"API","title":"Private types and methods","text":"","category":"section"},{"location":"api/","page":"API","title":"API","text":"Modules = [TestParticle]\nPublic = false","category":"page"},{"location":"api/#TestParticle.AbstractTraceSolution","page":"API","title":"TestParticle.AbstractTraceSolution","text":"Abstract type for tracing solutions.\n\n\n\n\n\n","category":"type"},{"location":"api/#TestParticle.Field","page":"API","title":"TestParticle.Field","text":"Field{itd, F} <: AbstractField{itd}\n\nA representation of a field function f, defined by:\n\ntime-independent field\n\nmathbfF = F(mathbfx)\n\ntime-dependent field\n\nmathbfF = F(mathbfx t)\n\nArguments\n\nfield_function::Function: the function of field.\nitd::Bool: whether the field function is time dependent.\nF: the type of field_function.\n\n\n\n\n\n","category":"type"},{"location":"api/#TestParticle.FullTPTuple","page":"API","title":"TestParticle.FullTPTuple","text":"The type of parameter tuple for full test particle problem.\n\n\n\n\n\n","category":"type"},{"location":"api/#TestParticle.Species","page":"API","title":"TestParticle.Species","text":"Type for the particles, Proton, Electron, Ion, or User.\n\n\n\n\n\n","category":"type"},{"location":"api/#TestParticle.TPNormalizedTuple","page":"API","title":"TestParticle.TPNormalizedTuple","text":"The type of parameter tuple for normalized test particle problem.\n\n\n\n\n\n","category":"type"},{"location":"api/#TestParticle.TPTuple","page":"API","title":"TestParticle.TPTuple","text":"The type of parameter tuple for normal test particle problem.\n\n\n\n\n\n","category":"type"},{"location":"api/#TestParticle.TraceSolution-Union{Tuple{Any}, Tuple{deriv}, Tuple{Any, Type{deriv}}} where deriv","page":"API","title":"TestParticle.TraceSolution","text":"Interpolate solution at time x. Forward tracing only.\n\n\n\n\n\n","category":"method"},{"location":"api/#TestParticle.VDF","page":"API","title":"TestParticle.VDF","text":"Abstract type for velocity distribution functions.\n\n\n\n\n\n","category":"type"},{"location":"api/#TestParticle._boris!-NTuple{9, Any}","page":"API","title":"TestParticle._boris!","text":"Apply Boris method for particles with index in irange.\n\n\n\n\n\n","category":"method"},{"location":"api/#TestParticle._prepare-NTuple{4, Any}","page":"API","title":"TestParticle._prepare","text":"Prepare for advancing.\n\n\n\n\n\n","category":"method"},{"location":"api/#TestParticle.cross!-Tuple{Any, Any, Any}","page":"API","title":"TestParticle.cross!","text":"In-place cross product.\n\n\n\n\n\n","category":"method"},{"location":"api/#TestParticle.dipole-Tuple{Any, Any}","page":"API","title":"TestParticle.dipole","text":"Calculates the magnetic field from a dipole with magnetic moment M at r.\n\n\n\n\n\n","category":"method"},{"location":"api/#TestParticle.dipole_fieldline","page":"API","title":"TestParticle.dipole_fieldline","text":"dipole_fieldline(L, ϕ, nP)\n\nCreates nP points on one field line of the magnetic field from a dipole. In a centered dipole magnetic field model, the path along a given L shell can be described as r = L*cos²λ, where r is the radial distance (in planetary radii) to a point on the line, λ is its co-latitude, and L is the L-shell of interest.\n\n\n\n\n\n","category":"function"},{"location":"api/#TestParticle.getB_CS_harris-Tuple{Any, Any, Any}","page":"API","title":"TestParticle.getB_CS_harris","text":"getB_CS_harris(B₀, L)\n\nReturn the magnetic field at location r near a current sheet with magnetic strength B₀ and sheet length L.\n\n\n\n\n\n","category":"method"},{"location":"api/#TestParticle.getB_bottle-NTuple{8, Any}","page":"API","title":"TestParticle.getB_bottle","text":"getB_bottle(x, y, z, distance, a, b, I1, I2) -> Vector{Float}\n\nGet magnetic field from a magnetic bottle. Reference: https://en.wikipedia.org/wiki/Magneticmirror#Magneticbottles\n\nArguments\n\nx,y,z::Float: particle coordinates in [m].\ndistance::Float: distance between solenoids in [m].\na::Float: radius of each side coil in [m].\nb::Float: radius of central coil in [m].\nI1::Float: current in the solenoid times number of windings in side coils.\nI2::Float: current in the central solenoid times number of windings in the\n\ncentral loop.\n\n\n\n\n\n","category":"method"},{"location":"api/#TestParticle.getB_dipole-Tuple{Any}","page":"API","title":"TestParticle.getB_dipole","text":"Analytic magnetic field function for testing. Return in SI unit.\n\n\n\n\n\n","category":"method"},{"location":"api/#TestParticle.getB_mirror-NTuple{6, Any}","page":"API","title":"TestParticle.getB_mirror","text":"getB_mirror(x, y, z, distance, a, I1) -> Vector{Float}\n\nGet magnetic field from a magnetic mirror generated from two coils.\n\nArguments\n\nx,y,z::Float: particle coordinates in [m].\ndistance::Float: distance between solenoids in [m].\na::Float: radius of each side coil in [m].\nI1::Float: current in the solenoid times number of windings in side coils.\n\n\n\n\n\n","category":"method"},{"location":"api/#TestParticle.getB_tokamak_coil-NTuple{7, Any}","page":"API","title":"TestParticle.getB_tokamak_coil","text":"getB_tokamak_coil(x, y, z, a, b, ICoils, IPlasma)\n\nGet the magnetic field from a Tokamak topology consists of 16 coils. Original: https://github.com/BoschSamuel/Simulation-of-a-Tokamak-Fusion-Reactor/blob/master/Simulation2.m\n\nArguments\n\nx,y,z::Float: location in [m].\na::Float: radius of each coil in [m].\nb::Float: radius of central region in [m].\nICoil::Float: current in the coil times number of windings.\nIPlasma::Float: current of the plasma?\n\n\n\n\n\n","category":"method"},{"location":"api/#TestParticle.getB_tokamak_profile-Tuple{AbstractFloat, AbstractFloat, AbstractFloat, Any, AbstractFloat, AbstractFloat, AbstractFloat}","page":"API","title":"TestParticle.getB_tokamak_profile","text":"getB_tokamak_profile(x, y, z, q_profile, a, R₀, Bζ0)\n\nReconstruct the magnetic field distribution from a safe factor(q) profile. The formulations are from the book \"Tokamak 4th Edition\" by John Wesson.\n\nArguments\n\nx,y,z::Float: location in [m].\nq_profile::Function: profile of q. The variable of this function must be the normalized radius.\na::Float: minor radius [m].\nR₀::Float: major radius [m].\nBζ0::Float: toroidal magnetic field on axis [T].\n\n\n\n\n\n","category":"method"},{"location":"api/#TestParticle.getE_dipole-Tuple{Any}","page":"API","title":"TestParticle.getE_dipole","text":"Analytic electric field function for testing.\n\n\n\n\n\n","category":"method"},{"location":"api/#TestParticle.get_gc-Tuple{Union{Tuple{Float64, TestParticle.AbstractField, TestParticle.AbstractField}, Tuple{Float64, Float64, TestParticle.AbstractField, TestParticle.AbstractField, TestParticle.AbstractField}}}","page":"API","title":"TestParticle.get_gc","text":"get_gc(param::Union{TPTuple, FullTPTuple})\n\nGet three functions for plotting the orbit of guiding center.\n\nFor example:\n\nparam = prepare(E, B; species=Proton)\ngc = get_gc(params)\n# The definitions of stateinit, tspan, E and B are ignored.\nprob = ODEProblem(trace!, stateinit, tspan, param)\nsol = solve(prob, Vern7(); dt=2e-11)\n\nf = Figure(fontsize=18)\nax = Axis3(f[1, 1], aspect = :data)\ngc_plot(x,y,z,vx,vy,vz) = (gc([x,y,z,vx,vy,vz])...,)\nlines!(ax, sol, idxs=(gc_plot, 1, 2, 3, 4, 5, 6))\n\n\n\n\n\n","category":"method"},{"location":"api/#TestParticle.get_rotation_matrix-Tuple{AbstractVector{<:AbstractFloat}, Real}","page":"API","title":"TestParticle.get_rotation_matrix","text":"get_rotation_matrix(axis::AbstractVector, angle::Real) --> SMatrix{3,3}\n\nCreate a rotation matrix for rotating a 3D vector around a unit axis by an angle in radians. Reference: Rotation matrix from axis and angle\n\nExample\n\nusing LinearAlgebra\nv = [-0.5, 1.0, 1.0]\nv̂ = normalize(v)\nθ = deg2rad(-74)\nR = get_rotation_matrix(v̂, θ)\n\n\n\n\n\n","category":"method"},{"location":"api/#TestParticle.getchargemass-Tuple{TestParticle.Species, AbstractFloat, AbstractFloat}","page":"API","title":"TestParticle.getchargemass","text":"getchargemass(species::Species, q::AbstractFloat, m::AbstractFloat)\n\nReturn charge and mass for species. if species = User, input q and m are returned.\n\n\n\n\n\n","category":"method"},{"location":"api/#TestParticle.getinterp","page":"API","title":"TestParticle.getinterp","text":"getinterp(A, gridx, gridy, gridz, order::Int=1, bc::Int=1)\ngetinterp(A, gridx, gridy, order::Int=1, bc::Int=1)\n\nReturn a function for interpolating array A on the grid given by gridx, gridy, and gridz.\n\nArguments\n\norder::Int=1: order of interpolation in [1,2,3].\nbc::Int=1: type of boundary conditions, 1 -> NaN, 2 -> periodic, 3 -> Flat.\n\n\n\n\n\n","category":"function"},{"location":"api/#TestParticle.guiding_center-Tuple{Any, Tuple{Float64, TestParticle.AbstractField, TestParticle.AbstractField}}","page":"API","title":"TestParticle.guiding_center","text":"guiding_center(xu, param::Union{TPTuple, FullTPTuple})\n\nCalculate the coordinates of the guiding center according to the phase space coordinates of a particle. Reference: https://en.wikipedia.org/wiki/Guiding_center\n\nA simple definition:\n\nmathbfX=mathbfx-mfracmathbfvtimesmathbfBqB\n\n\n\n\n\n","category":"method"},{"location":"api/#TestParticle.is_time_dependent-Tuple{Function}","page":"API","title":"TestParticle.is_time_dependent","text":"Judge whether the field function is time dependent.\n\n\n\n\n\n","category":"method"},{"location":"api/#TestParticle.makegrid-Union{Tuple{Meshes.CartesianGrid{3, T}}, Tuple{T}} where T","page":"API","title":"TestParticle.makegrid","text":"Return uniform range from 2D/3D CartesianGrid.\n\n\n\n\n\n","category":"method"},{"location":"api/#TestParticle.set_axes_equal-Tuple{Any}","page":"API","title":"TestParticle.set_axes_equal","text":"set_axes_equal(ax)\n\nSet 3D plot axes to equal scale for Matplotlib. Make axes of 3D plot have equal scale so that spheres appear as spheres and cubes as cubes. Required since ax.axis('equal') and ax.set_aspect('equal') don't work on 3D.\n\n\n\n\n\n","category":"method"},{"location":"api/#TestParticle.solve","page":"API","title":"TestParticle.solve","text":"solve(prob::TraceProblem; trajectories::Int=1,\n savestepinterval::Int=1, isoutofdomain::Function=ODE_DEFAULT_ISOUTOFDOMAIN)\n\nTrace particles using the Boris method with specified prob.\n\nkeywords\n\ntrajectories::Int: number of trajectories to trace.\nsavestepinterval::Int: saving output interval.\nisoutofdomain::Function: a function with input of position and velocity vector xv that determines whether to stop tracing.\n\n\n\n\n\n","category":"function"},{"location":"api/#TestParticle.sph2cart-Tuple{Any, Any, Any}","page":"API","title":"TestParticle.sph2cart","text":"Convert from spherical to Cartesian coordinates vector.\n\n\n\n\n\n","category":"method"},{"location":"api/#TestParticle.update_location!-Tuple{Any, Any}","page":"API","title":"TestParticle.update_location!","text":"Update location in one timestep dt.\n\n\n\n\n\n","category":"method"},{"location":"api/#TestParticle.update_velocity!-NTuple{4, Any}","page":"API","title":"TestParticle.update_velocity!","text":"update_velocity!(xv, paramBoris, param, dt)\n\nUpdate velocity using the Boris method, Birdsall, Plasma Physics via Computer Simulation. Reference: https://apps.dtic.mil/sti/citations/ADA023511\n\n\n\n\n\n","category":"method"},{"location":"examples/basics/demo_gravity_drift/","page":"ExF drift","title":"ExF drift","text":"EditURL = \"/home/runner/work/TestParticle.jl/TestParticle.jl/docs/examples/basics/demo_gravity_drift.jl\"","category":"page"},{"location":"examples/basics/demo_gravity_drift/#demo_uniformB_gravity","page":"ExF drift","title":"ExF drift","text":"","category":"section"},{"location":"examples/basics/demo_gravity_drift/","page":"ExF drift","title":"ExF drift","text":"(Image: Source code) (Image: compat) (Image: Author) (Image: Author) (Image: Update time)","category":"page"},{"location":"examples/basics/demo_gravity_drift/","page":"ExF drift","title":"ExF drift","text":"This example demonstrates a single proton motion under uniform B and gravity fields.","category":"page"},{"location":"examples/basics/demo_gravity_drift/","page":"ExF drift","title":"ExF drift","text":"using TestParticle\nusing TestParticle: get_gc\nusing OrdinaryDiffEq\nusing StaticArrays\nusing CairoMakie\n\n# Analytic EM fields\nB(x) = SA[0.0, 1e-8, 0.0]\nE(x) = SA[0.0, 0.0, 0.0]\n\n# Earth's gravity\nF(x) = SA[0.0, 0.0, -TestParticle.mᵢ*9.8]\n\n# Initial static particle\nstateinit = let x0 = [1.0, 0.0, 0.0], v0 = [0.0, 0.0, 0.0]\n [x0..., v0...]\nend\n# Time span\ntspan = (0, 1.0)\n\nparam = prepare(E, B, F, species=Proton)\nprob = ODEProblem(trace!, stateinit, tspan, param)\nsol = solve(prob, Vern9())\n# drift in x-direction + free fall in z-direction\nf = lines(sol, idxs=(3,1);\n figure = (; size = (800, 400), fontsize=18),\n axis = (; title=\"ExF Drift\", xlabel=\"Z [m]\", ylabel=\"X [m]\", aspect = DataAspect())\n)\n","category":"page"},{"location":"examples/basics/demo_gravity_drift/","page":"ExF drift","title":"ExF drift","text":"(Image: )","category":"page"},{"location":"examples/basics/demo_gravity_drift/","page":"ExF drift","title":"ExF drift","text":"","category":"page"},{"location":"examples/basics/demo_gravity_drift/","page":"ExF drift","title":"ExF drift","text":"This page was generated using DemoCards.jl and Literate.jl.","category":"page"},{"location":"examples/advanced/demo_magneticbottle/","page":"Electron in a magnetic bottle","title":"Electron in a magnetic bottle","text":"EditURL = \"/home/runner/work/TestParticle.jl/TestParticle.jl/docs/examples/advanced/demo_magneticbottle.jl\"","category":"page"},{"location":"examples/advanced/demo_magneticbottle/#demo_electron_bottle","page":"Electron in a magnetic bottle","title":"Electron in a magnetic bottle","text":"","category":"section"},{"location":"examples/advanced/demo_magneticbottle/","page":"Electron in a magnetic bottle","title":"Electron in a magnetic bottle","text":"(Image: Source code) (Image: compat) (Image: Author) (Image: Update time)","category":"page"},{"location":"examples/advanced/demo_magneticbottle/","page":"Electron in a magnetic bottle","title":"Electron in a magnetic bottle","text":"This example shows how to trace non-relativistic and relativistic electrons in a stationary magnetic field that corresponds to a magnetic bottle. Reference wiki","category":"page"},{"location":"examples/advanced/demo_magneticbottle/","page":"Electron in a magnetic bottle","title":"Electron in a magnetic bottle","text":"using TestParticle\nusing TestParticle: getB_bottle\nusing OrdinaryDiffEq\nusing StaticArrays\nusing Printf\nusing CairoMakie\n\n### Obtain field\n\n# Magnetic bottle parameters in SI units\nconst I1 = 20. # current in the solenoid\nconst N1 = 45 # number of windings\nconst I2 = 20. # current in the central solenoid\nconst N2 = 45 # number of windings\nconst distance = 10. # distance between solenoids\nconst a = 4.0 # radius of each coil\nconst b = 8.0 # radius of central coil\n\nfunction getB(xu)\n SVector{3}(getB_bottle(xu[1], xu[2], xu[3], distance, a, b, I1*N1, I2*N2))\nend\n\nfunction getE(xu)\n SA[0.0, 0.0, 0.0]\nend\n\n### Initialize particles\nm = TestParticle.mₑ\nq = TestParticle.qₑ\nc = TestParticle.c\n\n# initial velocity, [m/s]\nv₀ = [2.75, 2.5, 1.3] .* 0.03c # confined\n# initial position, [m]\nr₀ = [0.8, 0.8, 0.0]\nstateinit = [r₀..., v₀...]\n\n# Theoretically we can take advantage of the fact that magnetic field does not\n# accelerate particles, so that γ remains constant. However, we are not doing\n# that here since it is not generally true in the EM field.\n\nparam = prepare(getE, getB; species=Electron)\ntspan = (0.0, 1e-5)\n\nprob_rel = ODEProblem(trace_relativistic!, stateinit, tspan, param)\nprob_non = ODEProblem(trace!, stateinit, tspan, param)\n\nvratio = √(v₀[1]^2+v₀[2]^2+v₀[3]^2)/c\nE = (1 / √(1 - vratio^2) - 1)*m*c^2/abs(q)/1e6\n\nv_str = @sprintf \"V = %4.2f %s\" vratio*100 \"% c\"\ne_str = @sprintf \"E = %6.4f MeV\" E\n\nprintln(v_str)\nprintln(e_str)\n\n# Default Tsit5() and many solvers does not work in this case!\nsol_rel = solve(prob_rel, AB4(); dt=3e-9)\nsol_non = solve(prob_non, AB4(); dt=3e-9)\n\n### Visualization\n\nf = Figure(fontsize=18)\nax1 = Axis3(f[1, 1];\n aspect = :data,\n title = \"Relativistic e⁻, \\n\"*v_str*\", \"*e_str\n )\nax2 = Axis3(f[1, 2];\n aspect = :data,\n title = \"Non-relativistic e⁻, \\n\"*v_str*\", \"*e_str\n )\n\nlines!(ax1, sol_rel, idxs=(1, 2, 3))\nlines!(ax2, sol_non, idxs=(1, 2, 3))\n\n# Plot coils\nθ = range(0, 2π, length=100)\nx = a.*cos.(θ)\ny = a.*sin.(θ)\nz = fill(distance/2, size(x))\nfor ax in (f[1,1], f[1,2])\n lines!(ax, x, y, z, color=(:red, 0.7))\nend\nz = fill(-distance/2, size(x))\nfor ax in (f[1,1], f[1,2])\n lines!(ax, x, y, z, color=(:red, 0.7))\nend\n\nx = b.*cos.(θ)\ny = b.*sin.(θ)\nz = fill(0.0, size(x))\nfor ax in (f[1,1], f[1,2])\n lines!(ax, x, y, z, color=(:red, 0.7))\nend\n\nusing FieldTracer\n\nxrange = range(-4, 4, length=20)\nyrange = range(-4, 4, length=20)\nzrange = range(-10, 10, length=20)\n\nBx, By, Bz = let x=xrange, y=yrange, z=zrange\n Bx = zeros(length(x), length(y), length(z))\n By = zeros(length(x), length(y), length(z))\n Bz = zeros(length(x), length(y), length(z))\n for k in eachindex(z), j in eachindex(y), i in eachindex(x)\n Bx[i,j,k], By[i,j,k], Bz[i,j,k] = getB([x[i], y[j], z[k]])\n end\n\n Bx, By, Bz\nend\n\nfor i in 0:8\n if i == 0\n xs, ys, zs = 0.0, 0.0, 0.0\n else\n xs, ys, zs = 3*cos(2π*(i-1)/8), 3*sin(2π*(i-1)/8), 0.0\n end\n x1, y1, z1 = FieldTracer.trace(Bx, By, Bz, xs, ys, zs, xrange, yrange, zrange;\n ds=0.1, maxstep=1000)\n for ax in (f[1,1], f[1,2])\n lines!(ax, x1, y1, z1, color=:black)\n end\nend\n","category":"page"},{"location":"examples/advanced/demo_magneticbottle/","page":"Electron in a magnetic bottle","title":"Electron in a magnetic bottle","text":"(Image: )","category":"page"},{"location":"examples/advanced/demo_magneticbottle/","page":"Electron in a magnetic bottle","title":"Electron in a magnetic bottle","text":"","category":"page"},{"location":"examples/advanced/demo_magneticbottle/","page":"Electron in a magnetic bottle","title":"Electron in a magnetic bottle","text":"This page was generated using DemoCards.jl and Literate.jl.","category":"page"},{"location":"examples/advanced/demo_magneticmirror/","page":"Magnetic mirror","title":"Magnetic mirror","text":"EditURL = \"/home/runner/work/TestParticle.jl/TestParticle.jl/docs/examples/advanced/demo_magneticmirror.jl\"","category":"page"},{"location":"examples/advanced/demo_magneticmirror/#demo_magnetic_mirror","page":"Magnetic mirror","title":"Magnetic mirror","text":"","category":"section"},{"location":"examples/advanced/demo_magneticmirror/","page":"Magnetic mirror","title":"Magnetic mirror","text":"(Image: Source code) (Image: compat) (Image: Author) (Image: Author) (Image: Update time)","category":"page"},{"location":"examples/advanced/demo_magneticmirror/","page":"Magnetic mirror","title":"Magnetic mirror","text":"This example demonstrates the particle motion trajectory in a magnetic mirror and also illustrates the conservation of magnetic moment. From the third figure on the right, it can be seen that the zero-order quantity of magnetic moment is conserved, but its high-order part is not conserved under this definition of magnetic moment and oscillates rapidly. We can observe from this the oscillation characteristics of magnetic moments at different levels.","category":"page"},{"location":"examples/advanced/demo_magneticmirror/","page":"Magnetic mirror","title":"Magnetic mirror","text":"This example is based on demo_magneticbottle.jl.","category":"page"},{"location":"examples/advanced/demo_magneticmirror/","page":"Magnetic mirror","title":"Magnetic mirror","text":"using TestParticle\nusing TestParticle: getB_mirror\nusing OrdinaryDiffEq\nusing StaticArrays\nusing LinearAlgebra: normalize, norm, ⋅\nusing CairoMakie\n\n### Obtain field\n\n# Magnetic mirror parameters in SI units\nconst I = 20. # current in the solenoid\nconst N = 45 # number of windings\nconst distance = 10. # distance between solenoids\nconst a = 4.0 # radius of each coil\n\nfunction getB(xu)\n SVector{3}(getB_mirror(xu[1], xu[2], xu[3], distance, a, I*N))\nend\n\ngetE(xu) = SA[0.0, 0.0, 0.0]\n\n# velocity in the direction perpendicular to the magnetic field\nfunction v_perp(t, x, y, z, vx, vy, vz)\n xu = SA[x, y, z, vx, vy, vz]\n vu = @view xu[4:6]\n B = getB(xu)\n b = normalize(B)\n v_pa = (vu ⋅ b) .* b\n\n (t, norm(vu - v_pa))\nend\n\n# magnetic field\nabsB(t, x, y, z) = (t, hypot(getB(SA[x,y,z])...))\n\n# μ, magnetic moment\nfunction mu(t, x, y, z, vx, vy, vz)\n xu = SA[x, y, z, vx, vy, vz]\n\n (t, v_perp(t, x, y, z, vx, vy, vz)[2]^2 / hypot(getB(xu)...) )\nend\n\nEt(xu) = hypot(xu[4:6]...)\n\n### Initialize particles\nm = TestParticle.mₑ\nq = TestParticle.qₑ\nc = TestParticle.c\n\n# initial velocity, [m/s]\nv₀ = [2.75, 2.5, 1.3] .* 0.001c # confined\n##v₀ = [0.25, 0.25, 5.9595] .* 0.01c # escaped\n# initial position, [m]\nr₀ = [0.8, 0.8, 0.0] # confined\n##r₀ = [1.5, 1.5, 2.4] # escaped\nstateinit = [r₀..., v₀...]\n\nparam = prepare(getE, getB; species=Electron)\ntspan = (0.0, 1e-4)\n\nprob = ODEProblem(trace!, stateinit, tspan, param)\n\n# Default Tsit5() and many solvers does not work in this case!\n# AB4() has better performance in maintaining magnetic moment conservation compared to AB3().\nsol_non = solve(prob, AB4(); dt=3e-9)\n\n### Visualization\nf = Figure(size=(900, 600), fontsize=18)\nax1 = Axis3(f[1:3, 1],\n title = \"Magnetic Mirror\",\n xlabel = \"x [m]\",\n ylabel = \"y [m]\",\n zlabel = \"z [m]\",\n aspect = :data,\n azimuth = 0.9π,\n elevation = 0.1π\n)\nax2 = Axis(f[1,2], xlabel = \"time [s]\", ylabel = \"B [T]\")\nax3 = Axis(f[2,2], xlabel = \"time [s]\", ylabel = \"v_perp [m/s]\")\nax4 = Axis(f[3,2], xlabel = \"time [s]\", ylabel = \"mu\")\n\nlines!(ax1, sol_non, idxs=(1, 2, 3))\nlines!(ax2, sol_non, idxs=(absB, 0, 1, 2, 3))\nlines!(ax3, sol_non, idxs=(v_perp, 0, 1, 2, 3, 4, 5, 6))\nlines!(ax4, sol_non, idxs=(mu, 0, 1, 2, 3, 4, 5, 6))\n\n# Plot coils\nθ = range(0, 2π, length=100)\nx = a.*cos.(θ)\ny = a.*sin.(θ)\nz = fill(distance/2, size(x))\nlines!(ax1, x, y, z, color=:red)\nz = fill(-distance/2, size(x))\nlines!(ax1, x, y, z, color=:red)\n\n# # The distribution of magnetic field along the z-axis or x-axis\n# Bz(z) = hypot(getB(SA[0.0, 0.0, z])...)\n# Bx(x) = hypot(getB(SA[x, 0.0, 0.5*distance])...)\n# z = collect(-10:0.01:10)\n# x = collect(-0.99*a:0.01:0.99*a)\n# # Ba = Bz.(z)\n# Ba = Bx.(x)\n# # lines(z, Ba, color=:red)\n# lines(x, Ba, color=:red)\n","category":"page"},{"location":"examples/advanced/demo_magneticmirror/","page":"Magnetic mirror","title":"Magnetic mirror","text":"(Image: )","category":"page"},{"location":"examples/advanced/demo_magneticmirror/","page":"Magnetic mirror","title":"Magnetic mirror","text":"","category":"page"},{"location":"examples/advanced/demo_magneticmirror/","page":"Magnetic mirror","title":"Magnetic mirror","text":"This page was generated using DemoCards.jl and Literate.jl.","category":"page"},{"location":"examples/basics/demo_electron_proton/","page":"Proton and electron in a static EM field","title":"Proton and electron in a static EM field","text":"EditURL = \"/home/runner/work/TestParticle.jl/TestParticle.jl/docs/examples/basics/demo_electron_proton.jl\"","category":"page"},{"location":"examples/basics/demo_electron_proton/#demo_proton_electron","page":"Proton and electron in a static EM field","title":"Proton and electron in a static EM field","text":"","category":"section"},{"location":"examples/basics/demo_electron_proton/","page":"Proton and electron in a static EM field","title":"Proton and electron in a static EM field","text":"(Image: Source code) (Image: compat) (Image: Author) (Image: Author) (Image: Update time)","category":"page"},{"location":"examples/basics/demo_electron_proton/","page":"Proton and electron in a static EM field","title":"Proton and electron in a static EM field","text":"This example demonstrates tracing a single proton and electron motion under a uniform B field in real physical parameters. The E field is assumed to be zero such that there is no particle acceleration. Due to the fact that m_p m_e doteq 1836, the proton gyro-radius is 1800 times larger than the electron, if they start with the same velocity as in this case. In more common cases we would compare electrons and protons with the same energy, and their gyro-radii differ by a factor of sqrtm_pm_e sim 40.","category":"page"},{"location":"examples/basics/demo_electron_proton/","page":"Proton and electron in a static EM field","title":"Proton and electron in a static EM field","text":"using TestParticle\nusing OrdinaryDiffEq\nusing CairoMakie\n\n### Initialize grid and field\nx = range(-10, 10, length=15)\ny = range(-10, 10, length=20)\nz = range(-10, 10, length=25)\n\nB = fill(0.0, 3, length(x), length(y), length(z)) # [T]\nE = fill(0.0, 3, length(x), length(y), length(z)) # [V/m]\nB[3,:,:,:] .= 1e-11\nE[3,:,:,:] .= 5e-13\n\n### Initialize particles\nstateinit = let\n x0 = [0.0, 0.0, 0.0] # initial position, [m]\n u0 = [1.0, 0.0, 0.0] # initial velocity, [m/s]\n [x0..., u0...]\nend\nparam_electron = prepare(x, y, z, E, B, species=Electron)\ntspan_electron = (0.0, 15.0)\n\nparam_proton = prepare(x, y, z, E, B, species=Proton)\ntspan_proton = (0.0, 10.0)\n\n### Solve for the trajectories\nprob_e = ODEProblem(trace!, stateinit, tspan_electron, param_electron)\nprob_p = ODEProblem(trace!, stateinit, tspan_proton, param_proton)\n\nsol_e = solve(prob_e, Vern9())\nsol_p = solve(prob_p, Vern9())\n\n### Visualization\nf = Figure(fontsize=18)\nax = Axis3(f[1, 1],\n title = \"Electron and Ion Trajectories\",\n xlabel = \"x [m]\",\n ylabel = \"y [m]\",\n zlabel = \"z [m]\",\n aspect = :data,\n)\nplot!(sol_e, idxs=(1, 2, 3), color=:tomato, label=\"electron\")\nplot!(sol_p, idxs=(1, 2, 3), color=:deepskyblue3, label=\"proton\")\n\n##TODO: wait for https://github.com/MakieOrg/Makie.jl/issues/3623 to be fixed!\nax.scene.plots[9+2*1-1].color = :tomato\nax.scene.plots[9+2*2-1].color = :deepskyblue3\n\naxislegend()\n","category":"page"},{"location":"examples/basics/demo_electron_proton/","page":"Proton and electron in a static EM field","title":"Proton and electron in a static EM field","text":"(Image: )","category":"page"},{"location":"examples/basics/demo_electron_proton/","page":"Proton and electron in a static EM field","title":"Proton and electron in a static EM field","text":"","category":"page"},{"location":"examples/basics/demo_electron_proton/","page":"Proton and electron in a static EM field","title":"Proton and electron in a static EM field","text":"This page was generated using DemoCards.jl and Literate.jl.","category":"page"},{"location":"examples/basics/demo_dimensionless_periodic/","page":"Tracing with Dimensionless Units and Periodic Boundary","title":"Tracing with Dimensionless Units and Periodic Boundary","text":"EditURL = \"/home/runner/work/TestParticle.jl/TestParticle.jl/docs/examples/basics/demo_dimensionless_periodic.jl\"","category":"page"},{"location":"examples/basics/demo_dimensionless_periodic/#demo_dimensionless_periodic","page":"Tracing with Dimensionless Units and Periodic Boundary","title":"Tracing with Dimensionless Units and Periodic Boundary","text":"","category":"section"},{"location":"examples/basics/demo_dimensionless_periodic/","page":"Tracing with Dimensionless Units and Periodic Boundary","title":"Tracing with Dimensionless Units and Periodic Boundary","text":"(Image: Source code) (Image: compat) (Image: Author) (Image: Update time)","category":"page"},{"location":"examples/basics/demo_dimensionless_periodic/","page":"Tracing with Dimensionless Units and Periodic Boundary","title":"Tracing with Dimensionless Units and Periodic Boundary","text":"This example shows how to trace charged particles in dimensionless units and EM fields with periodic boundaries in a 2D spatial domain. For details about dimensionless units, please check Demo: dimensionless tracing.","category":"page"},{"location":"examples/basics/demo_dimensionless_periodic/","page":"Tracing with Dimensionless Units and Periodic Boundary","title":"Tracing with Dimensionless Units and Periodic Boundary","text":"Now let's demonstrate this with trace_normalized!.","category":"page"},{"location":"examples/basics/demo_dimensionless_periodic/","page":"Tracing with Dimensionless Units and Periodic Boundary","title":"Tracing with Dimensionless Units and Periodic Boundary","text":"using TestParticle\nusing TestParticle: qᵢ, mᵢ\nusing OrdinaryDiffEq\nusing StaticArrays\nusing CairoMakie\n\n# Number of cells for the field along each dimension\nnx, ny = 4, 6\n# Unit conversion factors between SI and dimensionless units\nB₀ = 10e-9 # [T]\nΩ = abs(qᵢ) * B₀ / mᵢ # [1/s]\nt₀ = 1 / Ω # [s]\nU₀ = 1.0 # [m/s]\nl₀ = U₀ * t₀ # [m]\nE₀ = U₀*B₀ # [V/m]\n\nx = range(-10, 10, length=nx) # [l₀]\ny = range(-10, 10, length=ny) # [l₀]\n\nB = fill(0.0, 3, nx, ny) # [B₀]\nB[3,:,:] .= 1.0\n\nE(x) = SA[0.0, 0.0, 0.0] # [E₀]\n\n# If bc == 1, we set a NaN value outside the domain (default);\n# If bc == 2, we set periodic boundary conditions.\nparam = prepare(x, y, E, B; species=User, bc=2);","category":"page"},{"location":"examples/basics/demo_dimensionless_periodic/","page":"Tracing with Dimensionless Units and Periodic Boundary","title":"Tracing with Dimensionless Units and Periodic Boundary","text":"Note that we set a radius of 10, so the trajectory extent from -20 to 0 in y, which is beyond the original y range.","category":"page"},{"location":"examples/basics/demo_dimensionless_periodic/","page":"Tracing with Dimensionless Units and Periodic Boundary","title":"Tracing with Dimensionless Units and Periodic Boundary","text":"# Initial conditions\nstateinit = let\n x0 = [0.0, 0.0, 0.0] # initial position [l₀]\n u0 = [10.0, 0.0, 0.0] # initial velocity [v₀]\n [x0..., u0...]\nend\n# Time span\ntspan = (0.0, 1.5π) # 3/4 gyroperiod\n\nprob = ODEProblem(trace_normalized!, stateinit, tspan, param)\nsol = solve(prob, Vern9());","category":"page"},{"location":"examples/basics/demo_dimensionless_periodic/","page":"Tracing with Dimensionless Units and Periodic Boundary","title":"Tracing with Dimensionless Units and Periodic Boundary","text":"Visualization","category":"page"},{"location":"examples/basics/demo_dimensionless_periodic/","page":"Tracing with Dimensionless Units and Periodic Boundary","title":"Tracing with Dimensionless Units and Periodic Boundary","text":"f = Figure(fontsize = 18)\nax = Axis(f[1, 1],\n title = \"Proton trajectory\",\n xlabel = \"X\",\n ylabel = \"Y\",\n limits = (-10.1, 10.1, -20.1, 0.1),\n aspect = DataAspect()\n)\n\nlines!(ax, sol, vars=(1,2))\n","category":"page"},{"location":"examples/basics/demo_dimensionless_periodic/","page":"Tracing with Dimensionless Units and Periodic Boundary","title":"Tracing with Dimensionless Units and Periodic Boundary","text":"(Image: )","category":"page"},{"location":"examples/basics/demo_dimensionless_periodic/","page":"Tracing with Dimensionless Units and Periodic Boundary","title":"Tracing with Dimensionless Units and Periodic Boundary","text":"","category":"page"},{"location":"examples/basics/demo_dimensionless_periodic/","page":"Tracing with Dimensionless Units and Periodic Boundary","title":"Tracing with Dimensionless Units and Periodic Boundary","text":"This page was generated using DemoCards.jl and Literate.jl.","category":"page"},{"location":"examples/advanced/demo_proton_dipole/","page":"Magnetic dipole","title":"Magnetic dipole","text":"EditURL = \"/home/runner/work/TestParticle.jl/TestParticle.jl/docs/examples/advanced/demo_proton_dipole.jl\"","category":"page"},{"location":"examples/advanced/demo_proton_dipole/#demo_dipole","page":"Magnetic dipole","title":"Magnetic dipole","text":"","category":"section"},{"location":"examples/advanced/demo_proton_dipole/","page":"Magnetic dipole","title":"Magnetic dipole","text":"(Image: Source code) (Image: compat) (Image: Author) (Image: Update time)","category":"page"},{"location":"examples/advanced/demo_proton_dipole/","page":"Magnetic dipole","title":"Magnetic dipole","text":"This example shows how to trace protons of a certain energy in a analytic Earth-like magnetic dipole field. There is a combination of grad-B drift, curvature drift, and the bounce motion between mirror points. It demonstrates the motions corresponding to the three adiabatic invariants.","category":"page"},{"location":"examples/advanced/demo_proton_dipole/","page":"Magnetic dipole","title":"Magnetic dipole","text":"using TestParticle\nusing TestParticle: getB_dipole, getE_dipole, sph2cart, dipole_fieldline, mᵢ, qᵢ, c, Rₑ\nusing OrdinaryDiffEq\nusing CairoMakie\n\n# Initial condition\nstateinit = let\n # Initial particle energy\n Ek = 5e7 # [eV]\n # initial velocity, [m/s]\n v₀ = sph2cart(c*sqrt(1-1/(1+Ek*qᵢ/(mᵢ*c^2))^2), 0.0, π/4)\n # initial position, [m]\n r₀ = sph2cart(2.5*Rₑ, 0.0, π/2)\n [r₀..., v₀...]\nend\n# obtain field\nparam = prepare(getE_dipole, getB_dipole)\ntspan = (0.0, 10.0)\n\nprob = ODEProblem(trace!, stateinit, tspan, param)\n\nsol = solve(prob, Vern9())\n\n### Visualization\n\nf = Figure(fontsize=18)\nax = Axis3(f[1, 1],\n title = \"50 MeV Proton trajectory in Earth's dipole field\",\n xlabel = \"x [Re]\",\n ylabel = \"y [Re]\",\n zlabel = \"z [Re]\",\n aspect = :data,\n limits = (-2.5, 2.5, -2.5, 2.5, -1, 1)\n)\n\ninvRE = 1 / Rₑ\nl = lines!(ax, sol, idxs=(1, 2, 3))\n##TODO: wait for https://github.com/MakieOrg/Makie.jl/issues/3623 to be fixed!\nscale!(ax.scene.plots[9+1], invRE, invRE, invRE)\n\nfor ϕ in range(0, stop=2*π, length=10)\n lines!(dipole_fieldline(ϕ)..., color=:tomato, alpha=0.3)\nend\n","category":"page"},{"location":"examples/advanced/demo_proton_dipole/","page":"Magnetic dipole","title":"Magnetic dipole","text":"(Image: )","category":"page"},{"location":"examples/advanced/demo_proton_dipole/","page":"Magnetic dipole","title":"Magnetic dipole","text":"Solver algorithm matters in terms of energy conservation. In the above we used Verner's “Most Efficient” 9/8 Runge-Kutta method. Let's check other algorithms.","category":"page"},{"location":"examples/advanced/demo_proton_dipole/","page":"Magnetic dipole","title":"Magnetic dipole","text":"function get_energy_ratio(sol)\n vx = getindex.(sol.u, 4)\n vy = getindex.(sol.u, 5)\n vz = getindex.(sol.u, 6)\n\n Einit = vx[1]^2 + vy[1]^2 + vz[1]^2\n Eend = vx[end]^2 + vy[end]^2 + vz[end]^2\n\n (Eend - Einit) / Einit\nend\n\n# `ImplicitMidpoint()` requires a fixed time step.\nsol = solve(prob, ImplicitMidpoint(); dt=1e-3)\nget_energy_ratio(sol)","category":"page"},{"location":"examples/advanced/demo_proton_dipole/","page":"Magnetic dipole","title":"Magnetic dipole","text":"-0.00045982272113011914","category":"page"},{"location":"examples/advanced/demo_proton_dipole/","page":"Magnetic dipole","title":"Magnetic dipole","text":"sol = solve(prob, ImplicitMidpoint(); dt=1e-4)\nget_energy_ratio(sol)","category":"page"},{"location":"examples/advanced/demo_proton_dipole/","page":"Magnetic dipole","title":"Magnetic dipole","text":"-4.940132338997509e-9","category":"page"},{"location":"examples/advanced/demo_proton_dipole/","page":"Magnetic dipole","title":"Magnetic dipole","text":"sol = solve(prob, Vern9())\nget_energy_ratio(sol)","category":"page"},{"location":"examples/advanced/demo_proton_dipole/","page":"Magnetic dipole","title":"Magnetic dipole","text":"-0.0070445259008460786","category":"page"},{"location":"examples/advanced/demo_proton_dipole/","page":"Magnetic dipole","title":"Magnetic dipole","text":"sol = solve(prob, Trapezoid())\nget_energy_ratio(sol)","category":"page"},{"location":"examples/advanced/demo_proton_dipole/","page":"Magnetic dipole","title":"Magnetic dipole","text":"-0.011643134931582444","category":"page"},{"location":"examples/advanced/demo_proton_dipole/","page":"Magnetic dipole","title":"Magnetic dipole","text":"sol = solve(prob, Vern6())\nget_energy_ratio(sol)","category":"page"},{"location":"examples/advanced/demo_proton_dipole/","page":"Magnetic dipole","title":"Magnetic dipole","text":"-0.06589639870599219","category":"page"},{"location":"examples/advanced/demo_proton_dipole/","page":"Magnetic dipole","title":"Magnetic dipole","text":"sol = solve(prob, Tsit5())\nget_energy_ratio(sol)","category":"page"},{"location":"examples/advanced/demo_proton_dipole/","page":"Magnetic dipole","title":"Magnetic dipole","text":"0.5351974995171843","category":"page"},{"location":"examples/advanced/demo_proton_dipole/","page":"Magnetic dipole","title":"Magnetic dipole","text":"Default stepsize settings may not be enough for our problem. By using a smaller abstol and reltol, we can guarantee much better conservation at a higher cost:","category":"page"},{"location":"examples/advanced/demo_proton_dipole/","page":"Magnetic dipole","title":"Magnetic dipole","text":"# This is roughly equivalent in accuracy and performance with Vern9() and `reltol=1e-3` (default)\nsol = solve(prob, Tsit5(); reltol=1e-4);","category":"page"},{"location":"examples/advanced/demo_proton_dipole/","page":"Magnetic dipole","title":"Magnetic dipole","text":"Or, for adaptive time step algorithms like Vern9(), with the help of callbacks, we can enforce a largest time step smaller than 1/10 of the local gyroperiod:","category":"page"},{"location":"examples/advanced/demo_proton_dipole/","page":"Magnetic dipole","title":"Magnetic dipole","text":"using DiffEqCallbacks\n\n# p = (charge_mass_ratio, E, B)\ndtFE(u, p, t) = 2π / (abs(p[1]) * hypot(p[3](u, t)...))\ncb = StepsizeLimiter(dtFE; safety_factor=1 // 10, max_step=true)\n\nsol = solve(prob, Vern9(); callback=cb, dt=0.1) # dt=0.1 is a dummy value\nget_energy_ratio(sol)","category":"page"},{"location":"examples/advanced/demo_proton_dipole/","page":"Magnetic dipole","title":"Magnetic dipole","text":"-5.399366794067525e-7","category":"page"},{"location":"examples/advanced/demo_proton_dipole/","page":"Magnetic dipole","title":"Magnetic dipole","text":"This is much more accurate, at the cost of more iterations. In terms of accuracy, this is roughly equivalent to solve(prob, Vern9(); reltol=1e-7); in terms of performance, it is 2x slower (0.04s v.s. 0.02s) and consumes about the same amount of memory 42 MiB. We can also use the classical Boris method implemented within the package:","category":"page"},{"location":"examples/advanced/demo_proton_dipole/","page":"Magnetic dipole","title":"Magnetic dipole","text":"dt = 1e-4\nprob = TraceProblem(stateinit, tspan, param)\nsol = TestParticle.solve(prob; dt)[1]\nget_energy_ratio(sol)","category":"page"},{"location":"examples/advanced/demo_proton_dipole/","page":"Magnetic dipole","title":"Magnetic dipole","text":"-1.0158504769154868e-15","category":"page"},{"location":"examples/advanced/demo_proton_dipole/","page":"Magnetic dipole","title":"Magnetic dipole","text":"The Boris method requires a fixed time step. It takes about 0.05s and consumes 53 MiB memory. In this specific case, the time step is determined empirically. If we increase the time step to 1e-2 seconds, the trajectory becomes completely off (but the energy is still conserved). Therefore, as a rule of thumb, we should not use the default Tsit5() scheme without decreasing reltol. Use adaptive Vern9() for an unfamiliar field configuration, then switch to more accurate schemes if needed. A more thorough test can be found here.","category":"page"},{"location":"examples/advanced/demo_proton_dipole/","page":"Magnetic dipole","title":"Magnetic dipole","text":"","category":"page"},{"location":"examples/advanced/demo_proton_dipole/","page":"Magnetic dipole","title":"Magnetic dipole","text":"This page was generated using DemoCards.jl and Literate.jl.","category":"page"},{"location":"examples/advanced/demo_shock/","page":"Shock","title":"Shock","text":"EditURL = \"/home/runner/work/TestParticle.jl/TestParticle.jl/docs/examples/advanced/demo_shock.jl\"","category":"page"},{"location":"examples/advanced/demo_shock/#demo_shock","page":"Shock","title":"Shock","text":"","category":"section"},{"location":"examples/advanced/demo_shock/","page":"Shock","title":"Shock","text":"(Image: Source code) (Image: compat) (Image: Author) (Image: Update time)","category":"page"},{"location":"examples/advanced/demo_shock/","page":"Shock","title":"Shock","text":"This example shows how to trace protons of a certain energy in MHD plane shocks.","category":"page"},{"location":"examples/advanced/demo_shock/","page":"Shock","title":"Shock","text":"using TestParticle, OrdinaryDiffEq\nusing TestParticle: mᵢ, kB\nusing LinearAlgebra\nusing Statistics: mean, std\nusing Printf\nusing Random\nusing CairoMakie, PairPlots\n\n# For reproducible results\nRandom.seed!(1234)\n\n\"Set initial conditions.\"\nfunction prob_func(prob, i, repeat)\n v₀ = sample(vdf₁)\n r₀ = [5_000e3, 0.0, 0.0]\n\n prob = remake(prob; u0 = [r₀..., v₀...])\nend","category":"page"},{"location":"examples/advanced/demo_shock/","page":"Shock","title":"Shock","text":"Main.var\"##55416\".prob_func","category":"page"},{"location":"examples/advanced/demo_shock/","page":"Shock","title":"Shock","text":"Perpendicular shock is a special shock in which both the upstream and downstream plasma flows are perpendicular to the magnetic field, as well as the shock front.","category":"page"},{"location":"examples/advanced/demo_shock/","page":"Shock","title":"Shock","text":"# MHD states in SI units\nn₁ = 1.0e6\nT₁ = 720471.8506664868\nPi₁ = 0.0049735919716217296 * 1e-9\nPe₁ = 0.0049735919716217296 * 1e-9\nPth₁ = Pi₁ + Pe₁\nV₁ = [-545.1484121835928, 0.0, 0.0] .* 1e3\nB₁ = [0.0, 0.0, 5.0] .* 1e-9\n\nn₂ = 3.1662479035540008e6\nT₂ = 5.957947703036288e6\nPi₂ = 0.13022511153415584 * 1e-9\nPe₂ = 0.13022511153415584 * 1e-9\nPth₂ = Pi₂ + Pe₂\nV₂ = [-172.17489874108816, 0.0, 0.0] .* 1e3\nB₂ = [0.0, 0.0, 15.831239517770003] .* 1e-9;","category":"page"},{"location":"examples/advanced/demo_shock/","page":"Shock","title":"Shock","text":"In ideal MHD, the electric field simply contains the convection term. For perpendicular shocks, the electric field across the shock is continuous. In the upstream, particles follow straight lines because it's purely an ExB drift; across the shock, ExB drift changes to the downstream bulk velocity.","category":"page"},{"location":"examples/advanced/demo_shock/","page":"Shock","title":"Shock","text":"E₁ = B₁ × V₁\nE₂ = B₂ × V₂\n\n# Shock normal direction range\nx = range(-5_000e3, 5_000e3, length=100)\nB = repeat(B₁, 1, length(x))\nE = repeat(E₁, 1, length(x))\n# Index for the shock location\nmid_ = length(x) ÷ 2\n\nB[:, 1:mid_] .= B₂\nE[:, 1:mid_] .= E₂\n\nconst vdf₁ = Maxwellian(V₁, Pth₁, n₁; m=mᵢ)\nvdf₂ = Maxwellian(V₂, Pth₂, n₂; m=mᵢ)\n\ntrajectories = 400\nweight₁ = n₁ / trajectories # relation between test particle and real particles\n\nprob = let\n # BC type 3 is Flat\n param = prepare(x, E, B; species=Proton, bc=3);\n stateinit = zeros(6) # particle position and velocity to be modified\n tspan = (0.0, 30.0)\n ODEProblem(trace!, stateinit, tspan, param)\nend\nensemble_prob = EnsembleProblem(prob; prob_func, safetycopy=false)\n\nsols = solve(ensemble_prob, Vern9(), EnsembleSerial(); trajectories);","category":"page"},{"location":"examples/advanced/demo_shock/","page":"Shock","title":"Shock","text":"Sample particle trajectories","category":"page"},{"location":"examples/advanced/demo_shock/","page":"Shock","title":"Shock","text":"function plot_traj(sols; azimuth=1.275pi, elevation=pi/8)\n f = Figure(fontsize=18)\n ax = Axis3(f[1, 1];\n title = \"Particles across MHD shock\",\n xlabel = \"x [km]\",\n ylabel = \"y [km]\",\n zlabel = \"z [km]\",\n aspect = :data,\n azimuth, elevation,\n )\n\n invL = 1 / 1e3\n\n for i in eachindex(sols)\n lines!(ax, sols[i], idxs=(1,2,3), label=\"$i\")\n ##TODO: wait for https://github.com/MakieOrg/Makie.jl/issues/3623 to be fixed!\n ax.scene.plots[9+2*i-1].color = Makie.wong_colors()[mod(i-1, 7)+1]\n scale!(ax.scene.plots[9+2*i-1], invL, invL, invL)\n end\n\n # Represent the shock front\n p1 = Point3f(0.0, -2e2, -2e2)\n p2 = Point3f(0.0, 2e2, -2e2)\n p3 = Point3f(0.0, 2e2, 2e2)\n p4 = Point3f(0.0, -2e2, 2e2)\n\n mesh!(ax, [p1, p2, p3], color = (:gray, 0.1), shading = Makie.automatic)\n mesh!(ax, [p1, p4, p3], color = (:gray, 0.1), shading = Makie.automatic)\n\n f\nend\n\nf = plot_traj(sols[1:4])","category":"page"},{"location":"examples/advanced/demo_shock/","page":"Shock","title":"Shock","text":"(Image: )","category":"page"},{"location":"examples/advanced/demo_shock/","page":"Shock","title":"Shock","text":"Phase space distributions","category":"page"},{"location":"examples/advanced/demo_shock/","page":"Shock","title":"Shock","text":"function plot_dist(x, sols; nxchunks::Int=2, ntchunks::Int=20)\n trange = range(sols[1].prob.tspan..., length=ntchunks)\n\n xrange = range(x[1], x[end], length=nxchunks+1)\n dx = (x[end] - x[1]) / nxchunks\n xmid = range(x[1] + 0.5dx, x[end] - 0.5dx, length=nxchunks) ./ 1e3\n\n vx = [Float64[] for _ in 1:nxchunks]\n\n for sol in sols\n for t in trange\n xv = sol(t)\n for i in 1:nxchunks\n if xrange[i] < xv[1] ≤ xrange[i+1]\n push!(vx[i], xv[4] / 1e3)\n end\n end\n end\n end\n\n for i in eachindex(vx)\n if isempty(vx[i])\n push!(vx[i], 0.0)\n end\n end\n\n f = Figure(size = (1200, 600), fontsize=18)\n ax = Axis(f[1, 1],\n limits = (nothing, nothing, -750, 400),\n title = \"Phase space distributions at different spatial locations\",\n xlabel = \"Location [km]\",\n ylabel = \"Vx [km/s]\",\n xminorticksvisible = true)\n\n for i in 1:nxchunks\n hist!(ax, vx[i], normalization = :pdf, bins = 50,\n scale_to=-5000/nxchunks, offset=xmid[i], direction=:x)\n end\n\n v̄x = mean.(vx)\n vth = [std(vx[i]; corrected=false, mean=v̄x[i]) for i in 1:nxchunks]\n means_str = [@sprintf \"Vx: %d [km/s]\" v̄x[i] for i in eachindex(v̄x)]\n std_str = [@sprintf \"Vth: %d [km/s]\" vth[i] for i in eachindex(vth)]\n text!(Point.(xmid.+400, 300.0), text = means_str, align = (:right, :center),\n offset = (-60, 0), color = :black, fontsize=24)\n text!(Point.(xmid.+400, -700.0), text = std_str, align = (:right, :center),\n offset = (-60, 0), color = :black, fontsize=24)\n\n f\nend\n\nf = plot_dist(x, sols; nxchunks=4, ntchunks=100)","category":"page"},{"location":"examples/advanced/demo_shock/","page":"Shock","title":"Shock","text":"(Image: )","category":"page"},{"location":"examples/advanced/demo_shock/","page":"Shock","title":"Shock","text":"Even with 400 particles, we are still able to statistically approximate the velocity moment downstream of the perpendicular shock. While the upstream thermal speed is close to what we set, the downstream thermal speed is higher than our precalculated value. (Why?)","category":"page"},{"location":"examples/advanced/demo_shock/","page":"Shock","title":"Shock","text":"Vth₁ = 77.1 km/s\nVth₂ = 221.7 km/s\n","category":"page"},{"location":"examples/advanced/demo_shock/","page":"Shock","title":"Shock","text":"A nice way to present the 3d distributions in 2d is via the pair plots:","category":"page"},{"location":"examples/advanced/demo_shock/","page":"Shock","title":"Shock","text":"function plot_dist_pairplot(x, sols; ntchunks::Int=20)\n nxchunks = 2\n trange = range(sols[1].prob.tspan..., length=ntchunks)\n\n xrange = range(x[1], x[end], length=nxchunks+1)\n\n table = [(;\n vx = Float64[],\n vy = Float64[],\n vz = Float64[],\n ) for _ in 1:nxchunks]\n\n for sol in sols\n for t in trange\n xv = sol(t)\n for i in 1:nxchunks\n if xrange[i] < xv[1] ≤ xrange[i+1]\n push!(table[i].vx, xv[4] / 1e3)\n push!(table[i].vy, xv[5] / 1e3)\n push!(table[i].vz, xv[6] / 1e3)\n end\n end\n end\n end\n\n for i in eachindex(table)\n if isempty(table[i].vx)\n push!(table[i].vx, 0.0)\n push!(table[i].vy, 0.0)\n push!(table[i].vz, 0.0)\n end\n end\n\n f = Figure(size = (1000, 600), fontsize=18)\n\n c1 = Makie.wong_colors(0.5)[1]\n c2 = Makie.wong_colors(0.5)[2]\n\n l1 = @sprintf \"x: [%d, %d] km downstream\" xrange[1]/1e3 xrange[2]/1e3\n l2 = @sprintf \"x: [%d, %d] km upstream\" xrange[2]/1e3 xrange[3]/1e3\n\n pairplot(f[1,1],\n PairPlots.Series(table[1], label=l1, color=c1, strokecolor=c1),\n PairPlots.Series(table[2], label=l2, color=c2, strokecolor=c2),\n bodyaxis=(; xgridvisible=true, ygridvisible=true),\n diagaxis=(; xgridvisible=true, ygridvisible=true)\n )\n\n f\nend\n\nf = plot_dist_pairplot(x, sols; ntchunks=20)","category":"page"},{"location":"examples/advanced/demo_shock/","page":"Shock","title":"Shock","text":"(Image: )","category":"page"},{"location":"examples/advanced/demo_shock/","page":"Shock","title":"Shock","text":"For parallel shocks, we have a different scenario. Parallel shock is another special shock in which both the upstream and downstream plasma flows are parallel to the magnetic field, as well as perpendicular to the shock front.","category":"page"},{"location":"examples/advanced/demo_shock/","page":"Shock","title":"Shock","text":"# MHD states in SI units\nn₁ = 1.0e6\nT₁ = 720471.8506664868\nPi₁ = 0.0049735919716217296 * 1e-9\nPe₁ = 0.0049735919716217296 * 1e-9\nPth₁ = Pi₁ + Pe₁\nV₁ = [-545.1484121835928, 0.0, 0.0] .* 1e3\nB₁ = [5.0, 0.0, 0.0] .* 1e-9\n\nn₂ = 3.6363636363636362e6\nT₂ = 7.380333520264822e6\nPi₂ = 0.18526630094290936 * 1e-9\nPe₂ = 0.18526630094290936 * 1e-9\nPth₂ = Pi₂ + Pe₂\nV₂ = [-149.91581335048804, 0.0, 0.0] .* 1e3\nB₂ = [5.0, 0.0, 0.0] .* 1e-9;","category":"page"},{"location":"examples/advanced/demo_shock/","page":"Shock","title":"Shock","text":"There is no convection electric field in the parallel shock:","category":"page"},{"location":"examples/advanced/demo_shock/","page":"Shock","title":"Shock","text":"E₁ = B₁ × V₁\nE₂ = B₂ × V₂","category":"page"},{"location":"examples/advanced/demo_shock/","page":"Shock","title":"Shock","text":"3-element Vector{Float64}:\n 0.0\n -0.0\n 0.0","category":"page"},{"location":"examples/advanced/demo_shock/","page":"Shock","title":"Shock","text":"Therefore, when we trace particles, there is no deceleration across the shock:","category":"page"},{"location":"examples/advanced/demo_shock/","page":"Shock","title":"Shock","text":"# Shock normal direction range\nx = range(-5_000e3, 5_000e3, length=100)\nB = repeat(B₁, 1, length(x))\nE = repeat(E₁, 1, length(x))\n# Index for the shock location\nmid_ = length(x) ÷ 2\n\nB[:, 1:mid_] .= B₂\nE[:, 1:mid_] .= E₂\n\nvdf₁ = Maxwellian(V₁, Pth₁, n₁; m=mᵢ)\nvdf₂ = Maxwellian(V₂, Pth₂, n₂; m=mᵢ)\n\ntrajectories = 2\nweight₁ = n₁ / trajectories\n\nprob = let\n # BC type 3 is Flat\n param = prepare(x, E, B; species=Proton, bc=3);\n stateinit = zeros(6) # particle position and velocity to be modified\n tspan = (0.0, 14.0)\n ODEProblem(trace!, stateinit, tspan, param)\nend\nensemble_prob = EnsembleProblem(prob; prob_func, safetycopy=false)\n\nsols = solve(ensemble_prob, Vern9(), EnsembleSerial(); trajectories);\n\nf = plot_traj(sols; azimuth=1.08π, elevation=pi/16)","category":"page"},{"location":"examples/advanced/demo_shock/","page":"Shock","title":"Shock","text":"(Image: )","category":"page"},{"location":"examples/advanced/demo_shock/","page":"Shock","title":"Shock","text":"Clearly, test particle tracing in MHD parallel shocks fails to recover physics. MHD parallel shocks are essentially hydrodynamic shocks where magnetic field plays no role. Due to the lack of collision and other diffusion processes, we are unable to capture the correct microscopic scenario here.","category":"page"},{"location":"examples/advanced/demo_shock/","page":"Shock","title":"Shock","text":"","category":"page"},{"location":"examples/advanced/demo_shock/","page":"Shock","title":"Shock","text":"This page was generated using DemoCards.jl and Literate.jl.","category":"page"},{"location":"examples/basics/demo_curvature_B/","page":"Curl-B drift","title":"Curl-B drift","text":"EditURL = \"/home/runner/work/TestParticle.jl/TestParticle.jl/docs/examples/basics/demo_curvature_B.jl\"","category":"page"},{"location":"examples/basics/demo_curvature_B/#demo_curlB","page":"Curl-B drift","title":"Curl-B drift","text":"","category":"section"},{"location":"examples/basics/demo_curvature_B/","page":"Curl-B drift","title":"Curl-B drift","text":"(Image: Source code) (Image: compat) (Image: Author) (Image: Author) (Image: Update time)","category":"page"},{"location":"examples/basics/demo_curvature_B/","page":"Curl-B drift","title":"Curl-B drift","text":"This example demonstrates a single proton motion under a vacuum non-uniform B field with gradient and curvature. Similar to the magnetic field gradient drift, analytic calculation should include both of the gradient drift and the curvature drift. More theoretical details can be found in Curvature Drift and Computational Plasma Physics by Toshi Tajima.","category":"page"},{"location":"examples/basics/demo_curvature_B/","page":"Curl-B drift","title":"Curl-B drift","text":"using TestParticle\nusing TestParticle: get_gc\nusing OrdinaryDiffEq\nusing StaticArrays\nusing LinearAlgebra: normalize, norm, ×, ⋅\nusing ForwardDiff: gradient, jacobian\nusing CairoMakie\n\nfunction curved_B(x)\n # satisify ∇⋅B=0\n # B_θ = 1/r => ∂B_θ/∂θ = 0\n θ = atan(x[3]/(x[1]+3))\n r = hypot(x[1]+3, x[3])\n return SA[-1e-7*sin(θ)/r, 0, 1e-7*cos(θ)/r]\nend\n\nfunction zero_E(x)\n return SA[0, 0, 0]\nend\n\nabs_B(x) = norm(curved_B(x)) # |B|\n\n# Trace the orbit of the guiding center using analytical drifts\nfunction trace_gc!(dx, x, p, t)\n q2m, E, B, sol = p\n xu = sol(t)\n gradient_B = gradient(abs_B, x) # ∇|B|\n Bv = B(x)\n b = normalize(Bv)\n v_par = (xu[4:6] ⋅ b).*b # (v⋅b)b\n v_perp = xu[4:6] - v_par\n Ω = q2m*norm(Bv)\n κ = jacobian(B, x)*Bv # B⋅∇B\n # v⟂^2*(B×∇|B|)/(2*Ω*B^2) + v∥^2*(B×(B⋅∇B))/(Ω*B^3) + (E×B)/B^2 + v∥\n dx[1:3] = norm(v_perp)^2*(Bv × gradient_B)/(2*Ω*norm(Bv)^2) +\n norm(v_par)^2*(Bv × κ)/Ω/norm(Bv)^3 + (E(x) × Bv)/norm(Bv)^2 + v_par\nend\n\n# Initial conditions\nstateinit = let x0 = [1.0, 0.0, 0.0], v0 = [0.0, 1.0, 0.1]\n [x0..., v0...]\nend\n# Time span\ntspan = (0, 40)\n# Trace particle\nparam = prepare(zero_E, curved_B, species=Proton)\nprob = ODEProblem(trace!, stateinit, tspan, param)\nsol = solve(prob, Vern9())\n# Functions for obtaining the guiding center from actual trajectory\ngc = get_gc(param)\ngc_x0 = gc(stateinit)\nprob_gc = ODEProblem(trace_gc!, gc_x0, tspan, (param..., sol))\nsol_gc = solve(prob_gc, Tsit5(); save_idxs=[1,2,3])\n\n# Numeric and analytic results\nf = Figure(fontsize=18)\nax = Axis3(f[1, 1],\n title = \"Curvature Drift\",\n xlabel = \"x [m]\",\n ylabel = \"y [m]\",\n zlabel = \"z [m]\",\n aspect = :data,\n azimuth = 0.3π,\n)\n\ngc_plot(x, y, z, vx, vy, vz) = (gc(SA[x, y, z, vx, vy, vz])...,)\n\nlines!(ax, sol, idxs=(1, 2, 3))\nlines!(ax, sol, idxs=(gc_plot, 1, 2, 3, 4, 5, 6))\nlines!(ax, sol_gc, idxs=(1, 2, 3))\n\nfor i in 1:3\n ##TODO: wait for https://github.com/MakieOrg/Makie.jl/issues/3623 to be fixed!\n ax.scene.plots[9+2*i-1].color = Makie.wong_colors()[i]\nend\n","category":"page"},{"location":"examples/basics/demo_curvature_B/","page":"Curl-B drift","title":"Curl-B drift","text":"(Image: )","category":"page"},{"location":"examples/basics/demo_curvature_B/","page":"Curl-B drift","title":"Curl-B drift","text":"","category":"page"},{"location":"examples/basics/demo_curvature_B/","page":"Curl-B drift","title":"Curl-B drift","text":"This page was generated using DemoCards.jl and Literate.jl.","category":"page"},{"location":"examples/basics/demo_multiple/","page":"Multiple particles","title":"Multiple particles","text":"EditURL = \"/home/runner/work/TestParticle.jl/TestParticle.jl/docs/examples/basics/demo_multiple.jl\"","category":"page"},{"location":"examples/basics/demo_multiple/#demo_multiple","page":"Multiple particles","title":"Multiple particles","text":"","category":"section"},{"location":"examples/basics/demo_multiple/","page":"Multiple particles","title":"Multiple particles","text":"(Image: Source code) (Image: compat) (Image: Author) (Image: Update time)","category":"page"},{"location":"examples/basics/demo_multiple/","page":"Multiple particles","title":"Multiple particles","text":"using TestParticle\nusing OrdinaryDiffEq\nusing Random\nusing CairoMakie\n\n# For reproducible results\nRandom.seed!(1234)\n\nfunction trace(x, y, z, E, B; trajectories::Int=10)\n # Initialize particles\n x0 = [0.0, 0.0, 0.0] # initial position, [m]\n u0 = [1.0, 0.0, 0.0] # initial velocity, [m/s]\n stateinit = [x0..., u0...]\n\n param = prepare(x, y, z, E, B, species=Electron)\n tspan = (0.0, 15.0)\n\n prob = ODEProblem(trace!, stateinit, tspan, param)\n\n sols = Vector{ODESolution}(undef, trajectories)\n # Sample from a Maxwellian with bulk speed 0 and thermal speed 1.0\n vdf = Maxwellian([0.0, 0.0, 0.0], 1.0)\n v = [sample(vdf) for _ in 1:trajectories]\n\n for i in 1:trajectories\n #prob = remake(prob; u0=[x0..., v[:,i]...])\n prob.u0[4:6] = v[i]\n\n sol = solve(prob, Vern9())\n sols[i] = sol\n end\n\n return sols\nend\n\n### Initialize grid and field\nx = range(-10, 10, length=15)\ny = range(-10, 10, length=20)\nz = range(-10, 10, length=25)\n\nB = fill(0.0, 3, length(x), length(y), length(z)) # [T]\nE = fill(0.0, 3, length(x), length(y), length(z)) # [V/m]\nB[3,:,:,:] .= 1e-11\nE[3,:,:,:] .= 5e-13\n\ntrajectories = 4\n\n### Solve for the trajectories\n\nsols = trace(x, y, z, E, B; trajectories)\n\n### Visualization\nf = Figure(fontsize = 18)\nax = Axis3(f[1, 1],\n title = \"Particle trajectories\",\n xlabel = \"X [m]\",\n ylabel = \"Y [m]\",\n zlabel = \"Z [m]\",\n aspect = :data,\n)\n\nfor i in eachindex(sols)\n lines!(ax, sols[i], idxs=(1, 2, 3), label=\"$i\")\nend\n","category":"page"},{"location":"examples/basics/demo_multiple/","page":"Multiple particles","title":"Multiple particles","text":"(Image: )","category":"page"},{"location":"examples/basics/demo_multiple/","page":"Multiple particles","title":"Multiple particles","text":"","category":"page"},{"location":"examples/basics/demo_multiple/","page":"Multiple particles","title":"Multiple particles","text":"This page was generated using DemoCards.jl and Literate.jl.","category":"page"},{"location":"examples/basics/demo_ExB_drift/","page":"E×B drift","title":"E×B drift","text":"EditURL = \"/home/runner/work/TestParticle.jl/TestParticle.jl/docs/examples/basics/demo_ExB_drift.jl\"","category":"page"},{"location":"examples/basics/demo_ExB_drift/#demo_ExB","page":"E×B drift","title":"E×B drift","text":"","category":"section"},{"location":"examples/basics/demo_ExB_drift/","page":"E×B drift","title":"E×B drift","text":"(Image: Source code) (Image: compat) (Image: Author) (Image: Author) (Image: Update time)","category":"page"},{"location":"examples/basics/demo_ExB_drift/","page":"E×B drift","title":"E×B drift","text":"This example demonstrates a single proton motion under uniform E and B fields. The electric field is parallel to the magnetic field in the z-direction, so the motion consists of a cyclotron gyration and an acceleration along z. On top of that, particles also exhibit an ExB drift in the direction perpendicular to both E and B field. Note that in this simple ExB drift case, the analytic and numeric guiding centers overlaps. More theoretical details can be found in ExB Drift.","category":"page"},{"location":"examples/basics/demo_ExB_drift/","page":"E×B drift","title":"E×B drift","text":"using TestParticle\nusing TestParticle: get_gc\nusing OrdinaryDiffEq\nusing StaticArrays\nusing LinearAlgebra: ⋅, ×, normalize\nusing CairoMakie\n\n# Analytic EM fields\nuniform_B(x) = SA[0, 0, 1e-8]\nuniform_E(x) = SA[1e-9, 0, 0]\n\n# Trace the orbit of the guiding center\nfunction trace_gc!(dx, x, p, t)\n _, E, B, sol = p\n xu = sol(t)\n Bv = B(x)\n b = normalize(Bv)\n v_par = @views (xu[4:6] ⋅ b) .* b\n B2 = sum(Bv.^2)\n dx[1:3] = (E(x) × Bv) / B2 + v_par\nend\n# Initial condition\nstateinit = let x0 = [1.0, 0.0, 0.0], v0 = [0.0, 1.0, 0.1]\n [x0..., v0...]\nend\n# Time span\ntspan = (0, 20)\n\n# Trace particle\nparam = prepare(uniform_E, uniform_B, species=Proton)\nprob = ODEProblem(trace!, stateinit, tspan, param)\nsol = solve(prob, Vern9())\n\n# Functions for obtaining the guiding center from actual trajectory\ngc = get_gc(param)\ngc_x0 = gc(stateinit)\nprob_gc = ODEProblem(trace_gc!, gc_x0, tspan, (param..., sol))\nsol_gc = solve(prob_gc, Vern9(); save_idxs=[1,2,3]);\n\n# Numeric and analytic results\nf = Figure(fontsize=18)\nax = Axis3(f[1, 1],\n title = \"ExB Drift\",\n xlabel = \"x [m]\",\n ylabel = \"y [m]\",\n zlabel = \"z [m]\",\n aspect = :data,\n azimuth = 0.3π,\n)\n\ngc_plot(x, y, z, vx, vy, vz) = (gc(SA[x, y, z, vx, vy, vz])...,)\n\nlines!(ax, sol, idxs=(1, 2, 3))\nlines!(ax, sol, idxs=(gc_plot, 1, 2, 3, 4, 5, 6))\nlines!(ax, sol_gc, idxs=(1, 2, 3))\n\nfor i in 1:3\n ##TODO: wait for https://github.com/MakieOrg/Makie.jl/issues/3623 to be fixed!\n ax.scene.plots[9+2*i-1].color = Makie.wong_colors()[i]\nend\n","category":"page"},{"location":"examples/basics/demo_ExB_drift/","page":"E×B drift","title":"E×B drift","text":"(Image: )","category":"page"},{"location":"examples/basics/demo_ExB_drift/","page":"E×B drift","title":"E×B drift","text":"","category":"page"},{"location":"examples/basics/demo_ExB_drift/","page":"E×B drift","title":"E×B drift","text":"This page was generated using DemoCards.jl and Literate.jl.","category":"page"},{"location":"examples/advanced/demo_boris_outofdomain/","page":"Advanced Boris tracing","title":"Advanced Boris tracing","text":"EditURL = \"/home/runner/work/TestParticle.jl/TestParticle.jl/docs/examples/advanced/demo_boris_outofdomain.jl\"","category":"page"},{"location":"examples/advanced/demo_boris_outofdomain/#demo_boris_advance","page":"Advanced Boris tracing","title":"Advanced Boris tracing","text":"","category":"section"},{"location":"examples/advanced/demo_boris_outofdomain/","page":"Advanced Boris tracing","title":"Advanced Boris tracing","text":"(Image: Source code) (Image: compat) (Image: Author) (Image: Update time)","category":"page"},{"location":"examples/advanced/demo_boris_outofdomain/","page":"Advanced Boris tracing","title":"Advanced Boris tracing","text":"This example shows how to trace charged particles using the Boris method in dimensionless units with additionally boundary check. If the particles travel out of the domain specified by the field, the tracing will stop. Check Demo: Dimensionless Units for explaining the unit conversion, and Demo: Boris Method for introducing the Boris method.","category":"page"},{"location":"examples/advanced/demo_boris_outofdomain/","page":"Advanced Boris tracing","title":"Advanced Boris tracing","text":"using TestParticle\nusing TestParticle: qᵢ, mᵢ\nusing StaticArrays\nusing OrdinaryDiffEq\nusing CairoMakie\n\nuniform_B(x) = SA[0.0, 0.0, 0.01]\nuniform_E(x) = SA[0.0, 0.0, 0.0]\n\n\"Set initial states.\"\nfunction prob_func(prob, i, repeat)\n prob = @views remake(prob; u0 = [prob.u0[1:3]..., 10.0 - i*2.0, prob.u0[5:6]...])\nend\n\nfunction isoutofdomain(xv, p, t)\n if isnan(xv[1])\n return true\n else\n return false\n end\nend\n\n# Number of cells for the field along each dimension\nnx, ny = 4, 6\n# Unit conversion factors between SI and dimensionless units\nB₀ = 10e-9 # [T]\nΩ = abs(qᵢ) * B₀ / mᵢ # [1/s]\nt₀ = 1 / Ω # [s]\nU₀ = 1.0 # [m/s]\nl₀ = U₀ * t₀ # [m]\nE₀ = U₀*B₀ # [V/m]\n\nx = range(0, 11, length=nx) # [l₀]\ny = range(-21, 0, length=ny) # [l₀]\n\nB = fill(0.0, 3, nx, ny) # [B₀]\nB[3,:,:] .= 1.0\n\nE(x) = SA[0.0, 0.0, 0.0] # [E₀]\n\n# If bc == 1, we set a NaN value outside the domain (default);\n# If bc == 2, we set periodic boundary conditions.\nparam = prepare(x, y, E, B; species=User, bc=1);","category":"page"},{"location":"examples/advanced/demo_boris_outofdomain/","page":"Advanced Boris tracing","title":"Advanced Boris tracing","text":"Note that we set a radius of 10, so the trajectory extent from -20 to 0 in y, and -10 to 10 in x. After half a cycle, the particle will move into the region where is field is not defined. The tracing will stop with the final step being all NaNs.","category":"page"},{"location":"examples/advanced/demo_boris_outofdomain/","page":"Advanced Boris tracing","title":"Advanced Boris tracing","text":"# Initial conditions to be modified in prob_func\nx0 = [0.0, 0.0, 0.0] # initial position [l₀]\nu0 = [0.0, 0.0, 0.0] # initial velocity [v₀]\nstateinit = [x0..., u0...]\ntspan = (0.0, 1.5π) # 3/4 gyroperiod\n\ndt = 0.1\nsavestepinterval = 1\ntrajectories = 2\nprob = TraceProblem(stateinit, tspan, param; prob_func)\n\nsols = TestParticle.solve(prob; dt, savestepinterval, isoutofdomain, trajectories)\n\nf = Figure(fontsize = 18)\nax = Axis(f[1, 1],\n title = \"Proton trajectory\",\n xlabel = \"X\",\n ylabel = \"Y\",\n limits = (-10.1, 10.1, -20.1, 0.1),\n aspect = DataAspect()\n)\n\nfor i in eachindex(sols)\n lines!(ax, sols[i]; idxs=(1, 2), label=string(i))\n ##TODO: wait for https://github.com/MakieOrg/Makie.jl/issues/3623 to be fixed!\n ax.scene.plots[2*i-1].color = Makie.wong_colors()[i]\nend\n\naxislegend(position=:lt, framevisible=false)\n","category":"page"},{"location":"examples/advanced/demo_boris_outofdomain/","page":"Advanced Boris tracing","title":"Advanced Boris tracing","text":"(Image: )","category":"page"},{"location":"examples/advanced/demo_boris_outofdomain/","page":"Advanced Boris tracing","title":"Advanced Boris tracing","text":"","category":"page"},{"location":"examples/advanced/demo_boris_outofdomain/","page":"Advanced Boris tracing","title":"Advanced Boris tracing","text":"This page was generated using DemoCards.jl and Literate.jl.","category":"page"},{"location":"examples/advanced/demo_ensemble/","page":"Ensemble tracing","title":"Ensemble tracing","text":"EditURL = \"/home/runner/work/TestParticle.jl/TestParticle.jl/docs/examples/advanced/demo_ensemble.jl\"","category":"page"},{"location":"examples/advanced/demo_ensemble/#demo_ensemble","page":"Ensemble tracing","title":"Ensemble tracing","text":"","category":"section"},{"location":"examples/advanced/demo_ensemble/","page":"Ensemble tracing","title":"Ensemble tracing","text":"(Image: Source code) (Image: compat) (Image: Author) (Image: Update time)","category":"page"},{"location":"examples/advanced/demo_ensemble/","page":"Ensemble tracing","title":"Ensemble tracing","text":"This example demonstrates tracing multiple electrons in an analytic EM field and how to take advantage of the multithreading support in the ODE solver. A multiproc version is also available. Check the official documentation of DifferentialEquations.jl for details. In performing test particle tracing, we want to share the field information for all particles. This can be achieved in the ensemble problem with safetycopy=false.","category":"page"},{"location":"examples/advanced/demo_ensemble/","page":"Ensemble tracing","title":"Ensemble tracing","text":"using TestParticle\nusing OrdinaryDiffEq\nusing StaticArrays\nusing CairoMakie\n\n\"Set initial state for EnsembleProblem.\"\nfunction prob_func(prob, i, repeat)\n prob = @views remake(prob, u0=[prob.u0[1:3]..., i/3, 0.0, 0.0])\nend\n\n# Initialization\n\nB(x) = SA[0, 0, 1e-11]\nE(x) = SA[0, 0, 1e-13]\n\nx0 = [0.0, 0.0, 0.0] # initial position, [m]\nu0 = [1.0, 0.0, 0.0] # initial velocity, [m/s]\nstateinit = [x0..., u0...]\n\nparam = prepare(E, B, species=Electron)\ntspan = (0.0, 10.0)\n\ntrajectories = 3\n\n# Solve for the trajectories\n\nprob = ODEProblem(trace!, stateinit, tspan, param)\nensemble_prob = EnsembleProblem(prob; prob_func, safetycopy=false)\nsols = solve(ensemble_prob, Tsit5(), EnsembleThreads(); trajectories)\n\n# Visualization\n\nf = Figure(fontsize = 18)\nax = Axis3(f[1, 1],\n title = \"Electron trajectories\",\n xlabel = \"X\",\n ylabel = \"Y\",\n zlabel = \"Z\",\n aspect = :data,\n)\n\nfor i in eachindex(sols)\n lines!(ax, sols[i], idxs=(1,2,3), label=\"$i\")\n ##TODO: wait for https://github.com/MakieOrg/Makie.jl/issues/3623 to be fixed!\n ax.scene.plots[9+2*i-1].color = Makie.wong_colors()[i]\nend\n","category":"page"},{"location":"examples/advanced/demo_ensemble/","page":"Ensemble tracing","title":"Ensemble tracing","text":"(Image: )","category":"page"},{"location":"examples/advanced/demo_ensemble/","page":"Ensemble tracing","title":"Ensemble tracing","text":"We can also solve this problem with the native Boris pusher. Note that the Boris pusher requires a additional parameters: a fixed timestep, and an output save interval.","category":"page"},{"location":"examples/advanced/demo_ensemble/","page":"Ensemble tracing","title":"Ensemble tracing","text":"dt = 0.1\nsavestepinterval = 1\n\n# Solve for the trajectories\n\nprob = TraceProblem(stateinit, tspan, param; prob_func)\ntrajs = TestParticle.solve(prob; dt, trajectories, savestepinterval)\n\n# Visualization\n\nf = Figure(fontsize = 18)\nax = Axis3(f[1, 1],\n title = \"Electron trajectories\",\n xlabel = \"X\",\n ylabel = \"Y\",\n zlabel = \"Z\",\n aspect = :data,\n)\n\nfor i in eachindex(trajs)\n lines!(ax, trajs[i]; idxs=(1,2,3), label=\"$i\")\n ##TODO: wait for https://github.com/MakieOrg/Makie.jl/issues/3623 to be fixed!\n ax.scene.plots[9+2*i-1].color = Makie.wong_colors()[i]\nend\n","category":"page"},{"location":"examples/advanced/demo_ensemble/","page":"Ensemble tracing","title":"Ensemble tracing","text":"(Image: )","category":"page"},{"location":"examples/advanced/demo_ensemble/","page":"Ensemble tracing","title":"Ensemble tracing","text":"Note that by default linear interpolation is applied when plotting the trajectories from the Boris method.","category":"page"},{"location":"examples/advanced/demo_ensemble/","page":"Ensemble tracing","title":"Ensemble tracing","text":"","category":"page"},{"location":"examples/advanced/demo_ensemble/","page":"Ensemble tracing","title":"Ensemble tracing","text":"This page was generated using DemoCards.jl and Literate.jl.","category":"page"},{"location":"examples/advanced/demo_tokamak_profile/","page":"Tokamak profile","title":"Tokamak profile","text":"EditURL = \"/home/runner/work/TestParticle.jl/TestParticle.jl/docs/examples/advanced/demo_tokamak_profile.jl\"","category":"page"},{"location":"examples/advanced/demo_tokamak_profile/#demo_tokamak_profile","page":"Tokamak profile","title":"Tokamak profile","text":"","category":"section"},{"location":"examples/advanced/demo_tokamak_profile/","page":"Tokamak profile","title":"Tokamak profile","text":"(Image: Source code) (Image: compat) (Image: Author) (Image: Update time)","category":"page"},{"location":"examples/advanced/demo_tokamak_profile/","page":"Tokamak profile","title":"Tokamak profile","text":"This example shows how to trace protons in a stationary magnetic field that corresponds to an ITER-like Tokamak.","category":"page"},{"location":"examples/advanced/demo_tokamak_profile/","page":"Tokamak profile","title":"Tokamak profile","text":"using TestParticle\nusing TestParticle: getB_tokamak_profile\nusing OrdinaryDiffEq\nusing StaticArrays\nusing GeometryBasics\nusing CairoMakie\n\n# Parameters from ITER, see http://fusionwiki.ciemat.es/wiki/ITER\nconst R₀ = 6.2 # Major radius [m]\nconst Bζ0 = 5.3 # toroidal field on axis [T]\nconst a = 2.0 # Minor radius [m]\n\n# Variable must be a radius normalized by minor radius.\nfunction q_profile(nr::Float64)\n return nr^2 + 2*nr + 0.5\nend\n\nfunction B(xu)\n SVector{3}(getB_tokamak_profile(xu[1], xu[2], xu[3], q_profile, a, R₀, Bζ0))\nend\n\nfunction E(xu)\n SA[0.0, 0.0, 0.0]\nend\n\n\"Contruct the topology of Tokamak.\"\nfunction get_tokamak_topology()\n nθ = LinRange(0, 2π, 30)\n nζ = LinRange(0, 2π, 30)\n nx = [R₀*cos(ζ) + a*cos(θ)*cos(ζ) for θ in nθ, ζ in nζ]\n ny = [R₀*sin(ζ) + a*cos(θ)*sin(ζ) for θ in nθ, ζ in nζ]\n nz = [a*sin(θ) for θ in nθ, ζ in nζ]\n points = vec([Point3f(xv, yv, zv) for (xv, yv, zv) in zip(nx, ny, nz)])\n faces = decompose(QuadFace{GLIndex}, Tesselation(Rect(0, 0, 1, 1), size(nz)))\n\n tor_mesh = GeometryBasics.Mesh(points, faces)\nend","category":"page"},{"location":"examples/advanced/demo_tokamak_profile/","page":"Tokamak profile","title":"Tokamak profile","text":"Main.var\"##56965\".get_tokamak_topology","category":"page"},{"location":"examples/advanced/demo_tokamak_profile/","page":"Tokamak profile","title":"Tokamak profile","text":"Passing proton in a Tokamak","category":"page"},{"location":"examples/advanced/demo_tokamak_profile/","page":"Tokamak profile","title":"Tokamak profile","text":"# initial velocity for passing particle\nv₀ = [0.0, 2.15, 3.1] .* 1e6\n# initial position, [m]. where q≈2, (2, 1) flux surface.\nr₀ = [7.3622, 0.0, 0.0]\nstateinit = [r₀..., v₀...]\n\nparam = prepare(E, B; species=Proton)\ntspan = (0.0, 4e-5) # [s]\n\nprob = ODEProblem(trace!, stateinit, tspan, param)\nsol = solve(prob, Vern7(); dt=2e-11);\n\ntor_mesh = get_tokamak_topology()\n\nfig1 = Figure(fontsize=18)\nax = Axis3(fig1[1, 1],\n title = \"Passing Particle\",\n xlabel = \"x [m]\",\n ylabel = \"y [m]\",\n zlabel = \"z [m]\",\n aspect = :data,\n)\n\nlines!(ax, sol; idxs=(1,2,3))\n# Plot the surface of Tokamak\nwireframe!(fig1[1, 1], tor_mesh, color=(:blue, 0.1), linewidth=0.5, transparency=true)\n","category":"page"},{"location":"examples/advanced/demo_tokamak_profile/","page":"Tokamak profile","title":"Tokamak profile","text":"(Image: )","category":"page"},{"location":"examples/advanced/demo_tokamak_profile/","page":"Tokamak profile","title":"Tokamak profile","text":"Trapped proton in a Tokamak that shows the banana orbit","category":"page"},{"location":"examples/advanced/demo_tokamak_profile/","page":"Tokamak profile","title":"Tokamak profile","text":"# initial velocity for trapped particle\nv₀ = [0.0, 1.15, 5.1] .* 1e6\n# initial position, [m]. where q≈1, (1, 1) flux surface.\nr₀ = [6.6494, 0.0, 0.0]\nstateinit = [r₀..., v₀...]\n\nparam = prepare(E, B; species=Proton)\ntspan = (0.0, 4e-5)\n\nprob = ODEProblem(trace!, stateinit, tspan, param)\nsol = solve(prob, Vern7(); dt=1e-11)\n\nfig2 = Figure(fontsize=18)\nax = Axis3(fig2[1, 1],\n title = \"Trapped Particle\",\n xlabel = \"x [m]\",\n ylabel = \"y [m]\",\n zlabel = \"z [m]\",\n aspect = :data,\n)\n\nlines!(ax, sol; idxs=(1,2,3))\nwireframe!(fig2[1, 1], tor_mesh, color=(:blue, 0.1), linewidth=0.5, transparency=true)\n","category":"page"},{"location":"examples/advanced/demo_tokamak_profile/","page":"Tokamak profile","title":"Tokamak profile","text":"(Image: )","category":"page"},{"location":"examples/advanced/demo_tokamak_profile/","page":"Tokamak profile","title":"Tokamak profile","text":"The trajectory of the trapped particle is sometimes called the \"banana orbit\".","category":"page"},{"location":"examples/advanced/demo_tokamak_profile/","page":"Tokamak profile","title":"Tokamak profile","text":"","category":"page"},{"location":"examples/advanced/demo_tokamak_profile/","page":"Tokamak profile","title":"Tokamak profile","text":"This page was generated using DemoCards.jl and Literate.jl.","category":"page"},{"location":"","page":"Home","title":"Home","text":"CurrentModule = TestParticle","category":"page"},{"location":"#TestParticle.jl","page":"Home","title":"TestParticle.jl","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"TestParticle.jl is a test particle tracer in a static electromagnetic field.","category":"page"},{"location":"","page":"Home","title":"Home","text":"This package supports charged particle tracing in analytic/numerical relativistic/non-relativistic","category":"page"},{"location":"","page":"Home","title":"Home","text":"electric and magnetic field;\nbody force field.","category":"page"},{"location":"","page":"Home","title":"Home","text":"All tracing are performed in 3D, as is the nature for the fields. For a numerical field, the mesh is constructed with Meshes.jl, and the field is interpolated with the aid of Interpolations.jl. For an analytical field, the user is responsible for providing the function for calculating the field at a given spatial location. The actual tracing is done through DifferentialEquations.jl, thanks to the ODE system of the equations of motion.","category":"page"},{"location":"","page":"Home","title":"Home","text":"For all the tracing methods, we provide both an inplace version (with ! at the end of the function name) and a non-inplace version using StaticArrays. The non-inplace version requires the initial conditions to be static a static vector. Use them at your convenience.","category":"page"},{"location":"","page":"Home","title":"Home","text":"The single particle motions are the basics in understanding the test particle method. Check out Single-Particle Motions for more complete maths.","category":"page"},{"location":"#Installation","page":"Home","title":"Installation","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"julia> ]\npkg> add TestParticle","category":"page"},{"location":"#Usage","page":"Home","title":"Usage","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"It would be better to understand the basic workflow of DifferentialEquations.jl before digging into TestParticle.jl. All we are doing here can be concluded as contructing the ODE system from Newton's 2nd law and preparing the field/particle data. Check more in examples.","category":"page"},{"location":"","page":"Home","title":"Home","text":"Additionally, we have a native Boris solver with a similar interface as DifferentialEquations.jl. Check out the details in later sections.","category":"page"},{"location":"#Acknowledgement","page":"Home","title":"Acknowledgement","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"Nothing can be done such easily without the support of the Julia community. We appreciate all the contributions from developers around the world.","category":"page"},{"location":"tutorial/#Tutorial","page":"Tutorial","title":"Tutorial","text":"","category":"section"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"What makes plasmas particularly difficult to analyze is the fact that the densities fall in an intermediate range. Fluids like water are so dense that the motions of individual molecules do not have to be considered. Collisions dominate, and the simple equations of ordinary fluid dynamics suffice. At the other extreme in very low-density devices, only single-particle trajectories need to be considered; collective effects are often unimportant. Plasma behaves sometimes like fluids, and sometimes like a collection of individual particles. The first step in learning how to deal with this schizophrenic personality is to understand how single particles behave in electric and magnetic fields.","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"Here we assume that the EM fields are prescribed and not affected by the charged particles. References can be found in classic textbooks like Introduction to Plasma Physics and Controlled Fusion by F.F.Chen, and Fundamentals of Plasma Physics by Paul Bellan. For more complete notes corresponding to the derivation online, please check out Single-Particle Motions.","category":"page"},{"location":"tutorial/#Choice-of-numerical-algorithms","page":"Tutorial","title":"Choice of numerical algorithms","text":"","category":"section"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"By default DifferentialEquations.jl applies Tsit5 to an ODE problem. If only OrdinaryDiffEq.jl is imported, then we need to explicitly select a solver. However, not all solvers are guaranteed to work. For example, the demo case of electron tracing in the magnetic bottle with strong magnetic field is tested to work only with fixed timestep algorithms like Euler and the Adams-Bashforth family.","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"Currently we recommend Vern9 as a starting point for adaptive timestepping, with additional fine tuning by changing reltol if needed. You can also try out the native implementation of the Boris method in TestParticle.jl, with a constraint of using a fixed time step.","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"Take you some time to figure out which algorithm works for your problem!","category":"page"},{"location":"tutorial/#Unit-conversions","page":"Tutorial","title":"Unit conversions","text":"","category":"section"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"By default SI units are applied within the package. However, users can also define their own units by setting the particle mass and charge (2 constants) and providing the basic scales of magnetic field, length, time, or velocity (3 reference scales required; length, time and velocity are interchangable).","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"In the dimensionless natural unit system, we have the most number of ones which in turn gives the simplest form for computation. Check out the demos on unit conversions for more details.","category":"page"},{"location":"tutorial/#Tracing-backwards-in-time","page":"Tutorial","title":"Tracing backwards in time","text":"","category":"section"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"It is easy to trace a charged particle backwards in time. In a forward tracing problem, we set tspan = (t1, t2) where t1 < t2. In a backward tracing problem, we simply set t1 > t2, e.g. tspan = (0.0, -1.0). Note that tracing backwards in time is different from inversing the velocity because of the cross product in the Lorentz force. More specifically, the drift velocities will not change sign if one inverses velocity.","category":"page"},{"location":"tutorial/#Multiple-particles-tracing","page":"Tutorial","title":"Multiple particles tracing","text":"","category":"section"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"There are two ways to trace multiple particles simultaneously:","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"Extracting the solution in a loop with varying initial conditions. See the example demo_ensemble.\nConstructing the Ensemble Simulations. One example can be found here. However, note that by default the ensemble type replicates the parameters for each solution, which is very memory inefficient for tracing in a numeric field. We need to set safetycopy=false to make the field as a reference in the parameter of each trajectory.","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"The Boris pusher follows a similar interface for multithreading.","category":"page"},{"location":"tutorial/#Guiding-center-drifts","page":"Tutorial","title":"Guiding center drifts","text":"","category":"section"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"By solving the trajectories of particles, we can calculate the actual guiding center orbits by following the definition. This is supported directly via get_gc. In theoretical treatments, all kinds of drifts have been derived with analytical formulas listed below. With additional ODEs for solving the drifts, we can separate the different effects or check the deviation of actual orbits from the low order analytical formulas.","category":"page"},{"location":"tutorial/#Summary-of-guiding-center-drifts","page":"Tutorial","title":"Summary of guiding center drifts","text":"","category":"section"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"General force:","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"mathbfv_f = frac1qfracmathbfFtimesmathbfBB^2","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"Electric field:","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"mathbfv_E = fracmathbfEtimesmathbfBB^2","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"Gravitational field:","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"mathbfv_g = fracmqfracmathbfgtimesmathbfBB^2","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"Nonuniform electric field:","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"mathbfv_E = Big( 1+frac14r_L^2 nabla^2 Big)fracmathbfEtimesmathbfBB^2","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"Nonuniform magnetic field:","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"Grad-B:","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"beginaligned\nmathbfv_nabla B = pm frac12v_perp r_LfracmathbfBtimesnabla BB^2 \n=fracmv_perp^22qB^3mathbfBtimesnabla B\nendaligned","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"Curvature drift:","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"beginaligned\nmathbfv_c = fracmv_parallel^2qfracmathbfR_c timesmathbfBR_c^2 B^2 \n= fracmv_parallel^2qB^4mathbfBtimesleft mathbfBcdotnablamathbfB right \n= fracm v_parallel^2qB^3mathbfBtimesnabla Btext(current-free)\nendaligned","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"Curved vacuum field:","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"beginaligned\nmathbfv_c + mathbfv_nabla B = fracmqBig( v_parallel^2 + frac12v_perp^2 Big) fracmathbfR_c timesmathbfBR_c^2 B^2 \n= fracmqfracmathbfBtimesnabla BB^3Big( v_parallel^2 + frac12v_perp^2 Big)\nendaligned","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"Polarization drift:[2]","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"mathbfv_p = pm frac1omega_c BfracdmathbfEdt","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"[2]: In common test particle models, we assume static EM fields, so the polarization drift as well as adiabatic heating is not present. However, it is easily achieveable here in this package.","category":"page"},{"location":"tutorial/#Adiabatic-Invariants","page":"Tutorial","title":"Adiabatic Invariants","text":"","category":"section"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"See more thorough notes on the adiabatic invariants.","category":"page"},{"location":"tutorial/#Solution-Interpolations","page":"Tutorial","title":"Solution Interpolations","text":"","category":"section"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"All the tracing solutions come with an interpolation method to make them continuous. Depending on the order of the scheme, different orders of interpolations are applied.","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"Note that if the scheme comes with an \"lazy\" interpolation (e.g. Vern family), you can either output the full solution (default) and interpolate","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"sol = solve(prob, Vern9())","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"or turn off the lazy interpolation and select part of the solution","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"sol = solve(prob, Vern9(lazy=false), save_idxs=[1,2,3])","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"The interpolated solution would be wrong if lazy=true and save_idxs!=[1,2,3,4,5,6].","category":"page"},{"location":"tutorial/#Presentations","page":"Tutorial","title":"Presentations","text":"","category":"section"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"Please checkout:","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"TestParticle.jl: A New Tool for An Old Problem\n基于开源工具链的测试粒子模型","category":"page"},{"location":"examples/advanced/demo_tokamak_coil/","page":"Coil Tokamak","title":"Coil Tokamak","text":"EditURL = \"/home/runner/work/TestParticle.jl/TestParticle.jl/docs/examples/advanced/demo_tokamak_coil.jl\"","category":"page"},{"location":"examples/advanced/demo_tokamak_coil/#demo_tokamak_coil","page":"Coil Tokamak","title":"Coil Tokamak","text":"","category":"section"},{"location":"examples/advanced/demo_tokamak_coil/","page":"Coil Tokamak","title":"Coil Tokamak","text":"(Image: Source code) (Image: compat) (Image: Author) (Image: Update time)","category":"page"},{"location":"examples/advanced/demo_tokamak_coil/","page":"Coil Tokamak","title":"Coil Tokamak","text":"This example shows how to trace protons in a stationary magnetic field that corresponds to a Tokamak represented by a circle of coils. A excellent introduction video to Tokamak can be found here in Mandarin.","category":"page"},{"location":"examples/advanced/demo_tokamak_coil/","page":"Coil Tokamak","title":"Coil Tokamak","text":"using TestParticle\nusing TestParticle: getB_tokamak_coil\nusing OrdinaryDiffEq\nusing StaticArrays\nusing Statistics: mean\nusing Printf\nusing CairoMakie\n\n### Obtain field\n\n# Magnetic bottle parameters in SI units\nconst ICoil = 80. # current in the coil\nconst N = 15000 # number of windings\nconst IPlasma = 1e6 # current in the plasma\nconst a = 1.5 # radius of each coil\nconst b = 0.8 # radius of central region\n\nfunction getB(xu)\n SVector{3}(getB_tokamak_coil(xu[1], xu[2], xu[3], a, b, ICoil*N, IPlasma))\nend\n\nfunction getE(xu)\n SA[0.0, 0.0, 0.0]\nend\n\n### Initialize particles\nm = TestParticle.mᵢ\nq = TestParticle.qᵢ\nc = TestParticle.c\n\n# initial velocity, [m/s]\nv₀ = [-0.1, -0.15, 0.0] .* c\n# initial position, [m]\nr₀ = [2.3, 0.0, 0.0]\nstateinit = [r₀..., v₀...]\n\nparam = prepare(getE, getB; species=Proton)\ntspan = (0.0, 1e-6)\n\nprob = ODEProblem(trace!, stateinit, tspan, param)\n\n@printf \"Speed = %6.4f %s\\n\" √(v₀[1]^2+v₀[2]^2+v₀[3]^2)/c*100 \"% speed of light\"\n@printf \"Energy = %6.4f MeV\\n\" (1/√(1-(v₀[1]/c)^2-(v₀[2]/c)^2-(v₀[3]/c)^2)-1)*m*c^2/abs(q)/1e6\n\n# Default Tsit5() alone does not work in this case! You need to set a maximum\n# timestep to maintain stability, or choose a different algorithm as well.\n# The sample figure in the gallery is generated with AB3() and dt=2e-11.\nsol = solve(prob, Tsit5(); dt=2e-11, save_idxs=[1,2,3])\n\n### Visualization\n\nf = Figure(fontsize=18)\nax = Axis3(f[1, 1],\n title = \"Particle trajectory in Tokamak\",\n xlabel = \"x [m]\",\n ylabel = \"y [m]\",\n zlabel = \"z [m]\",\n aspect = :data,\n)\n\nplot!(sol, label=\"proton\")\n\n# Plot coils\nθ = range(0, 2π, length=201)\ny = a * cos.(θ)\nz = a * sin.(θ)\nfor i in 0:17\n ϕ = i*π/9\n plot!(y*sin(ϕ) .+ (a+b)*sin(ϕ), y*cos(ϕ) .+ (a+b)*cos(ϕ), z, color=(:red, 0.3))\nend\n\n# Plot Tokamak\nu = range(0, 2π, length=100)\nv = range(0, 2π, length=100)\n\nU = [y for _ in u, y in v]\nV = [x for x in u, _ in v]\n\nX = @. (a + b + (a - 0.05)*cos(U)) * cos(V)\nY = @. (a + b + (a - 0.05)*cos(U)) * sin(V)\nZ = @. (a - 0.05) * sin(U)\n\nwireframe!(ax, X, Y, Z, color=(:blue, 0.1), linewidth=0.5, transparency=true)\n","category":"page"},{"location":"examples/advanced/demo_tokamak_coil/","page":"Coil Tokamak","title":"Coil Tokamak","text":"(Image: )","category":"page"},{"location":"examples/advanced/demo_tokamak_coil/","page":"Coil Tokamak","title":"Coil Tokamak","text":"","category":"page"},{"location":"examples/advanced/demo_tokamak_coil/","page":"Coil Tokamak","title":"Coil Tokamak","text":"This page was generated using DemoCards.jl and Literate.jl.","category":"page"}] +[{"location":"examples/basics/demo_dimensionless/","page":"Dimensionless Units","title":"Dimensionless Units","text":"EditURL = \"/home/runner/work/TestParticle.jl/TestParticle.jl/docs/examples/basics/demo_dimensionless.jl\"","category":"page"},{"location":"examples/basics/demo_dimensionless/#demo_dimensionless","page":"Dimensionless Units","title":"Dimensionless Units","text":"","category":"section"},{"location":"examples/basics/demo_dimensionless/","page":"Dimensionless Units","title":"Dimensionless Units","text":"(Image: Source code) (Image: compat) (Image: Author) (Image: Update time)","category":"page"},{"location":"examples/basics/demo_dimensionless/","page":"Dimensionless Units","title":"Dimensionless Units","text":"This example shows how to trace charged particles in dimensionless units. After normalization, q=1 B=1 m=1 so that the gyroradius r_L = mv_perpqB = v_perp. All the quantities are given in dimensionless units. If the magnetic field is homogeneous and the initial perpendicular velocity is 4, then the gyroradius is 4. To convert them to the original units, v_perp = 4*U_0 and r_L = 4*l_0. Check Demo: single tracing with additional diagnostics for explaining the unit conversion.","category":"page"},{"location":"examples/basics/demo_dimensionless/","page":"Dimensionless Units","title":"Dimensionless Units","text":"Tracing in dimensionless units is beneficial for many scenarios. For example, MHD simulations do not have intrinsic scales. Therefore, we can do dimensionless particle tracing in MHD fields, and then convert to any scale we would like.","category":"page"},{"location":"examples/basics/demo_dimensionless/","page":"Dimensionless Units","title":"Dimensionless Units","text":"Now let's demonstrate this with trace_normalized!.","category":"page"},{"location":"examples/basics/demo_dimensionless/","page":"Dimensionless Units","title":"Dimensionless Units","text":"using TestParticle\nusing TestParticle: qᵢ, mᵢ\nusing OrdinaryDiffEq\n\nusing CairoMakie\n\n# Number of cells for the field along each dimension\nnx, ny, nz = 4, 6, 8\n# Unit conversion factors between SI and dimensionless units\nB₀ = 10e-9 # [T]\nΩ = abs(qᵢ) * B₀ / mᵢ # [1/s]\nt₀ = 1 / Ω # [s]\nU₀ = 1.0 # [m/s]\nl₀ = U₀ * t₀ # [m]\nE₀ = U₀*B₀ # [V/m]\n# All quantities are in dimensionless units\nx = range(-10, 10, length=nx) # [l₀]\ny = range(-10, 10, length=ny) # [l₀]\nz = range(-10, 10, length=nz) # [l₀]\n\nB = fill(0.0, 3, nx, ny, nz) # [B₀]\nB[3,:,:,:] .= 1.0\nE = fill(0.0, 3, nx, ny, nz) # [E₀]\n\nparam = prepare(x, y, z, E, B; species=User)\n\n# Initial condition\nstateinit = let\n x0 = [0.0, 0.0, 0.0] # initial position [l₀]\n u0 = [4.0, 0.0, 0.0] # initial velocity [v₀]\n [x0..., u0...]\nend\n# Time span\ntspan = (0.0, π) # half gyroperiod\n\nprob = ODEProblem(trace_normalized!, stateinit, tspan, param)\nsol = solve(prob, Vern9())\n\n### Visualization\nf = Figure(fontsize = 18)\nax = Axis(f[1, 1],\n title = \"Proton trajectory\",\n xlabel = \"X\",\n ylabel = \"Y\",\n limits = (-4.1, 4.1, -8.1, 0.1),\n aspect = DataAspect()\n)\n\nlines!(ax, sol, vars=(1,2))\n","category":"page"},{"location":"examples/basics/demo_dimensionless/","page":"Dimensionless Units","title":"Dimensionless Units","text":"(Image: )","category":"page"},{"location":"examples/basics/demo_dimensionless/","page":"Dimensionless Units","title":"Dimensionless Units","text":"","category":"page"},{"location":"examples/basics/demo_dimensionless/","page":"Dimensionless Units","title":"Dimensionless Units","text":"This page was generated using DemoCards.jl and Literate.jl.","category":"page"},{"location":"examples/advanced/demo_currentsheet/","page":"Current sheet","title":"Current sheet","text":"EditURL = \"/home/runner/work/TestParticle.jl/TestParticle.jl/docs/examples/advanced/demo_currentsheet.jl\"","category":"page"},{"location":"examples/advanced/demo_currentsheet/#demo_currentsheet","page":"Current sheet","title":"Current sheet","text":"","category":"section"},{"location":"examples/advanced/demo_currentsheet/","page":"Current sheet","title":"Current sheet","text":"(Image: Source code) (Image: compat) (Image: Author) (Image: Update time)","category":"page"},{"location":"examples/advanced/demo_currentsheet/","page":"Current sheet","title":"Current sheet","text":"This example shows how to trace protons in a stationary magnetic field that corresponds to the 1D Harris current sheet defined by a reference strength and width. Reference: https://en.wikipedia.org/wiki/Current_sheet","category":"page"},{"location":"examples/advanced/demo_currentsheet/","page":"Current sheet","title":"Current sheet","text":"using TestParticle\nusing TestParticle: getB_CS_harris\nusing OrdinaryDiffEq\nusing StaticArrays\nusing CairoMakie\n\n### Obtain field\n\n# Harris current sheet parameters in SI units\nconst B₀, L = 20e-9, 0.4TestParticle.Rₑ\n\nfunction getB(xu)\n SVector{3}(getB_CS_harris(xu[1:3], B₀, L))\nend\n\nfunction getE(xu)\n SA[0.0, 0.0, 0.0]\nend\n\n### Initialize particles\n\nm = TestParticle.mᵢ\nq = TestParticle.qᵢ\nc = TestParticle.c\nRₑ = TestParticle.Rₑ\n# Initial condition\nstateinit = let\n # initial particle energy, [eV]\n Ek = 5e7\n # initial velocity, [m/s]\n v₀ = [c*√(1-1/(1+Ek*q/(m*c^2))^2), 0.0, 0.0]\n # initial position, [m]\n r₀ = [-5.0Rₑ, 0.0, 0.0]\n\n [r₀..., v₀...]\nend\n\nparam = prepare(getE, getB)\ntspan = (0.0, 10.0)\n\nprob = ODEProblem(trace!, stateinit, tspan, param)\n\nsol = solve(prob, Tsit5(); save_idxs=[1,2,3])\n\n### Visualization\n\nf = Figure(fontsize=18)\nax = Axis3(f[1, 1],\n title = \"Particle trajectory near the Harris current sheet\",\n xlabel = \"x [Re]\",\n ylabel = \"y [Re]\",\n zlabel = \"z [Re]\",\n aspect = :data,\n)\n\nn = 100 # number of timepoints\nts = range(tspan..., length=n)\nx = sol(ts, idxs=1)./Rₑ |> Vector\ny = sol(ts, idxs=2)./Rₑ |> Vector\nz = sol(ts, idxs=3)./Rₑ |> Vector\n\nl = lines!(ax, x, y, z, label=\"50 MeV proton, B0 = 20 nT\")\naxislegend()\n\nX, Y, Z = let xrange = range(-8, 8, length=20)\n X = collect(Float32, xrange)\n Y = zeros(Float32, size(X)...)\n Z = zeros(Float32, size(X)...)\n X, Y, Z\nend\n\nB = zeros(Float32, 3, size(X)...)\n\ni = 1\nfor (x,y) in zip(X, Y)\n B[1+3*(i-1):3*i] = getB_CS_harris([x,0.0,0.0], 4e-2, 1.0)\n global i += 1\nend\n\nfor s = 1:3\n quiver!(ax, X, Y, Z, vec(B[1,:,:]), vec(B[2,:,:]), vec(B[3,:,:]),\n color=:black, alpha=0.6, lengthscale=100.0)\n @. Y -= 15\nend\n","category":"page"},{"location":"examples/advanced/demo_currentsheet/","page":"Current sheet","title":"Current sheet","text":"(Image: )","category":"page"},{"location":"examples/advanced/demo_currentsheet/","page":"Current sheet","title":"Current sheet","text":"","category":"page"},{"location":"examples/advanced/demo_currentsheet/","page":"Current sheet","title":"Current sheet","text":"This page was generated using DemoCards.jl and Literate.jl.","category":"page"},{"location":"examples/advanced/demo_savingcallback/","page":"Single tracing with additional diagnostics","title":"Single tracing with additional diagnostics","text":"EditURL = \"/home/runner/work/TestParticle.jl/TestParticle.jl/docs/examples/advanced/demo_savingcallback.jl\"","category":"page"},{"location":"examples/advanced/demo_savingcallback/#demo_savingcallback","page":"Single tracing with additional diagnostics","title":"Single tracing with additional diagnostics","text":"","category":"section"},{"location":"examples/advanced/demo_savingcallback/","page":"Single tracing with additional diagnostics","title":"Single tracing with additional diagnostics","text":"(Image: Source code) (Image: compat) (Image: Author) (Image: Update time)","category":"page"},{"location":"examples/advanced/demo_savingcallback/","page":"Single tracing with additional diagnostics","title":"Single tracing with additional diagnostics","text":"This example demonstrates tracing one proton in an analytic E field and numerical B field. It also combines one type of normalization using a reference velocity U₀, a reference magnetic field B₀, and a reference time 1/Ω, where Ω is the gyrofrequency. This indicates that in the dimensionless units, a proton with initial perpendicular velocity 1 under magnetic field magnitude 1 will possess a gyro-radius of 1. In the dimensionless spatial coordinates, we can zoom in/out the EM field to control the number of discrete points encountered in a gyroperiod. For example, if dx=dy=dz=1, it means that a particle with perpendicular velocity 1 will \"see\" one discrete point along a certain direction oriented from the gyro-center within the gyro-radius; if dx=dy=dz=0.5, then the particle will \"see\" two discrete points. MHD models, for instance, are dimensionless by nature. There will be customized (dimensionless) units for (x,y,z,E,B) that we needs to convert the dimensionless units for computing. If we simulate a turbulence field with MHD, we want to include more discrete points within a gyro-radius for the effect of small scale perturbations to take place. (Otherwise within one gyro-period all you will see is a nice-looking helix!) However, we cannot simply shrink the spatial coordinates as we wish, otherwise we will quickly encounter the boundary of our simulation.","category":"page"},{"location":"examples/advanced/demo_savingcallback/","page":"Single tracing with additional diagnostics","title":"Single tracing with additional diagnostics","text":"The SavingCallback from DiffEqCallbacks.jl can be used to save additional outputs for diagnosis. Here we save the magnetic field along the trajectory, together with the parallel velocity. Note that SavingCallback is currently not compatible with ensemble problems; for multiple particle tracing with customized outputs, see Demo: ensemble tracing with extra saving.","category":"page"},{"location":"examples/advanced/demo_savingcallback/","page":"Single tracing with additional diagnostics","title":"Single tracing with additional diagnostics","text":"using TestParticle\nusing TestParticle: qᵢ, mᵢ\nusing OrdinaryDiffEq\nusing StaticArrays\nusing Statistics\nusing LinearAlgebra: normalize, ×, ⋅\nusing DiffEqCallbacks\n\n# Number of cells for the field along each dimension\nnx, ny, nz = 4, 6, 8\n# Spatial coordinates given in customized units\nx = range(-0.5, 0.5, length=nx)\ny = range(-0.5, 0.5, length=ny)\nz = range(-0.5, 0.5, length=nz)\n# Numerical magnetic field given in customized units\nB = Array{Float32, 4}(undef, 3, nx, ny, nz)\n\nB[1,:,:,:] .= 0.0\nB[2,:,:,:] .= 0.0\nB[3,:,:,:] .= 2.0\n\n# Reference values for unit conversions between the customized and dimensionless units\nconst B₀ = let Bmag = @views hypot.(B[1,:,:,:], B[2,:,:,:], B[3,:,:,:])\n sqrt(mean(vec(Bmag) .^ 2))\nend\nconst U₀ = 1.0\nconst l₀ = 4*nx\nconst t₀ = l₀ / U₀\nconst E₀ = U₀ * B₀\n\n### Convert from customized to default dimensionless units\n# Dimensionless spatial extents [l₀]\nx /= l₀\ny /= l₀\nz /= l₀\n# For full EM problems, the normalization of E and B should be done separately.\nB ./= B₀\nE(x) = SA[0.0/E₀, 0.0/E₀, 0.0/E₀]\n\n# By default User type assumes q=1, m=1; bc=2 uses periodic boundary conditions\nparam = prepare(x, y, z, E, B; species=User, bc=2)\n\ntspan = (0.0, π) # half averaged gyroperiod based on B₀\n\n# Dummy initial state; positions have units l₀; velocities have units U₀\nstateinit = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]\n\nprob = ODEProblem(trace_normalized!, stateinit, tspan, param)\n\nprob.u0[4:6] = let\n B0 = prob.p[3](prob.u0)\n B0 = normalize(B0)\n\n Bperp1 = SA[0.0, -B0[3], B0[2]] |> normalize\n Bperp2 = B0 × Bperp1 |> normalize\n\n # initial velocity azimuthal angle\n ϕ = 2π*0\n # initial velocity pitch angle w.r.t. B\n θ = acos(0.0)\n\n sinϕ, cosϕ = sincos(ϕ)\n @. (B0*cos(θ) + Bperp1*(sin(θ)*cosϕ) + Bperp2*(sin(θ)*sinϕ)) * U₀\nend\n\nsaved_values = SavedValues(Float64, Tuple{SVector{3, Float64}, Float64})\n\nfunction save_B_mu(u, t, integrator)\n b = integrator.p[3](u)\n μ = @views b ⋅ u[4:6] / hypot(b...)\n\n b, μ\nend\n\ncb = SavingCallback(save_B_mu, saved_values)\n\nsol = solve(prob, Vern9(); callback=cb);","category":"page"},{"location":"examples/advanced/demo_savingcallback/","page":"Single tracing with additional diagnostics","title":"Single tracing with additional diagnostics","text":"The extra values are saved in saved_values:","category":"page"},{"location":"examples/advanced/demo_savingcallback/","page":"Single tracing with additional diagnostics","title":"Single tracing with additional diagnostics","text":"saved_values","category":"page"},{"location":"examples/advanced/demo_savingcallback/","page":"Single tracing with additional diagnostics","title":"Single tracing with additional diagnostics","text":"SavedValues{tType=Float64, savevalType=Tuple{StaticArraysCore.SVector{3, Float64}, Float64}}\nt:\n[0.0, 0.0007064003808057418, 0.006151342961409062, 0.03990852562861322, 0.24729664360869114, 0.8655117969873891, 1.8106352974346311, 2.900697481526176, 3.141592653589793]\nsaveval:\nTuple{StaticArraysCore.SVector{3, Float64}, Float64}[([0.0, 0.0, 1.0], 6.123233995736766e-17), ([0.0, 0.0, 1.0], 6.123233995736766e-17), ([0.0, 0.0, 1.0], 6.123233995736766e-17), ([0.0, 0.0, 1.0], 6.123233995736766e-17), ([0.0, 0.0, 1.0], 6.123233995736766e-17), ([0.0, 0.0, 1.0], 6.123233995736766e-17), ([0.0, 0.0, 1.0], 6.123233995736766e-17), ([0.0, 0.0, 1.0], 6.123233995736766e-17), ([0.0, 0.0, 1.0], 6.123233995736766e-17)]","category":"page"},{"location":"examples/advanced/demo_savingcallback/","page":"Single tracing with additional diagnostics","title":"Single tracing with additional diagnostics","text":"","category":"page"},{"location":"examples/advanced/demo_savingcallback/","page":"Single tracing with additional diagnostics","title":"Single tracing with additional diagnostics","text":"This page was generated using DemoCards.jl and Literate.jl.","category":"page"},{"location":"examples/basics/demo_polarization_drift/","page":"Polarization drift","title":"Polarization drift","text":"EditURL = \"/home/runner/work/TestParticle.jl/TestParticle.jl/docs/examples/basics/demo_polarization_drift.jl\"","category":"page"},{"location":"examples/basics/demo_polarization_drift/#demo_polarization_drift","page":"Polarization drift","title":"Polarization drift","text":"","category":"section"},{"location":"examples/basics/demo_polarization_drift/","page":"Polarization drift","title":"Polarization drift","text":"(Image: Source code) (Image: compat) (Image: Author) (Image: Author) (Image: Update time)","category":"page"},{"location":"examples/basics/demo_polarization_drift/","page":"Polarization drift","title":"Polarization drift","text":"This example demonstrates a single proton motion under time-varying E field. More theoretical details can be found in Time-Varying E Drift, and Fundamentals of Plasma Physics by Paul Bellan.","category":"page"},{"location":"examples/basics/demo_polarization_drift/","page":"Polarization drift","title":"Polarization drift","text":"using TestParticle\nusing TestParticle: get_gc\nusing OrdinaryDiffEq\nusing StaticArrays\nusing CairoMakie\n\nfunction uniform_B(x)\n return SA[0, 0, 1e-8]\nend\n\nfunction time_varying_E(x, t)\n # return SA[0, 1e-9*cos(0.1*t), 0]\n return SA[0, 1e-9*0.1*t, 0]\nend\n\n# Initial condition\nstateinit = let x0 = [1.0, 0, 0], v0 = [0.0, 1.0, 0.1]\n [x0..., v0...]\nend\n# Time span\ntspan = (0, 100)\nparam = prepare(time_varying_E, uniform_B, species=Proton)\nprob = ODEProblem(trace!, stateinit, tspan, param)\nsol = solve(prob, Vern9())\n# Functions for obtaining the guiding center from actual trajectory\ngc = get_gc(param)\nv_perp(xu) = hypot(xu[4], xu[5])\n\n# Visualization\nf = Figure(size=(800, 600), fontsize=18)\nax1 = Axis3(f[1:3, 1],\n title = \"Polarization Drift\",\n xlabel = \"x [m]\",\n ylabel = \"y [m]\",\n zlabel = \"z [m]\",\n aspect = :data,\n azimuth = 0.9π,\n elevation = 0.1π\n)\nax2 = Axis(f[1,2], xlabel = \"time [s]\", ylabel = \"v_perp [m/s]\")\nax3 = Axis(f[2,2], xlabel = \"time [s]\", ylabel = \"y [m]\")\nax4 = Axis(f[3,2], xlabel = \"time [s]\", ylabel = \"gc_y [m]\")\n\ngc_y(t, x, y, z, vx, vy, vz) = (t, gc(SA[x, y, z, vx, vy, vz])[2])\nv_perp(t, vy, vz) = (t, sqrt(vy^2 + vz^2))\n\nlines!(ax1, sol, idxs=(1, 2, 3))\nlines!(ax2, sol, idxs=(v_perp, 0, 5, 6))\nlines!(ax3, sol, idxs=2)\nlines!(ax4, sol, idxs=(gc_y, 0, 1, 2, 3, 4, 5, 6))\n","category":"page"},{"location":"examples/basics/demo_polarization_drift/","page":"Polarization drift","title":"Polarization drift","text":"(Image: )","category":"page"},{"location":"examples/basics/demo_polarization_drift/","page":"Polarization drift","title":"Polarization drift","text":"","category":"page"},{"location":"examples/basics/demo_polarization_drift/","page":"Polarization drift","title":"Polarization drift","text":"This page was generated using DemoCards.jl and Literate.jl.","category":"page"},{"location":"examples/basics/demo_gradient_B/","page":"Grad-B drift","title":"Grad-B drift","text":"EditURL = \"/home/runner/work/TestParticle.jl/TestParticle.jl/docs/examples/basics/demo_gradient_B.jl\"","category":"page"},{"location":"examples/basics/demo_gradient_B/#demo_gradB","page":"Grad-B drift","title":"Grad-B drift","text":"","category":"section"},{"location":"examples/basics/demo_gradient_B/","page":"Grad-B drift","title":"Grad-B drift","text":"(Image: Source code) (Image: compat) (Image: Author) (Image: Author) (Image: Update time)","category":"page"},{"location":"examples/basics/demo_gradient_B/","page":"Grad-B drift","title":"Grad-B drift","text":"This example demonstrates a single proton motion under a non-uniform B field with gradient ∇B ⊥ B. The orbit of guiding center includes some high order terms, it is different from the formula of magnetic field gradient drift of some textbooks which just preserves the first order term. It is more complex than the simpler ExB drift. More theoretical details can be found in Grad-B Drift, and Fundamentals of Plasma Physics by Paul Bellan.","category":"page"},{"location":"examples/basics/demo_gradient_B/","page":"Grad-B drift","title":"Grad-B drift","text":"using TestParticle\nusing TestParticle: get_gc\nusing OrdinaryDiffEq\nusing StaticArrays\nusing LinearAlgebra: ×, ⋅, normalize, norm\nusing ForwardDiff: gradient\nusing CairoMakie\n\nfunction grad_B(x)\n return SA[0, 0, 1e-8 + 1e-9*x[2]]\nend\n\nfunction uniform_E(x)\n return SA[1e-9, 0, 0]\nend\n\nabs_B(x) = norm(grad_B(x))\n\n# Trace the orbit of the guiding center using analytical drifts\nfunction trace_gc!(dx, x, p, t)\n q2m, E, B, sol = p\n xu = sol(t)\n gradient_B = gradient(abs_B, x)\n Bv = B(x)\n b = normalize(Bv)\n v_par = (xu[4:6] ⋅ b) .* b\n v_perp = xu[4:6] - v_par\n dx[1:3] = norm(v_perp)^2*(Bv × gradient_B)/(2*q2m*norm(Bv)^3) +\n (E(x) × Bv) / norm(Bv)^2 + v_par\nend\n\n# Initial condition\nstateinit = let x0 = [1.0, 0, 0], v0 = [0.0, 1.0, 0.1]\n [x0..., v0...]\nend\n# Time span\ntspan = (0, 20)\nparam = prepare(uniform_E, grad_B, species=Proton)\nprob = ODEProblem(trace!, stateinit, tspan, param)\nsol = solve(prob, Vern9())\n# Functions for obtaining the guiding center from actual trajectory\ngc = get_gc(param)\ngc_x0 = gc(stateinit)\nprob_gc = ODEProblem(trace_gc!, gc_x0, tspan, (param..., sol))\nsol_gc = solve(prob_gc, Tsit5(); save_idxs=[1,2,3])\n\n# Numeric and analytic results\nf = Figure(fontsize=18)\nax = Axis3(f[1, 1],\n title = \"Grad-B Drift\",\n xlabel = \"x [m]\",\n ylabel = \"y [m]\",\n zlabel = \"z [m]\",\n aspect = :data,\n azimuth = 0.3π,\n)\n\ngc_plot(x, y, z, vx, vy, vz) = (gc(SA[x, y, z, vx, vy, vz])...,)\n\nlines!(ax, sol, idxs=(1, 2, 3))\nlines!(ax, sol, idxs=(gc_plot, 1, 2, 3, 4, 5, 6))\nlines!(ax, sol_gc, idxs=(1, 2, 3))\n\nfor i in 1:3\n ##TODO: wait for https://github.com/MakieOrg/Makie.jl/issues/3623 to be fixed!\n ax.scene.plots[9+2*i-1].color = Makie.wong_colors()[i]\nend\n","category":"page"},{"location":"examples/basics/demo_gradient_B/","page":"Grad-B drift","title":"Grad-B drift","text":"(Image: )","category":"page"},{"location":"examples/basics/demo_gradient_B/","page":"Grad-B drift","title":"Grad-B drift","text":"Note that in this grad-B drift case, the analytic and numeric guiding centers have different trajectories.","category":"page"},{"location":"examples/basics/demo_gradient_B/","page":"Grad-B drift","title":"Grad-B drift","text":"","category":"page"},{"location":"examples/basics/demo_gradient_B/","page":"Grad-B drift","title":"Grad-B drift","text":"This page was generated using DemoCards.jl and Literate.jl.","category":"page"},{"location":"examples/basics/demo_dimensionless_dimensional/","page":"Dimensionless and Dimensional Tracing","title":"Dimensionless and Dimensional Tracing","text":"EditURL = \"/home/runner/work/TestParticle.jl/TestParticle.jl/docs/examples/basics/demo_dimensionless_dimensional.jl\"","category":"page"},{"location":"examples/basics/demo_dimensionless_dimensional/#demo_dimensionless_dimensional","page":"Dimensionless and Dimensional Tracing","title":"Dimensionless and Dimensional Tracing","text":"","category":"section"},{"location":"examples/basics/demo_dimensionless_dimensional/","page":"Dimensionless and Dimensional Tracing","title":"Dimensionless and Dimensional Tracing","text":"(Image: Source code) (Image: compat) (Image: Author) (Image: Update time)","category":"page"},{"location":"examples/basics/demo_dimensionless_dimensional/","page":"Dimensionless and Dimensional Tracing","title":"Dimensionless and Dimensional Tracing","text":"This example shows how to trace charged particles in both dimensional and dimensionless units. We first solve the Lorentz equation in SI units, and then convert the quantities to normalized units and solve it again in dimensionless units.","category":"page"},{"location":"examples/basics/demo_dimensionless_dimensional/","page":"Dimensionless and Dimensional Tracing","title":"Dimensionless and Dimensional Tracing","text":"The Lorentz equation in SI units is written as","category":"page"},{"location":"examples/basics/demo_dimensionless_dimensional/","page":"Dimensionless and Dimensional Tracing","title":"Dimensionless and Dimensional Tracing","text":"fracmathrmdmathbfvmathrmdt = fracqmleft( mathbfvtimesmathbfB + mathbfE right)","category":"page"},{"location":"examples/basics/demo_dimensionless_dimensional/","page":"Dimensionless and Dimensional Tracing","title":"Dimensionless and Dimensional Tracing","text":"It can be normalized to","category":"page"},{"location":"examples/basics/demo_dimensionless_dimensional/","page":"Dimensionless and Dimensional Tracing","title":"Dimensionless and Dimensional Tracing","text":"fracmathrmdmathbfv^primemathrmdt^prime = mathbfv^primetimesmathbfB^prime + mathbfE^prime","category":"page"},{"location":"examples/basics/demo_dimensionless_dimensional/","page":"Dimensionless and Dimensional Tracing","title":"Dimensionless and Dimensional Tracing","text":"with the following transformation","category":"page"},{"location":"examples/basics/demo_dimensionless_dimensional/","page":"Dimensionless and Dimensional Tracing","title":"Dimensionless and Dimensional Tracing","text":"beginaligned\nmathbfv = mathbfv^prime V_0 \nt = t^prime t_0 = t^prime Omega^-1 = t^prime fracmqB_0 \nmathbfB = mathbfB^prime B_0 \nmathbfE = mathbfE^prime E_0 = mathbfE^prime V_0 B_0\nendaligned","category":"page"},{"location":"examples/basics/demo_dimensionless_dimensional/","page":"Dimensionless and Dimensional Tracing","title":"Dimensionless and Dimensional Tracing","text":"where all the coefficients with subscript 0 are expressed in SI units. All the variables with a prime are written in the dimensionless units.","category":"page"},{"location":"examples/basics/demo_dimensionless_dimensional/","page":"Dimensionless and Dimensional Tracing","title":"Dimensionless and Dimensional Tracing","text":"using TestParticle\nusing TestParticle: c, qᵢ, mᵢ\nusing OrdinaryDiffEq\nusing StaticArrays\nusing CairoMakie\n\n# Unit conversion factors between SI and dimensionless units\nconst B₀ = 1e-8 # [T]\nconst U₀ = c # [m/s]\nconst E₀ = U₀*B₀ # [V/m]\nconst Ω = abs(qᵢ) * B₀ / mᵢ # [1/s]\nconst t₀ = 1 / Ω # [s]\nconst l₀ = U₀ * t₀ # [m]\n# Electric field magnitude in SI units\nconst Emag = 1e-8 # [V/m]\n### Solving in SI units\nB(x) = SA[0, 0, B₀]\nE(x) = SA[Emag, 0.0, 0.0]\n\n# Initial conditions\nx0 = [0.0, 0.0, 0.0] # [m]\nv0 = [0.0, 0.01c, 0.0] # [m/s]\nstateinit1 = [x0..., v0...]\ntspan1 = (0, 2π*t₀) # [s]\n\nparam1 = prepare(E, B, species=Proton)\nprob1 = ODEProblem(trace!, stateinit1, tspan1, param1)\nsol1 = solve(prob1, Vern9(); reltol=1e-4, abstol=1e-6)\n\n### Solving in dimensionless units\nB_normalize(x) = SA[0, 0, B₀/B₀]\nE_normalize(x) = SA[Emag/E₀, 0.0, 0.0]\n# For full EM problems, the normalization of E and B should be done separately.\nparam2 = prepare(E_normalize, B_normalize; species=User)\n# Scale initial conditions by the conversion factors\nx0 ./= l₀\nv0 ./= U₀\ntspan2 = (0, 2π)\nstateinit2 = [x0..., v0...]\n\nprob2 = ODEProblem(trace_normalized!, stateinit2, tspan2, param2)\nsol2 = solve(prob2, Vern9(); reltol=1e-4, abstol=1e-6)\n\n### Visualization\nf = Figure(fontsize=18)\nax = Axis(f[1, 1],\n xlabel = \"x\",\n ylabel = \"y\",\n aspect = DataAspect(),\n)\n\nlines!(ax, sol1, idxs=(1, 2))\n# Interpolate dimensionless solutions and map back to SI units\nxp, yp = let trange = range(tspan2..., length=40)\n sol2.(trange, idxs=1) .* l₀, sol2.(trange, idxs=2) .* l₀\nend\nlines!(ax, xp, yp, linestyle=:dashdot, linewidth=5)\n","category":"page"},{"location":"examples/basics/demo_dimensionless_dimensional/","page":"Dimensionless and Dimensional Tracing","title":"Dimensionless and Dimensional Tracing","text":"(Image: )","category":"page"},{"location":"examples/basics/demo_dimensionless_dimensional/","page":"Dimensionless and Dimensional Tracing","title":"Dimensionless and Dimensional Tracing","text":"We see that the results are almost identical, with only floating point numerical errors. Tracing in dimensionless units usually allows larger timesteps, which leads to faster computation.","category":"page"},{"location":"examples/basics/demo_dimensionless_dimensional/","page":"Dimensionless and Dimensional Tracing","title":"Dimensionless and Dimensional Tracing","text":"","category":"page"},{"location":"examples/basics/demo_dimensionless_dimensional/","page":"Dimensionless and Dimensional Tracing","title":"Dimensionless and Dimensional Tracing","text":"This page was generated using DemoCards.jl and Literate.jl.","category":"page"},{"location":"examples/basics/demo_Buniform_Ezero/","page":"Helix motion","title":"Helix motion","text":"EditURL = \"/home/runner/work/TestParticle.jl/TestParticle.jl/docs/examples/basics/demo_Buniform_Ezero.jl\"","category":"page"},{"location":"examples/basics/demo_Buniform_Ezero/#demo_uniformB_zeroE","page":"Helix motion","title":"Helix motion","text":"","category":"section"},{"location":"examples/basics/demo_Buniform_Ezero/","page":"Helix motion","title":"Helix motion","text":"(Image: Source code) (Image: compat) (Image: Author) (Image: Author) (Image: Update time)","category":"page"},{"location":"examples/basics/demo_Buniform_Ezero/","page":"Helix motion","title":"Helix motion","text":"This example demonstrates a single proton motion under a uniform B field. The E field is assumed to be zero such that there is no particle acceleration.","category":"page"},{"location":"examples/basics/demo_Buniform_Ezero/","page":"Helix motion","title":"Helix motion","text":"using TestParticle\nusing TestParticle: get_gc\nusing OrdinaryDiffEq\nusing StaticArrays\nusing LinearAlgebra\nusing CairoMakie\n\nuniform_B(x) = SA[0.0, 0.0, 1e-8]\nuniform_E(x) = SA[0.0, 0.0, 0.0]\n\n# Initial condition\nstateinit = let x0 = [1.0, 0, 0], v0 = [0.0, 1.0, 0.1]\n [x0..., v0...]\nend\n# Time span\ntspan = (0, 18)\n\nparam = prepare(uniform_E, uniform_B, species=Proton)\nprob = ODEProblem(trace!, stateinit, tspan, param)\nsol = solve(prob, Vern9())\n\n# Visualization\nf = Figure(fontsize=18)\nax = Axis3(f[1, 1],\n title = \"Helix Trajectory\",\n xlabel = \"x [m]\",\n ylabel = \"y [m]\",\n zlabel = \"z [m]\",\n aspect = :data,\n)\n\nplot!(ax, sol, idxs=(1, 2, 3))\n","category":"page"},{"location":"examples/basics/demo_Buniform_Ezero/","page":"Helix motion","title":"Helix motion","text":"(Image: )","category":"page"},{"location":"examples/basics/demo_Buniform_Ezero/","page":"Helix motion","title":"Helix motion","text":"","category":"page"},{"location":"examples/basics/demo_Buniform_Ezero/","page":"Helix motion","title":"Helix motion","text":"This page was generated using DemoCards.jl and Literate.jl.","category":"page"},{"location":"plotfunctions/#Plot-Functions","page":"Plot Functions","title":"Plot Functions","text":"","category":"section"},{"location":"plotfunctions/","page":"Plot Functions","title":"Plot Functions","text":"TestParticle.jl wraps types of AbstractODESolution from DifferentialEquations.jl. The plotting recipes for Plots.jl and Makie.jl work automatically for the particle tracing solutions.","category":"page"},{"location":"plotfunctions/","page":"Plot Functions","title":"Plot Functions","text":"Before using the plot recipes of Testparticle.jl, you need to import Makie package via using GLMakie or using CairoMakie, which depends on your choice for the backend. All plot recipes depend on Makie.jl.","category":"page"},{"location":"plotfunctions/#Convention","page":"Plot Functions","title":"Convention","text":"","category":"section"},{"location":"plotfunctions/","page":"Plot Functions","title":"Plot Functions","text":"By convention, we use integers to represent the 7 dimensions in the input argument vars for all plotting methods:","category":"page"},{"location":"plotfunctions/","page":"Plot Functions","title":"Plot Functions","text":"Component Meaning\n0 time\n1 x\n2 y\n3 z\n4 vx\n5 vy\n6 vz","category":"page"},{"location":"plotfunctions/#Basic-recipe","page":"Plot Functions","title":"Basic recipe","text":"","category":"section"},{"location":"plotfunctions/","page":"Plot Functions","title":"Plot Functions","text":"The simplest usage is directly calling the plot or lines function provided by Makie. For example,","category":"page"},{"location":"plotfunctions/","page":"Plot Functions","title":"Plot Functions","text":"plot(sol, idxs=(1, 2, 3))","category":"page"},{"location":"plotfunctions/","page":"Plot Functions","title":"Plot Functions","text":"The keyword argument idxs can be used to select the variables to be plotted; if not specified, it will plot the timeseries of x, y, z, vx, vy, vz. Please refer to Choose Variables for details.","category":"page"},{"location":"plotfunctions/","page":"Plot Functions","title":"Plot Functions","text":"If you want to plot a function of time, position or velocity, you can first define the function. For example,","category":"page"},{"location":"plotfunctions/","page":"Plot Functions","title":"Plot Functions","text":"Eₖ(t, vx, vy, vz) = (t, mₑ*(vx^2 + vy^2 + vz^2)/2)\nlines(sol, idxs=(Eₖ, 0, 4, 5, 6))","category":"page"},{"location":"plotfunctions/","page":"Plot Functions","title":"Plot Functions","text":"will plot the kinetic energy as a function of x.","category":"page"},{"location":"plotfunctions/","page":"Plot Functions","title":"Plot Functions","text":"You can choose the plotting time span via the keyword argument tspan. For example,","category":"page"},{"location":"plotfunctions/","page":"Plot Functions","title":"Plot Functions","text":"lines(sol, tspan=(0, 1))","category":"page"},{"location":"plotfunctions/","page":"Plot Functions","title":"Plot Functions","text":"The plots can be customized further:","category":"page"},{"location":"plotfunctions/","page":"Plot Functions","title":"Plot Functions","text":"f = Figure(size=(1200, 800), fontsize=18)\nax = Axis3(f[1,1],\n title = \"Particle trajectory\",\n xlabel = \"X\",\n ylabel = \"Y\",\n zlabel = \"Z\",\n)\n\nplot!(sol, idxs=(1, 2, 3))","category":"page"},{"location":"plotfunctions/","page":"Plot Functions","title":"Plot Functions","text":"Multiple particle trajectories saved as the type EnsembleSolution is also supported by the Makie recipe.","category":"page"},{"location":"plotfunctions/#Advanced-recipe","page":"Plot Functions","title":"Advanced recipe","text":"","category":"section"},{"location":"plotfunctions/","page":"Plot Functions","title":"Plot Functions","text":"note: Note\nWe currently rely on the Makie recipe implemented in DiffEqBase.jl. This recipe depends on an experimental API introduced in Makie v0.20, which may be unstable and contains bugs. Once it becomes more stabilized with bug fixes, we will recover the interactive widgets support, e.g. orbit and monitor.","category":"page"},{"location":"plotfunctions/#orbit","page":"Plot Functions","title":"orbit","text":"","category":"section"},{"location":"plotfunctions/","page":"Plot Functions","title":"Plot Functions","text":"(Image: )","category":"page"},{"location":"plotfunctions/","page":"Plot Functions","title":"Plot Functions","text":"The slider can control the time span, and the maximum of time span will be displayed on the right of the slider.","category":"page"},{"location":"plotfunctions/","page":"Plot Functions","title":"Plot Functions","text":"The reset button can reset the scale of lines when the axis limits change. When you drag the slider, clicking reset button will reset the axis limits to fit the data.","category":"page"},{"location":"plotfunctions/#monitor","page":"Plot Functions","title":"monitor","text":"","category":"section"},{"location":"plotfunctions/","page":"Plot Functions","title":"Plot Functions","text":"","category":"page"},{"location":"plotfunctions/","page":"Plot Functions","title":"Plot Functions","text":"(Image: )","category":"page"},{"location":"plotfunctions/","page":"Plot Functions","title":"Plot Functions","text":"After first click of the run button, the evolution of the orbit will be displayed from the beginning. For other times, it will start from the time set by the time slider. The functionality of reset button is the same as above.","category":"page"},{"location":"plotfunctions/","page":"Plot Functions","title":"Plot Functions","text":"The time slider controls the time span. The speed slider controls the speed of the animation.","category":"page"},{"location":"examples/advanced/demo_output_func/","page":"Ensemble tracing with extra saving","title":"Ensemble tracing with extra saving","text":"EditURL = \"/home/runner/work/TestParticle.jl/TestParticle.jl/docs/examples/advanced/demo_output_func.jl\"","category":"page"},{"location":"examples/advanced/demo_output_func/#demo_output_func","page":"Ensemble tracing with extra saving","title":"Ensemble tracing with extra saving","text":"","category":"section"},{"location":"examples/advanced/demo_output_func/","page":"Ensemble tracing with extra saving","title":"Ensemble tracing with extra saving","text":"(Image: Source code) (Image: compat) (Image: Author) (Image: Update time)","category":"page"},{"location":"examples/advanced/demo_output_func/","page":"Ensemble tracing with extra saving","title":"Ensemble tracing with extra saving","text":"This example demonstrates tracing multiple protons in an analytic E field and numerical B field. See Demo: single tracing with additional diagnostics for explaining the unit conversion. Also check Demo: Ensemble for basic usages of the ensemble problem.","category":"page"},{"location":"examples/advanced/demo_output_func/","page":"Ensemble tracing with extra saving","title":"Ensemble tracing with extra saving","text":"The output_func argument can be used to change saving outputs. It works as a reduction function, but here we demonstrate how to add additional outputs. Besides the regular outputs, we also save the magnetic field along the trajectory, together with the parallel velocity.","category":"page"},{"location":"examples/advanced/demo_output_func/","page":"Ensemble tracing with extra saving","title":"Ensemble tracing with extra saving","text":"using TestParticle\nusing TestParticle: qᵢ, mᵢ\nusing OrdinaryDiffEq\nusing StaticArrays\nusing Statistics\nusing LinearAlgebra: normalize, ×, ⋅\nusing Random\nusing CairoMakie\n\nseed = 1 # seed for random number\nRandom.seed!(seed)\n\n\"Set initial state for EnsembleProblem.\"\nfunction prob_func(prob, i, repeat)\n B0 = prob.p[3](prob.u0)\n B0 = normalize(B0)\n\n Bperp1 = SA[0.0, -B0[3], B0[2]] |> normalize\n Bperp2 = B0 × Bperp1 |> normalize\n\n # initial azimuthal angle\n ϕ = 2π*rand()\n # initial pitch angle\n θ = acos(0.5)\n\n sinϕ, cosϕ = sincos(ϕ)\n u = @. (B0*cos(θ) + Bperp1*(sin(θ)*cosϕ) + Bperp2*(sin(θ)*sinϕ)) * U₀\n\n prob = @views remake(prob; u0=[prob.u0[1:3]..., u...])\nend\n\n# Number of cells for the field along each dimension\nnx, ny, nz = 4, 6, 8\n# Spatial coordinates given in customized units\nx = range(0, 1, length=nx)\ny = range(0, 1, length=ny)\nz = range(0, 1, length=nz)\n# Numerical magnetic field given in customized units\nB = Array{Float32, 4}(undef, 3, nx, ny, nz)\n\nB[1,:,:,:] .= 0.0\nB[2,:,:,:] .= 0.0\nB[3,:,:,:] .= 2.0\n\n# Reference values for unit conversions between the customized and dimensionless units\nconst B₀ = let Bmag = @views hypot.(B[1,:,:,:], B[2,:,:,:], B[3,:,:,:])\n sqrt(mean(vec(Bmag) .^ 2))\nend\nconst U₀ = 1.0\nconst l₀ = 2*nx\nconst t₀ = l₀ / U₀\nconst E₀ = U₀ * B₀;","category":"page"},{"location":"examples/advanced/demo_output_func/","page":"Ensemble tracing with extra saving","title":"Ensemble tracing with extra saving","text":"Convert from customized to default dimensionless units","category":"page"},{"location":"examples/advanced/demo_output_func/","page":"Ensemble tracing with extra saving","title":"Ensemble tracing with extra saving","text":"# Dimensionless spatial extents [l₀]\nx /= l₀\ny /= l₀\nz /= l₀\n# For full EM problems, the normalization of E and B should be done separately.\nB ./= B₀\nE(x) = SA[0.0/E₀, 0.0/E₀, 0.0/E₀]\n\n# By default User type assumes q=1, m=1\n# bc=2 uses periodic boundary conditions\nparam = prepare(x, y, z, E, B; species=User, bc=2)\n\n# Initial condition\nstateinit = let\n x0 = [0.0, 0.0, 0.0] # initial position [l₀]\n u0 = [1.0, 0.0, 0.0] # initial velocity [v₀]\n [x0..., u0...]\nend\n# Time span\ntspan = (0.0, 2π) # one averaged gyroperiod based on B₀\n\nsaveat = tspan[2] / 40 # save interval\n\nprob = ODEProblem(trace_normalized!, stateinit, tspan, param)\n\n\"Set customized outputs for the ensemble problem.\"\nfunction output_func(sol, i)\n Bfunc = sol.prob.p[3]\n b = Bfunc.(sol.u)\n\n μ = [@views b[i] ⋅ sol[4:6, i] / hypot(b[i]...) for i in eachindex(sol)]\n\n (sol.u, b, μ), false\nend\n\n# Number of trajectories\ntrajectories = 2\n\nensemble_prob = EnsembleProblem(prob; prob_func, output_func, safetycopy=false)\nsols = solve(ensemble_prob, Vern9(), EnsembleThreads(); trajectories, saveat);","category":"page"},{"location":"examples/advanced/demo_output_func/","page":"Ensemble tracing with extra saving","title":"Ensemble tracing with extra saving","text":"Visualization","category":"page"},{"location":"examples/advanced/demo_output_func/","page":"Ensemble tracing with extra saving","title":"Ensemble tracing with extra saving","text":"f = Figure(fontsize = 18)\nax = Axis3(f[1, 1],\n title = \"Proton trajectories\",\n xlabel = \"X\",\n ylabel = \"Y\",\n zlabel = \"Z\",\n aspect = :data,\n)\n\nfor i in eachindex(sols)\n xp = [s[1] for s in sols[i][1]]\n yp = [s[2] for s in sols[i][1]]\n zp = [s[3] for s in sols[i][1]]\n lines!(ax, xp, yp, zp, label=\"$i\")\nend\n","category":"page"},{"location":"examples/advanced/demo_output_func/","page":"Ensemble tracing with extra saving","title":"Ensemble tracing with extra saving","text":"(Image: )","category":"page"},{"location":"examples/advanced/demo_output_func/","page":"Ensemble tracing with extra saving","title":"Ensemble tracing with extra saving","text":"","category":"page"},{"location":"examples/advanced/demo_output_func/","page":"Ensemble tracing with extra saving","title":"Ensemble tracing with extra saving","text":"This page was generated using DemoCards.jl and Literate.jl.","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"EditURL = \"/home/runner/work/TestParticle.jl/TestParticle.jl/docs/examples/index.md\"","category":"page"},{"location":"examples/#examples","page":"Examples","title":"Examples","text":"","category":"section"},{"location":"examples/","page":"Examples","title":"Examples","text":"This section contains thorough examples for using TestParticle.jl. If you prefer to run the demos on your local computer with interactive support, we suggest switching from using CairoMakie (which is a Makie frontend for static 2D rendering) to using GLMakie (which is another Makie frontend for interactive 3D rendering).","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"","category":"page"},{"location":"examples/#Basics","page":"Examples","title":"Basics","text":"","category":"section"},{"location":"examples/","page":"Examples","title":"Examples","text":"
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n\n \n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Energy Conservation","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Demonstrate energy conservation in uniform fields.","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n\n \n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Boris method","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Simple electron trajectory under uniform B and zero E","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n\n \n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Helix motion","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Simple proton trajectory under uniform B and zero E","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n\n \n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Dimensionless Units","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Tracing charged particle in dimensionless units","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n\n \n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Tracing with Dimensionless Units and Periodic Boundary","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Tracing charged particle in dimensionless units and periodic boundary","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n\n \n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Dimensionless and Dimensional Tracing","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Tracing charged particle in both dimensional and dimensionless units.","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n\n \n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Proton and electron in a static EM field","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Simple particle trajectories under uniform B and zero E","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n\n \n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Multiple particles","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Tracing multiple charged particles in a static EM field","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n\n \n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"E×B drift","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Simple ExB drift demonstration","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n\n \n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"ExF drift","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Simple proton trajectory under uniform B and gravity","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n\n \n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Grad-B drift","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Simple magnetic field gradient drift demonstration","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n\n \n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Curl-B drift","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Simple magnetic field curvature drift demonstration","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n\n \n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Finite-Larmor-Radius effect","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Finite Larmor radius effect demonstration using Makie","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n\n \n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Polarization drift","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Simple polarization drift under time-varying E field","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
","category":"page"},{"location":"examples/#Advanced","page":"Examples","title":"Advanced","text":"","category":"section"},{"location":"examples/","page":"Examples","title":"Examples","text":"
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n\n \n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Advanced Boris tracing","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Boris ensemble tracing with field out-of-domain check","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n\n \n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Ensemble tracing","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Tracing multiple charged particles in a static EM field","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n\n \n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Single tracing with additional diagnostics","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Tracing one charged particle with additional diagnostics","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n\n \n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Ensemble tracing with extra saving","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Tracing multiple charged particles in a static EM field","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n\n \n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Current sheet","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Tracing charged particle in the Harris current sheet","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n\n \n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Magnetic mirror","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Charged particle in the magnetic mirror","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n\n \n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Electron in a magnetic bottle","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Tracing charged particle in a magnetic bottle","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n\n \n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Magnetic dipole","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Tracing charged particle in a static analytic dipole magnetic field","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n\n \n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Analytical magnetosphere","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Tracing charged particle in an analytical magnetosphere","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n\n \n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Shock","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Tracing charged particle in an MHD plane shock","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n\n \n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Coil Tokamak","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Tracing protons in a Tokamak","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n\n \n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Tokamak profile","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Tracing passing and trapped proton in a Tokamak","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n\n \n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Tracing particle from PIC","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"This demo shows how to trace particles from structured SWMF outputs","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
\n
\n
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"
","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"","category":"page"},{"location":"examples/advanced/demo_batsrus_3dstructured/","page":"Tracing particle from PIC","title":"Tracing particle from PIC","text":"EditURL = \"/home/runner/work/TestParticle.jl/TestParticle.jl/docs/examples/advanced/demo_batsrus_3dstructured.md\"","category":"page"},{"location":"examples/advanced/demo_batsrus_3dstructured/#demo_pic","page":"Tracing particle from PIC","title":"Tracing particle from PIC","text":"","category":"section"},{"location":"examples/advanced/demo_batsrus_3dstructured/","page":"Tracing particle from PIC","title":"Tracing particle from PIC","text":"(Image: Author) (Image: Update time)","category":"page"},{"location":"examples/advanced/demo_batsrus_3dstructured/","page":"Tracing particle from PIC","title":"Tracing particle from PIC","text":"This example shows how to trace charged particles in the structured SWMF outputs from an MHD-EPIC simulation of Ganymede. For more details about the field file format, please checkout Batsrus.jl.","category":"page"},{"location":"examples/advanced/demo_batsrus_3dstructured/","page":"Tracing particle from PIC","title":"Tracing particle from PIC","text":"using Statistics: mean\nusing Batsrus\nusing TestParticle\nusing OrdinaryDiffEq\nusing FieldTracer\nusing PyPlot\n\n## Utility functions\n\nfunction initial_conditions(i)\n j = i - 1\n [x[xc_], y[yc_+j], z[zc_], Uix[xc_,yc_+j,zc_], Uiy[xc_,yc_+j,zc_], Uiz[xc_,yc_+j,zc_]]\nend\n\n\"Set initial conditions.\"\nfunction prob_func(prob, i, repeat)\n remake(prob, u0=initial_conditions(i))\nend\n\n## Data processing\n\nfilename = \"3d_var_region0_0_t00002500_n00106468.out\"\ndata = readdata(filename)\n\nvar = getvars(data, [\"Bx\", \"By\", \"Bz\", \"Ex\", \"Ey\", \"Ez\", \"uxs0\", \"uys0\", \"uzs0\", \"uxs1\", \"uys1\", \"uzs1\"])\n\nconst RG = 2634e3 # [m]\n\nx = range(extrema(data.x[:,:,:,1])..., length=size(data.x, 1)) .* RG\ny = range(extrema(data.x[:,:,:,2])..., length=size(data.x, 2)) .* RG\nz = range(extrema(data.x[:,:,:,3])..., length=size(data.x, 3)) .* RG\nB = zeros(Float32, 3, length(x), length(y), length(z)) # [T]\nE = zeros(Float32, 3, length(x), length(y), length(z)) # [V/m]\n\n# Convert into SI units\nB[1,:,:,:] .= var[\"Bx\"] .* 1e-9\nB[2,:,:,:] .= var[\"By\"] .* 1e-9\nB[3,:,:,:] .= var[\"Bz\"] .* 1e-9\nE[1,:,:,:] .= var[\"Ex\"] .* 1e-6\nE[2,:,:,:] .= var[\"Ey\"] .* 1e-6\nE[3,:,:,:] .= var[\"Ez\"] .* 1e-6\n\n## Initial conditions\n\nUex, Uey, Uez = var[\"uxs0\"] .* 1e3, var[\"uys0\"] .* 1e3, var[\"uzs0\"] .* 1e3\nUix, Uiy, Uiz = var[\"uxs1\"] .* 1e3, var[\"uys1\"] .* 1e3, var[\"uzs1\"] .* 1e3\n\nxc_ = floor(Int, length(x)/2) + 1\nyc_ = floor(Int, length(y)/2) + 1\nzc_ = floor(Int, length(z)/2) + 1\n\nstateinit_e = [x[xc_], y[yc_], z[zc_], Uex[xc_,yc_,zc_], Uey[xc_,yc_,zc_], Uez[xc_,yc_,zc_]]\nstateinit_p = [x[xc_], y[yc_], z[zc_], Uix[xc_,yc_,zc_], Uiy[xc_,yc_,zc_], Uiz[xc_,yc_,zc_]]\n\nparam_electron = prepare(x, y, z, E, B, species=Electron)\ntspan_electron = (0.0, 0.1)\n\nparam_proton = prepare(x, y, z, E, B, species=Proton)\ntspan_proton = (0.0, 10.0)\ntrajectories = 5\n\nprob_p = ODEProblem(trace!, stateinit_p, tspan_proton, param_proton)\nensemble_prob = EnsembleProblem(prob_p, prob_func=prob_func)\nsol_p = solve(ensemble_prob, Vern9(), EnsembleThreads(); trajectories)\n\n## Visualization\n\nusing3D()\nfig = plt.figure(figsize=(10,6))\nax = fig.gca(projection=\"3d\")\n\n## Field tracing\n\nfor i in 1:10:length(x)\n xs, ys, zs = x[i], 0.0, 0.0\n x1, y1, z1 = trace(B[1,:,:,:], B[2,:,:,:], B[3,:,:,:], xs, ys, zs, x, y, z, ds=0.2, maxstep=1000)\n line = ax.plot(x1 ./ RG, y1 ./RG, z1 ./ RG, \"k-\", alpha=0.3)\nend\n\nfor i in 1:10:length(y)\n xs, ys, zs = x[xc_], y[i], z[zc_]\n x1, y1, z1 = trace(B[1,:,:,:], B[2,:,:,:], B[3,:,:,:], xs, ys, zs, x, y, z, ds=0.2, maxstep=1000)\n line = ax.plot(x1 ./ RG, y1 ./RG, z1 ./ RG, \"k-\", alpha=0.3)\nend\n\nn = 200 # number of timepoints\n\nts = range(0, stop=tspan_proton[2], length=n)\nfor i = 1:trajectories\n if sol_p[i].t[end] < tspan_proton[2]\n ts⁺ = range(0, stop=sol_p[i].t[end], length=n)\n ax.plot(sol_p[i](ts⁺,idxs=1) ./ RG, sol_p[i](ts⁺,idxs=2) ./ RG, sol_p[i](ts⁺,idxs=3) ./ RG, label=\"proton $i\", lw=1.5)\n else\n ax.plot(sol_p[i](ts,idxs=1) ./ RG, sol_p[i](ts,idxs=2) ./ RG, sol_p[i](ts,idxs=3) ./ RG, label=\"proton $i\", lw=1.5)\n end\nend\n\n#ax.plot(sol_p[1,:], sol_p[2,:], sol_p[3,:], label=\"proton\")\n\nax.legend()\ntitle(\"Particle trajectory near Ganymede's magnetopause from PIC\")\nxlabel(\"x [Rg]\")\nylabel(\"y [Rg]\")\nzlabel(\"z [Rg]\")\n\nax.set_box_aspect([1.17,4,4])\nTestParticle.set_axes_equal(ax)","category":"page"},{"location":"examples/advanced/demo_batsrus_3dstructured/","page":"Tracing particle from PIC","title":"Tracing particle from PIC","text":"(Image: )","category":"page"},{"location":"examples/advanced/demo_batsrus_3dstructured/","page":"Tracing particle from PIC","title":"Tracing particle from PIC","text":"","category":"page"},{"location":"examples/advanced/demo_batsrus_3dstructured/","page":"Tracing particle from PIC","title":"Tracing particle from PIC","text":"This page was generated using DemoCards.jl.","category":"page"},{"location":"examples/basics/demo_boris/","page":"Boris method","title":"Boris method","text":"EditURL = \"/home/runner/work/TestParticle.jl/TestParticle.jl/docs/examples/basics/demo_boris.jl\"","category":"page"},{"location":"examples/basics/demo_boris/#demo_boris","page":"Boris method","title":"Boris method","text":"","category":"section"},{"location":"examples/basics/demo_boris/","page":"Boris method","title":"Boris method","text":"(Image: Source code) (Image: compat) (Image: Author) (Image: Update time)","category":"page"},{"location":"examples/basics/demo_boris/","page":"Boris method","title":"Boris method","text":"This example demonstrates a single electron motion under a uniform B field. The E field is assumed to be zero such that there is no particle acceleration. We use the Boris method for phase space conservation under a fixed time step. This is compared against other ODE general algorithms for performance and accuracy.","category":"page"},{"location":"examples/basics/demo_boris/","page":"Boris method","title":"Boris method","text":"using TestParticle\nusing StaticArrays\nusing OrdinaryDiffEq\nusing CairoMakie\n\nfunction plot_trajectory(sol_boris, sol1, sol2)\n f = Figure(size=(700, 600), fontsize=18)\n ax = Axis(f[1, 1], aspect=1, limits = (-3, 1, -2, 2),\n xlabel = \"X\",\n ylabel = \"Y\")\n idxs = (1, 2)\n ##TODO: wait for https://github.com/MakieOrg/Makie.jl/issues/3623 to be fixed!\n l0 = lines!(ax, sol_boris[1]; idxs, linewidth=2, label=\"Boris\")\n l1 = lines!(ax, sol1; idxs, linewidth=2, linestyle=:dashdot, label=\"Tsit5 fixed\")\n l2 = linesegments!(ax, sol2; idxs, linewidth=2, linestyle=:dot, label=\"Tsit5 adaptive\")\n\n ax.scene.plots[1].linewidth = 2\n ax.scene.plots[5].linewidth = 2\n\n ax.scene.plots[3].color = Makie.wong_colors()[2]\n ax.scene.plots[5].color = Makie.wong_colors()[3]\n\n scale!(ax.scene.plots[1], invrL, invrL)\n scale!(ax.scene.plots[3], invrL, invrL)\n scale!(ax.scene.plots[5], invrL, invrL)\n\n axislegend(position=:rt, framevisible=false)\n\n f\nend\n\nconst Bmag = 0.01\nuniform_B(x) = SA[0.0, 0.0, Bmag]\nuniform_E(x) = SA[0.0, 0.0, 0.0]\n\nx0 = [0.0, 0.0, 0.0]\nv0 = [0.0, 1e5, 0.0]\nstateinit = [x0..., v0...]\n\nparam = prepare(uniform_E, uniform_B, species=Electron)\n\n# Reference parameters\nconst tperiod = 2π / (abs(param[1]) * hypot(param[3]([0.0,0.0,0.0], 0.0)...))\nconst rL = hypot(v0...) / (abs(param[1]) * Bmag)\nconst invrL = 1 / rL;","category":"page"},{"location":"examples/basics/demo_boris/","page":"Boris method","title":"Boris method","text":"We first trace the particle for one period with a discrete time step of a quarter period.","category":"page"},{"location":"examples/basics/demo_boris/","page":"Boris method","title":"Boris method","text":"tspan = (0.0, tperiod)\ndt = tperiod / 4\n\nprob = TraceProblem(stateinit, tspan, param)\n\nsol_boris = TestParticle.solve(prob; dt, savestepinterval=1);","category":"page"},{"location":"examples/basics/demo_boris/","page":"Boris method","title":"Boris method","text":"Let's compare against the default ODE solver Tsit5 from DifferentialEquations.jl, in both fixed time step mode and adaptive mode:","category":"page"},{"location":"examples/basics/demo_boris/","page":"Boris method","title":"Boris method","text":"prob = ODEProblem(trace!, stateinit, tspan, param)\nsol1 = solve(prob, Tsit5(); adaptive=false, dt, dense=false, saveat=dt);\nsol2 = solve(prob, Tsit5());\n\n# Visualization\nf = plot_trajectory(sol_boris, sol1, sol2)","category":"page"},{"location":"examples/basics/demo_boris/","page":"Boris method","title":"Boris method","text":"(Image: )","category":"page"},{"location":"examples/basics/demo_boris/","page":"Boris method","title":"Boris method","text":"It is clear that the Boris method comes with larger phase errors (mathcalO(Delta t)) compared with Tsit5. The phase error gets smaller using a smaller dt:","category":"page"},{"location":"examples/basics/demo_boris/","page":"Boris method","title":"Boris method","text":"dt = tperiod / 8\n\nprob = TraceProblem(stateinit, tspan, param)\n\nsol_boris = TestParticle.solve(prob; dt, savestepinterval=1);\n\nprob = ODEProblem(trace!, stateinit, tspan, param)\nsol1 = solve(prob, Tsit5(); adaptive=false, dt, dense=false, saveat=dt);\n\n# Visualization\nf = plot_trajectory(sol_boris, sol1, sol2)","category":"page"},{"location":"examples/basics/demo_boris/","page":"Boris method","title":"Boris method","text":"(Image: )","category":"page"},{"location":"examples/basics/demo_boris/","page":"Boris method","title":"Boris method","text":"The Boris pusher shines when we do long time tracing, which is fast and conserves energy:","category":"page"},{"location":"examples/basics/demo_boris/","page":"Boris method","title":"Boris method","text":"tspan = (0.0, 200*tperiod)\ndt = tperiod / 12\n\nprob_boris = TraceProblem(stateinit, tspan, param)\nprob = ODEProblem(trace!, stateinit, tspan, param)\n\nsol_boris = TestParticle.solve(prob_boris; dt, savestepinterval=10);\nsol1 = solve(prob, Tsit5(); adaptive=false, dt, dense=false, saveat=dt);\nsol2 = solve(prob, Tsit5());\n\n# Visualization\nf = plot_trajectory(sol_boris, sol1, sol2)","category":"page"},{"location":"examples/basics/demo_boris/","page":"Boris method","title":"Boris method","text":"(Image: )","category":"page"},{"location":"examples/basics/demo_boris/","page":"Boris method","title":"Boris method","text":"Fixed time step Tsit5() is ok, but adaptive Tsit5() is pretty bad for long time evolutions. The change in radius indicates change in energy, which is sometimes known as numerical heating.","category":"page"},{"location":"examples/basics/demo_boris/","page":"Boris method","title":"Boris method","text":"Another aspect to compare is performance:","category":"page"},{"location":"examples/basics/demo_boris/","page":"Boris method","title":"Boris method","text":"@time sol_boris = TestParticle.solve(prob_boris; dt, savestepinterval=10)[1];\n@time sol1 = solve(prob, Tsit5(); adaptive=false, dt, dense=false, saveat=dt);\n@time sol2 = solve(prob, Tsit5());","category":"page"},{"location":"examples/basics/demo_boris/","page":"Boris method","title":"Boris method","text":" 0.000091 seconds (257 allocations: 21.609 KiB)\n 0.000789 seconds (2.46 k allocations: 377.031 KiB)\n 0.000738 seconds (11.86 k allocations: 1.329 MiB)\n","category":"page"},{"location":"examples/basics/demo_boris/","page":"Boris method","title":"Boris method","text":"We can extract the solution (x, y, z, vx, vy, vz) at any given time by performing a linear interpolation:","category":"page"},{"location":"examples/basics/demo_boris/","page":"Boris method","title":"Boris method","text":"t = tspan[2] / 2\nsol_boris(t)","category":"page"},{"location":"examples/basics/demo_boris/","page":"Boris method","title":"Boris method","text":"6-element StaticArraysCore.MVector{6, Float64} with indices SOneTo(6):\n -3.911568318655031e-5\n -5.5137270837814596e-5\n 0.0\n 99639.26547015733\n 8486.269885387323\n 0.0","category":"page"},{"location":"examples/basics/demo_boris/","page":"Boris method","title":"Boris method","text":"The Boris method is faster and consumes less memory. However, in practice, it is pretty hard to find an optimal algorithm. When calling OrdinaryDiffEq.jl, we recommend using Vern9() as a starting point instead of Tsit5(), especially combined with adaptive timestepping.","category":"page"},{"location":"examples/basics/demo_boris/","page":"Boris method","title":"Boris method","text":"","category":"page"},{"location":"examples/basics/demo_boris/","page":"Boris method","title":"Boris method","text":"This page was generated using DemoCards.jl and Literate.jl.","category":"page"},{"location":"examples/basics/demo_FLR/","page":"Finite-Larmor-Radius effect","title":"Finite-Larmor-Radius effect","text":"EditURL = \"/home/runner/work/TestParticle.jl/TestParticle.jl/docs/examples/basics/demo_FLR.jl\"","category":"page"},{"location":"examples/basics/demo_FLR/#demo_FLR","page":"Finite-Larmor-Radius effect","title":"Finite-Larmor-Radius effect","text":"","category":"section"},{"location":"examples/basics/demo_FLR/","page":"Finite-Larmor-Radius effect","title":"Finite-Larmor-Radius effect","text":"(Image: Source code) (Image: compat) (Image: Author) (Image: Update time)","category":"page"},{"location":"examples/basics/demo_FLR/","page":"Finite-Larmor-Radius effect","title":"Finite-Larmor-Radius effect","text":"More theoretical details can be found in Non-uniform E Field.","category":"page"},{"location":"examples/basics/demo_FLR/","page":"Finite-Larmor-Radius effect","title":"Finite-Larmor-Radius effect","text":"using TestParticle\nusing TestParticle: get_gc\nusing OrdinaryDiffEq\nusing StaticArrays\nusing LinearAlgebra: ×, ⋅, norm, normalize\nusing Tensors: laplace\nimport Tensors: Vec as Vec3\n# using SpecialFunctions\nusing CairoMakie\n\nfunction uniform_B(x)\n return SA[0, 0, 1e-8]\nend\n\nfunction nonuniform_E(x)\n return SA[1e-9*cos(0.3*x[1]), 0, 0]\nend\n\n# trace the orbit of the guiding center\nfunction trace_gc!(dx, x, p, t)\n q2m, E, B, sol = p\n xu = sol(t)\n xp = @view xu[1:3]\n Bv = B(xp)\n b = normalize(Bv)\n v_par = (xu[4:6] ⋅ b) .* b # (v ⋅ b)b\n v_perp = xu[4:6] - v_par\n r4 = (norm(v_perp) / q2m / norm(Bv))^2 / 4\n EB(x) = (E(x) × B(x)) / norm(B(x))^2\n # dx[1:3] = EB(xp) + v_par\n dx[1:3] = EB(x) + r4*laplace.(EB, Vec3(x...)) + v_par\n\n # more accurate\n # dx[1:3] = besselj0(0.3*norm(v_perp)/q2m/norm(Bv))*EB(x) + v_par\nend\n\n# Initial condition\nstateinit = let x0 = [1.0, 0, 0], v0 = [0.0, 1.0, 0.1]\n [x0..., v0...]\nend\n# Time span\ntspan = (0, 20)\nparam = prepare(nonuniform_E, uniform_B, species=Proton)\nprob = ODEProblem(trace!, stateinit, tspan, param)\nsol = solve(prob, Vern9())\n\ngc = get_gc(param)\ngc_x0 = gc(stateinit)\nprob_gc = ODEProblem(trace_gc!, gc_x0, tspan, (param..., sol))\nsol_gc = solve(prob_gc, Tsit5(); save_idxs=[1,2,3])\n\n# numeric result and analytic result\nf = Figure(fontsize=18)\nax = Axis3(f[1, 1],\n title = \"Finite Larmor Radius Effect\",\n xlabel = \"x [m]\",\n ylabel = \"y [m]\",\n zlabel = \"z [m]\",\n aspect = :data,\n azimuth = 0.3π,\n)\n\ngc_plot(x, y, z, vx, vy, vz) = (gc(SA[x, y, z, vx, vy, vz])...,)\n\nlines!(ax, sol, idxs=(1, 2, 3))\nlines!(ax, sol, idxs=(gc_plot, 1, 2, 3, 4, 5, 6))\nlines!(ax, sol_gc, idxs=(1, 2, 3))\n\nfor i in 1:3\n ##TODO: wait for https://github.com/MakieOrg/Makie.jl/issues/3623 to be fixed!\n ax.scene.plots[9+2*i-1].color = Makie.wong_colors()[i]\nend\n","category":"page"},{"location":"examples/basics/demo_FLR/","page":"Finite-Larmor-Radius effect","title":"Finite-Larmor-Radius effect","text":"(Image: )","category":"page"},{"location":"examples/basics/demo_FLR/","page":"Finite-Larmor-Radius effect","title":"Finite-Larmor-Radius effect","text":"","category":"page"},{"location":"examples/basics/demo_FLR/","page":"Finite-Larmor-Radius effect","title":"Finite-Larmor-Radius effect","text":"This page was generated using DemoCards.jl and Literate.jl.","category":"page"},{"location":"examples/basics/demo_energy_conservation/","page":"Energy Conservation","title":"Energy Conservation","text":"EditURL = \"/home/runner/work/TestParticle.jl/TestParticle.jl/docs/examples/basics/demo_energy_conservation.jl\"","category":"page"},{"location":"examples/basics/demo_energy_conservation/#demo_energy_conservation","page":"Energy Conservation","title":"Energy Conservation","text":"","category":"section"},{"location":"examples/basics/demo_energy_conservation/","page":"Energy Conservation","title":"Energy Conservation","text":"(Image: Source code) (Image: compat) (Image: Author) (Image: Update time)","category":"page"},{"location":"examples/basics/demo_energy_conservation/","page":"Energy Conservation","title":"Energy Conservation","text":"This example demonstrates the energy conservation of a single proton motion in two cases. The first one is under a uniform B field and zero E field. The second on is under a zero B field and uniform E field.","category":"page"},{"location":"examples/basics/demo_energy_conservation/","page":"Energy Conservation","title":"Energy Conservation","text":"using TestParticle\nusing OrdinaryDiffEq\nusing StaticArrays\nusing LinearAlgebra: ×\nusing CairoMakie\n\nconst B₀ = 1e-8 # [T]\nconst E₀ = 3e-2 # [V/m]\n\n\"f2\"\nfunction location!(dx, v, x, p::TestParticle.TPTuple, t)\n dx .= v\nend\n\n\"f1\"\nfunction lorentz!(dv, v, x, p::TestParticle.TPTuple, t)\n q2m, E, B = p\n dv .= q2m*(E(x, t) + v × (B(x, t)))\nend\n\n### Initialize field\n\nfunction uniform_B(x)\n return SA[0, 0, B₀]\nend\n\nfunction uniform_E(x)\n return SA[E₀, 0.0, 0.0]\nend\n\nfunction zero_B(x)\n return SA[0.0, 0.0, 0.0]\nend\n\nfunction zero_E(x)\n return SA[0.0, 0.0, 0.0]\nend\n\n\"Check energy conservation.\"\nE(dx, dy, dz) = 1 // 2 * (dx^2 + dy^2 + dz^2)\n\n### Initialize particles\n\nx0 = [0.0, 0, 0]\nv0 = [0.0, 1e2, 0.0]\nstateinit = [x0..., v0...]\ntspan_proton = (0.0, 2000.0);","category":"page"},{"location":"examples/basics/demo_energy_conservation/","page":"Energy Conservation","title":"Energy Conservation","text":"Uniform B field and zero E field","category":"page"},{"location":"examples/basics/demo_energy_conservation/","page":"Energy Conservation","title":"Energy Conservation","text":"param_proton = prepare(zero_E, uniform_B, species=Proton)\n\n### Solve for the trajectories\n\nprob_p = DynamicalODEProblem(lorentz!, location!, v0, x0, tspan_proton, param_proton)\n\nΩᵢ = TestParticle.qᵢ * B₀ / TestParticle.mᵢ\nTᵢ = 2π / Ωᵢ\nprintln(\"Number of gyrations: \", tspan_proton[2] / Tᵢ)\n\nsol = solve(prob_p, ImplicitMidpoint(), dt = Tᵢ/15)\n\nf = Figure(fontsize=18)\nax = Axis(f[1, 1],\n title = \"Proton in a uniform B field and zero E field\",\n xlabel = \"x\",\n ylabel = \"y\",\n aspect = 1,\n)\n\nlines!(ax, sol, idxs=(1, 2))\n","category":"page"},{"location":"examples/basics/demo_energy_conservation/","page":"Energy Conservation","title":"Energy Conservation","text":"(Image: )","category":"page"},{"location":"examples/basics/demo_energy_conservation/","page":"Energy Conservation","title":"Energy Conservation","text":"Zero B field and uniform E field","category":"page"},{"location":"examples/basics/demo_energy_conservation/","page":"Energy Conservation","title":"Energy Conservation","text":"param_proton = prepare(uniform_E, zero_B, species=Proton)\n\n# acceleration, [m/s²]\na = param_proton[1] * E₀\n# predicted final speed, [m/s]\nv_final_predict = a * tspan_proton[2]\n# predicted travel distance, [m/s]\nd_final_predict = 0.5 * tspan_proton[2] * v_final_predict\n# predicted energy gain, [eV]\nE_predict = E₀ * d_final_predict\n\nprob_p = DynamicalODEProblem(lorentz!, location!, v0, x0, tspan_proton, param_proton)\n\nsol = solve(prob_p, Vern6())\n\nenergy = map(x -> E(x[1:3]...), sol.u) .* TestParticle.mᵢ;","category":"page"},{"location":"examples/basics/demo_energy_conservation/","page":"Energy Conservation","title":"Energy Conservation","text":"Predicted final speed","category":"page"},{"location":"examples/basics/demo_energy_conservation/","page":"Energy Conservation","title":"Energy Conservation","text":"predicted final speed: 5.744086746808526e9 [m/s]\n","category":"page"},{"location":"examples/basics/demo_energy_conservation/","page":"Energy Conservation","title":"Energy Conservation","text":"Simulated final speed","category":"page"},{"location":"examples/basics/demo_energy_conservation/","page":"Energy Conservation","title":"Energy Conservation","text":"simulated final speed: 5.744086746808613e9 [m/s]\n","category":"page"},{"location":"examples/basics/demo_energy_conservation/","page":"Energy Conservation","title":"Energy Conservation","text":"Predicted travel distance","category":"page"},{"location":"examples/basics/demo_energy_conservation/","page":"Energy Conservation","title":"Energy Conservation","text":"predicted travel distance: 5.744086746808526e12 [m]\n","category":"page"},{"location":"examples/basics/demo_energy_conservation/","page":"Energy Conservation","title":"Energy Conservation","text":"Simulated travel distance","category":"page"},{"location":"examples/basics/demo_energy_conservation/","page":"Energy Conservation","title":"Energy Conservation","text":"simulated travel distance: 5.744086746768604e12 [m]\n","category":"page"},{"location":"examples/basics/demo_energy_conservation/","page":"Energy Conservation","title":"Energy Conservation","text":"Predicted final energy","category":"page"},{"location":"examples/basics/demo_energy_conservation/","page":"Energy Conservation","title":"Energy Conservation","text":"predicted energy gain: 1.723226024042558e11 [eV]\n","category":"page"},{"location":"examples/basics/demo_energy_conservation/","page":"Energy Conservation","title":"Energy Conservation","text":"Simulated final energy","category":"page"},{"location":"examples/basics/demo_energy_conservation/","page":"Energy Conservation","title":"Energy Conservation","text":"simulated final energy: 1.7232260240426102e11 [eV]\n","category":"page"},{"location":"examples/basics/demo_energy_conservation/","page":"Energy Conservation","title":"Energy Conservation","text":"","category":"page"},{"location":"examples/basics/demo_energy_conservation/","page":"Energy Conservation","title":"Energy Conservation","text":"This page was generated using DemoCards.jl and Literate.jl.","category":"page"},{"location":"examples/advanced/demo_analytic_magnetosphere/","page":"Analytical magnetosphere","title":"Analytical magnetosphere","text":"EditURL = \"/home/runner/work/TestParticle.jl/TestParticle.jl/docs/examples/advanced/demo_analytic_magnetosphere.jl\"","category":"page"},{"location":"examples/advanced/demo_analytic_magnetosphere/#demo_analytic_magnetosphere","page":"Analytical magnetosphere","title":"Analytical magnetosphere","text":"","category":"section"},{"location":"examples/advanced/demo_analytic_magnetosphere/","page":"Analytical magnetosphere","title":"Analytical magnetosphere","text":"(Image: Source code) (Image: compat) (Image: Author) (Image: Update time)","category":"page"},{"location":"examples/advanced/demo_analytic_magnetosphere/","page":"Analytical magnetosphere","title":"Analytical magnetosphere","text":"This demo shows how to trace particles in a vacuum superposition of a dipolar magnetic field mathbfB_D with a uniform background magnetic field mathbfB_mathrmIMF. In this slightly modified dipole field, magnetic null points appear near 14 Earth's radii, and the particle orbits are also distorted from the idealized motions in Demo: magnetic dipole.","category":"page"},{"location":"examples/advanced/demo_analytic_magnetosphere/","page":"Analytical magnetosphere","title":"Analytical magnetosphere","text":"using TestParticle\nusing TestParticle: getB_dipole, getE_dipole, sph2cart, mᵢ, qᵢ, c, Rₑ\nusing OrdinaryDiffEq\nusing StaticArrays\nusing FieldTracer\nusing CairoMakie\n\nfunction getB_superposition(xu)\n getB_dipole(xu) + SA[0.0, 0.0, -10e-9]\nend\n\n\"Boundary condition check.\"\nfunction isoutofdomain(u, p, t)\n rout = 18Rₑ\n if hypot(u[1], u[2], u[3]) < 1.1Rₑ ||\n abs(u[1]) > rout || abs(u[2]) > rout || abs(u[3]) > rout\n return true\n else\n return false\n end\nend\n\n\"Set initial conditions.\"\nfunction prob_func(prob, i, repeat)\n # initial particle energy\n Ek = 5e3 # [eV]\n # initial velocity, [m/s]\n v₀ = sph2cart(c*sqrt(1-1/(1+Ek*qᵢ/(mᵢ*c^2))^2), 0.0, π/4)\n # initial position, [m]\n r₀ = sph2cart(13*Rₑ, π*i, π/2)\n\n prob = remake(prob; u0 = [r₀..., v₀...])\nend\n\n# obtain field\nparam = prepare(getE_dipole, getB_superposition)\nstateinit = zeros(6) # particle position and velocity to be modified\ntspan = (0.0, 2000.0)\ntrajectories = 2\n\nprob = ODEProblem(trace!, stateinit, tspan, param)\nensemble_prob = EnsembleProblem(prob; prob_func, safetycopy=false)\n\n# See https://docs.sciml.ai/DiffEqDocs/stable/basics/common_solver_opts/\n# for the solver options\nsols = solve(ensemble_prob, Vern9(), EnsembleSerial(); reltol=1e-5,\n trajectories, isoutofdomain, dense=true, save_on=true)\n\n### Visualization\n\nf = Figure(fontsize=18)\nax = Axis3(f[1, 1],\n title = \"5 keV Protons in a vacuum superposition magnetosphere\",\n xlabel = \"x [Re]\",\n ylabel = \"y [Re]\",\n zlabel = \"z [Re]\",\n aspect = :data,\n limits = (-14, 14, -14, 14, -5, 5)\n)\n\ninvRE = 1 / Rₑ\n\nfor (i, sol) in enumerate(sols)\n l = lines!(ax, sol, idxs=(1, 2, 3))\n ##TODO: wait for https://github.com/MakieOrg/Makie.jl/issues/3623 to be fixed!\n scale!(ax.scene.plots[9+2*i-1], invRE, invRE, invRE)\n ax.scene.plots[9+2*i-1].color = Makie.wong_colors()[i]\nend\n\n# Field lines\nfunction get_numerical_field(x, y, z)\n bx = zeros(length(x), length(y), length(z))\n by = similar(bx)\n bz = similar(bx)\n\n for i in CartesianIndices(bx)\n pos = [x[i[1]], y[i[2]], z[i[3]]]\n bx[i], by[i], bz[i] = getB_superposition(pos)\n end\n\n bx, by, bz\nend\n\nfunction trace_field!(ax, x, y, z, unitscale)\n bx, by, bz = get_numerical_field(x, y, z)\n\n zs = 0.0\n nr, nϕ = 8, 4\n dϕ = 2π / nϕ\n for r in range(8Rₑ, 16Rₑ, length=nr), ϕ in range(0, 2π-dϕ, length=nϕ)\n xs = r * cos(ϕ)\n ys = r * sin(ϕ)\n\n x1, y1, z1 = FieldTracer.trace(bx, by, bz, xs, ys, zs, x, y, z; ds=0.1, maxstep=10000)\n\n lines!(ax, x1.*unitscale, y1.*unitscale, z1.*unitscale, color=:gray)\n end\nend\n\nx = range(-18Rₑ, 18Rₑ, length=50)\ny = range(-18Rₑ, 18Rₑ, length=50)\nz = range(-18Rₑ, 18Rₑ, length=50)\n\ntrace_field!(ax, x, y, z, invRE)\n","category":"page"},{"location":"examples/advanced/demo_analytic_magnetosphere/","page":"Analytical magnetosphere","title":"Analytical magnetosphere","text":"(Image: )","category":"page"},{"location":"examples/advanced/demo_analytic_magnetosphere/","page":"Analytical magnetosphere","title":"Analytical magnetosphere","text":"","category":"page"},{"location":"examples/advanced/demo_analytic_magnetosphere/","page":"Analytical magnetosphere","title":"Analytical magnetosphere","text":"This page was generated using DemoCards.jl and Literate.jl.","category":"page"},{"location":"api/#Internal","page":"API","title":"Internal","text":"","category":"section"},{"location":"api/#Public-APIs","page":"API","title":"Public APIs","text":"","category":"section"},{"location":"api/","page":"API","title":"API","text":"Modules = [TestParticle]\nPrivate = false","category":"page"},{"location":"api/#TestParticle.BiMaxwellian","page":"API","title":"TestParticle.BiMaxwellian","text":"Type for BiMaxwellian velocity distributions with respect to the magnetic field.\n\n\n\n\n\n","category":"type"},{"location":"api/#TestParticle.BiMaxwellian-Union{Tuple{U}, Tuple{T}, Tuple{Vector{U}, Vector{T}, T, T, Any}} where {T<:AbstractFloat, U<:AbstractFloat}","page":"API","title":"TestParticle.BiMaxwellian","text":"BiMaxwellian(B::Vector{U}, u0::Vector{T}, ppar::T, pperp::T, n; m=mᵢ)\n\nConstruct 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.\n\n\n\n\n\n","category":"method"},{"location":"api/#TestParticle.Maxwellian","page":"API","title":"TestParticle.Maxwellian","text":"Type for Maxwellian velocity distributions. \n\n\n\n\n\n","category":"type"},{"location":"api/#TestParticle.Maxwellian-Union{Tuple{T}, Tuple{Vector{T}, T, Any}} where T","page":"API","title":"TestParticle.Maxwellian","text":"Maxwellian(u0::Vector{T}, p::T, n; m=mᵢ)\n\nConstruct a Maxwellian distribution with bulk velocity u0, thermal pressure p, and number density n in SI units. The default particle is proton.\n\n\n\n\n\n","category":"method"},{"location":"api/#TestParticle.prepare-Union{Tuple{TB}, Tuple{TE}, Tuple{Meshes.CartesianGrid, TE, TB}} where {TE, TB}","page":"API","title":"TestParticle.prepare","text":"prepare(grid::CartesianGrid, E, B; kwargs...) -> (q2m, E, B)\n\nReturn a tuple consists of particle charge-mass ratio for a prescribed species and interpolated EM field functions.\n\nkeywords\n\norder::Int=1: order of interpolation in [1,2,3].\nbc::Int=1: type of boundary conditions, 1 -> NaN, 2 -> periodic.\nspecies::Species=Proton: particle species.\nq::AbstractFloat=1.0: particle charge. Only works when Species=User.\nm::AbstractFloat=1.0: particle mass. Only works when Species=User.\nprepare(grid::CartesianGrid, E, B, F; species=Proton, q=1.0, m=1.0) -> (q, m, E, B, F)\n\nReturn a tuple consists of particle charge, mass for a prescribed species of charge q and mass m, interpolated EM field functions, and external force F.\n\nprepare(x::AbstractRange, y::AbstractRange, z::AbstractRange, E, B; kwargs...) -> (q2m, E, B)\nprepare(x, y, E, B; kwargs...) -> (q2m, E, B)\nprepare(x::AbstractRange, E, B; kwargs...) -> (q2m, E, B)\n\nDirect range input for uniform grid in 2/3D is also accepted.\n\nprepare(E, B; kwargs...) -> (q2m, E, B)\n\nReturn a tuple consists of particle charge-mass ratio for a prescribed species of charge q and mass m and analytic EM field functions. Prescribed species are Electron and Proton; other species can be manually specified with species=Ion/User, q and m.\n\nprepare(E, B, F; kwargs...) -> (q, m, E, B, F)\n\nReturn a tuple consists of particle charge, mass for a prescribed species of charge q and mass m, analytic EM field functions, and external force F.\n\n\n\n\n\n","category":"method"},{"location":"api/#TestParticle.sample-Union{Tuple{Maxwellian{T}}, Tuple{T}} where T","page":"API","title":"TestParticle.sample","text":"sample(vdf::Maxwellian)\n\nSample a 3D velocity from a Maxwellian distribution vdf using the Box-Muller method.\n\nsample(vdf::BiMaxwellian)\n\nSample a 3D velocity from a BiMaxwellian distribution vdf using the Box-Muller method.\n\n\n\n\n\n","category":"method"},{"location":"api/#TestParticle.trace!-Tuple{Any, Any, Tuple{Float64, TestParticle.AbstractField, TestParticle.AbstractField}, Any}","page":"API","title":"TestParticle.trace!","text":"trace!(dy, y, p::TPTuple, t)\ntrace!(dy, y, p::FullTPTuple, t)\n\nODE equations for charged particle moving in static EM field with in-place form.\n\nODE equations for charged particle moving in static EM field and external force field with in-place form.\n\n\n\n\n\n","category":"method"},{"location":"api/#TestParticle.trace-Tuple{Any, Tuple{Float64, TestParticle.AbstractField, TestParticle.AbstractField}, Any}","page":"API","title":"TestParticle.trace","text":"trace(y, p::TPTuple, t) -> SVector{6, Float64}\ntrace(y, p::FullTPTuple, t) -> SVector{6, Float64}\n\nODE equations for charged particle moving in static EM field with out-of-place form.\n\nODE equations for charged particle moving in static EM field and external force field with out-of-place form.\n\n\n\n\n\n","category":"method"},{"location":"api/#TestParticle.trace_normalized!-Tuple{Any, Any, Tuple{AbstractFloat, TestParticle.AbstractField, TestParticle.AbstractField}, Any}","page":"API","title":"TestParticle.trace_normalized!","text":"trace_normalized!(dy, y, p::TPNormalizedTuple, t)\n\nNormalized ODE equations for charged particle moving in static EM field with in-place form. If the field is in 2D X-Y plane, periodic boundary should be applied for the field in z via the extrapolation function provided by Interpolations.jl.\n\n\n\n\n\n","category":"method"},{"location":"api/#TestParticle.trace_relativistic!-Tuple{Any, Any, Tuple{Float64, TestParticle.AbstractField, TestParticle.AbstractField}, Any}","page":"API","title":"TestParticle.trace_relativistic!","text":"trace_relativistic!(dy, y, p::TPTuple, t)\n\nODE equations for relativistic charged particle moving in static EM field with in-place form.\n\n\n\n\n\n","category":"method"},{"location":"api/#TestParticle.trace_relativistic-Tuple{Any, Tuple{Float64, TestParticle.AbstractField, TestParticle.AbstractField}, Any}","page":"API","title":"TestParticle.trace_relativistic","text":"trace_relativistic(y, p::TPTuple, t) -> SVector{6, Float64}\n\nODE equations for relativistic charged particle moving in static EM field with out-of-place form.\n\n\n\n\n\n","category":"method"},{"location":"api/#TestParticle.trace_relativistic_normalized!-Tuple{Any, Any, Tuple{AbstractFloat, TestParticle.AbstractField, TestParticle.AbstractField}, Any}","page":"API","title":"TestParticle.trace_relativistic_normalized!","text":"trace_relativistic_normalized!(dy, y, p::TPNormalizedTuple, t)\n\nNormalized ODE equations for relativistic charged particle moving in static EM field with in-place form.\n\n\n\n\n\n","category":"method"},{"location":"api/#Private-types-and-methods","page":"API","title":"Private types and methods","text":"","category":"section"},{"location":"api/","page":"API","title":"API","text":"Modules = [TestParticle]\nPublic = false","category":"page"},{"location":"api/#TestParticle.AbstractTraceSolution","page":"API","title":"TestParticle.AbstractTraceSolution","text":"Abstract type for tracing solutions.\n\n\n\n\n\n","category":"type"},{"location":"api/#TestParticle.Field","page":"API","title":"TestParticle.Field","text":"Field{itd, F} <: AbstractField{itd}\n\nA representation of a field function f, defined by:\n\ntime-independent field\n\nmathbfF = F(mathbfx)\n\ntime-dependent field\n\nmathbfF = F(mathbfx t)\n\nArguments\n\nfield_function::Function: the function of field.\nitd::Bool: whether the field function is time dependent.\nF: the type of field_function.\n\n\n\n\n\n","category":"type"},{"location":"api/#TestParticle.FullTPTuple","page":"API","title":"TestParticle.FullTPTuple","text":"The type of parameter tuple for full test particle problem.\n\n\n\n\n\n","category":"type"},{"location":"api/#TestParticle.Species","page":"API","title":"TestParticle.Species","text":"Type for the particles, Proton, Electron, Ion, or User.\n\n\n\n\n\n","category":"type"},{"location":"api/#TestParticle.TPNormalizedTuple","page":"API","title":"TestParticle.TPNormalizedTuple","text":"The type of parameter tuple for normalized test particle problem.\n\n\n\n\n\n","category":"type"},{"location":"api/#TestParticle.TPTuple","page":"API","title":"TestParticle.TPTuple","text":"The type of parameter tuple for normal test particle problem.\n\n\n\n\n\n","category":"type"},{"location":"api/#TestParticle.TraceSolution-Union{Tuple{Any}, Tuple{deriv}, Tuple{Any, Type{deriv}}} where deriv","page":"API","title":"TestParticle.TraceSolution","text":"Interpolate solution at time x. Forward tracing only.\n\n\n\n\n\n","category":"method"},{"location":"api/#TestParticle.VDF","page":"API","title":"TestParticle.VDF","text":"Abstract type for velocity distribution functions.\n\n\n\n\n\n","category":"type"},{"location":"api/#TestParticle._boris!-NTuple{9, Any}","page":"API","title":"TestParticle._boris!","text":"Apply Boris method for particles with index in irange.\n\n\n\n\n\n","category":"method"},{"location":"api/#TestParticle._prepare-NTuple{4, Any}","page":"API","title":"TestParticle._prepare","text":"Prepare for advancing.\n\n\n\n\n\n","category":"method"},{"location":"api/#TestParticle.cross!-Tuple{Any, Any, Any}","page":"API","title":"TestParticle.cross!","text":"In-place cross product.\n\n\n\n\n\n","category":"method"},{"location":"api/#TestParticle.dipole-Tuple{Any, Any}","page":"API","title":"TestParticle.dipole","text":"Calculates the magnetic field from a dipole with magnetic moment M at r.\n\n\n\n\n\n","category":"method"},{"location":"api/#TestParticle.dipole_fieldline","page":"API","title":"TestParticle.dipole_fieldline","text":"dipole_fieldline(L, ϕ, nP)\n\nCreates nP points on one field line of the magnetic field from a dipole. In a centered dipole magnetic field model, the path along a given L shell can be described as r = L*cos²λ, where r is the radial distance (in planetary radii) to a point on the line, λ is its co-latitude, and L is the L-shell of interest.\n\n\n\n\n\n","category":"function"},{"location":"api/#TestParticle.getB_CS_harris-Tuple{Any, Any, Any}","page":"API","title":"TestParticle.getB_CS_harris","text":"getB_CS_harris(B₀, L)\n\nReturn the magnetic field at location r near a current sheet with magnetic strength B₀ and sheet length L.\n\n\n\n\n\n","category":"method"},{"location":"api/#TestParticle.getB_bottle-NTuple{8, Any}","page":"API","title":"TestParticle.getB_bottle","text":"getB_bottle(x, y, z, distance, a, b, I1, I2) -> Vector{Float}\n\nGet magnetic field from a magnetic bottle. Reference: https://en.wikipedia.org/wiki/Magneticmirror#Magneticbottles\n\nArguments\n\nx,y,z::Float: particle coordinates in [m].\ndistance::Float: distance between solenoids in [m].\na::Float: radius of each side coil in [m].\nb::Float: radius of central coil in [m].\nI1::Float: current in the solenoid times number of windings in side coils.\nI2::Float: current in the central solenoid times number of windings in the\n\ncentral loop.\n\n\n\n\n\n","category":"method"},{"location":"api/#TestParticle.getB_dipole-Tuple{Any}","page":"API","title":"TestParticle.getB_dipole","text":"Analytic magnetic field function for testing. Return in SI unit.\n\n\n\n\n\n","category":"method"},{"location":"api/#TestParticle.getB_mirror-NTuple{6, Any}","page":"API","title":"TestParticle.getB_mirror","text":"getB_mirror(x, y, z, distance, a, I1) -> Vector{Float}\n\nGet magnetic field from a magnetic mirror generated from two coils.\n\nArguments\n\nx,y,z::Float: particle coordinates in [m].\ndistance::Float: distance between solenoids in [m].\na::Float: radius of each side coil in [m].\nI1::Float: current in the solenoid times number of windings in side coils.\n\n\n\n\n\n","category":"method"},{"location":"api/#TestParticle.getB_tokamak_coil-NTuple{7, Any}","page":"API","title":"TestParticle.getB_tokamak_coil","text":"getB_tokamak_coil(x, y, z, a, b, ICoils, IPlasma)\n\nGet the magnetic field from a Tokamak topology consists of 16 coils. Original: https://github.com/BoschSamuel/Simulation-of-a-Tokamak-Fusion-Reactor/blob/master/Simulation2.m\n\nArguments\n\nx,y,z::Float: location in [m].\na::Float: radius of each coil in [m].\nb::Float: radius of central region in [m].\nICoil::Float: current in the coil times number of windings.\nIPlasma::Float: current of the plasma?\n\n\n\n\n\n","category":"method"},{"location":"api/#TestParticle.getB_tokamak_profile-Tuple{AbstractFloat, AbstractFloat, AbstractFloat, Any, AbstractFloat, AbstractFloat, AbstractFloat}","page":"API","title":"TestParticle.getB_tokamak_profile","text":"getB_tokamak_profile(x, y, z, q_profile, a, R₀, Bζ0)\n\nReconstruct the magnetic field distribution from a safe factor(q) profile. The formulations are from the book \"Tokamak 4th Edition\" by John Wesson.\n\nArguments\n\nx,y,z::Float: location in [m].\nq_profile::Function: profile of q. The variable of this function must be the normalized radius.\na::Float: minor radius [m].\nR₀::Float: major radius [m].\nBζ0::Float: toroidal magnetic field on axis [T].\n\n\n\n\n\n","category":"method"},{"location":"api/#TestParticle.getE_dipole-Tuple{Any}","page":"API","title":"TestParticle.getE_dipole","text":"Analytic electric field function for testing.\n\n\n\n\n\n","category":"method"},{"location":"api/#TestParticle.get_gc-Tuple{Union{Tuple{Float64, TestParticle.AbstractField, TestParticle.AbstractField}, Tuple{Float64, Float64, TestParticle.AbstractField, TestParticle.AbstractField, TestParticle.AbstractField}}}","page":"API","title":"TestParticle.get_gc","text":"get_gc(param::Union{TPTuple, FullTPTuple})\n\nGet three functions for plotting the orbit of guiding center.\n\nFor example:\n\nparam = prepare(E, B; species=Proton)\ngc = get_gc(params)\n# The definitions of stateinit, tspan, E and B are ignored.\nprob = ODEProblem(trace!, stateinit, tspan, param)\nsol = solve(prob, Vern7(); dt=2e-11)\n\nf = Figure(fontsize=18)\nax = Axis3(f[1, 1], aspect = :data)\ngc_plot(x,y,z,vx,vy,vz) = (gc([x,y,z,vx,vy,vz])...,)\nlines!(ax, sol, idxs=(gc_plot, 1, 2, 3, 4, 5, 6))\n\n\n\n\n\n","category":"method"},{"location":"api/#TestParticle.get_rotation_matrix-Tuple{AbstractVector{<:AbstractFloat}, Real}","page":"API","title":"TestParticle.get_rotation_matrix","text":"get_rotation_matrix(axis::AbstractVector, angle::Real) --> SMatrix{3,3}\n\nCreate a rotation matrix for rotating a 3D vector around a unit axis by an angle in radians. Reference: Rotation matrix from axis and angle\n\nExample\n\nusing LinearAlgebra\nv = [-0.5, 1.0, 1.0]\nv̂ = normalize(v)\nθ = deg2rad(-74)\nR = get_rotation_matrix(v̂, θ)\n\n\n\n\n\n","category":"method"},{"location":"api/#TestParticle.getchargemass-Tuple{TestParticle.Species, AbstractFloat, AbstractFloat}","page":"API","title":"TestParticle.getchargemass","text":"getchargemass(species::Species, q::AbstractFloat, m::AbstractFloat)\n\nReturn charge and mass for species. if species = User, input q and m are returned.\n\n\n\n\n\n","category":"method"},{"location":"api/#TestParticle.getinterp","page":"API","title":"TestParticle.getinterp","text":"getinterp(A, gridx, gridy, gridz, order::Int=1, bc::Int=1)\ngetinterp(A, gridx, gridy, order::Int=1, bc::Int=1)\n\nReturn a function for interpolating array A on the grid given by gridx, gridy, and gridz.\n\nArguments\n\norder::Int=1: order of interpolation in [1,2,3].\nbc::Int=1: type of boundary conditions, 1 -> NaN, 2 -> periodic, 3 -> Flat.\n\n\n\n\n\n","category":"function"},{"location":"api/#TestParticle.guiding_center-Tuple{Any, Tuple{Float64, TestParticle.AbstractField, TestParticle.AbstractField}}","page":"API","title":"TestParticle.guiding_center","text":"guiding_center(xu, param::Union{TPTuple, FullTPTuple})\n\nCalculate the coordinates of the guiding center according to the phase space coordinates of a particle. Reference: https://en.wikipedia.org/wiki/Guiding_center\n\nA simple definition:\n\nmathbfX=mathbfx-mfracmathbfvtimesmathbfBqB\n\n\n\n\n\n","category":"method"},{"location":"api/#TestParticle.is_time_dependent-Tuple{Function}","page":"API","title":"TestParticle.is_time_dependent","text":"Judge whether the field function is time dependent.\n\n\n\n\n\n","category":"method"},{"location":"api/#TestParticle.makegrid-Union{Tuple{Meshes.CartesianGrid{3, T}}, Tuple{T}} where T","page":"API","title":"TestParticle.makegrid","text":"Return uniform range from 2D/3D CartesianGrid.\n\n\n\n\n\n","category":"method"},{"location":"api/#TestParticle.set_axes_equal-Tuple{Any}","page":"API","title":"TestParticle.set_axes_equal","text":"set_axes_equal(ax)\n\nSet 3D plot axes to equal scale for Matplotlib. Make axes of 3D plot have equal scale so that spheres appear as spheres and cubes as cubes. Required since ax.axis('equal') and ax.set_aspect('equal') don't work on 3D.\n\n\n\n\n\n","category":"method"},{"location":"api/#TestParticle.solve","page":"API","title":"TestParticle.solve","text":"solve(prob::TraceProblem; trajectories::Int=1,\n savestepinterval::Int=1, isoutofdomain::Function=ODE_DEFAULT_ISOUTOFDOMAIN)\n\nTrace particles using the Boris method with specified prob.\n\nkeywords\n\ntrajectories::Int: number of trajectories to trace.\nsavestepinterval::Int: saving output interval.\nisoutofdomain::Function: a function with input of position and velocity vector xv that determines whether to stop tracing.\n\n\n\n\n\n","category":"function"},{"location":"api/#TestParticle.sph2cart-Tuple{Any, Any, Any}","page":"API","title":"TestParticle.sph2cart","text":"Convert from spherical to Cartesian coordinates vector.\n\n\n\n\n\n","category":"method"},{"location":"api/#TestParticle.update_location!-Tuple{Any, Any}","page":"API","title":"TestParticle.update_location!","text":"Update location in one timestep dt.\n\n\n\n\n\n","category":"method"},{"location":"api/#TestParticle.update_velocity!-NTuple{4, Any}","page":"API","title":"TestParticle.update_velocity!","text":"update_velocity!(xv, paramBoris, param, dt)\n\nUpdate velocity using the Boris method, Birdsall, Plasma Physics via Computer Simulation. Reference: https://apps.dtic.mil/sti/citations/ADA023511\n\n\n\n\n\n","category":"method"},{"location":"examples/basics/demo_gravity_drift/","page":"ExF drift","title":"ExF drift","text":"EditURL = \"/home/runner/work/TestParticle.jl/TestParticle.jl/docs/examples/basics/demo_gravity_drift.jl\"","category":"page"},{"location":"examples/basics/demo_gravity_drift/#demo_uniformB_gravity","page":"ExF drift","title":"ExF drift","text":"","category":"section"},{"location":"examples/basics/demo_gravity_drift/","page":"ExF drift","title":"ExF drift","text":"(Image: Source code) (Image: compat) (Image: Author) (Image: Author) (Image: Update time)","category":"page"},{"location":"examples/basics/demo_gravity_drift/","page":"ExF drift","title":"ExF drift","text":"This example demonstrates a single proton motion under uniform B and gravity fields.","category":"page"},{"location":"examples/basics/demo_gravity_drift/","page":"ExF drift","title":"ExF drift","text":"using TestParticle\nusing TestParticle: get_gc\nusing OrdinaryDiffEq\nusing StaticArrays\nusing CairoMakie\n\n# Analytic EM fields\nB(x) = SA[0.0, 1e-8, 0.0]\nE(x) = SA[0.0, 0.0, 0.0]\n\n# Earth's gravity\nF(x) = SA[0.0, 0.0, -TestParticle.mᵢ*9.8]\n\n# Initial static particle\nstateinit = let x0 = [1.0, 0.0, 0.0], v0 = [0.0, 0.0, 0.0]\n [x0..., v0...]\nend\n# Time span\ntspan = (0, 1.0)\n\nparam = prepare(E, B, F, species=Proton)\nprob = ODEProblem(trace!, stateinit, tspan, param)\nsol = solve(prob, Vern9())\n# drift in x-direction + free fall in z-direction\nf = lines(sol, idxs=(3,1);\n figure = (; size = (800, 400), fontsize=18),\n axis = (; title=\"ExF Drift\", xlabel=\"Z [m]\", ylabel=\"X [m]\", aspect = DataAspect())\n)\n","category":"page"},{"location":"examples/basics/demo_gravity_drift/","page":"ExF drift","title":"ExF drift","text":"(Image: )","category":"page"},{"location":"examples/basics/demo_gravity_drift/","page":"ExF drift","title":"ExF drift","text":"","category":"page"},{"location":"examples/basics/demo_gravity_drift/","page":"ExF drift","title":"ExF drift","text":"This page was generated using DemoCards.jl and Literate.jl.","category":"page"},{"location":"examples/advanced/demo_magneticbottle/","page":"Electron in a magnetic bottle","title":"Electron in a magnetic bottle","text":"EditURL = \"/home/runner/work/TestParticle.jl/TestParticle.jl/docs/examples/advanced/demo_magneticbottle.jl\"","category":"page"},{"location":"examples/advanced/demo_magneticbottle/#demo_electron_bottle","page":"Electron in a magnetic bottle","title":"Electron in a magnetic bottle","text":"","category":"section"},{"location":"examples/advanced/demo_magneticbottle/","page":"Electron in a magnetic bottle","title":"Electron in a magnetic bottle","text":"(Image: Source code) (Image: compat) (Image: Author) (Image: Update time)","category":"page"},{"location":"examples/advanced/demo_magneticbottle/","page":"Electron in a magnetic bottle","title":"Electron in a magnetic bottle","text":"This example shows how to trace non-relativistic and relativistic electrons in a stationary magnetic field that corresponds to a magnetic bottle. Reference wiki","category":"page"},{"location":"examples/advanced/demo_magneticbottle/","page":"Electron in a magnetic bottle","title":"Electron in a magnetic bottle","text":"using TestParticle\nusing TestParticle: getB_bottle\nusing OrdinaryDiffEq\nusing StaticArrays\nusing Printf\nusing CairoMakie\n\n### Obtain field\n\n# Magnetic bottle parameters in SI units\nconst I1 = 20. # current in the solenoid\nconst N1 = 45 # number of windings\nconst I2 = 20. # current in the central solenoid\nconst N2 = 45 # number of windings\nconst distance = 10. # distance between solenoids\nconst a = 4.0 # radius of each coil\nconst b = 8.0 # radius of central coil\n\nfunction getB(xu)\n SVector{3}(getB_bottle(xu[1], xu[2], xu[3], distance, a, b, I1*N1, I2*N2))\nend\n\nfunction getE(xu)\n SA[0.0, 0.0, 0.0]\nend\n\n### Initialize particles\nm = TestParticle.mₑ\nq = TestParticle.qₑ\nc = TestParticle.c\n\n# initial velocity, [m/s]\nv₀ = [2.75, 2.5, 1.3] .* 0.03c # confined\n# initial position, [m]\nr₀ = [0.8, 0.8, 0.0]\nstateinit = [r₀..., v₀...]\n\n# Theoretically we can take advantage of the fact that magnetic field does not\n# accelerate particles, so that γ remains constant. However, we are not doing\n# that here since it is not generally true in the EM field.\n\nparam = prepare(getE, getB; species=Electron)\ntspan = (0.0, 1e-5)\n\nprob_rel = ODEProblem(trace_relativistic!, stateinit, tspan, param)\nprob_non = ODEProblem(trace!, stateinit, tspan, param)\n\nvratio = √(v₀[1]^2+v₀[2]^2+v₀[3]^2)/c\nE = (1 / √(1 - vratio^2) - 1)*m*c^2/abs(q)/1e6\n\nv_str = @sprintf \"V = %4.2f %s\" vratio*100 \"% c\"\ne_str = @sprintf \"E = %6.4f MeV\" E\n\nprintln(v_str)\nprintln(e_str)\n\n# Default Tsit5() and many solvers does not work in this case!\nsol_rel = solve(prob_rel, AB4(); dt=3e-9)\nsol_non = solve(prob_non, AB4(); dt=3e-9)\n\n### Visualization\n\nf = Figure(fontsize=18)\nax1 = Axis3(f[1, 1];\n aspect = :data,\n title = \"Relativistic e⁻, \\n\"*v_str*\", \"*e_str\n )\nax2 = Axis3(f[1, 2];\n aspect = :data,\n title = \"Non-relativistic e⁻, \\n\"*v_str*\", \"*e_str\n )\n\nlines!(ax1, sol_rel, idxs=(1, 2, 3))\nlines!(ax2, sol_non, idxs=(1, 2, 3))\n\n# Plot coils\nθ = range(0, 2π, length=100)\nx = a.*cos.(θ)\ny = a.*sin.(θ)\nz = fill(distance/2, size(x))\nfor ax in (f[1,1], f[1,2])\n lines!(ax, x, y, z, color=(:red, 0.7))\nend\nz = fill(-distance/2, size(x))\nfor ax in (f[1,1], f[1,2])\n lines!(ax, x, y, z, color=(:red, 0.7))\nend\n\nx = b.*cos.(θ)\ny = b.*sin.(θ)\nz = fill(0.0, size(x))\nfor ax in (f[1,1], f[1,2])\n lines!(ax, x, y, z, color=(:red, 0.7))\nend\n\nusing FieldTracer\n\nxrange = range(-4, 4, length=20)\nyrange = range(-4, 4, length=20)\nzrange = range(-10, 10, length=20)\n\nBx, By, Bz = let x=xrange, y=yrange, z=zrange\n Bx = zeros(length(x), length(y), length(z))\n By = zeros(length(x), length(y), length(z))\n Bz = zeros(length(x), length(y), length(z))\n for k in eachindex(z), j in eachindex(y), i in eachindex(x)\n Bx[i,j,k], By[i,j,k], Bz[i,j,k] = getB([x[i], y[j], z[k]])\n end\n\n Bx, By, Bz\nend\n\nfor i in 0:8\n if i == 0\n xs, ys, zs = 0.0, 0.0, 0.0\n else\n xs, ys, zs = 3*cos(2π*(i-1)/8), 3*sin(2π*(i-1)/8), 0.0\n end\n x1, y1, z1 = FieldTracer.trace(Bx, By, Bz, xs, ys, zs, xrange, yrange, zrange;\n ds=0.1, maxstep=1000)\n for ax in (f[1,1], f[1,2])\n lines!(ax, x1, y1, z1, color=:black)\n end\nend\n","category":"page"},{"location":"examples/advanced/demo_magneticbottle/","page":"Electron in a magnetic bottle","title":"Electron in a magnetic bottle","text":"(Image: )","category":"page"},{"location":"examples/advanced/demo_magneticbottle/","page":"Electron in a magnetic bottle","title":"Electron in a magnetic bottle","text":"","category":"page"},{"location":"examples/advanced/demo_magneticbottle/","page":"Electron in a magnetic bottle","title":"Electron in a magnetic bottle","text":"This page was generated using DemoCards.jl and Literate.jl.","category":"page"},{"location":"examples/advanced/demo_magneticmirror/","page":"Magnetic mirror","title":"Magnetic mirror","text":"EditURL = \"/home/runner/work/TestParticle.jl/TestParticle.jl/docs/examples/advanced/demo_magneticmirror.jl\"","category":"page"},{"location":"examples/advanced/demo_magneticmirror/#demo_magnetic_mirror","page":"Magnetic mirror","title":"Magnetic mirror","text":"","category":"section"},{"location":"examples/advanced/demo_magneticmirror/","page":"Magnetic mirror","title":"Magnetic mirror","text":"(Image: Source code) (Image: compat) (Image: Author) (Image: Author) (Image: Update time)","category":"page"},{"location":"examples/advanced/demo_magneticmirror/","page":"Magnetic mirror","title":"Magnetic mirror","text":"This example demonstrates the particle motion trajectory in a magnetic mirror and also illustrates the conservation of magnetic moment. From the third figure on the right, it can be seen that the zero-order quantity of magnetic moment is conserved, but its high-order part is not conserved under this definition of magnetic moment and oscillates rapidly. We can observe from this the oscillation characteristics of magnetic moments at different levels.","category":"page"},{"location":"examples/advanced/demo_magneticmirror/","page":"Magnetic mirror","title":"Magnetic mirror","text":"This example is based on demo_magneticbottle.jl.","category":"page"},{"location":"examples/advanced/demo_magneticmirror/","page":"Magnetic mirror","title":"Magnetic mirror","text":"using TestParticle\nusing TestParticle: getB_mirror\nusing OrdinaryDiffEq\nusing StaticArrays\nusing LinearAlgebra: normalize, norm, ⋅\nusing CairoMakie\n\n### Obtain field\n\n# Magnetic mirror parameters in SI units\nconst I = 20. # current in the solenoid\nconst N = 45 # number of windings\nconst distance = 10. # distance between solenoids\nconst a = 4.0 # radius of each coil\n\nfunction getB(xu)\n SVector{3}(getB_mirror(xu[1], xu[2], xu[3], distance, a, I*N))\nend\n\ngetE(xu) = SA[0.0, 0.0, 0.0]\n\n# velocity in the direction perpendicular to the magnetic field\nfunction v_perp(t, x, y, z, vx, vy, vz)\n xu = SA[x, y, z, vx, vy, vz]\n vu = @view xu[4:6]\n B = getB(xu)\n b = normalize(B)\n v_pa = (vu ⋅ b) .* b\n\n (t, norm(vu - v_pa))\nend\n\n# magnetic field\nabsB(t, x, y, z) = (t, hypot(getB(SA[x,y,z])...))\n\n# μ, magnetic moment\nfunction mu(t, x, y, z, vx, vy, vz)\n xu = SA[x, y, z, vx, vy, vz]\n\n (t, v_perp(t, x, y, z, vx, vy, vz)[2]^2 / hypot(getB(xu)...) )\nend\n\nEt(xu) = hypot(xu[4:6]...)\n\n### Initialize particles\nm = TestParticle.mₑ\nq = TestParticle.qₑ\nc = TestParticle.c\n\n# initial velocity, [m/s]\nv₀ = [2.75, 2.5, 1.3] .* 0.001c # confined\n##v₀ = [0.25, 0.25, 5.9595] .* 0.01c # escaped\n# initial position, [m]\nr₀ = [0.8, 0.8, 0.0] # confined\n##r₀ = [1.5, 1.5, 2.4] # escaped\nstateinit = [r₀..., v₀...]\n\nparam = prepare(getE, getB; species=Electron)\ntspan = (0.0, 1e-4)\n\nprob = ODEProblem(trace!, stateinit, tspan, param)\n\n# Default Tsit5() and many solvers does not work in this case!\n# AB4() has better performance in maintaining magnetic moment conservation compared to AB3().\nsol_non = solve(prob, AB4(); dt=3e-9)\n\n### Visualization\nf = Figure(size=(900, 600), fontsize=18)\nax1 = Axis3(f[1:3, 1],\n title = \"Magnetic Mirror\",\n xlabel = \"x [m]\",\n ylabel = \"y [m]\",\n zlabel = \"z [m]\",\n aspect = :data,\n azimuth = 0.9π,\n elevation = 0.1π\n)\nax2 = Axis(f[1,2], xlabel = \"time [s]\", ylabel = \"B [T]\")\nax3 = Axis(f[2,2], xlabel = \"time [s]\", ylabel = \"v_perp [m/s]\")\nax4 = Axis(f[3,2], xlabel = \"time [s]\", ylabel = \"mu\")\n\nlines!(ax1, sol_non, idxs=(1, 2, 3))\nlines!(ax2, sol_non, idxs=(absB, 0, 1, 2, 3))\nlines!(ax3, sol_non, idxs=(v_perp, 0, 1, 2, 3, 4, 5, 6))\nlines!(ax4, sol_non, idxs=(mu, 0, 1, 2, 3, 4, 5, 6))\n\n# Plot coils\nθ = range(0, 2π, length=100)\nx = a.*cos.(θ)\ny = a.*sin.(θ)\nz = fill(distance/2, size(x))\nlines!(ax1, x, y, z, color=:red)\nz = fill(-distance/2, size(x))\nlines!(ax1, x, y, z, color=:red)\n\n# # The distribution of magnetic field along the z-axis or x-axis\n# Bz(z) = hypot(getB(SA[0.0, 0.0, z])...)\n# Bx(x) = hypot(getB(SA[x, 0.0, 0.5*distance])...)\n# z = collect(-10:0.01:10)\n# x = collect(-0.99*a:0.01:0.99*a)\n# # Ba = Bz.(z)\n# Ba = Bx.(x)\n# # lines(z, Ba, color=:red)\n# lines(x, Ba, color=:red)\n","category":"page"},{"location":"examples/advanced/demo_magneticmirror/","page":"Magnetic mirror","title":"Magnetic mirror","text":"(Image: )","category":"page"},{"location":"examples/advanced/demo_magneticmirror/","page":"Magnetic mirror","title":"Magnetic mirror","text":"","category":"page"},{"location":"examples/advanced/demo_magneticmirror/","page":"Magnetic mirror","title":"Magnetic mirror","text":"This page was generated using DemoCards.jl and Literate.jl.","category":"page"},{"location":"examples/basics/demo_electron_proton/","page":"Proton and electron in a static EM field","title":"Proton and electron in a static EM field","text":"EditURL = \"/home/runner/work/TestParticle.jl/TestParticle.jl/docs/examples/basics/demo_electron_proton.jl\"","category":"page"},{"location":"examples/basics/demo_electron_proton/#demo_proton_electron","page":"Proton and electron in a static EM field","title":"Proton and electron in a static EM field","text":"","category":"section"},{"location":"examples/basics/demo_electron_proton/","page":"Proton and electron in a static EM field","title":"Proton and electron in a static EM field","text":"(Image: Source code) (Image: compat) (Image: Author) (Image: Author) (Image: Update time)","category":"page"},{"location":"examples/basics/demo_electron_proton/","page":"Proton and electron in a static EM field","title":"Proton and electron in a static EM field","text":"This example demonstrates tracing a single proton and electron motion under a uniform B field in real physical parameters. The E field is assumed to be zero such that there is no particle acceleration. Due to the fact that m_p m_e doteq 1836, the proton gyro-radius is 1800 times larger than the electron, if they start with the same velocity as in this case. In more common cases we would compare electrons and protons with the same energy, and their gyro-radii differ by a factor of sqrtm_pm_e sim 40.","category":"page"},{"location":"examples/basics/demo_electron_proton/","page":"Proton and electron in a static EM field","title":"Proton and electron in a static EM field","text":"using TestParticle\nusing OrdinaryDiffEq\nusing CairoMakie\n\n### Initialize grid and field\nx = range(-10, 10, length=15)\ny = range(-10, 10, length=20)\nz = range(-10, 10, length=25)\n\nB = fill(0.0, 3, length(x), length(y), length(z)) # [T]\nE = fill(0.0, 3, length(x), length(y), length(z)) # [V/m]\nB[3,:,:,:] .= 1e-11\nE[3,:,:,:] .= 5e-13\n\n### Initialize particles\nstateinit = let\n x0 = [0.0, 0.0, 0.0] # initial position, [m]\n u0 = [1.0, 0.0, 0.0] # initial velocity, [m/s]\n [x0..., u0...]\nend\nparam_electron = prepare(x, y, z, E, B, species=Electron)\ntspan_electron = (0.0, 15.0)\n\nparam_proton = prepare(x, y, z, E, B, species=Proton)\ntspan_proton = (0.0, 10.0)\n\n### Solve for the trajectories\nprob_e = ODEProblem(trace!, stateinit, tspan_electron, param_electron)\nprob_p = ODEProblem(trace!, stateinit, tspan_proton, param_proton)\n\nsol_e = solve(prob_e, Vern9())\nsol_p = solve(prob_p, Vern9())\n\n### Visualization\nf = Figure(fontsize=18)\nax = Axis3(f[1, 1],\n title = \"Electron and Ion Trajectories\",\n xlabel = \"x [m]\",\n ylabel = \"y [m]\",\n zlabel = \"z [m]\",\n aspect = :data,\n)\nplot!(sol_e, idxs=(1, 2, 3), color=:tomato, label=\"electron\")\nplot!(sol_p, idxs=(1, 2, 3), color=:deepskyblue3, label=\"proton\")\n\n##TODO: wait for https://github.com/MakieOrg/Makie.jl/issues/3623 to be fixed!\nax.scene.plots[9+2*1-1].color = :tomato\nax.scene.plots[9+2*2-1].color = :deepskyblue3\n\naxislegend()\n","category":"page"},{"location":"examples/basics/demo_electron_proton/","page":"Proton and electron in a static EM field","title":"Proton and electron in a static EM field","text":"(Image: )","category":"page"},{"location":"examples/basics/demo_electron_proton/","page":"Proton and electron in a static EM field","title":"Proton and electron in a static EM field","text":"","category":"page"},{"location":"examples/basics/demo_electron_proton/","page":"Proton and electron in a static EM field","title":"Proton and electron in a static EM field","text":"This page was generated using DemoCards.jl and Literate.jl.","category":"page"},{"location":"examples/basics/demo_dimensionless_periodic/","page":"Tracing with Dimensionless Units and Periodic Boundary","title":"Tracing with Dimensionless Units and Periodic Boundary","text":"EditURL = \"/home/runner/work/TestParticle.jl/TestParticle.jl/docs/examples/basics/demo_dimensionless_periodic.jl\"","category":"page"},{"location":"examples/basics/demo_dimensionless_periodic/#demo_dimensionless_periodic","page":"Tracing with Dimensionless Units and Periodic Boundary","title":"Tracing with Dimensionless Units and Periodic Boundary","text":"","category":"section"},{"location":"examples/basics/demo_dimensionless_periodic/","page":"Tracing with Dimensionless Units and Periodic Boundary","title":"Tracing with Dimensionless Units and Periodic Boundary","text":"(Image: Source code) (Image: compat) (Image: Author) (Image: Update time)","category":"page"},{"location":"examples/basics/demo_dimensionless_periodic/","page":"Tracing with Dimensionless Units and Periodic Boundary","title":"Tracing with Dimensionless Units and Periodic Boundary","text":"This example shows how to trace charged particles in dimensionless units and EM fields with periodic boundaries in a 2D spatial domain. For details about dimensionless units, please check Demo: dimensionless tracing.","category":"page"},{"location":"examples/basics/demo_dimensionless_periodic/","page":"Tracing with Dimensionless Units and Periodic Boundary","title":"Tracing with Dimensionless Units and Periodic Boundary","text":"Now let's demonstrate this with trace_normalized!.","category":"page"},{"location":"examples/basics/demo_dimensionless_periodic/","page":"Tracing with Dimensionless Units and Periodic Boundary","title":"Tracing with Dimensionless Units and Periodic Boundary","text":"using TestParticle\nusing TestParticle: qᵢ, mᵢ\nusing OrdinaryDiffEq\nusing StaticArrays\nusing CairoMakie\n\n# Number of cells for the field along each dimension\nnx, ny = 4, 6\n# Unit conversion factors between SI and dimensionless units\nB₀ = 10e-9 # [T]\nΩ = abs(qᵢ) * B₀ / mᵢ # [1/s]\nt₀ = 1 / Ω # [s]\nU₀ = 1.0 # [m/s]\nl₀ = U₀ * t₀ # [m]\nE₀ = U₀*B₀ # [V/m]\n\nx = range(-10, 10, length=nx) # [l₀]\ny = range(-10, 10, length=ny) # [l₀]\n\nB = fill(0.0, 3, nx, ny) # [B₀]\nB[3,:,:] .= 1.0\n\nE(x) = SA[0.0, 0.0, 0.0] # [E₀]\n\n# If bc == 1, we set a NaN value outside the domain (default);\n# If bc == 2, we set periodic boundary conditions.\nparam = prepare(x, y, E, B; species=User, bc=2);","category":"page"},{"location":"examples/basics/demo_dimensionless_periodic/","page":"Tracing with Dimensionless Units and Periodic Boundary","title":"Tracing with Dimensionless Units and Periodic Boundary","text":"Note that we set a radius of 10, so the trajectory extent from -20 to 0 in y, which is beyond the original y range.","category":"page"},{"location":"examples/basics/demo_dimensionless_periodic/","page":"Tracing with Dimensionless Units and Periodic Boundary","title":"Tracing with Dimensionless Units and Periodic Boundary","text":"# Initial conditions\nstateinit = let\n x0 = [0.0, 0.0, 0.0] # initial position [l₀]\n u0 = [10.0, 0.0, 0.0] # initial velocity [v₀]\n [x0..., u0...]\nend\n# Time span\ntspan = (0.0, 1.5π) # 3/4 gyroperiod\n\nprob = ODEProblem(trace_normalized!, stateinit, tspan, param)\nsol = solve(prob, Vern9());","category":"page"},{"location":"examples/basics/demo_dimensionless_periodic/","page":"Tracing with Dimensionless Units and Periodic Boundary","title":"Tracing with Dimensionless Units and Periodic Boundary","text":"Visualization","category":"page"},{"location":"examples/basics/demo_dimensionless_periodic/","page":"Tracing with Dimensionless Units and Periodic Boundary","title":"Tracing with Dimensionless Units and Periodic Boundary","text":"f = Figure(fontsize = 18)\nax = Axis(f[1, 1],\n title = \"Proton trajectory\",\n xlabel = \"X\",\n ylabel = \"Y\",\n limits = (-10.1, 10.1, -20.1, 0.1),\n aspect = DataAspect()\n)\n\nlines!(ax, sol, vars=(1,2))\n","category":"page"},{"location":"examples/basics/demo_dimensionless_periodic/","page":"Tracing with Dimensionless Units and Periodic Boundary","title":"Tracing with Dimensionless Units and Periodic Boundary","text":"(Image: )","category":"page"},{"location":"examples/basics/demo_dimensionless_periodic/","page":"Tracing with Dimensionless Units and Periodic Boundary","title":"Tracing with Dimensionless Units and Periodic Boundary","text":"","category":"page"},{"location":"examples/basics/demo_dimensionless_periodic/","page":"Tracing with Dimensionless Units and Periodic Boundary","title":"Tracing with Dimensionless Units and Periodic Boundary","text":"This page was generated using DemoCards.jl and Literate.jl.","category":"page"},{"location":"examples/advanced/demo_proton_dipole/","page":"Magnetic dipole","title":"Magnetic dipole","text":"EditURL = \"/home/runner/work/TestParticle.jl/TestParticle.jl/docs/examples/advanced/demo_proton_dipole.jl\"","category":"page"},{"location":"examples/advanced/demo_proton_dipole/#demo_dipole","page":"Magnetic dipole","title":"Magnetic dipole","text":"","category":"section"},{"location":"examples/advanced/demo_proton_dipole/","page":"Magnetic dipole","title":"Magnetic dipole","text":"(Image: Source code) (Image: compat) (Image: Author) (Image: Update time)","category":"page"},{"location":"examples/advanced/demo_proton_dipole/","page":"Magnetic dipole","title":"Magnetic dipole","text":"This example shows how to trace protons of a certain energy in a analytic Earth-like magnetic dipole field. There is a combination of grad-B drift, curvature drift, and the bounce motion between mirror points. It demonstrates the motions corresponding to the three adiabatic invariants.","category":"page"},{"location":"examples/advanced/demo_proton_dipole/","page":"Magnetic dipole","title":"Magnetic dipole","text":"using TestParticle\nusing TestParticle: getB_dipole, getE_dipole, sph2cart, dipole_fieldline, mᵢ, qᵢ, c, Rₑ\nusing OrdinaryDiffEq\nusing CairoMakie\n\n# Initial condition\nstateinit = let\n # Initial particle energy\n Ek = 5e7 # [eV]\n # initial velocity, [m/s]\n v₀ = sph2cart(c*sqrt(1-1/(1+Ek*qᵢ/(mᵢ*c^2))^2), 0.0, π/4)\n # initial position, [m]\n r₀ = sph2cart(2.5*Rₑ, 0.0, π/2)\n [r₀..., v₀...]\nend\n# obtain field\nparam = prepare(getE_dipole, getB_dipole)\ntspan = (0.0, 10.0)\n\nprob = ODEProblem(trace!, stateinit, tspan, param)\n\nsol = solve(prob, Vern9())\n\n### Visualization\n\nf = Figure(fontsize=18)\nax = Axis3(f[1, 1],\n title = \"50 MeV Proton trajectory in Earth's dipole field\",\n xlabel = \"x [Re]\",\n ylabel = \"y [Re]\",\n zlabel = \"z [Re]\",\n aspect = :data,\n limits = (-2.5, 2.5, -2.5, 2.5, -1, 1)\n)\n\ninvRE = 1 / Rₑ\nl = lines!(ax, sol, idxs=(1, 2, 3))\n##TODO: wait for https://github.com/MakieOrg/Makie.jl/issues/3623 to be fixed!\nscale!(ax.scene.plots[9+1], invRE, invRE, invRE)\n\nfor ϕ in range(0, stop=2*π, length=10)\n lines!(dipole_fieldline(ϕ)..., color=:tomato, alpha=0.3)\nend\n","category":"page"},{"location":"examples/advanced/demo_proton_dipole/","page":"Magnetic dipole","title":"Magnetic dipole","text":"(Image: )","category":"page"},{"location":"examples/advanced/demo_proton_dipole/","page":"Magnetic dipole","title":"Magnetic dipole","text":"Solver algorithm matters in terms of energy conservation. In the above we used Verner's “Most Efficient” 9/8 Runge-Kutta method. Let's check other algorithms.","category":"page"},{"location":"examples/advanced/demo_proton_dipole/","page":"Magnetic dipole","title":"Magnetic dipole","text":"function get_energy_ratio(sol)\n vx = getindex.(sol.u, 4)\n vy = getindex.(sol.u, 5)\n vz = getindex.(sol.u, 6)\n\n Einit = vx[1]^2 + vy[1]^2 + vz[1]^2\n Eend = vx[end]^2 + vy[end]^2 + vz[end]^2\n\n (Eend - Einit) / Einit\nend\n\n# `ImplicitMidpoint()` requires a fixed time step.\nsol = solve(prob, ImplicitMidpoint(); dt=1e-3)\nget_energy_ratio(sol)","category":"page"},{"location":"examples/advanced/demo_proton_dipole/","page":"Magnetic dipole","title":"Magnetic dipole","text":"-0.00045982272113011914","category":"page"},{"location":"examples/advanced/demo_proton_dipole/","page":"Magnetic dipole","title":"Magnetic dipole","text":"sol = solve(prob, ImplicitMidpoint(); dt=1e-4)\nget_energy_ratio(sol)","category":"page"},{"location":"examples/advanced/demo_proton_dipole/","page":"Magnetic dipole","title":"Magnetic dipole","text":"-4.940132338997509e-9","category":"page"},{"location":"examples/advanced/demo_proton_dipole/","page":"Magnetic dipole","title":"Magnetic dipole","text":"sol = solve(prob, Vern9())\nget_energy_ratio(sol)","category":"page"},{"location":"examples/advanced/demo_proton_dipole/","page":"Magnetic dipole","title":"Magnetic dipole","text":"-0.0070445259008460786","category":"page"},{"location":"examples/advanced/demo_proton_dipole/","page":"Magnetic dipole","title":"Magnetic dipole","text":"sol = solve(prob, Trapezoid())\nget_energy_ratio(sol)","category":"page"},{"location":"examples/advanced/demo_proton_dipole/","page":"Magnetic dipole","title":"Magnetic dipole","text":"-0.011643134931582444","category":"page"},{"location":"examples/advanced/demo_proton_dipole/","page":"Magnetic dipole","title":"Magnetic dipole","text":"sol = solve(prob, Vern6())\nget_energy_ratio(sol)","category":"page"},{"location":"examples/advanced/demo_proton_dipole/","page":"Magnetic dipole","title":"Magnetic dipole","text":"-0.06589639870599219","category":"page"},{"location":"examples/advanced/demo_proton_dipole/","page":"Magnetic dipole","title":"Magnetic dipole","text":"sol = solve(prob, Tsit5())\nget_energy_ratio(sol)","category":"page"},{"location":"examples/advanced/demo_proton_dipole/","page":"Magnetic dipole","title":"Magnetic dipole","text":"0.5351974995171843","category":"page"},{"location":"examples/advanced/demo_proton_dipole/","page":"Magnetic dipole","title":"Magnetic dipole","text":"Default stepsize settings may not be enough for our problem. By using a smaller abstol and reltol, we can guarantee much better conservation at a higher cost:","category":"page"},{"location":"examples/advanced/demo_proton_dipole/","page":"Magnetic dipole","title":"Magnetic dipole","text":"# This is roughly equivalent in accuracy and performance with Vern9() and `reltol=1e-3` (default)\nsol = solve(prob, Tsit5(); reltol=1e-4);","category":"page"},{"location":"examples/advanced/demo_proton_dipole/","page":"Magnetic dipole","title":"Magnetic dipole","text":"Or, for adaptive time step algorithms like Vern9(), with the help of callbacks, we can enforce a largest time step smaller than 1/10 of the local gyroperiod:","category":"page"},{"location":"examples/advanced/demo_proton_dipole/","page":"Magnetic dipole","title":"Magnetic dipole","text":"using DiffEqCallbacks\n\n# p = (charge_mass_ratio, E, B)\ndtFE(u, p, t) = 2π / (abs(p[1]) * hypot(p[3](u, t)...))\ncb = StepsizeLimiter(dtFE; safety_factor=1 // 10, max_step=true)\n\nsol = solve(prob, Vern9(); callback=cb, dt=0.1) # dt=0.1 is a dummy value\nget_energy_ratio(sol)","category":"page"},{"location":"examples/advanced/demo_proton_dipole/","page":"Magnetic dipole","title":"Magnetic dipole","text":"-5.399366794067525e-7","category":"page"},{"location":"examples/advanced/demo_proton_dipole/","page":"Magnetic dipole","title":"Magnetic dipole","text":"This is much more accurate, at the cost of more iterations. In terms of accuracy, this is roughly equivalent to solve(prob, Vern9(); reltol=1e-7); in terms of performance, it is 2x slower (0.04s v.s. 0.02s) and consumes about the same amount of memory 42 MiB. We can also use the classical Boris method implemented within the package:","category":"page"},{"location":"examples/advanced/demo_proton_dipole/","page":"Magnetic dipole","title":"Magnetic dipole","text":"dt = 1e-4\nprob = TraceProblem(stateinit, tspan, param)\nsol = TestParticle.solve(prob; dt)[1]\nget_energy_ratio(sol)","category":"page"},{"location":"examples/advanced/demo_proton_dipole/","page":"Magnetic dipole","title":"Magnetic dipole","text":"-1.0158504769154868e-15","category":"page"},{"location":"examples/advanced/demo_proton_dipole/","page":"Magnetic dipole","title":"Magnetic dipole","text":"The Boris method requires a fixed time step. It takes about 0.05s and consumes 53 MiB memory. In this specific case, the time step is determined empirically. If we increase the time step to 1e-2 seconds, the trajectory becomes completely off (but the energy is still conserved). Therefore, as a rule of thumb, we should not use the default Tsit5() scheme without decreasing reltol. Use adaptive Vern9() for an unfamiliar field configuration, then switch to more accurate schemes if needed. A more thorough test can be found here.","category":"page"},{"location":"examples/advanced/demo_proton_dipole/","page":"Magnetic dipole","title":"Magnetic dipole","text":"","category":"page"},{"location":"examples/advanced/demo_proton_dipole/","page":"Magnetic dipole","title":"Magnetic dipole","text":"This page was generated using DemoCards.jl and Literate.jl.","category":"page"},{"location":"examples/advanced/demo_shock/","page":"Shock","title":"Shock","text":"EditURL = \"/home/runner/work/TestParticle.jl/TestParticle.jl/docs/examples/advanced/demo_shock.jl\"","category":"page"},{"location":"examples/advanced/demo_shock/#demo_shock","page":"Shock","title":"Shock","text":"","category":"section"},{"location":"examples/advanced/demo_shock/","page":"Shock","title":"Shock","text":"(Image: Source code) (Image: compat) (Image: Author) (Image: Update time)","category":"page"},{"location":"examples/advanced/demo_shock/","page":"Shock","title":"Shock","text":"This example shows how to trace protons of a certain energy in MHD plane shocks.","category":"page"},{"location":"examples/advanced/demo_shock/","page":"Shock","title":"Shock","text":"using TestParticle, OrdinaryDiffEq\nusing TestParticle: mᵢ, kB\nusing LinearAlgebra\nusing Statistics: mean, std\nusing Printf\nusing Random\nusing CairoMakie, PairPlots\n\n# For reproducible results\nRandom.seed!(1234)\n\n\"Set initial conditions.\"\nfunction prob_func(prob, i, repeat)\n v₀ = sample(vdf₁)\n r₀ = [5_000e3, 0.0, 0.0]\n\n prob = remake(prob; u0 = [r₀..., v₀...])\nend","category":"page"},{"location":"examples/advanced/demo_shock/","page":"Shock","title":"Shock","text":"Main.var\"##55416\".prob_func","category":"page"},{"location":"examples/advanced/demo_shock/","page":"Shock","title":"Shock","text":"Perpendicular shock is a special shock in which both the upstream and downstream plasma flows are perpendicular to the magnetic field, as well as the shock front.","category":"page"},{"location":"examples/advanced/demo_shock/","page":"Shock","title":"Shock","text":"# MHD states in SI units\nn₁ = 1.0e6\nT₁ = 720471.8506664868\nPi₁ = 0.0049735919716217296 * 1e-9\nPe₁ = 0.0049735919716217296 * 1e-9\nPth₁ = Pi₁ + Pe₁\nV₁ = [-545.1484121835928, 0.0, 0.0] .* 1e3\nB₁ = [0.0, 0.0, 5.0] .* 1e-9\n\nn₂ = 3.1662479035540008e6\nT₂ = 5.957947703036288e6\nPi₂ = 0.13022511153415584 * 1e-9\nPe₂ = 0.13022511153415584 * 1e-9\nPth₂ = Pi₂ + Pe₂\nV₂ = [-172.17489874108816, 0.0, 0.0] .* 1e3\nB₂ = [0.0, 0.0, 15.831239517770003] .* 1e-9;","category":"page"},{"location":"examples/advanced/demo_shock/","page":"Shock","title":"Shock","text":"In ideal MHD, the electric field simply contains the convection term. For perpendicular shocks, the electric field across the shock is continuous. In the upstream, particles follow straight lines because it's purely an ExB drift; across the shock, ExB drift changes to the downstream bulk velocity.","category":"page"},{"location":"examples/advanced/demo_shock/","page":"Shock","title":"Shock","text":"E₁ = B₁ × V₁\nE₂ = B₂ × V₂\n\n# Shock normal direction range\nx = range(-5_000e3, 5_000e3, length=100)\nB = repeat(B₁, 1, length(x))\nE = repeat(E₁, 1, length(x))\n# Index for the shock location\nmid_ = length(x) ÷ 2\n\nB[:, 1:mid_] .= B₂\nE[:, 1:mid_] .= E₂\n\nconst vdf₁ = Maxwellian(V₁, Pth₁, n₁; m=mᵢ)\nvdf₂ = Maxwellian(V₂, Pth₂, n₂; m=mᵢ)\n\ntrajectories = 400\nweight₁ = n₁ / trajectories # relation between test particle and real particles\n\nprob = let\n # BC type 3 is Flat\n param = prepare(x, E, B; species=Proton, bc=3);\n stateinit = zeros(6) # particle position and velocity to be modified\n tspan = (0.0, 30.0)\n ODEProblem(trace!, stateinit, tspan, param)\nend\nensemble_prob = EnsembleProblem(prob; prob_func, safetycopy=false)\n\nsols = solve(ensemble_prob, Vern9(), EnsembleSerial(); trajectories);","category":"page"},{"location":"examples/advanced/demo_shock/","page":"Shock","title":"Shock","text":"Sample particle trajectories","category":"page"},{"location":"examples/advanced/demo_shock/","page":"Shock","title":"Shock","text":"function plot_traj(sols; azimuth=1.275pi, elevation=pi/8)\n f = Figure(fontsize=18)\n ax = Axis3(f[1, 1];\n title = \"Particles across MHD shock\",\n xlabel = \"x [km]\",\n ylabel = \"y [km]\",\n zlabel = \"z [km]\",\n aspect = :data,\n azimuth, elevation,\n )\n\n invL = 1 / 1e3\n\n for i in eachindex(sols)\n lines!(ax, sols[i], idxs=(1,2,3), label=\"$i\")\n ##TODO: wait for https://github.com/MakieOrg/Makie.jl/issues/3623 to be fixed!\n ax.scene.plots[9+2*i-1].color = Makie.wong_colors()[mod(i-1, 7)+1]\n scale!(ax.scene.plots[9+2*i-1], invL, invL, invL)\n end\n\n # Represent the shock front\n p1 = Point3f(0.0, -2e2, -2e2)\n p2 = Point3f(0.0, 2e2, -2e2)\n p3 = Point3f(0.0, 2e2, 2e2)\n p4 = Point3f(0.0, -2e2, 2e2)\n\n mesh!(ax, [p1, p2, p3], color = (:gray, 0.1), shading = Makie.automatic)\n mesh!(ax, [p1, p4, p3], color = (:gray, 0.1), shading = Makie.automatic)\n\n f\nend\n\nf = plot_traj(sols[1:4])","category":"page"},{"location":"examples/advanced/demo_shock/","page":"Shock","title":"Shock","text":"(Image: )","category":"page"},{"location":"examples/advanced/demo_shock/","page":"Shock","title":"Shock","text":"Phase space distributions","category":"page"},{"location":"examples/advanced/demo_shock/","page":"Shock","title":"Shock","text":"function plot_dist(x, sols; nxchunks::Int=2, ntchunks::Int=20)\n trange = range(sols[1].prob.tspan..., length=ntchunks)\n\n xrange = range(x[1], x[end], length=nxchunks+1)\n dx = (x[end] - x[1]) / nxchunks\n xmid = range(x[1] + 0.5dx, x[end] - 0.5dx, length=nxchunks) ./ 1e3\n\n vx = [Float64[] for _ in 1:nxchunks]\n\n for sol in sols\n for t in trange\n xv = sol(t)\n for i in 1:nxchunks\n if xrange[i] < xv[1] ≤ xrange[i+1]\n push!(vx[i], xv[4] / 1e3)\n end\n end\n end\n end\n\n for i in eachindex(vx)\n if isempty(vx[i])\n push!(vx[i], 0.0)\n end\n end\n\n f = Figure(size = (1200, 600), fontsize=18)\n ax = Axis(f[1, 1],\n limits = (nothing, nothing, -750, 400),\n title = \"Phase space distributions at different spatial locations\",\n xlabel = \"Location [km]\",\n ylabel = \"Vx [km/s]\",\n xminorticksvisible = true)\n\n for i in 1:nxchunks\n hist!(ax, vx[i], normalization = :pdf, bins = 50,\n scale_to=-5000/nxchunks, offset=xmid[i], direction=:x)\n end\n\n v̄x = mean.(vx)\n vth = [std(vx[i]; corrected=false, mean=v̄x[i]) for i in 1:nxchunks]\n means_str = [@sprintf \"Vx: %d [km/s]\" v̄x[i] for i in eachindex(v̄x)]\n std_str = [@sprintf \"Vth: %d [km/s]\" vth[i] for i in eachindex(vth)]\n text!(Point.(xmid.+400, 300.0), text = means_str, align = (:right, :center),\n offset = (-60, 0), color = :black, fontsize=24)\n text!(Point.(xmid.+400, -700.0), text = std_str, align = (:right, :center),\n offset = (-60, 0), color = :black, fontsize=24)\n\n f\nend\n\nf = plot_dist(x, sols; nxchunks=4, ntchunks=100)","category":"page"},{"location":"examples/advanced/demo_shock/","page":"Shock","title":"Shock","text":"(Image: )","category":"page"},{"location":"examples/advanced/demo_shock/","page":"Shock","title":"Shock","text":"Even with 400 particles, we are still able to statistically approximate the velocity moment downstream of the perpendicular shock. While the upstream thermal speed is close to what we set, the downstream thermal speed is higher than our precalculated value. (Why?)","category":"page"},{"location":"examples/advanced/demo_shock/","page":"Shock","title":"Shock","text":"Vth₁ = 77.1 km/s\nVth₂ = 221.7 km/s\n","category":"page"},{"location":"examples/advanced/demo_shock/","page":"Shock","title":"Shock","text":"A nice way to present the 3d distributions in 2d is via the pair plots:","category":"page"},{"location":"examples/advanced/demo_shock/","page":"Shock","title":"Shock","text":"function plot_dist_pairplot(x, sols; ntchunks::Int=20)\n nxchunks = 2\n trange = range(sols[1].prob.tspan..., length=ntchunks)\n\n xrange = range(x[1], x[end], length=nxchunks+1)\n\n table = [(;\n vx = Float64[],\n vy = Float64[],\n vz = Float64[],\n ) for _ in 1:nxchunks]\n\n for sol in sols\n for t in trange\n xv = sol(t)\n for i in 1:nxchunks\n if xrange[i] < xv[1] ≤ xrange[i+1]\n push!(table[i].vx, xv[4] / 1e3)\n push!(table[i].vy, xv[5] / 1e3)\n push!(table[i].vz, xv[6] / 1e3)\n end\n end\n end\n end\n\n for i in eachindex(table)\n if isempty(table[i].vx)\n push!(table[i].vx, 0.0)\n push!(table[i].vy, 0.0)\n push!(table[i].vz, 0.0)\n end\n end\n\n f = Figure(size = (1000, 600), fontsize=18)\n\n c1 = Makie.wong_colors(0.5)[1]\n c2 = Makie.wong_colors(0.5)[2]\n\n l1 = @sprintf \"x: [%d, %d] km downstream\" xrange[1]/1e3 xrange[2]/1e3\n l2 = @sprintf \"x: [%d, %d] km upstream\" xrange[2]/1e3 xrange[3]/1e3\n\n pairplot(f[1,1],\n PairPlots.Series(table[1], label=l1, color=c1, strokecolor=c1),\n PairPlots.Series(table[2], label=l2, color=c2, strokecolor=c2),\n bodyaxis=(; xgridvisible=true, ygridvisible=true),\n diagaxis=(; xgridvisible=true, ygridvisible=true)\n )\n\n f\nend\n\nf = plot_dist_pairplot(x, sols; ntchunks=20)","category":"page"},{"location":"examples/advanced/demo_shock/","page":"Shock","title":"Shock","text":"(Image: )","category":"page"},{"location":"examples/advanced/demo_shock/","page":"Shock","title":"Shock","text":"For parallel shocks, we have a different scenario. Parallel shock is another special shock in which both the upstream and downstream plasma flows are parallel to the magnetic field, as well as perpendicular to the shock front.","category":"page"},{"location":"examples/advanced/demo_shock/","page":"Shock","title":"Shock","text":"# MHD states in SI units\nn₁ = 1.0e6\nT₁ = 720471.8506664868\nPi₁ = 0.0049735919716217296 * 1e-9\nPe₁ = 0.0049735919716217296 * 1e-9\nPth₁ = Pi₁ + Pe₁\nV₁ = [-545.1484121835928, 0.0, 0.0] .* 1e3\nB₁ = [5.0, 0.0, 0.0] .* 1e-9\n\nn₂ = 3.6363636363636362e6\nT₂ = 7.380333520264822e6\nPi₂ = 0.18526630094290936 * 1e-9\nPe₂ = 0.18526630094290936 * 1e-9\nPth₂ = Pi₂ + Pe₂\nV₂ = [-149.91581335048804, 0.0, 0.0] .* 1e3\nB₂ = [5.0, 0.0, 0.0] .* 1e-9;","category":"page"},{"location":"examples/advanced/demo_shock/","page":"Shock","title":"Shock","text":"There is no convection electric field in the parallel shock:","category":"page"},{"location":"examples/advanced/demo_shock/","page":"Shock","title":"Shock","text":"E₁ = B₁ × V₁\nE₂ = B₂ × V₂","category":"page"},{"location":"examples/advanced/demo_shock/","page":"Shock","title":"Shock","text":"3-element Vector{Float64}:\n 0.0\n -0.0\n 0.0","category":"page"},{"location":"examples/advanced/demo_shock/","page":"Shock","title":"Shock","text":"Therefore, when we trace particles, there is no deceleration across the shock:","category":"page"},{"location":"examples/advanced/demo_shock/","page":"Shock","title":"Shock","text":"# Shock normal direction range\nx = range(-5_000e3, 5_000e3, length=100)\nB = repeat(B₁, 1, length(x))\nE = repeat(E₁, 1, length(x))\n# Index for the shock location\nmid_ = length(x) ÷ 2\n\nB[:, 1:mid_] .= B₂\nE[:, 1:mid_] .= E₂\n\nvdf₁ = Maxwellian(V₁, Pth₁, n₁; m=mᵢ)\nvdf₂ = Maxwellian(V₂, Pth₂, n₂; m=mᵢ)\n\ntrajectories = 2\nweight₁ = n₁ / trajectories\n\nprob = let\n # BC type 3 is Flat\n param = prepare(x, E, B; species=Proton, bc=3);\n stateinit = zeros(6) # particle position and velocity to be modified\n tspan = (0.0, 14.0)\n ODEProblem(trace!, stateinit, tspan, param)\nend\nensemble_prob = EnsembleProblem(prob; prob_func, safetycopy=false)\n\nsols = solve(ensemble_prob, Vern9(), EnsembleSerial(); trajectories);\n\nf = plot_traj(sols; azimuth=1.08π, elevation=pi/16)","category":"page"},{"location":"examples/advanced/demo_shock/","page":"Shock","title":"Shock","text":"(Image: )","category":"page"},{"location":"examples/advanced/demo_shock/","page":"Shock","title":"Shock","text":"Clearly, test particle tracing in MHD parallel shocks fails to recover physics. MHD parallel shocks are essentially hydrodynamic shocks where magnetic field plays no role. Due to the lack of collision and other diffusion processes, we are unable to capture the correct microscopic scenario here.","category":"page"},{"location":"examples/advanced/demo_shock/","page":"Shock","title":"Shock","text":"","category":"page"},{"location":"examples/advanced/demo_shock/","page":"Shock","title":"Shock","text":"This page was generated using DemoCards.jl and Literate.jl.","category":"page"},{"location":"examples/basics/demo_curvature_B/","page":"Curl-B drift","title":"Curl-B drift","text":"EditURL = \"/home/runner/work/TestParticle.jl/TestParticle.jl/docs/examples/basics/demo_curvature_B.jl\"","category":"page"},{"location":"examples/basics/demo_curvature_B/#demo_curlB","page":"Curl-B drift","title":"Curl-B drift","text":"","category":"section"},{"location":"examples/basics/demo_curvature_B/","page":"Curl-B drift","title":"Curl-B drift","text":"(Image: Source code) (Image: compat) (Image: Author) (Image: Author) (Image: Update time)","category":"page"},{"location":"examples/basics/demo_curvature_B/","page":"Curl-B drift","title":"Curl-B drift","text":"This example demonstrates a single proton motion under a vacuum non-uniform B field with gradient and curvature. Similar to the magnetic field gradient drift, analytic calculation should include both of the gradient drift and the curvature drift. More theoretical details can be found in Curvature Drift and Computational Plasma Physics by Toshi Tajima.","category":"page"},{"location":"examples/basics/demo_curvature_B/","page":"Curl-B drift","title":"Curl-B drift","text":"using TestParticle\nusing TestParticle: get_gc\nusing OrdinaryDiffEq\nusing StaticArrays\nusing LinearAlgebra: normalize, norm, ×, ⋅\nusing ForwardDiff: gradient, jacobian\nusing CairoMakie\n\nfunction curved_B(x)\n # satisify ∇⋅B=0\n # B_θ = 1/r => ∂B_θ/∂θ = 0\n θ = atan(x[3]/(x[1]+3))\n r = hypot(x[1]+3, x[3])\n return SA[-1e-7*sin(θ)/r, 0, 1e-7*cos(θ)/r]\nend\n\nfunction zero_E(x)\n return SA[0, 0, 0]\nend\n\nabs_B(x) = norm(curved_B(x)) # |B|\n\n# Trace the orbit of the guiding center using analytical drifts\nfunction trace_gc!(dx, x, p, t)\n q2m, E, B, sol = p\n xu = sol(t)\n gradient_B = gradient(abs_B, x) # ∇|B|\n Bv = B(x)\n b = normalize(Bv)\n v_par = (xu[4:6] ⋅ b).*b # (v⋅b)b\n v_perp = xu[4:6] - v_par\n Ω = q2m*norm(Bv)\n κ = jacobian(B, x)*Bv # B⋅∇B\n # v⟂^2*(B×∇|B|)/(2*Ω*B^2) + v∥^2*(B×(B⋅∇B))/(Ω*B^3) + (E×B)/B^2 + v∥\n dx[1:3] = norm(v_perp)^2*(Bv × gradient_B)/(2*Ω*norm(Bv)^2) +\n norm(v_par)^2*(Bv × κ)/Ω/norm(Bv)^3 + (E(x) × Bv)/norm(Bv)^2 + v_par\nend\n\n# Initial conditions\nstateinit = let x0 = [1.0, 0.0, 0.0], v0 = [0.0, 1.0, 0.1]\n [x0..., v0...]\nend\n# Time span\ntspan = (0, 40)\n# Trace particle\nparam = prepare(zero_E, curved_B, species=Proton)\nprob = ODEProblem(trace!, stateinit, tspan, param)\nsol = solve(prob, Vern9())\n# Functions for obtaining the guiding center from actual trajectory\ngc = get_gc(param)\ngc_x0 = gc(stateinit)\nprob_gc = ODEProblem(trace_gc!, gc_x0, tspan, (param..., sol))\nsol_gc = solve(prob_gc, Tsit5(); save_idxs=[1,2,3])\n\n# Numeric and analytic results\nf = Figure(fontsize=18)\nax = Axis3(f[1, 1],\n title = \"Curvature Drift\",\n xlabel = \"x [m]\",\n ylabel = \"y [m]\",\n zlabel = \"z [m]\",\n aspect = :data,\n azimuth = 0.3π,\n)\n\ngc_plot(x, y, z, vx, vy, vz) = (gc(SA[x, y, z, vx, vy, vz])...,)\n\nlines!(ax, sol, idxs=(1, 2, 3))\nlines!(ax, sol, idxs=(gc_plot, 1, 2, 3, 4, 5, 6))\nlines!(ax, sol_gc, idxs=(1, 2, 3))\n\nfor i in 1:3\n ##TODO: wait for https://github.com/MakieOrg/Makie.jl/issues/3623 to be fixed!\n ax.scene.plots[9+2*i-1].color = Makie.wong_colors()[i]\nend\n","category":"page"},{"location":"examples/basics/demo_curvature_B/","page":"Curl-B drift","title":"Curl-B drift","text":"(Image: )","category":"page"},{"location":"examples/basics/demo_curvature_B/","page":"Curl-B drift","title":"Curl-B drift","text":"","category":"page"},{"location":"examples/basics/demo_curvature_B/","page":"Curl-B drift","title":"Curl-B drift","text":"This page was generated using DemoCards.jl and Literate.jl.","category":"page"},{"location":"examples/basics/demo_multiple/","page":"Multiple particles","title":"Multiple particles","text":"EditURL = \"/home/runner/work/TestParticle.jl/TestParticle.jl/docs/examples/basics/demo_multiple.jl\"","category":"page"},{"location":"examples/basics/demo_multiple/#demo_multiple","page":"Multiple particles","title":"Multiple particles","text":"","category":"section"},{"location":"examples/basics/demo_multiple/","page":"Multiple particles","title":"Multiple particles","text":"(Image: Source code) (Image: compat) (Image: Author) (Image: Update time)","category":"page"},{"location":"examples/basics/demo_multiple/","page":"Multiple particles","title":"Multiple particles","text":"using TestParticle\nusing OrdinaryDiffEq\nusing Random\nusing CairoMakie\n\n# For reproducible results\nRandom.seed!(1234)\n\nfunction trace(x, y, z, E, B; trajectories::Int=10)\n # Initialize particles\n x0 = [0.0, 0.0, 0.0] # initial position, [m]\n u0 = [1.0, 0.0, 0.0] # initial velocity, [m/s]\n stateinit = [x0..., u0...]\n\n param = prepare(x, y, z, E, B, species=Electron)\n tspan = (0.0, 15.0)\n\n prob = ODEProblem(trace!, stateinit, tspan, param)\n\n sols = Vector{ODESolution}(undef, trajectories)\n # Sample from a Maxwellian with bulk speed 0 and thermal speed 1.0\n vdf = Maxwellian([0.0, 0.0, 0.0], 1.0)\n v = [sample(vdf) for _ in 1:trajectories]\n\n for i in 1:trajectories\n #prob = remake(prob; u0=[x0..., v[:,i]...])\n prob.u0[4:6] = v[i]\n\n sol = solve(prob, Vern9())\n sols[i] = sol\n end\n\n return sols\nend\n\n### Initialize grid and field\nx = range(-10, 10, length=15)\ny = range(-10, 10, length=20)\nz = range(-10, 10, length=25)\n\nB = fill(0.0, 3, length(x), length(y), length(z)) # [T]\nE = fill(0.0, 3, length(x), length(y), length(z)) # [V/m]\nB[3,:,:,:] .= 1e-11\nE[3,:,:,:] .= 5e-13\n\ntrajectories = 4\n\n### Solve for the trajectories\n\nsols = trace(x, y, z, E, B; trajectories)\n\n### Visualization\nf = Figure(fontsize = 18)\nax = Axis3(f[1, 1],\n title = \"Particle trajectories\",\n xlabel = \"X [m]\",\n ylabel = \"Y [m]\",\n zlabel = \"Z [m]\",\n aspect = :data,\n)\n\nfor i in eachindex(sols)\n lines!(ax, sols[i], idxs=(1, 2, 3), label=\"$i\")\nend\n","category":"page"},{"location":"examples/basics/demo_multiple/","page":"Multiple particles","title":"Multiple particles","text":"(Image: )","category":"page"},{"location":"examples/basics/demo_multiple/","page":"Multiple particles","title":"Multiple particles","text":"","category":"page"},{"location":"examples/basics/demo_multiple/","page":"Multiple particles","title":"Multiple particles","text":"This page was generated using DemoCards.jl and Literate.jl.","category":"page"},{"location":"examples/basics/demo_ExB_drift/","page":"E×B drift","title":"E×B drift","text":"EditURL = \"/home/runner/work/TestParticle.jl/TestParticle.jl/docs/examples/basics/demo_ExB_drift.jl\"","category":"page"},{"location":"examples/basics/demo_ExB_drift/#demo_ExB","page":"E×B drift","title":"E×B drift","text":"","category":"section"},{"location":"examples/basics/demo_ExB_drift/","page":"E×B drift","title":"E×B drift","text":"(Image: Source code) (Image: compat) (Image: Author) (Image: Author) (Image: Update time)","category":"page"},{"location":"examples/basics/demo_ExB_drift/","page":"E×B drift","title":"E×B drift","text":"This example demonstrates a single proton motion under uniform E and B fields. The electric field is parallel to the magnetic field in the z-direction, so the motion consists of a cyclotron gyration and an acceleration along z. On top of that, particles also exhibit an ExB drift in the direction perpendicular to both E and B field. Note that in this simple ExB drift case, the analytic and numeric guiding centers overlaps. More theoretical details can be found in ExB Drift.","category":"page"},{"location":"examples/basics/demo_ExB_drift/","page":"E×B drift","title":"E×B drift","text":"using TestParticle\nusing TestParticle: get_gc\nusing OrdinaryDiffEq\nusing StaticArrays\nusing LinearAlgebra: ⋅, ×, normalize\nusing CairoMakie\n\n# Analytic EM fields\nuniform_B(x) = SA[0, 0, 1e-8]\nuniform_E(x) = SA[1e-9, 0, 0]\n\n# Trace the orbit of the guiding center\nfunction trace_gc!(dx, x, p, t)\n _, E, B, sol = p\n xu = sol(t)\n Bv = B(x)\n b = normalize(Bv)\n v_par = @views (xu[4:6] ⋅ b) .* b\n B2 = sum(Bv.^2)\n dx[1:3] = (E(x) × Bv) / B2 + v_par\nend\n# Initial condition\nstateinit = let x0 = [1.0, 0.0, 0.0], v0 = [0.0, 1.0, 0.1]\n [x0..., v0...]\nend\n# Time span\ntspan = (0, 20)\n\n# Trace particle\nparam = prepare(uniform_E, uniform_B, species=Proton)\nprob = ODEProblem(trace!, stateinit, tspan, param)\nsol = solve(prob, Vern9())\n\n# Functions for obtaining the guiding center from actual trajectory\ngc = get_gc(param)\ngc_x0 = gc(stateinit)\nprob_gc = ODEProblem(trace_gc!, gc_x0, tspan, (param..., sol))\nsol_gc = solve(prob_gc, Vern9(); save_idxs=[1,2,3]);\n\n# Numeric and analytic results\nf = Figure(fontsize=18)\nax = Axis3(f[1, 1],\n title = \"ExB Drift\",\n xlabel = \"x [m]\",\n ylabel = \"y [m]\",\n zlabel = \"z [m]\",\n aspect = :data,\n azimuth = 0.3π,\n)\n\ngc_plot(x, y, z, vx, vy, vz) = (gc(SA[x, y, z, vx, vy, vz])...,)\n\nlines!(ax, sol, idxs=(1, 2, 3))\nlines!(ax, sol, idxs=(gc_plot, 1, 2, 3, 4, 5, 6))\nlines!(ax, sol_gc, idxs=(1, 2, 3))\n\nfor i in 1:3\n ##TODO: wait for https://github.com/MakieOrg/Makie.jl/issues/3623 to be fixed!\n ax.scene.plots[9+2*i-1].color = Makie.wong_colors()[i]\nend\n","category":"page"},{"location":"examples/basics/demo_ExB_drift/","page":"E×B drift","title":"E×B drift","text":"(Image: )","category":"page"},{"location":"examples/basics/demo_ExB_drift/","page":"E×B drift","title":"E×B drift","text":"","category":"page"},{"location":"examples/basics/demo_ExB_drift/","page":"E×B drift","title":"E×B drift","text":"This page was generated using DemoCards.jl and Literate.jl.","category":"page"},{"location":"examples/advanced/demo_boris_outofdomain/","page":"Advanced Boris tracing","title":"Advanced Boris tracing","text":"EditURL = \"/home/runner/work/TestParticle.jl/TestParticle.jl/docs/examples/advanced/demo_boris_outofdomain.jl\"","category":"page"},{"location":"examples/advanced/demo_boris_outofdomain/#demo_boris_advance","page":"Advanced Boris tracing","title":"Advanced Boris tracing","text":"","category":"section"},{"location":"examples/advanced/demo_boris_outofdomain/","page":"Advanced Boris tracing","title":"Advanced Boris tracing","text":"(Image: Source code) (Image: compat) (Image: Author) (Image: Update time)","category":"page"},{"location":"examples/advanced/demo_boris_outofdomain/","page":"Advanced Boris tracing","title":"Advanced Boris tracing","text":"This example shows how to trace charged particles using the Boris method in dimensionless units with additionally boundary check. If the particles travel out of the domain specified by the field, the tracing will stop. Check Demo: Dimensionless Units for explaining the unit conversion, and Demo: Boris Method for introducing the Boris method.","category":"page"},{"location":"examples/advanced/demo_boris_outofdomain/","page":"Advanced Boris tracing","title":"Advanced Boris tracing","text":"using TestParticle\nusing TestParticle: qᵢ, mᵢ\nusing StaticArrays\nusing OrdinaryDiffEq\nusing CairoMakie\n\nuniform_B(x) = SA[0.0, 0.0, 0.01]\nuniform_E(x) = SA[0.0, 0.0, 0.0]\n\n\"Set initial states.\"\nfunction prob_func(prob, i, repeat)\n prob = @views remake(prob; u0 = [prob.u0[1:3]..., 10.0 - i*2.0, prob.u0[5:6]...])\nend\n\nfunction isoutofdomain(xv, p, t)\n if isnan(xv[1])\n return true\n else\n return false\n end\nend\n\n# Number of cells for the field along each dimension\nnx, ny = 4, 6\n# Unit conversion factors between SI and dimensionless units\nB₀ = 10e-9 # [T]\nΩ = abs(qᵢ) * B₀ / mᵢ # [1/s]\nt₀ = 1 / Ω # [s]\nU₀ = 1.0 # [m/s]\nl₀ = U₀ * t₀ # [m]\nE₀ = U₀*B₀ # [V/m]\n\nx = range(0, 11, length=nx) # [l₀]\ny = range(-21, 0, length=ny) # [l₀]\n\nB = fill(0.0, 3, nx, ny) # [B₀]\nB[3,:,:] .= 1.0\n\nE(x) = SA[0.0, 0.0, 0.0] # [E₀]\n\n# If bc == 1, we set a NaN value outside the domain (default);\n# If bc == 2, we set periodic boundary conditions.\nparam = prepare(x, y, E, B; species=User, bc=1);","category":"page"},{"location":"examples/advanced/demo_boris_outofdomain/","page":"Advanced Boris tracing","title":"Advanced Boris tracing","text":"Note that we set a radius of 10, so the trajectory extent from -20 to 0 in y, and -10 to 10 in x. After half a cycle, the particle will move into the region where is field is not defined. The tracing will stop with the final step being all NaNs.","category":"page"},{"location":"examples/advanced/demo_boris_outofdomain/","page":"Advanced Boris tracing","title":"Advanced Boris tracing","text":"# Initial conditions to be modified in prob_func\nx0 = [0.0, 0.0, 0.0] # initial position [l₀]\nu0 = [0.0, 0.0, 0.0] # initial velocity [v₀]\nstateinit = [x0..., u0...]\ntspan = (0.0, 1.5π) # 3/4 gyroperiod\n\ndt = 0.1\nsavestepinterval = 1\ntrajectories = 2\nprob = TraceProblem(stateinit, tspan, param; prob_func)\n\nsols = TestParticle.solve(prob; dt, savestepinterval, isoutofdomain, trajectories)\n\nf = Figure(fontsize = 18)\nax = Axis(f[1, 1],\n title = \"Proton trajectory\",\n xlabel = \"X\",\n ylabel = \"Y\",\n limits = (-10.1, 10.1, -20.1, 0.1),\n aspect = DataAspect()\n)\n\nfor i in eachindex(sols)\n lines!(ax, sols[i]; idxs=(1, 2), label=string(i))\n ##TODO: wait for https://github.com/MakieOrg/Makie.jl/issues/3623 to be fixed!\n ax.scene.plots[2*i-1].color = Makie.wong_colors()[i]\nend\n\naxislegend(position=:lt, framevisible=false)\n","category":"page"},{"location":"examples/advanced/demo_boris_outofdomain/","page":"Advanced Boris tracing","title":"Advanced Boris tracing","text":"(Image: )","category":"page"},{"location":"examples/advanced/demo_boris_outofdomain/","page":"Advanced Boris tracing","title":"Advanced Boris tracing","text":"","category":"page"},{"location":"examples/advanced/demo_boris_outofdomain/","page":"Advanced Boris tracing","title":"Advanced Boris tracing","text":"This page was generated using DemoCards.jl and Literate.jl.","category":"page"},{"location":"examples/advanced/demo_ensemble/","page":"Ensemble tracing","title":"Ensemble tracing","text":"EditURL = \"/home/runner/work/TestParticle.jl/TestParticle.jl/docs/examples/advanced/demo_ensemble.jl\"","category":"page"},{"location":"examples/advanced/demo_ensemble/#demo_ensemble","page":"Ensemble tracing","title":"Ensemble tracing","text":"","category":"section"},{"location":"examples/advanced/demo_ensemble/","page":"Ensemble tracing","title":"Ensemble tracing","text":"(Image: Source code) (Image: compat) (Image: Author) (Image: Update time)","category":"page"},{"location":"examples/advanced/demo_ensemble/","page":"Ensemble tracing","title":"Ensemble tracing","text":"This example demonstrates tracing multiple electrons in an analytic EM field and how to take advantage of the multithreading support in the ODE solver. A multiproc version is also available. Check the official documentation of DifferentialEquations.jl for details. In performing test particle tracing, we want to share the field information for all particles. This can be achieved in the ensemble problem with safetycopy=false.","category":"page"},{"location":"examples/advanced/demo_ensemble/","page":"Ensemble tracing","title":"Ensemble tracing","text":"using TestParticle\nusing OrdinaryDiffEq\nusing StaticArrays\nusing CairoMakie\n\n\"Set initial state for EnsembleProblem.\"\nfunction prob_func(prob, i, repeat)\n prob = @views remake(prob, u0=[prob.u0[1:3]..., i/3, 0.0, 0.0])\nend\n\n# Initialization\n\nB(x) = SA[0, 0, 1e-11]\nE(x) = SA[0, 0, 1e-13]\n\nx0 = [0.0, 0.0, 0.0] # initial position, [m]\nu0 = [1.0, 0.0, 0.0] # initial velocity, [m/s]\nstateinit = [x0..., u0...]\n\nparam = prepare(E, B, species=Electron)\ntspan = (0.0, 10.0)\n\ntrajectories = 3\n\n# Solve for the trajectories\n\nprob = ODEProblem(trace!, stateinit, tspan, param)\nensemble_prob = EnsembleProblem(prob; prob_func, safetycopy=false)\nsols = solve(ensemble_prob, Tsit5(), EnsembleThreads(); trajectories)\n\n# Visualization\n\nf = Figure(fontsize = 18)\nax = Axis3(f[1, 1],\n title = \"Electron trajectories\",\n xlabel = \"X\",\n ylabel = \"Y\",\n zlabel = \"Z\",\n aspect = :data,\n)\n\nfor i in eachindex(sols)\n lines!(ax, sols[i], idxs=(1,2,3), label=\"$i\")\n ##TODO: wait for https://github.com/MakieOrg/Makie.jl/issues/3623 to be fixed!\n ax.scene.plots[9+2*i-1].color = Makie.wong_colors()[i]\nend\n","category":"page"},{"location":"examples/advanced/demo_ensemble/","page":"Ensemble tracing","title":"Ensemble tracing","text":"(Image: )","category":"page"},{"location":"examples/advanced/demo_ensemble/","page":"Ensemble tracing","title":"Ensemble tracing","text":"We can also solve this problem with the native Boris pusher. Note that the Boris pusher requires a additional parameters: a fixed timestep, and an output save interval.","category":"page"},{"location":"examples/advanced/demo_ensemble/","page":"Ensemble tracing","title":"Ensemble tracing","text":"dt = 0.1\nsavestepinterval = 1\n\n# Solve for the trajectories\n\nprob = TraceProblem(stateinit, tspan, param; prob_func)\ntrajs = TestParticle.solve(prob; dt, trajectories, savestepinterval)\n\n# Visualization\n\nf = Figure(fontsize = 18)\nax = Axis3(f[1, 1],\n title = \"Electron trajectories\",\n xlabel = \"X\",\n ylabel = \"Y\",\n zlabel = \"Z\",\n aspect = :data,\n)\n\nfor i in eachindex(trajs)\n lines!(ax, trajs[i]; idxs=(1,2,3), label=\"$i\")\n ##TODO: wait for https://github.com/MakieOrg/Makie.jl/issues/3623 to be fixed!\n ax.scene.plots[9+2*i-1].color = Makie.wong_colors()[i]\nend\n","category":"page"},{"location":"examples/advanced/demo_ensemble/","page":"Ensemble tracing","title":"Ensemble tracing","text":"(Image: )","category":"page"},{"location":"examples/advanced/demo_ensemble/","page":"Ensemble tracing","title":"Ensemble tracing","text":"Note that by default linear interpolation is applied when plotting the trajectories from the Boris method.","category":"page"},{"location":"examples/advanced/demo_ensemble/","page":"Ensemble tracing","title":"Ensemble tracing","text":"","category":"page"},{"location":"examples/advanced/demo_ensemble/","page":"Ensemble tracing","title":"Ensemble tracing","text":"This page was generated using DemoCards.jl and Literate.jl.","category":"page"},{"location":"examples/advanced/demo_tokamak_profile/","page":"Tokamak profile","title":"Tokamak profile","text":"EditURL = \"/home/runner/work/TestParticle.jl/TestParticle.jl/docs/examples/advanced/demo_tokamak_profile.jl\"","category":"page"},{"location":"examples/advanced/demo_tokamak_profile/#demo_tokamak_profile","page":"Tokamak profile","title":"Tokamak profile","text":"","category":"section"},{"location":"examples/advanced/demo_tokamak_profile/","page":"Tokamak profile","title":"Tokamak profile","text":"(Image: Source code) (Image: compat) (Image: Author) (Image: Update time)","category":"page"},{"location":"examples/advanced/demo_tokamak_profile/","page":"Tokamak profile","title":"Tokamak profile","text":"This example shows how to trace protons in a stationary magnetic field that corresponds to an ITER-like Tokamak.","category":"page"},{"location":"examples/advanced/demo_tokamak_profile/","page":"Tokamak profile","title":"Tokamak profile","text":"using TestParticle\nusing TestParticle: getB_tokamak_profile\nusing OrdinaryDiffEq\nusing StaticArrays\nusing GeometryBasics\nusing CairoMakie\n\n# Parameters from ITER, see http://fusionwiki.ciemat.es/wiki/ITER\nconst R₀ = 6.2 # Major radius [m]\nconst Bζ0 = 5.3 # toroidal field on axis [T]\nconst a = 2.0 # Minor radius [m]\n\n# Variable must be a radius normalized by minor radius.\nfunction q_profile(nr::Float64)\n return nr^2 + 2*nr + 0.5\nend\n\nfunction B(xu)\n SVector{3}(getB_tokamak_profile(xu[1], xu[2], xu[3], q_profile, a, R₀, Bζ0))\nend\n\nfunction E(xu)\n SA[0.0, 0.0, 0.0]\nend\n\n\"Contruct the topology of Tokamak.\"\nfunction get_tokamak_topology()\n nθ = LinRange(0, 2π, 30)\n nζ = LinRange(0, 2π, 30)\n nx = [R₀*cos(ζ) + a*cos(θ)*cos(ζ) for θ in nθ, ζ in nζ]\n ny = [R₀*sin(ζ) + a*cos(θ)*sin(ζ) for θ in nθ, ζ in nζ]\n nz = [a*sin(θ) for θ in nθ, ζ in nζ]\n points = vec([Point3f(xv, yv, zv) for (xv, yv, zv) in zip(nx, ny, nz)])\n faces = decompose(QuadFace{GLIndex}, Tesselation(Rect(0, 0, 1, 1), size(nz)))\n\n tor_mesh = GeometryBasics.Mesh(points, faces)\nend","category":"page"},{"location":"examples/advanced/demo_tokamak_profile/","page":"Tokamak profile","title":"Tokamak profile","text":"Main.var\"##56965\".get_tokamak_topology","category":"page"},{"location":"examples/advanced/demo_tokamak_profile/","page":"Tokamak profile","title":"Tokamak profile","text":"Passing proton in a Tokamak","category":"page"},{"location":"examples/advanced/demo_tokamak_profile/","page":"Tokamak profile","title":"Tokamak profile","text":"# initial velocity for passing particle\nv₀ = [0.0, 2.15, 3.1] .* 1e6\n# initial position, [m]. where q≈2, (2, 1) flux surface.\nr₀ = [7.3622, 0.0, 0.0]\nstateinit = [r₀..., v₀...]\n\nparam = prepare(E, B; species=Proton)\ntspan = (0.0, 4e-5) # [s]\n\nprob = ODEProblem(trace!, stateinit, tspan, param)\nsol = solve(prob, Vern7(); dt=2e-11);\n\ntor_mesh = get_tokamak_topology()\n\nfig1 = Figure(fontsize=18)\nax = Axis3(fig1[1, 1],\n title = \"Passing Particle\",\n xlabel = \"x [m]\",\n ylabel = \"y [m]\",\n zlabel = \"z [m]\",\n aspect = :data,\n)\n\nlines!(ax, sol; idxs=(1,2,3))\n# Plot the surface of Tokamak\nwireframe!(fig1[1, 1], tor_mesh, color=(:blue, 0.1), linewidth=0.5, transparency=true)\n","category":"page"},{"location":"examples/advanced/demo_tokamak_profile/","page":"Tokamak profile","title":"Tokamak profile","text":"(Image: )","category":"page"},{"location":"examples/advanced/demo_tokamak_profile/","page":"Tokamak profile","title":"Tokamak profile","text":"Trapped proton in a Tokamak that shows the banana orbit","category":"page"},{"location":"examples/advanced/demo_tokamak_profile/","page":"Tokamak profile","title":"Tokamak profile","text":"# initial velocity for trapped particle\nv₀ = [0.0, 1.15, 5.1] .* 1e6\n# initial position, [m]. where q≈1, (1, 1) flux surface.\nr₀ = [6.6494, 0.0, 0.0]\nstateinit = [r₀..., v₀...]\n\nparam = prepare(E, B; species=Proton)\ntspan = (0.0, 4e-5)\n\nprob = ODEProblem(trace!, stateinit, tspan, param)\nsol = solve(prob, Vern7(); dt=1e-11)\n\nfig2 = Figure(fontsize=18)\nax = Axis3(fig2[1, 1],\n title = \"Trapped Particle\",\n xlabel = \"x [m]\",\n ylabel = \"y [m]\",\n zlabel = \"z [m]\",\n aspect = :data,\n)\n\nlines!(ax, sol; idxs=(1,2,3))\nwireframe!(fig2[1, 1], tor_mesh, color=(:blue, 0.1), linewidth=0.5, transparency=true)\n","category":"page"},{"location":"examples/advanced/demo_tokamak_profile/","page":"Tokamak profile","title":"Tokamak profile","text":"(Image: )","category":"page"},{"location":"examples/advanced/demo_tokamak_profile/","page":"Tokamak profile","title":"Tokamak profile","text":"The trajectory of the trapped particle is sometimes called the \"banana orbit\".","category":"page"},{"location":"examples/advanced/demo_tokamak_profile/","page":"Tokamak profile","title":"Tokamak profile","text":"","category":"page"},{"location":"examples/advanced/demo_tokamak_profile/","page":"Tokamak profile","title":"Tokamak profile","text":"This page was generated using DemoCards.jl and Literate.jl.","category":"page"},{"location":"","page":"Home","title":"Home","text":"CurrentModule = TestParticle","category":"page"},{"location":"#TestParticle.jl","page":"Home","title":"TestParticle.jl","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"TestParticle.jl is a test particle tracer in a static electromagnetic field.","category":"page"},{"location":"","page":"Home","title":"Home","text":"This package supports charged particle tracing in analytic/numerical relativistic/non-relativistic","category":"page"},{"location":"","page":"Home","title":"Home","text":"electric and magnetic field;\nbody force field.","category":"page"},{"location":"","page":"Home","title":"Home","text":"All tracing are performed in 3D, as is the nature for the fields. For a numerical field, the mesh is constructed with Meshes.jl, and the field is interpolated with the aid of Interpolations.jl. For an analytical field, the user is responsible for providing the function for calculating the field at a given spatial location. The actual tracing is done through DifferentialEquations.jl, thanks to the ODE system of the equations of motion.","category":"page"},{"location":"","page":"Home","title":"Home","text":"For all the tracing methods, we provide both an inplace version (with ! at the end of the function name) and a non-inplace version using StaticArrays. The non-inplace version requires the initial conditions to be static a static vector. Use them at your convenience.","category":"page"},{"location":"","page":"Home","title":"Home","text":"The single particle motions are the basics in understanding the test particle method. Check out Single-Particle Motions for more complete maths.","category":"page"},{"location":"#Installation","page":"Home","title":"Installation","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"julia> ]\npkg> add TestParticle","category":"page"},{"location":"#Usage","page":"Home","title":"Usage","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"It would be better to understand the basic workflow of DifferentialEquations.jl before digging into TestParticle.jl. All we are doing here can be concluded as contructing the ODE system from Newton's 2nd law and preparing the field/particle data. Check more in examples.","category":"page"},{"location":"","page":"Home","title":"Home","text":"Additionally, we have a native Boris solver with a similar interface as DifferentialEquations.jl. Check out the details in later sections.","category":"page"},{"location":"#Acknowledgement","page":"Home","title":"Acknowledgement","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"Nothing can be done such easily without the support of the Julia community. We appreciate all the contributions from developers around the world.","category":"page"},{"location":"tutorial/#Tutorial","page":"Tutorial","title":"Tutorial","text":"","category":"section"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"What makes plasmas particularly difficult to analyze is the fact that the densities fall in an intermediate range. Fluids like water are so dense that the motions of individual molecules do not have to be considered. Collisions dominate, and the simple equations of ordinary fluid dynamics suffice. At the other extreme in very low-density devices, only single-particle trajectories need to be considered; collective effects are often unimportant. Plasma behaves sometimes like fluids, and sometimes like a collection of individual particles. The first step in learning how to deal with this schizophrenic personality is to understand how single particles behave in electric and magnetic fields.","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"Here we assume that the EM fields are prescribed and not affected by the charged particles. References can be found in classic textbooks like Introduction to Plasma Physics and Controlled Fusion by F.F.Chen, and Fundamentals of Plasma Physics by Paul Bellan. For more complete notes corresponding to the derivation online, please check out Single-Particle Motions.","category":"page"},{"location":"tutorial/#Choice-of-numerical-algorithms","page":"Tutorial","title":"Choice of numerical algorithms","text":"","category":"section"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"By default DifferentialEquations.jl applies Tsit5 to an ODE problem. If only OrdinaryDiffEq.jl is imported, then we need to explicitly select a solver. However, not all solvers are guaranteed to work. For example, the demo case of electron tracing in the magnetic bottle with strong magnetic field is tested to work only with fixed timestep algorithms like Euler and the Adams-Bashforth family.","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"Currently we recommend Vern9 as a starting point for adaptive timestepping, with additional fine tuning by changing reltol if needed. You can also try out the native implementation of the Boris method in TestParticle.jl, with a constraint of using a fixed time step.","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"Take you some time to figure out which algorithm works for your problem!","category":"page"},{"location":"tutorial/#Unit-conversions","page":"Tutorial","title":"Unit conversions","text":"","category":"section"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"By default SI units are applied within the package. However, users can also define their own units by setting the particle mass and charge (2 constants) and providing the basic scales of magnetic field, length, time, or velocity (3 reference scales required; length, time and velocity are interchangable).","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"In the dimensionless natural unit system, we have the most number of ones which in turn gives the simplest form for computation. Check out the demos on unit conversions for more details.","category":"page"},{"location":"tutorial/#Tracing-backwards-in-time","page":"Tutorial","title":"Tracing backwards in time","text":"","category":"section"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"It is easy to trace a charged particle backwards in time. In a forward tracing problem, we set tspan = (t1, t2) where t1 < t2. In a backward tracing problem, we simply set t1 > t2, e.g. tspan = (0.0, -1.0). Note that tracing backwards in time is different from inversing the velocity because of the cross product in the Lorentz force. More specifically, the drift velocities will not change sign if one inverses velocity.","category":"page"},{"location":"tutorial/#Multiple-particles-tracing","page":"Tutorial","title":"Multiple particles tracing","text":"","category":"section"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"There are two ways to trace multiple particles simultaneously:","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"Extracting the solution in a loop with varying initial conditions. See the example demo_ensemble.\nConstructing the Ensemble Simulations. One example can be found here. However, note that by default the ensemble type replicates the parameters for each solution, which is very memory inefficient for tracing in a numeric field. We need to set safetycopy=false to make the field as a reference in the parameter of each trajectory.","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"The Boris pusher follows a similar interface for multithreading.","category":"page"},{"location":"tutorial/#Guiding-center-drifts","page":"Tutorial","title":"Guiding center drifts","text":"","category":"section"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"By solving the trajectories of particles, we can calculate the actual guiding center orbits by following the definition. This is supported directly via get_gc. In theoretical treatments, all kinds of drifts have been derived with analytical formulas listed below. With additional ODEs for solving the drifts, we can separate the different effects or check the deviation of actual orbits from the low order analytical formulas.","category":"page"},{"location":"tutorial/#Summary-of-guiding-center-drifts","page":"Tutorial","title":"Summary of guiding center drifts","text":"","category":"section"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"General force:","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"mathbfv_f = frac1qfracmathbfFtimesmathbfBB^2","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"Electric field:","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"mathbfv_E = fracmathbfEtimesmathbfBB^2","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"Gravitational field:","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"mathbfv_g = fracmqfracmathbfgtimesmathbfBB^2","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"Nonuniform electric field:","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"mathbfv_E = Big( 1+frac14r_L^2 nabla^2 Big)fracmathbfEtimesmathbfBB^2","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"Nonuniform magnetic field:","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"Grad-B:","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"beginaligned\nmathbfv_nabla B = pm frac12v_perp r_LfracmathbfBtimesnabla BB^2 \n=fracmv_perp^22qB^3mathbfBtimesnabla B\nendaligned","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"Curvature drift:","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"beginaligned\nmathbfv_c = fracmv_parallel^2qfracmathbfR_c timesmathbfBR_c^2 B^2 \n= fracmv_parallel^2qB^4mathbfBtimesleft mathbfBcdotnablamathbfB right \n= fracm v_parallel^2qB^3mathbfBtimesnabla Btext(current-free)\nendaligned","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"Curved vacuum field:","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"beginaligned\nmathbfv_c + mathbfv_nabla B = fracmqBig( v_parallel^2 + frac12v_perp^2 Big) fracmathbfR_c timesmathbfBR_c^2 B^2 \n= fracmqfracmathbfBtimesnabla BB^3Big( v_parallel^2 + frac12v_perp^2 Big)\nendaligned","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"Polarization drift:[2]","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"mathbfv_p = pm frac1omega_c BfracdmathbfEdt","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"[2]: In common test particle models, we assume static EM fields, so the polarization drift as well as adiabatic heating is not present. However, it is easily achieveable here in this package.","category":"page"},{"location":"tutorial/#Adiabatic-Invariants","page":"Tutorial","title":"Adiabatic Invariants","text":"","category":"section"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"See more thorough notes on the adiabatic invariants.","category":"page"},{"location":"tutorial/#Solution-Interpolations","page":"Tutorial","title":"Solution Interpolations","text":"","category":"section"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"All the tracing solutions come with an interpolation method to make them continuous. Depending on the order of the scheme, different orders of interpolations are applied.","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"Note that if the scheme comes with an \"lazy\" interpolation (e.g. Vern family), you can either output the full solution (default) and interpolate","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"sol = solve(prob, Vern9())","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"or turn off the lazy interpolation and select part of the solution","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"sol = solve(prob, Vern9(lazy=false), save_idxs=[1,2,3])","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"The interpolated solution would be wrong if lazy=true and save_idxs!=[1,2,3,4,5,6].","category":"page"},{"location":"tutorial/#Presentations","page":"Tutorial","title":"Presentations","text":"","category":"section"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"Please checkout:","category":"page"},{"location":"tutorial/","page":"Tutorial","title":"Tutorial","text":"TestParticle.jl: A New Tool for An Old Problem\n基于开源工具链的测试粒子模型","category":"page"},{"location":"examples/advanced/demo_tokamak_coil/","page":"Coil Tokamak","title":"Coil Tokamak","text":"EditURL = \"/home/runner/work/TestParticle.jl/TestParticle.jl/docs/examples/advanced/demo_tokamak_coil.jl\"","category":"page"},{"location":"examples/advanced/demo_tokamak_coil/#demo_tokamak_coil","page":"Coil Tokamak","title":"Coil Tokamak","text":"","category":"section"},{"location":"examples/advanced/demo_tokamak_coil/","page":"Coil Tokamak","title":"Coil Tokamak","text":"(Image: Source code) (Image: compat) (Image: Author) (Image: Update time)","category":"page"},{"location":"examples/advanced/demo_tokamak_coil/","page":"Coil Tokamak","title":"Coil Tokamak","text":"This example shows how to trace protons in a stationary magnetic field that corresponds to a Tokamak represented by a circle of coils. A excellent introduction video to Tokamak can be found here in Mandarin.","category":"page"},{"location":"examples/advanced/demo_tokamak_coil/","page":"Coil Tokamak","title":"Coil Tokamak","text":"using TestParticle\nusing TestParticle: getB_tokamak_coil\nusing OrdinaryDiffEq\nusing StaticArrays\nusing Statistics: mean\nusing Printf\nusing CairoMakie\n\n### Obtain field\n\n# Magnetic bottle parameters in SI units\nconst ICoil = 80. # current in the coil\nconst N = 15000 # number of windings\nconst IPlasma = 1e6 # current in the plasma\nconst a = 1.5 # radius of each coil\nconst b = 0.8 # radius of central region\n\nfunction getB(xu)\n SVector{3}(getB_tokamak_coil(xu[1], xu[2], xu[3], a, b, ICoil*N, IPlasma))\nend\n\nfunction getE(xu)\n SA[0.0, 0.0, 0.0]\nend\n\n### Initialize particles\nm = TestParticle.mᵢ\nq = TestParticle.qᵢ\nc = TestParticle.c\n\n# initial velocity, [m/s]\nv₀ = [-0.1, -0.15, 0.0] .* c\n# initial position, [m]\nr₀ = [2.3, 0.0, 0.0]\nstateinit = [r₀..., v₀...]\n\nparam = prepare(getE, getB; species=Proton)\ntspan = (0.0, 1e-6)\n\nprob = ODEProblem(trace!, stateinit, tspan, param)\n\n@printf \"Speed = %6.4f %s\\n\" √(v₀[1]^2+v₀[2]^2+v₀[3]^2)/c*100 \"% speed of light\"\n@printf \"Energy = %6.4f MeV\\n\" (1/√(1-(v₀[1]/c)^2-(v₀[2]/c)^2-(v₀[3]/c)^2)-1)*m*c^2/abs(q)/1e6\n\n# Default Tsit5() alone does not work in this case! You need to set a maximum\n# timestep to maintain stability, or choose a different algorithm as well.\n# The sample figure in the gallery is generated with AB3() and dt=2e-11.\nsol = solve(prob, Tsit5(); dt=2e-11, save_idxs=[1,2,3])\n\n### Visualization\n\nf = Figure(fontsize=18)\nax = Axis3(f[1, 1],\n title = \"Particle trajectory in Tokamak\",\n xlabel = \"x [m]\",\n ylabel = \"y [m]\",\n zlabel = \"z [m]\",\n aspect = :data,\n)\n\nplot!(sol, label=\"proton\")\n\n# Plot coils\nθ = range(0, 2π, length=201)\ny = a * cos.(θ)\nz = a * sin.(θ)\nfor i in 0:17\n ϕ = i*π/9\n plot!(y*sin(ϕ) .+ (a+b)*sin(ϕ), y*cos(ϕ) .+ (a+b)*cos(ϕ), z, color=(:red, 0.3))\nend\n\n# Plot Tokamak\nu = range(0, 2π, length=100)\nv = range(0, 2π, length=100)\n\nU = [y for _ in u, y in v]\nV = [x for x in u, _ in v]\n\nX = @. (a + b + (a - 0.05)*cos(U)) * cos(V)\nY = @. (a + b + (a - 0.05)*cos(U)) * sin(V)\nZ = @. (a - 0.05) * sin(U)\n\nwireframe!(ax, X, Y, Z, color=(:blue, 0.1), linewidth=0.5, transparency=true)\n","category":"page"},{"location":"examples/advanced/demo_tokamak_coil/","page":"Coil Tokamak","title":"Coil Tokamak","text":"(Image: )","category":"page"},{"location":"examples/advanced/demo_tokamak_coil/","page":"Coil Tokamak","title":"Coil Tokamak","text":"","category":"page"},{"location":"examples/advanced/demo_tokamak_coil/","page":"Coil Tokamak","title":"Coil Tokamak","text":"This page was generated using DemoCards.jl and Literate.jl.","category":"page"}] } diff --git a/dev/tutorial/index.html b/dev/tutorial/index.html index b80e4a8d..df3f5f3e 100644 --- a/dev/tutorial/index.html +++ b/dev/tutorial/index.html @@ -9,4 +9,4 @@ \end{aligned}\]

Curved vacuum field:

\[\begin{aligned} \mathbf{v}_c + \mathbf{v}_{\nabla B} &= \frac{m}{q}\Big( v_\parallel^2 + \frac{1}{2}v_\perp^2 \Big) \frac{\mathbf{R}_c \times\mathbf{B}}{R_c^2 B^2} \\ &= \frac{m}{q}\frac{\mathbf{B}\times\nabla B}{B^3}\Big( v_\parallel^2 + \frac{1}{2}v_\perp^2 \Big) -\end{aligned}\]

Polarization drift:[2]

\[\mathbf{v}_p = \pm \frac{1}{\omega_c B}\frac{d\mathbf{E}}{dt}\]

Adiabatic Invariants

See more thorough notes on the adiabatic invariants.

Solution Interpolations

All the tracing solutions come with an interpolation method to make them continuous. Depending on the order of the scheme, different orders of interpolations are applied.

Note that if the scheme comes with an "lazy" interpolation (e.g. Vern family), you can either output the full solution (default) and interpolate

sol = solve(prob, Vern9())

or turn off the lazy interpolation and select part of the solution

sol = solve(prob, Vern9(lazy=false), save_idxs=[1,2,3])

The interpolated solution would be wrong if lazy=true and save_idxs!=[1,2,3,4,5,6].

Presentations

Please checkout:

+\end{aligned}\]

Polarization drift:[2]

\[\mathbf{v}_p = \pm \frac{1}{\omega_c B}\frac{d\mathbf{E}}{dt}\]

Adiabatic Invariants

See more thorough notes on the adiabatic invariants.

Solution Interpolations

All the tracing solutions come with an interpolation method to make them continuous. Depending on the order of the scheme, different orders of interpolations are applied.

Note that if the scheme comes with an "lazy" interpolation (e.g. Vern family), you can either output the full solution (default) and interpolate

sol = solve(prob, Vern9())

or turn off the lazy interpolation and select part of the solution

sol = solve(prob, Vern9(lazy=false), save_idxs=[1,2,3])

The interpolated solution would be wrong if lazy=true and save_idxs!=[1,2,3,4,5,6].

Presentations

Please checkout: