Skip to content

Commit e9764f9

Browse files
committed
Merge branch 'develop'
- SliceSet:Introduced particles_outside_cut - Particles: indeces handled correcly by: __add__, __radd__, and extract_slices - Particles: Add include_non_sliced functionality to extract_slices From tests with leptons: - Better estimate of Q_s in generate_6D_Gaussian_bunch - Longitudinal tracking and matching work with negative charge - Added CLIC-DR example with electrons
2 parents aabd655 + 763f8bc commit e9764f9

File tree

8 files changed

+578
-99
lines changed

8 files changed

+578
-99
lines changed

machines/synchrotron.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -101,15 +101,19 @@ def generate_6D_Gaussian_bunch(self, n_macroparticles, intensity,
101101
'''
102102
if self.longitudinal_mode == 'linear':
103103
check_inside_bucket = lambda z,dp : np.array(len(z)*[True])
104+
Qs = self.longitudinal_map.Qs
104105
elif self.longitudinal_mode == 'non-linear':
105-
check_inside_bucket = self.longitudinal_map.get_bucket(
106-
gamma=self.gamma).make_is_accepted(margin=0.05)
106+
bucket = self.longitudinal_map.get_bucket(
107+
gamma=self.gamma, mass=self.mass, charge=self.charge)
108+
check_inside_bucket = bucket.make_is_accepted(margin=0.05)
109+
Qs = bucket.Qs
110+
107111
else:
108112
raise NotImplementedError(
109113
'Something wrong with self.longitudinal_mode')
110114

111115
eta = self.longitudinal_map.alpha_array[0] - self.gamma**-2
112-
beta_z = np.abs(eta)*self.circumference/2./np.pi/self.longitudinal_map.Qs
116+
beta_z = np.abs(eta)*self.circumference/2./np.pi/Qs
113117
sigma_dp = sigma_z/beta_z
114118
epsx_geo = epsn_x/self.betagamma
115119
epsy_geo = epsn_y/self.betagamma

particles/particles.py

Lines changed: 42 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -151,34 +151,64 @@ def get_slices(self, slicer, *args, **kwargs):
151151
return self._slice_sets[slicer]
152152

153153

154-
def extract_slices(self, slicer, *args, **kwargs):
155-
'''Return a list Particles object with the different slices.
154+
def extract_slices(self, slicer, include_non_sliced='if_any', *args, **kwargs):
155+
'''Return a list Particles object with the different slices.
156+
The last element of the list contains particles not assigned to any slice.
157+
158+
include_non_sliced : {'always', 'never', 'if_any'}, optional
159+
'always':
160+
extra element in the list with particles not belonging to any slice
161+
is always inserted (it can be empty).
162+
'never':
163+
extra element in the list with particles not belonging to any slice
164+
is never inserted.
165+
'if_any':
166+
extra element in the list with particles not belonging to any slice
167+
is inserted only if such particles exist.
156168
'''
157-
169+
170+
if include_non_sliced not in ['if_any', 'always', 'never']:
171+
raise ValueError("include_non_sliced=%s is not valid!\n"%include_non_sliced+
172+
"Possible values are {'always', 'never', 'if_any'}" )
173+
158174
slices = self.get_slices(slicer, *args, **kwargs)
159175
self_coords_n_momenta_dict = self.get_coords_n_momenta_dict()
160176
slice_object_list = []
161-
177+
162178
for i_sl in xrange(slices.n_slices):
163-
179+
164180
ix = slices.particle_indices_of_slice(i_sl)
165181
macroparticlenumber = len(ix)
166-
182+
167183
slice_object = Particles(macroparticlenumber=macroparticlenumber,
168184
particlenumber_per_mp=self.particlenumber_per_mp, charge=self.charge,
169185
mass=self.mass, circumference=self.circumference, gamma=self.gamma, coords_n_momenta_dict={})
170186

171187
for coord in self_coords_n_momenta_dict.keys():
172188
slice_object.update({coord: self_coords_n_momenta_dict[coord][ix]})
173-
189+
190+
slice_object.id[:] = self.id[ix]
191+
174192
slice_object.slice_info = {\
175193
'z_bin_center': slices.z_centers[i_sl],\
176194
'z_bin_right':slices.z_bins[i_sl+1],\
177195
'z_bin_left':slices.z_bins[i_sl]}
178196

179197
slice_object_list.append(slice_object)
180-
181-
198+
199+
# handle unsliced
200+
if include_non_sliced is not 'never':
201+
ix = slices.particles_outside_cuts
202+
if len(ix)>0 or include_non_sliced is 'always':
203+
slice_object = Particles(macroparticlenumber=len(ix),
204+
particlenumber_per_mp=self.particlenumber_per_mp, charge=self.charge,
205+
mass=self.mass, circumference=self.circumference, gamma=self.gamma, coords_n_momenta_dict={})
206+
for coord in self_coords_n_momenta_dict.keys():
207+
slice_object.update({coord: self_coords_n_momenta_dict[coord][ix]})
208+
slice_object.id[:] = self.id[ix]
209+
slice_object.slice_info = 'unsliced'
210+
slice_object_list.append(slice_object)
211+
182212
return slice_object_list
183213

184214
def clean_slices(self):
@@ -251,6 +281,8 @@ def __add__(self, other):
251281
#setattr(result, coord, np.concatenate((self_coords_n_momenta_dict[coord].copy(), other_coords_n_momenta_dict[coord].copy())))
252282
result.update({coord: np.concatenate((self_coords_n_momenta_dict[coord].copy(), other_coords_n_momenta_dict[coord].copy()))})
253283

284+
result.id = np.concatenate((self.id.copy(), other.id.copy()))
285+
254286
return result
255287

256288
def __radd__(self, other):
@@ -263,6 +295,7 @@ def __radd__(self, other):
263295
for coord in self_coords_n_momenta_dict.keys():
264296
#setattr(result, coord, np.concatenate((self_coords_n_momenta_dict[coord].copy(), other_coords_n_momenta_dict[coord].copy())))
265297
result.update({coord: self_coords_n_momenta_dict[coord].copy()})
298+
result.id = self.id.copy()
266299
else:
267300
result = self.__add__(other)
268301

particles/slicing.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,17 @@ def particles_within_cuts(self):
174174
(self.slice_index_of_particle < self.n_slices)
175175
)[0])
176176
return particles_within_cuts_
177+
178+
@property
179+
# @memoize
180+
def particles_outside_cuts(self):
181+
'''All particle indices which are situated outside the slicing
182+
region defined by [z_cut_tail, z_cut_head).'''
183+
particles_ouside_cuts_ = make_int32(np.where(np.logical_not(
184+
(self.slice_index_of_particle > -1) &
185+
(self.slice_index_of_particle < self.n_slices))
186+
)[0])
187+
return particles_ouside_cuts_
177188

178189
@property
179190
# @memoize

testing/interactive-tests/SlicingTest.ipynb

Lines changed: 222 additions & 80 deletions
Large diffs are not rendered by default.

testing/script-tests/CLIC_DR.py

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
from PyHEADTAIL.machines.synchrotron import BasicSynchrotron
2+
import numpy as np
3+
from scipy.constants import c, e, m_e
4+
5+
class CLIC_DR(BasicSynchrotron):
6+
7+
def __init__(self, machine_configuration=None, optics_mode='smooth', **kwargs):
8+
9+
10+
alpha = 0.0001276102729
11+
h_RF = 1465
12+
mass = m_e
13+
charge = e
14+
RF_at = 'middle'
15+
16+
if machine_configuration=='3TeV':
17+
longitudinal_mode = 'non-linear'
18+
p0 = 2.86e9 * e /c
19+
p_increment = 0.
20+
accQ_x = 48.35
21+
accQ_y = 10.4
22+
V_RF = 5.1e6
23+
dphi_RF = 0.
24+
Q_s = None
25+
elif machine_configuration=='3TeV_linear':
26+
Q_s = 0.0059
27+
longitudinal_mode = 'linear'
28+
p0 = 2.86e9 * e /c
29+
p_increment = 0.
30+
accQ_x = 48.35
31+
accQ_y = 10.4
32+
V_RF = 5.1e6
33+
dphi_RF = 0.
34+
else:
35+
raise ValueError('machine_configuration not recognized!')
36+
37+
38+
39+
if optics_mode=='smooth':
40+
if 's' in kwargs.keys(): raise ValueError('s vector cannot be provided if optics_mode = "smooth"')
41+
42+
n_segments = kwargs['n_segments']
43+
circumference = 427.5
44+
45+
name = None
46+
47+
beta_x = 3.475
48+
D_x = 0
49+
beta_y = 9.233
50+
D_y = 0
51+
52+
alpha_x = None
53+
alpha_y = None
54+
55+
s = None
56+
57+
elif optics_mode=='non-smooth':
58+
if 'n_segments' in kwargs.keys(): raise ValueError('n_segments cannot be provided if optics_mode = "non-smooth"')
59+
n_segments = None
60+
circumference = None
61+
62+
name = kwargs['name']
63+
64+
beta_x = kwargs['beta_x']
65+
beta_y = kwargs['beta_y']
66+
67+
try:
68+
D_x = kwargs['D_x']
69+
except KeyError:
70+
D_x = 0*np.array(kwargs['s'])
71+
try:
72+
D_y = kwargs['D_y']
73+
except KeyError:
74+
D_y = 0*np.array(kwargs['s'])
75+
76+
alpha_x = kwargs['alpha_x']
77+
alpha_y = kwargs['alpha_y']
78+
79+
s = kwargs['s']
80+
81+
else:
82+
raise ValueError('optics_mode not recognized!')
83+
84+
85+
# detunings
86+
Qp_x = 0
87+
Qp_y = 0
88+
89+
app_x = 0
90+
app_y = 0
91+
app_xy = 0
92+
93+
94+
95+
for attr in kwargs.keys():
96+
if kwargs[attr] is not None:
97+
if type(kwargs[attr]) is list or type(kwargs[attr]) is np.ndarray:
98+
str2print = '[%s ...]'%repr(kwargs[attr][0])
99+
else:
100+
str2print = repr(kwargs[attr])
101+
self.prints('Synchrotron init. From kwargs: %s = %s'
102+
% (attr, str2print))
103+
temp = kwargs[attr]
104+
exec('%s = temp'%attr)
105+
106+
107+
108+
super(CLIC_DR, self).__init__(optics_mode=optics_mode, circumference=circumference, n_segments=n_segments, s=s, name=name,
109+
alpha_x=alpha_x, beta_x=beta_x, D_x=D_x, alpha_y=alpha_y, beta_y=beta_y, D_y=D_y,
110+
accQ_x=accQ_x, accQ_y=accQ_y, Qp_x=Qp_x, Qp_y=Qp_y, app_x=app_x, app_y=app_y, app_xy=app_xy,
111+
alpha_mom_compaction=alpha, longitudinal_mode=longitudinal_mode,
112+
h_RF=np.atleast_1d(h_RF), V_RF=np.atleast_1d(V_RF), dphi_RF=np.atleast_1d(dphi_RF), p0=p0, p_increment=p_increment,
113+
charge=charge, mass=mass, RF_at=RF_at, Q_s=Q_s)
114+
115+

0 commit comments

Comments
 (0)