Developer's Guide
The design of BeamTracking.jl
is built with modularity, high performance, differentiability, and polymorphism as key principles. Adding a new type of tracking method or lattice element should be simple.
The entire package is centered around one single track!
function, which has the following format:
track!(beam::Beam, element, work=work)
Here, beam
is a Beam
struct, which is described in detail below. element
is some element which to track the beam through, and work
is an optional tuple of the minimal number of temporaries needed for use inside the tracking function (for some elements, it is an empty tuple).
After calling track!
, the beam
struct is mutated to contain the particle phase space coordinates (and possiblly spin transport quaternions) after propagation through the element
. With this track!
function, all one needs to do is define their own custom element
type, and then when looping through a vector of elements, Julia's multiple dispatch will take care of each particular element.
For example, using the Linear.Drift
and Linear.Quadrupole
elements:
d = Linear.Drift(L=0.2)
qf = Linear.Quadrupole(Bn1=-18.5, L=0.5)
qd = Linear.Quadrupole(Bn1=18.5, L=0.5)
fodo = (qf, d, qd, d)
beam = Beam(x=1e-3, px=1e-4, pz=1e-4, beta_gamma_ref=35000.) # Creates a Beam with one particle
for ele in fodo
track!(beam, ele)
end
The Beam
Struct
The Beam
struct contains the following fields:
species::Species
– ASpecies
type storing the beam species (e.g. electron)beta_gamma_ref::Float64
– A reference Lorentz $\beta\gamma$ for normalizing the transverse momenta tov::T
– All particles' 6D phase space coordinates stored in a structure-of-arrays (SoA) memory layoutq::U
– If spin tracking, then all particles' spin transport quaternions (using the quaternion type defined inReferenceFrameRotations.jl
) stored in a structure-of-arrays layout. Else,nothing
If you are unfamiliar with structure-of-arrays (SoA) and array-of-structures (AoS), you should read this Wikipedia article.
This package extensively uses the StructArrays.jl
, which features efficient and highly convenient implementation of structs for an SoA memory layout. A basic understanding of this package would be useful too.
In BeamTracking.jl
, we define the Coord
type which is a simple static vector of the 6D phase space coordinates:
# Static phase space coordinate vector
Base.@kwdef struct Coord{T} <: FieldVector{6, T}
x::T = 0.0
px::T = 0.0
y::T = 0.0
py::T = 0.0
z::T = 0.0
pz::T = 0.0
end
Note that this is static and immutable, so you cannot change a field in Coord
. Also note that a Vector{Coord{T}}
is an AoS memory layout, and NOT SoA! StructArrays.jl
makes it easy for us to use this type in a SoA layout, by simple using the StructArray
type. For example,
N_particles = 1000
x = rand(N_particles)
px = rand(N_particles)
y = rand(N_particles)
py = rand(N_particles)
z = rand(N_particles)
pz = rand(N_particles)
v = StructArray{Coord{Float64}}((x, px, y, py, z, pz))
v.x # accesses x array
v.px # accesses px array
v[1] # This goes from SoA to a single Coord struct representing the first Coord!
In the above example, v
is what is stored in the Beam
struct. The choice of SoA was made after careful speed benchmarks and the desire to have a mutable interface (track!
instead of track.
for a beam).
When there spin tracking, q
is a StructArray{<:Quaternion}
Rules for track!
Implementations
Vectorized operations should be used. The function should be type-stable and, when pre-allocating the necessary work
, have zero allocations included when tracking a single non-parametric GTPSA map or tracking a beam of particles as immutable numbers (Float64
or Dual
numbers for example). For parametric GTPSA maps (e.g. when a quadrupole strength is included as a parameter in the GTPSA), the allocation restriction is loosened in order to maintain readable code. Tests for this are included in the test_matrix
function in runtests.jl
Compatibility with GTPSA.jl
GTPSA.jl
is a package which is used extensively in the SciBmad
ecosystem for Taylor map tracking and normal form calculations.
Adding a dependency to a submodule
If you would like to add a dependency to the project (e.g. OrdinaryDiffEq.jl
), you should:
- In the
~/.julia/dev/BeamTracking.jl
directory, startjulia
- Enter package-mode using
]
, and then typeactivate .
. This activates theProject.toml
in the current directory you're in - In package-mode, type
add OrdinaryDiffEq
. This will add the package as a dependency to theProject.toml
. - In the main module
src/BeamTracking.jl
, add ausing OrdinaryDiffEq
to the top ofBeamTracking.jl
, and in your particularly module defined below add ausing ..OrdinaryDiffEq
which basically says, go one module level up and getOrdinaryDiffEq
.