Skip to content

Commit 803b778

Browse files
authored
Merge pull request #1 from jkomppula/develop
Version 0.2.1
2 parents 7c795b8 + f42e0b9 commit 803b778

30 files changed

+7575
-171
lines changed

core.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import numpy as np
22
import types
33
import copy
4-
version = '0.2'
4+
version = '0.2.1_dev'
55

66
"""
77
@author Jani Komppula
Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
1+
'''
2+
Simulation of beam interaction with coupling impedance and damper
3+
for a single bunch
4+
'''
5+
6+
# using Python 2.7:
7+
from __future__ import division, print_function
8+
range_ = range
9+
range = xrange
10+
11+
import sys
12+
# PyHEADTAIL location if it's not already in the PYTHONPATH environment variable
13+
sys.path.append('../../')
14+
15+
16+
17+
import time
18+
19+
import numpy as np
20+
np.random.seed(10000042)
21+
import h5py
22+
from scipy.constants import e, m_p, c
23+
24+
from PyHEADTAIL.particles.slicing import UniformBinSlicer
25+
from PyHEADTAIL.impedances.wakes import WakeTable, WakeField
26+
from PyHEADTAIL.feedback.feedback import IdealBunchFeedback
27+
from PyHEADTAIL.monitors.monitors import (
28+
BunchMonitor, ParticleMonitor, SliceMonitor)
29+
30+
31+
n_macroparticles = 100000 # number of macro-particles to resolve the beam
32+
n_turns = 6000 # simulation time
33+
n_turns_slicemon = 64 # recording span of the slice statistics monitor
34+
35+
36+
# COMMENT THE NON-WANTED SET-UP:
37+
38+
# # injection
39+
machine_configuration = 'LHC-injection'
40+
wakefile = ('./wakeforhdtl_PyZbase_Allthemachine_450GeV'
41+
'_B1_LHC_inj_450GeV_B1.dat')
42+
43+
# flat-top
44+
# machine_configuration = 'LHC_6.5TeV_collision_2016'
45+
# wakefile = ('./wakeforhdtl_PyZbase_Allthemachine_6p5TeV'
46+
# '_B1_LHC_ft_6.5TeV_B1.dat')
47+
48+
# ---
49+
50+
def get_nonlinear_params(chroma, i_oct, p0=6.5e12*e/c):
51+
'''Arguments:
52+
- chroma: first-order chromaticity Q'_{x,y}, identical
53+
for both transverse planes
54+
- i_oct: octupole current in A (positive i_oct means
55+
LOF = i_oct > 0 and LOD = -i_oct < 0)
56+
'''
57+
# factor 2p0 is PyHEADTAIL's convention for d/dJx instead of
58+
# MAD-X's convention of d/d(2Jx)
59+
print('p0: ' + str(p0/(e/c)))
60+
61+
app_x = 2 * p0 * 27380.10941 * i_oct / 100.
62+
app_y = 2 * p0 * 28875.03442 * i_oct / 100.
63+
app_xy = 2 * p0 * -21766.48714 * i_oct / 100.
64+
Qpp_x = 4889.00298 * i_oct / 100.
65+
Qpp_y = -2323.147896 * i_oct / 100.
66+
return {
67+
'app_x': app_x,
68+
'app_y': app_y,
69+
'app_xy': app_xy,
70+
'Qp_x': [chroma,],# Qpp_x],
71+
'Qp_y': [chroma,],# Qpp_y],
72+
# second-order chroma commented out above!
73+
}
74+
75+
76+
def run(intensity, chroma=0, i_oct=0):
77+
'''Arguments:
78+
- intensity: integer number of charges in beam
79+
- chroma: first-order chromaticity Q'_{x,y}, identical
80+
for both transverse planes
81+
- i_oct: octupole current in A (positive i_oct means
82+
LOF = i_oct > 0 and LOD = -i_oct < 0)
83+
'''
84+
85+
86+
# BEAM AND MACHINE PARAMETERS
87+
# ============================
88+
from LHC import LHC
89+
# energy set above will enter get_nonlinear_params p0
90+
assert machine_configuration == 'LHC-injection'
91+
machine = LHC(n_segments=1,
92+
machine_configuration=machine_configuration,
93+
**get_nonlinear_params(chroma=chroma, i_oct=i_oct,p0=0.45e12*e/c))
94+
95+
96+
# BEAM
97+
# ====
98+
epsn_x = 3.e-6 # normalised horizontal emittance
99+
epsn_y = 3.e-6 # normalised vertical emittance
100+
sigma_z = 1.2e-9 * machine.beta*c/4. # RMS bunch length in meters
101+
102+
bunch = machine.generate_6D_Gaussian_bunch(
103+
n_macroparticles, intensity, epsn_x, epsn_y, sigma_z=sigma_z, matched=True)
104+
105+
print ("\n--> Bunch length and emittance: {:g} m, {:g} eVs.".format(
106+
bunch.sigma_z(), bunch.epsn_z()))
107+
108+
109+
# CREATE BEAM SLICERS
110+
# ===================
111+
slicer_for_slicemonitor = UniformBinSlicer(
112+
50, z_cuts=(-3*sigma_z, 3*sigma_z))
113+
slicer_for_wakefields = UniformBinSlicer(
114+
50, z_cuts=(-3*sigma_z, 3*sigma_z),
115+
circumference=machine.circumference,
116+
h_bunch=machine.h_bunch)
117+
print("Slice")
118+
119+
120+
121+
# CREATE WAKES
122+
# ============
123+
wake_table1 = WakeTable(wakefile,
124+
['time', 'dipole_x', 'dipole_y',
125+
# 'quadrupole_x', 'quadrupole_y',
126+
'noquadrupole_x', 'noquadrupole_y',
127+
# 'dipole_xy', 'dipole_yx',
128+
'nodipole_xy', 'nodipole_yx',
129+
])
130+
wake_field = WakeField(slicer_for_wakefields, wake_table1, mpi=True)
131+
132+
133+
# CREATE DAMPER
134+
# =============
135+
dampingtime = 50.
136+
gain = 2./dampingtime
137+
damper = IdealBunchFeedback(gain)
138+
139+
140+
# CREATE MONITORS
141+
# ===============
142+
try:
143+
bucket = machine.longitudinal_map.get_bucket(bunch)
144+
except AttributeError:
145+
bucket = machine.rfbucket
146+
147+
simulation_parameters_dict = {
148+
'gamma' : machine.gamma,
149+
'intensity': intensity,
150+
'Qx' : machine.Q_x,
151+
'Qy' : machine.Q_y,
152+
'Qs' : bucket.Q_s,
153+
'beta_x' : bunch.beta_Twiss_x(),
154+
'beta_y' : bunch.beta_Twiss_y(),
155+
'beta_z' : bucket.beta_z,
156+
'epsn_x' : bunch.epsn_x(),
157+
'epsn_y' : bunch.epsn_y(),
158+
'sigma_z' : bunch.sigma_z(),
159+
}
160+
bunchmonitor = BunchMonitor(
161+
outputpath+'/bunchmonitor_{:04d}_chroma={:g}'.format(it, chroma),
162+
n_turns, simulation_parameters_dict,
163+
write_buffer_to_file_every=512,
164+
buffer_size=4096)
165+
slicemonitor = SliceMonitor(
166+
outputpath+'/slicemonitor_{:04d}_chroma={:g}'.format(it, chroma),
167+
n_turns_slicemon,
168+
slicer_for_slicemonitor, simulation_parameters_dict,
169+
write_buffer_to_file_every=1, buffer_size=n_turns_slicemon)
170+
171+
172+
# TRACKING LOOP
173+
# =============
174+
# machine.one_turn_map.append(damper)
175+
machine.one_turn_map.append(wake_field)
176+
177+
178+
# for slice statistics monitoring:
179+
s_cnt = 0
180+
monitorswitch = False
181+
182+
print ('\n--> Begin tracking...\n')
183+
184+
# GO!!!
185+
for i in range(n_turns):
186+
187+
t0 = time.clock()
188+
189+
# track the beam around the machine for one turn:
190+
machine.track(bunch)
191+
192+
ex, ey, ez = bunch.epsn_x(), bunch.epsn_y(), bunch.epsn_z()
193+
mx, my, mz = bunch.mean_x(), bunch.mean_y(), bunch.mean_z()
194+
195+
# monitor the bunch statistics (once per turn):
196+
bunchmonitor.dump(bunch)
197+
198+
# if the centroid becomes unstable (>1cm motion)
199+
# then monitor the slice statistics:
200+
if not monitorswitch:
201+
if mx > 1e-2 or my > 1e-2 or i > n_turns - n_turns_slicemon:
202+
print ("--> Activate slice monitor")
203+
monitorswitch = True
204+
else:
205+
if s_cnt < n_turns_slicemon:
206+
slicemonitor.dump(bunch)
207+
s_cnt += 1
208+
209+
# stop the tracking as soon as we have not-a-number values:
210+
if not all(np.isfinite(c) for c in [ex, ey, ez, mx, my, mz]):
211+
print ('*** STOPPING SIMULATION: non-finite bunch stats!')
212+
break
213+
214+
# print status all 1000 turns:
215+
if i % 100 == 0:
216+
t1 = time.clock()
217+
print ('Emittances: ({:.3g}, {:.3g}, {:.3g}) '
218+
'& Centroids: ({:.3g}, {:.3g}, {:.3g})'
219+
'@ turn {:d}, {:g} ms, {:s}'.format(
220+
ex, ey, ez, mx, my, mz, i, (t1-t0)*1e3, time.strftime(
221+
"%d/%m/%Y %H:%M:%S", time.localtime()))
222+
)
223+
224+
print ('\n*** Successfully completed!')
225+
226+
227+
if __name__ == '__main__':
228+
# iteration, attached to monitor name:
229+
it = 0
230+
# outputpath relative to this file:
231+
outputpath = './'
232+
233+
# run the simulation:
234+
run(intensity=1.1e11, chroma=12, i_oct=0)

0 commit comments

Comments
 (0)