Skip to content

Commit 30266a2

Browse files
Merge branch 'PyORBIT-Collaboration:main' into tune
2 parents 3d47f39 + fe0aa5e commit 30266a2

File tree

4 files changed

+463
-120
lines changed

4 files changed

+463
-120
lines changed
Lines changed: 329 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,329 @@
1+
#--------------------------------------------------------------------
2+
# This script tests the functionality of AxisFieldRF_Gap class.
3+
# It also performs benchmark this calss with the zero-length BaseRfGap() model.
4+
# There are cases:
5+
# =============================================================
6+
# Case 1: Cavity phase scan assuming the phase at z=0 position.
7+
# Case 2: Cavity phase scan assuming the phase at the entrance
8+
# of the cavity
9+
# Case 3: Cavity phase scan with non-empty bunch assuming the phase
10+
# at the entrance of the cavity
11+
# Case 4: Thin (zero length) RF Gap and Axis Field Gap comparison
12+
#---------------------------------------------------------------------
13+
14+
import math
15+
import random
16+
import time
17+
import sys
18+
19+
# from linac import the C++ RF gap classes
20+
from orbit.core.linac import BaseRfGap, MatrixRfGap, RfGapTTF
21+
22+
from orbit.core.bunch import Bunch
23+
from orbit.py_linac.lattice import BaseRF_Gap, AxisFieldRF_Gap, RF_Cavity
24+
from orbit.py_linac.lattice import Drift
25+
26+
from orbit.core.orbit_utils import Function, SplineCH, GaussLegendreIntegrator, Polynomial
27+
from orbit.utils.fitting import PolynomialFit
28+
29+
from orbit.utils import phaseNearTargetPhase
30+
from orbit.utils import phaseNearTargetPhaseDeg
31+
32+
#--------------------------------------------
33+
# Auxiliary Functions
34+
#--------------------------------------------
35+
36+
def getRectangularRfAxisField(z_min,z_max):
37+
"""
38+
The RF axial field step function normalized to the integral value of 1.
39+
The center position will be assumed at z=0.
40+
"""
41+
value = 1./(z_max - z_min)
42+
npoints = 10
43+
z_step = (z_max - z_min)/(npoints - 1)
44+
func = Function()
45+
for ind in range(npoints):
46+
z = z_min + z_step*ind
47+
func.add(z,value)
48+
return func
49+
50+
def transitTimeFactor(bunch,cav_frequency,length):
51+
"""
52+
Returns the TTF parameter for the thin RF Gap
53+
with a rectangular axis field.
54+
beta - v/c -relativistic factor
55+
w = cav_frequency [Hz]
56+
L2 = length/2 [m]
57+
lambda = beta*c/w
58+
TTF = lambda/L2*sin((L2/lambda)
59+
"""
60+
L2 = length/2
61+
beta = bunch.getSyncParticle().beta()
62+
w = 2*math.pi*cav_frequency
63+
lmbd = beta*2.997924e+8/w
64+
TTF = (lmbd/L2)*math.sin(L2/lmbd)
65+
return TTF
66+
#--------------------------------------------
67+
# Script start
68+
#--------------------------------------------
69+
70+
#---- The RF axial field step function normalized to the integral value of 1.
71+
z_min = -0.1
72+
z_max = 0.1
73+
axis_field_func = getRectangularRfAxisField(z_min,z_max)
74+
75+
#---- Integral E*L= 20 MeV , here E0L parameter is in GeV
76+
E0L = 0.020
77+
#---- The RF gap object representing one RF gap
78+
accNode = BaseRF_Gap("BaseRfGap")
79+
three_point_gap = AxisFieldRF_Gap(accNode)
80+
three_point_gap.setAsFirstRFGap(True)
81+
three_point_gap.setAxisFieldFunction(axis_field_func, z_step = 0.01)
82+
three_point_gap.setParam("E0L", E0L)
83+
#---- mode = 0 - phase as it is, mode = 1 shift phase by +PI
84+
three_point_gap.addParam("mode", 1)
85+
86+
#---- The RF cavity. Here it has only one RF gap
87+
cav_amp = 1.0
88+
cav_frequency = 704.42e6
89+
cav = RF_Cavity("AxisFieldCavity")
90+
cav.setAmp(cav_amp)
91+
cav.setFrequency(cav_frequency)
92+
cav.setPosition((z_min+z_max)/2.)
93+
cav.addRF_GapNode(three_point_gap)
94+
95+
#---- At the entrance of ESS spoke.
96+
#---- Kinetic energy and mass in GeV
97+
mass = 0.93827208943
98+
Ekin_init = 0.6201
99+
bunch_init = Bunch()
100+
bunch_init.mass(mass)
101+
bunch_init.charge(1.0)
102+
bunch_init.getSyncParticle().kinEnergy(Ekin_init)
103+
104+
#---- Parameters of the cavity phase scans.
105+
#---- Phases are in radians.
106+
Nph = 72
107+
phase_step = 2*math.pi/Nph
108+
109+
#--------------------------------------------------------------------
110+
# Case 1: Cavity phase scan assuming the phase at z=0 position.
111+
# We have several types of parameters:
112+
# cav_phase - the input parameter. It is a phase at
113+
# the center of the gap
114+
# cav_1st_gap_entr_phase0 - the phase at the entrance of the 1-st gap
115+
# (we have only one gap) for the cav_phase = 0.
116+
# cav_1st_gap_entr_phase - the phase at the entrance of the 1-st gap
117+
# for the defined cav_phase
118+
# gap_phase - the calculated phase in the center of the RF gap after fitting
119+
# process to get the cav_phase (default accuracy is 0.001 deg)
120+
# delta_phase = cav_1st_gap_entr_phase - cav_phase - cav_1st_gap_entr_phase0
121+
# this phase will be 0 at cav_phase = 0, and it characterizes
122+
# how wrong we are from the realistic phase scan when we
123+
# we define cavity phase at the entrance of the cavity
124+
# delta_eKin_out - the bunch energy gain after acceleration
125+
#--------------------------------------------------------------------
126+
three_point_gap.getRF_Cavity().setPhase(0.)
127+
bunch = Bunch()
128+
bunch_init.copyEmptyBunchTo(bunch)
129+
three_point_gap.trackDesignBunch(bunch)
130+
cav_1st_gap_entr_phase0 = three_point_gap.getRF_Cavity().getFirstGapEtnrancePhase()
131+
132+
print ("=============================================================")
133+
print ("Case 1: Cavity phase scan assuming the phase at z=0 position.")
134+
print ("=============================================================")
135+
136+
st = "CavPhase[deg] CavPhaseErr[deg] DeltaPhase[deg] 1stGapPhase[deg] DeltaE[MeV] "
137+
print (st)
138+
139+
for ind in range(Nph+1):
140+
cav_phase = ind*phase_step
141+
three_point_gap.getRF_Cavity().setPhase(cav_phase)
142+
143+
bunch = Bunch()
144+
bunch_init.copyEmptyBunchTo(bunch)
145+
three_point_gap.trackDesignBunch(bunch)
146+
147+
cav_1st_gap_entr_phase = phaseNearTargetPhase(three_point_gap.getRF_Cavity().getFirstGapEtnrancePhase(),0.)
148+
delta_phase = phaseNearTargetPhase(cav_1st_gap_entr_phase - cav_phase - cav_1st_gap_entr_phase0,0.)
149+
gap_phase = phaseNearTargetPhase(three_point_gap.getGapPhase(),cav_phase)
150+
delta_eKin_out = bunch.getSyncParticle().kinEnergy() - Ekin_init
151+
st = " %+8.2f "%(cav_phase*180./math.pi)
152+
st += " %+9.6f "%((gap_phase-cav_phase)*180./math.pi)
153+
st += " %+8.4f "%(delta_phase*180./math.pi)
154+
st += " %+8.2f "%(cav_1st_gap_entr_phase*180./math.pi)
155+
st += " %+10.6f "%(delta_eKin_out*1000.)
156+
print (st)
157+
158+
159+
#--------------------------------------------------------------------
160+
# Case 2: Cavity phase scan assuming the phase at the entrance
161+
# of the cavity
162+
# We have several types of parameters:
163+
# cav_phase - the input parameter. It is a phase at
164+
# the center of the gap
165+
# cav_1st_gap_entr_phase0 - the phase at the entrance of the 1-st gap
166+
# (we have only one gap) for the cav_phase = 0.
167+
# cav_1st_gap_entr_phase - the phase at the entrance of the 1-st gap
168+
# for the defined cav_phase
169+
# gap_phase - the calculated phase in the center of the RF gap after fitting
170+
# process to get the cav_phase (default accuracy is 0.001 deg)
171+
# delta_phase = cav_1st_gap_entr_phase - cav_phase - cav_1st_gap_entr_phase0
172+
# this phase will be 0 at cav_phase = 0, and it characterizes
173+
# how wrong we are from the realistic phase scan when we
174+
# we define cavity phase at the entrance of the cavity
175+
# delta_eKin_out - the bunch energy gain after acceleration
176+
#--------------------------------------------------------------------
177+
178+
#---- Set the cavity property
179+
three_point_gap.getRF_Cavity().setUsePhaseAtEntrance(True)
180+
181+
three_point_gap.getRF_Cavity().setPhase(0.)
182+
bunch = Bunch()
183+
bunch_init.copyEmptyBunchTo(bunch)
184+
three_point_gap.trackDesignBunch(bunch)
185+
cav_1st_gap_entr_phase0 = three_point_gap.getRF_Cavity().getFirstGapEtnrancePhase()
186+
187+
print ("=============================================================")
188+
print ("Case 2: Cavity phase scan assuming the phase at the entrance.")
189+
print ("=============================================================")
190+
191+
st = "CavPhase[deg] CavPhaseErr[deg] DeltaPhase[deg] 1stGapPhase[deg] DeltaE[MeV] "
192+
print (st)
193+
194+
for ind in range(Nph+1):
195+
cav_phase = ind*phase_step
196+
three_point_gap.getRF_Cavity().setPhase(cav_phase)
197+
198+
bunch = Bunch()
199+
bunch_init.copyEmptyBunchTo(bunch)
200+
three_point_gap.trackDesignBunch(bunch)
201+
202+
cav_1st_gap_entr_phase = phaseNearTargetPhase(three_point_gap.getRF_Cavity().getFirstGapEtnrancePhase(),0.)
203+
delta_phase = phaseNearTargetPhase(cav_1st_gap_entr_phase - cav_phase - cav_1st_gap_entr_phase0,0.)
204+
gap_phase = phaseNearTargetPhase(three_point_gap.getGapPhase(),cav_phase)
205+
delta_eKin_out = bunch.getSyncParticle().kinEnergy() - Ekin_init
206+
st = " %+8.2f "%(cav_phase*180./math.pi)
207+
st += " %+8.4f "%((gap_phase-cav_phase)*180./math.pi)
208+
st += " %+8.2f "%(delta_phase*180./math.pi)
209+
st += " %+8.2f "%(cav_1st_gap_entr_phase*180./math.pi)
210+
st += " %+10.6f "%(delta_eKin_out*1000.)
211+
print (st)
212+
213+
#--------------------------------------------------------------------
214+
# Case 3: Cavity phase scan with non-empty bunch assuming the phase
215+
# at the entrance of the cavity.
216+
# We have several types of parameters:
217+
# cav_phase - the input parameter. It is a phase at
218+
# the center of the gap
219+
# delta_eKin_out - the bunch energy gain after acceleration
220+
# (x,xp,y,yp,z,dE) - 6D coordinates of the particle
221+
#--------------------------------------------------------------------
222+
223+
#---- Set the cavity property
224+
three_point_gap.getRF_Cavity().setUsePhaseAtEntrance(True)
225+
226+
three_point_gap.getRF_Cavity().setPhase(0.)
227+
bunch = Bunch()
228+
bunch_init.copyEmptyBunchTo(bunch)
229+
three_point_gap.trackDesignBunch(bunch)
230+
231+
print ("=============================================================")
232+
print ("Case 3: Cavity phase scan with one particle in the bunch.")
233+
print ("=============================================================")
234+
235+
st = "CavPhase[deg] DeltaE[MeV] x[mm] xp[mrad] y[mm] yp[mrad] z[mm] dE[MeV] "
236+
print (st)
237+
238+
for ind in range(Nph+1):
239+
cav_phase = ind*phase_step
240+
three_point_gap.getRF_Cavity().setPhase(cav_phase)
241+
242+
bunch = Bunch()
243+
bunch_init.copyBunchTo(bunch)
244+
bunch.addParticle(0.01,0.,0.01,0.,0.,0.)
245+
246+
three_point_gap.trackBunch(bunch)
247+
248+
delta_eKin_out = bunch.getSyncParticle().kinEnergy() - Ekin_init
249+
st = " %+8.2f %+8.4f "%(cav_phase*180./math.pi,delta_eKin_out*1000.)
250+
st += " %8.3f %+8.3f "%(bunch.x(0)*1000.,bunch.xp(0)*1000.)
251+
st += " %8.3f %+8.3f "%(bunch.y(0)*1000.,bunch.yp(0)*1000.)
252+
st += " %8.3f %+8.3f "%(bunch.z(0)*1000.,bunch.dE(0)*1000.)
253+
print (st)
254+
255+
print ("====================================================================")
256+
print ("Case 4: Thin (zero length) RF Gap and Axis Field Gap comparison")
257+
print ("====================================================================")
258+
TTF = transitTimeFactor(bunch_init,cav_frequency,z_max-z_min)
259+
print ("Zero-length RF Gap: Transit Time Factor TTF=",TTF)
260+
261+
base_rf_gap = BaseRF_Gap("BaseRfGap")
262+
base_rf_gap.setParam("E0TL", E0L*TTF)
263+
#---- setting RF gap tracker
264+
base_rf_gap.setCppGapModel(BaseRfGap())
265+
266+
#---- The RF cavity for zero-length RF gap. Here it has only one RF gap
267+
cav_amp = 1.0
268+
cav_frequency = 704.42e6
269+
cav_base = RF_Cavity("BaseGapCavity")
270+
cav_base.setAmp(cav_amp)
271+
cav_base.setFrequency(cav_frequency)
272+
cav_base.setPosition((z_min+z_max)/2.)
273+
cav_base.addRF_GapNode(base_rf_gap)
274+
275+
#---- Tracking design for both types of RF gaps
276+
base_rf_gap.getRF_Cavity().setPhase(0.)
277+
bunch = Bunch()
278+
bunch_init.copyEmptyBunchTo(bunch)
279+
base_rf_gap.trackDesignBunch(bunch)
280+
281+
three_point_gap.getRF_Cavity().setPhase(0.)
282+
bunch = Bunch()
283+
bunch_init.copyEmptyBunchTo(bunch)
284+
three_point_gap.trackDesignBunch(bunch)
285+
286+
#---- drifts before and after zero-length RF gap
287+
drift_before = Drift("drift_before")
288+
drift_before.setLength((z_max-z_min)/2)
289+
drift_after = Drift("drift_after")
290+
drift_after.setLength((z_max-z_min)/2)
291+
292+
print ("==== 0 - for zero-length and 1 - for axis field RF gap ====")
293+
st = "CavPhase[deg] deltaE_0[MeV] deltaE_1[MeV] delta_0-1[MeV] delta_phase_0-1[deg]"
294+
print (st)
295+
296+
for ind in range(Nph+1):
297+
cav_phase = ind*phase_step
298+
base_rf_gap.getRF_Cavity().setPhase(cav_phase)
299+
three_point_gap.getRF_Cavity().setPhase(cav_phase)
300+
301+
bunch = Bunch()
302+
bunch_init.copyBunchTo(bunch)
303+
drift_before.trackBunch(bunch)
304+
base_rf_gap.trackBunch(bunch)
305+
drift_after.trackBunch(bunch)
306+
delta_eKin_out_0 = bunch.getSyncParticle().kinEnergy() - Ekin_init
307+
exit_phase0 = (bunch.getSyncParticle().time()*cav_frequency)*180./math.pi
308+
exit_phase0 = phaseNearTargetPhaseDeg(exit_phase0,0.)
309+
310+
bunch = Bunch()
311+
bunch_init.copyBunchTo(bunch)
312+
three_point_gap.trackBunch(bunch)
313+
delta_eKin_out_1 = bunch.getSyncParticle().kinEnergy() - Ekin_init
314+
exit_phase1 = (bunch.getSyncParticle().time()*cav_frequency)*180./math.pi
315+
exit_phase1 = phaseNearTargetPhaseDeg(exit_phase1,exit_phase0)
316+
317+
delta_phase = exit_phase0 - exit_phase1
318+
319+
st = " %+8.2f "%(cav_phase*180./math.pi)
320+
st += " %+8.4f "%(delta_eKin_out_0*1000.)
321+
st += " %+8.4f "%(delta_eKin_out_1*1000.)
322+
st += " %+8.4f "%((delta_eKin_out_0 - delta_eKin_out_1)*1000.)
323+
st += " %+8.4f "%delta_phase
324+
325+
print (st)
326+
327+
328+
print ("Stop.")
329+
sys.exit(0)

py/orbit/py_linac/lattice/LinacAccLatticeLib.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,14 @@ def __init__(self, name="none"):
375375
self.addParam("designArrivalTime", 0.0)
376376
self.addParam("isDesignSetUp", False)
377377
self.addParam("pos", 0.0)
378+
#---- This parameter is only for RF gap types with
379+
#---- the continuous RF fields on the axis of the cavity.
380+
#---- If usePhaseAtEntrance = False we will use the phase at the center
381+
#---- of RF gap as for a standard cavity representations as a set
382+
#---- of zero-length RF gaps.
383+
#---- If usePhaseAtEntrance = False we use the phase at the entrance
384+
#---- of the cavity.
385+
self.usePhaseAtEntrance = False
378386

379387
def setDesignSetUp(self, designOnOf):
380388
"""Sets the design set up information (True,False)."""
@@ -482,6 +490,26 @@ def getAvgGapPhase(self):
482490
def getAvgGapPhaseDeg(self):
483491
"""Returns average phase in degrees for all RF gaps in the cavity"""
484492
return self.getAvgGapPhase() * 180.0 / math.pi
493+
494+
def setUsePhaseAtEntrance(self,usePhaseAtEntrance):
495+
"""
496+
This parameter is only for RF gap types with the continuous RF fields
497+
on the axis of the cavity.
498+
If usePhaseAtEntrance = False we will use the phase at the center
499+
of 1 st RF gap as for a standard cavity representations as a set
500+
of zero-length RF gaps.
501+
If usePhaseAtEntrance = False we use the phase at the entrance
502+
of the cavity.
503+
By default it is False. Switching to True will change cavity phase and
504+
all longitudinal dynamics calculations. You should calculate and set
505+
of the cavity phase before the bunch tracking. And, of course, you
506+
should start with the trackDesignBunch(...) lattice method.
507+
"""
508+
self.usePhaseAtEntrance = usePhaseAtEntrance
509+
510+
def getUsePhaseAtEntrance(self):
511+
""" Returns the usePhaseAtEntrance parameter of the cavity """
512+
return self.usePhaseAtEntrance
485513

486514
def removeAllGapNodes(self):
487515
"""Remove all rf gaps from this cavity."""

0 commit comments

Comments
 (0)