Skip to content

Commit dc46b10

Browse files
committed
Added pyht test
1 parent ddf5f83 commit dc46b10

File tree

1 file changed

+289
-0
lines changed

1 file changed

+289
-0
lines changed

tests/test_pyht_interface.py

Lines changed: 289 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,289 @@
1+
import pathlib
2+
3+
def test_instability():
4+
import time
5+
import numpy as np
6+
from scipy import constants
7+
8+
protonMass = constants.value("proton mass energy equivalent in MeV") * 1e6
9+
from scipy.stats import linregress
10+
from scipy.signal import hilbert
11+
12+
import xobjects as xo
13+
import xtrack as xt
14+
from xtrack.pyheadtail_interface.pyhtxtparticles import PyHtXtParticles
15+
16+
from PyHEADTAIL.particles.generators import generate_Gaussian6DTwiss
17+
from PyHEADTAIL.particles.slicing import UniformBinSlicer
18+
from PyHEADTAIL.impedances.wakes import WakeTable, WakeField
19+
from PyHEADTAIL.feedback.transverse_damper import TransverseDamper
20+
from PyHEADTAIL.trackers.transverse_tracking import TransverseSegmentMap
21+
from PyHEADTAIL.trackers.longitudinal_tracking import LinearMap
22+
from PyHEADTAIL.trackers.detuners import ChromaticitySegment, AmplitudeDetuningSegment
23+
24+
context = xo.ContextCpu(omp_num_threads=1)
25+
26+
nTurn = 5000 # int(1E4)
27+
bunch_intensity = 1.8e11
28+
n_macroparticles = int(1e4)
29+
energy = 7e3 # [GeV]
30+
gamma = energy * 1e9 / protonMass
31+
betar = np.sqrt(1 - 1 / gamma ** 2)
32+
normemit = 1.8e-6
33+
beta_x = 68.9
34+
beta_y = 70.34
35+
Q_x = 0.31
36+
Q_y = 0.32
37+
chroma = -5.0 # 10.0
38+
sigma_4t = 1.2e-9
39+
sigma_z = sigma_4t / 4.0 * constants.c
40+
momentumCompaction = 3.483575072011584e-04
41+
eta = momentumCompaction - 1.0 / gamma ** 2
42+
voltage = 12.0e6
43+
h = 35640
44+
p0 = constants.m_p * betar * gamma * constants.c
45+
Q_s = np.sqrt(constants.e * voltage * eta * h / (2 * np.pi * betar * constants.c * p0))
46+
circumference = 26658.883199999
47+
averageRadius = circumference / (2 * np.pi)
48+
sigma_delta = Q_s * sigma_z / (averageRadius * eta)
49+
beta_s = sigma_z / sigma_delta
50+
emit_s = 4 * np.pi * sigma_z * sigma_delta * p0 / constants.e # eVs for PyHEADTAIL
51+
52+
n_slices_wakes = 200
53+
limit_z = 3 * sigma_z
54+
55+
wake_folder = pathlib.Path(
56+
__file__).parent.joinpath('../examples/pyheadtail_interface/wakes').absolute()
57+
wakefile = wake_folder.joinpath(
58+
"wakeforhdtl_PyZbase_Allthemachine_7000GeV_B1_2021_TeleIndex1_wake.dat")
59+
slicer_for_wakefields = UniformBinSlicer(n_slices_wakes, z_cuts=(-limit_z, limit_z))
60+
waketable = WakeTable(
61+
wakefile, ["time", "dipole_x", "dipole_y", "quadrupole_x", "quadrupole_y"]
62+
)
63+
wake_field = WakeField(slicer_for_wakefields, waketable)
64+
65+
damping_time = 7000 # 33.
66+
damper = TransverseDamper(dampingrate_x=damping_time, dampingrate_y=damping_time)
67+
i_oct = 15.
68+
detx_x = 1.4e5 * i_oct / 550.0 # from PTC with ATS optics, telescopic factor 1.0
69+
detx_y = -1.0e5 * i_oct / 550.0
70+
71+
checkTurn = 1000
72+
73+
########### PyHEADTAIL part ##############
74+
75+
particles = generate_Gaussian6DTwiss(
76+
macroparticlenumber=n_macroparticles,
77+
intensity=bunch_intensity,
78+
charge=constants.e,
79+
mass=constants.m_p,
80+
circumference=circumference,
81+
gamma=gamma,
82+
alpha_x=0.0,
83+
alpha_y=0.0,
84+
beta_x=beta_x,
85+
beta_y=beta_y,
86+
beta_z=beta_s,
87+
epsn_x=normemit,
88+
epsn_y=normemit,
89+
epsn_z=emit_s,
90+
)
91+
92+
x0 = np.copy(particles.x)
93+
px0 = np.copy(particles.xp)
94+
y0 = np.copy(particles.y)
95+
py0 = np.copy(particles.yp)
96+
zeta0 = np.copy(particles.z)
97+
delta0 = np.copy(particles.dp)
98+
99+
print(
100+
"PyHt size comp x", particles.sigma_x(), np.sqrt(normemit * beta_x / gamma / betar)
101+
)
102+
print(
103+
"PyHt size comp y", particles.sigma_y(), np.sqrt(normemit * beta_y / gamma / betar)
104+
)
105+
print("PyHt size comp z", particles.sigma_z(), sigma_z)
106+
print("PyHt size comp delta", particles.sigma_dp(), sigma_delta)
107+
108+
chromatic_detuner = ChromaticitySegment(dQp_x=chroma, dQp_y=0.0)
109+
transverse_detuner = AmplitudeDetuningSegment(
110+
dapp_x=detx_x * p0,
111+
dapp_y=detx_x * p0,
112+
dapp_xy=detx_y * p0,
113+
dapp_yx=detx_y * p0,
114+
alpha_x=0.0,
115+
beta_x=beta_x,
116+
alpha_y=0.0,
117+
beta_y=beta_y,
118+
)
119+
arc_transverse = TransverseSegmentMap(
120+
alpha_x_s0=0.0,
121+
beta_x_s0=beta_x,
122+
D_x_s0=0.0,
123+
alpha_x_s1=0.0,
124+
beta_x_s1=beta_x,
125+
D_x_s1=0.0,
126+
alpha_y_s0=0.0,
127+
beta_y_s0=beta_y,
128+
D_y_s0=0.0,
129+
alpha_y_s1=0.0,
130+
beta_y_s1=beta_y,
131+
D_y_s1=0.0,
132+
dQ_x=Q_x,
133+
dQ_y=Q_y,
134+
segment_detuners=[chromatic_detuner, transverse_detuner],
135+
)
136+
arc_longitudinal = LinearMap(
137+
alpha_array=[momentumCompaction], circumference=circumference, Q_s=Q_s
138+
)
139+
140+
turns = np.arange(nTurn)
141+
x = np.zeros(nTurn, dtype=float)
142+
for turn in range(nTurn):
143+
if turn == checkTurn:
144+
x1 = np.copy(particles.x)
145+
px1 = np.copy(particles.xp)
146+
y1 = np.copy(particles.y)
147+
py1 = np.copy(particles.yp)
148+
zeta1 = np.copy(particles.z)
149+
delta1 = np.copy(particles.dp)
150+
151+
time0 = time.time()
152+
arc_transverse.track(particles)
153+
arc_longitudinal.track(particles)
154+
time1 = time.time()
155+
wake_field.track(particles)
156+
time2 = time.time()
157+
damper.track(particles)
158+
time3 = time.time()
159+
x[turn] = np.average(particles.x)
160+
if turn % 1000 == 0:
161+
print(
162+
f"PyHt - turn {turn}: time for arc {time1-time0}s, for wake {time2-time1}s, for damper {time3-time2}s"
163+
)
164+
x /= np.sqrt(normemit * beta_x / gamma / betar)
165+
iMin = 1000
166+
iMax = nTurn - 1000
167+
if iMin >= iMax:
168+
iMin = 0
169+
iMax = nTurn
170+
ampl = np.abs(hilbert(x))
171+
b, a, r, p, stderr = linregress(turns[iMin:iMax], np.log(ampl[iMin:iMax]))
172+
gr_pyht = b
173+
print(f"Growth rate {b*1E4} [$10^{-4}$/turn]")
174+
175+
############ xsuite-PyHEADTAIL part (the WakeField instance is shared) ########################
176+
177+
if True: # Use the initial coordinates generated by PyHEADTAIL
178+
particles = PyHtXtParticles(
179+
circumference=circumference,
180+
particlenumber_per_mp=bunch_intensity / n_macroparticles,
181+
_context=context,
182+
q0=1,
183+
mass0=protonMass,
184+
gamma0=gamma,
185+
x=x0,
186+
px=px0,
187+
y=y0,
188+
py=py0,
189+
zeta=zeta0,
190+
delta=delta0,
191+
)
192+
else: # Use new random coordinates with the same distribution
193+
checkTurn = -1
194+
particles = PyHtXtParticles(
195+
circumference=circumference,
196+
particlenumber_per_mp=bunch_intensity / n_macroparticles,
197+
_context=context,
198+
q0=1,
199+
mass0=protonMass,
200+
gamma0=gamma,
201+
x=np.sqrt(normemit * beta_x / gamma / betar)
202+
* np.random.randn(n_macroparticles),
203+
px=np.sqrt(normemit / beta_x / gamma / betar)
204+
* np.random.randn(n_macroparticles),
205+
y=np.sqrt(normemit * beta_y / gamma / betar)
206+
* np.random.randn(n_macroparticles),
207+
py=np.sqrt(normemit / beta_y / gamma / betar)
208+
* np.random.randn(n_macroparticles),
209+
zeta=sigma_z * np.random.randn(n_macroparticles),
210+
delta=sigma_delta * np.random.randn(n_macroparticles),
211+
)
212+
213+
print(
214+
"PyHtXt size comp x",
215+
particles.sigma_x(),
216+
np.sqrt(normemit * beta_x / gamma / betar),
217+
)
218+
print(
219+
"PyHtXt size comp y",
220+
particles.sigma_y(),
221+
np.sqrt(normemit * beta_y / gamma / betar),
222+
)
223+
print("PyHtXt size comp z", particles.sigma_z(), sigma_z)
224+
print("PyHtXt size comp delta", particles.sigma_dp(), sigma_delta)
225+
226+
arc = xt.LinearTransferMatrix(
227+
_context=context,
228+
alpha_x_0=0.0,
229+
beta_x_0=beta_x,
230+
disp_x_0=0.0,
231+
alpha_x_1=0.0,
232+
beta_x_1=beta_x,
233+
disp_x_1=0.0,
234+
alpha_y_0=0.0,
235+
beta_y_0=beta_y,
236+
disp_y_0=0.0,
237+
alpha_y_1=0.0,
238+
beta_y_1=beta_y,
239+
disp_y_1=0.0,
240+
Q_x=Q_x,
241+
Q_y=Q_y,
242+
beta_s=beta_s,
243+
Q_s=-Q_s,
244+
chroma_x=chroma,
245+
chroma_y=0.0,
246+
detx_x=detx_x,
247+
detx_y=detx_y,
248+
dety_y=detx_x,
249+
dety_x=detx_y,
250+
energy_ref_increment=0.0,
251+
energy_increment=0,
252+
)
253+
254+
turns = np.arange(nTurn)
255+
x = np.zeros(nTurn, dtype=float)
256+
for turn in range(nTurn):
257+
if turn == checkTurn:
258+
print("x", particles.x - x1)
259+
print("px", particles.px - px1)
260+
print("y", particles.y - y1)
261+
print("py", particles.py - py1)
262+
print("z", particles.zeta - zeta1)
263+
print("delta", particles.delta - delta1)
264+
265+
time0 = time.time()
266+
arc.track(particles)
267+
time1 = time.time()
268+
wake_field.track(particles)
269+
time2 = time.time()
270+
damper.track(particles)
271+
time3 = time.time()
272+
x[turn] = np.average(particles.x)
273+
if turn % 1000 == 0:
274+
print(
275+
f"PyHtXt - turn {turn}: time for arc {time1-time0}s, for wake {time2-time1}s, for damper {time3-time2}s"
276+
)
277+
x /= np.sqrt(normemit * beta_x / gamma / betar)
278+
iMin = 1000
279+
iMax = nTurn - 1000
280+
if iMin >= iMax:
281+
iMin = 0
282+
iMax = nTurn
283+
ampl = np.abs(hilbert(x))
284+
b, a, r, p, stderr = linregress(turns[iMin:iMax], np.log(ampl[iMin:iMax]))
285+
gr_xtpyht = b
286+
print(f"Growth rate {b*1E4} [$10^{-4}$/turn]")
287+
288+
assert np.isclose(gr_xtpyht, gr_pyht, rtol=1e-3, atol=1e-100)
289+

0 commit comments

Comments
 (0)