Cavity Class
The public Cavity class is an OpticElement wrapper around CavityAtom, which inherits from Element. It represents a standing-wave RF cavity with accelerating voltage, RF phase, and optional upstream and downstream coupler kicks.
In this page we focus on the first-order matrix and on how the cavity-specific tracking path is implemented in OCELOT.
Cavity in the Element Architecture
Cavity is an edge-aware RF family:
- public wrapper:
Cavity - atom:
CavityAtom - always-available optics path: first-order
TransferMap - default active tracking transformation:
CavityTM supported_tms = {CavityTM}has_edge = True, so the element is represented byENTRANCE -> MAIN -> EXIT
For Cavity, the entrance and exit maps are not geometric pole-face edges as in a bend. They are used for optional coupler-kick matrices and offsets. If coupler coefficients are zero, these edge maps reduce to identity maps.
Class Definition Snippet
from ocelot.cpbd.elements import Cavity
cav = Cavity(l=1.0, v=0.02, phi=0.0, freq=1.3e9)
# full parameter list:
# Cavity(l=0.0, v=0.0, phi=0.0, freq=0.0,
# vx_up=0, vy_up=0, vxx_up=0, vxy_up=0,
# vx_down=0, vy_down=0, vxx_down=0, vxy_down=0,
# eid=None, tm=None)
The wrapper also provides:
cav.remove_coupler_kick()
which resets all upstream and downstream coupler coefficients to zero.
Parameters
l(float): cavity length [m]v(float): total RF voltage [GV]phi(float): RF phase [deg]freq(float): RF frequency [Hz]vx_up,vy_up(complexorfloat): zero-order upstream coupler-kick coefficientsvxx_up,vxy_up(complexorfloat): first-order upstream coupler coefficientsvx_down,vy_down(complexorfloat): zero-order downstream coupler-kick coefficientsvxx_down,vxy_down(complexorfloat): first-order downstream coupler coefficientseid(str, optional): element identifier
With the current OCELOT convention,
phi = 0gives maximum accelerating voltagephi = 90^\circis the zero-crossing case
because the reference energy gain is computed as
First-Order Main Matrix
The cavity body matrix is computed in CavityAtom._R_main_matrix(...). For a slice of length z, OCELOT uses the slice voltage
and then builds a first-order matrix of the form
The transverse blocks are identical in x and y, which reflects the symmetric RF focusing model used here.
OCELOT defines
with
and
Then the transverse coefficients are
For the longitudinal block, OCELOT computes
with
and
In the general RF case, the code uses
At zero crossing, where the code detects both vanishing energy gain and , it switches to the analytic limit
The longitudinal coefficients are implemented in ocelot.cpbd.rf_maps and shared by Cavity and TWCavity.
If v = 0, the code falls back to the drift-like uni_matrix(...) result.
Coupler Entrance and Exit Maps
If coupler coefficients are present, CavityAtom builds additional entrance and exit maps. The first-order coupler matrix is
with
and the corresponding additive kick vector is
The entrance map uses the upstream coefficients and the exit map uses the downstream coefficients.
How OCELOT Implements It
The cavity has two related paths:
Cavity
-> CavityAtom
-> create_first_order_*_params(...)
-> FirstOrderParams
-> TransferMap # first_order_tms, R(), Twiss
Cavity
-> CavityAtom
-> create_cavity_tm_*_params(...)
-> CavityParams
-> CavityTM # active tracking path
First-order optics path
For R() and linear optics, CavityAtom provides:
create_first_order_main_params(...)create_first_order_entrance_params(...)create_first_order_exit_params(...)
These return FirstOrderParams, so the wrapper can keep the usual TransferMap-based optics path.
Active cavity tracking path
For actual tracking, the wrapper uses CavityTM. The atom therefore also implements:
create_cavity_tm_main_params(...)create_cavity_tm_entrance_params(...)create_cavity_tm_exit_params(...)
These methods package the same linear R, B, and tilt data together with v, freq, and phi into CavityParams.
CavityTM then:
- uses the linear
R/Bmap fromCavityParams - applies reference-energy change through
create_delta_e(...) - adds the cavity-specific longitudinal RF update in
map4cav(...)for theMAINmap
For ENTRANCE and EXIT, CavityTM behaves like a linear first-order map, which is exactly what is needed for the coupler-kick pieces.
Travelling-wave structures
Use TWCavity for a uniform travelling-wave accelerating structure:
import ocelot as ocl
cavity = ocl.TWCavity(l=1.0, v=0.02, phi=25.0, freq=1.3e9)
lattice = ocl.MagneticLattice([cavity])
seed = ocl.Twiss(E=0.15, beta_x=1.2, beta_y=2.1)
optics = ocl.twiss(lattice, seed)
The voltage is the total structure voltage in GV; the example uses 20 MV.
Energy is total electron energy in GeV. The reference energy gain is
v*cos(phi), with phi in degrees.
TWCavity uses entrance and exit focusing lenses and a body with adiabatic
momentum damping, without standing-wave body focusing or coupler kicks.
Its active tracking method is TWCavityTM; first-order maps remain available
for Twiss propagation. The transverse damping is the entrance-to-exit momentum
ratio. Twiss propagation removes the damping measured from the transverse map,
so its beta and alpha functions remain consistent with particle tracking even
when momentum and energy ratios differ appreciably.
The structure uses a uniform gradient and fixed reference phase, without
phase slippage or a transverse RF force in the body. The shared longitudinal
RF approximation retains the standing-wave model's limitations, including
finite-beta matrix/tracking differences and sensitivity to slicing at low
energy. The improved transverse momentum map does not remove those limits.
To slice a cavity during track, attach a physics process across it;
changing Navigator.unit_step alone does not subdivide elements.
Example of Use
For an element with has_edge=True, R() returns the full sequence [entrance, main, exit]:
import numpy as np
from ocelot.cpbd.elements import Cavity
cav = Cavity(l=1.0, v=0.02, phi=0.0, freq=1.3e9)
np.set_printoptions(precision=4, suppress=True)
print(cav.R(energy=0.15)) # [GeV]
[array([[1., 0., 0., 0., 0., 0.],
[0., 1., 0., 0., 0., 0.],
[0., 0., 1., 0., 0., 0.],
[0., 0., 0., 1., 0., 0.],
[0., 0., 0., 0., 1., 0.],
[0., 0., 0., 0., 0., 1.]]),
array([[ 0.9365, 0.9384, 0. , 0. , 0. , 0. ],
[-0.0055, 0.9367, 0. , 0. , 0. , 0. ],
[ 0. , 0. , 0.9365, 0.9384, 0. , 0. ],
[ 0. , 0. , -0.0055, 0.9367, 0. , 0. ],
[ 0. , 0. , 0. , 0. , 1. , -0. ],
[ 0. , 0. , 0. , 0. , 0. , 0.8824]]),
array([[1., 0., 0., 0., 0., 0.],
[0., 1., 0., 0., 0., 0.],
[0., 0., 1., 0., 0., 0.],
[0., 0., 0., 1., 0., 0.],
[0., 0., 0., 0., 1., 0.],
[0., 0., 0., 0., 0., 1.]])]
In this example the entrance and exit maps are identity matrices because no coupler coefficients were provided.