Skip to content

Commit 36590ff

Browse files
committed
release-script: Merge branch 'release/v1.13.3'
2 parents 63a7235 + 573aff4 commit 36590ff

File tree

4 files changed

+95
-30
lines changed

4 files changed

+95
-30
lines changed

PyHEADTAIL/_version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = '1.13.2'
1+
__version__ = '1.13.3'

PyHEADTAIL/particles/generators.py

Lines changed: 92 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -19,34 +19,87 @@
1919
from scipy.constants import e, c
2020

2121

22-
def generate_Gaussian6DTwiss(macroparticlenumber, intensity, charge, mass,
23-
circumference, gamma,
24-
alpha_x, alpha_y, beta_x, beta_y, beta_z,
25-
epsn_x, epsn_y, epsn_z,
26-
dispersion_x=None, dispersion_y=None):
27-
""" Convenience wrapper which generates a 6D gaussian phase space
28-
with the specified parameters
22+
def generate_Gaussian6DTwiss(
23+
macroparticlenumber, intensity, charge, mass,
24+
circumference, gamma,
25+
alpha_x, alpha_y, beta_x, beta_y, beta_z,
26+
epsn_x, epsn_y, epsn_z,
27+
dispersion_x=None, dispersion_y=None,
28+
limit_n_rms_x=None, limit_n_rms_y=None, limit_n_rms_z=None,
29+
):
30+
""" Convenience wrapper generating a 6D Gaussian phase space
31+
distribution of macro-particles with the specified parameters:
32+
2933
Args:
30-
the usual suspects
31-
Returns: A particle instance with the phase space matched to the arguments
34+
macroparticlenumber: number of macro-particles in the beam
35+
intensity: number of represented beam particles
36+
charge: charge per particle [SI unit Coul]
37+
mass: mass per particle [SI unit kg]
38+
circumference: ring circumference (needed for effective models)
39+
gamma: relativistic Lorentz factor
40+
alpha_[x,y]: Twiss parameter. The corresponding transverse phase
41+
space gets matched to (alpha_[], beta_[])
42+
beta_[x,y]: Twiss parameter. The corresponding transverse phase
43+
space gets matched to (alpha_[], beta_[])
44+
beta_z: corresponding longitudinal Twiss parameter
45+
amounting to |eta| * circumference / (2 * pi * Qs)
46+
epsn_x: horizontal normalised RMS emittance [m.rad]
47+
epsn_y: vertical normalised RMS emittance [m.rad]
48+
epsn_z: longitudinal 90% emittance (4x the RMS emittance) [eV.s]
49+
50+
Optional args:
51+
dispersion_x: horizontal optics dispersion value for matching
52+
dispersion_y: vertical optics dispersion value for matching
53+
limit_n_rms_[x,y]: number of RMS amplitudes to cut distribution
54+
limit_n_rms_z: longitudinal number of RMS amplitudes to cut
55+
distribution (remember that epsn_z is already 4x the RMS
56+
value, i.e. 2 amplitudes)
57+
58+
Return a Particles instance with the phase space matched to the
59+
arguments.
3260
"""
33-
beta = np.sqrt(1.-gamma**(-2))
61+
62+
beta = np.sqrt(1. - gamma**-2)
3463
p0 = np.sqrt(gamma**2 - 1) * mass * c
35-
eps_geo_x = epsn_x/(beta*gamma)
36-
eps_geo_y = epsn_y/(beta*gamma)
64+
eps_geo_x = epsn_x / (beta * gamma)
65+
eps_geo_y = epsn_y / (beta * gamma)
3766
eps_geo_z = epsn_z * e / (4. * np.pi * p0)
67+
3868
# a bit of a hack: epsn_z is a parameter even though the ParticleGenerator
3969
# does not have such a parameter. This is kept for backwards compatiblity.
4070
# Therefore, some fake eta, Qs parameters are invented s.t.
4171
# beta_z = |eta| * circumference / (2 * pi * Qs)
4272
# holds (circumference is fixed). Does not have any side effects.
43-
Qs = 1./(2 * np.pi)
73+
Qs = 1. / (2 * np.pi)
4474
eta = beta_z / circumference
75+
76+
distribution_x = gaussian2D(eps_geo_x)
77+
distribution_y = gaussian2D(eps_geo_y)
78+
distribution_z = gaussian2D(eps_geo_z)
79+
# cutting distributions:
80+
if limit_n_rms_x:
81+
distribution_x = cut_distribution(
82+
distribution=distribution_x,
83+
is_accepted=make_is_accepted_within_n_sigma(
84+
eps_geo_x, limit_n_rms_x)
85+
)
86+
if limit_n_rms_y:
87+
distribution_y = cut_distribution(
88+
distribution=distribution_y,
89+
is_accepted=make_is_accepted_within_n_sigma(
90+
eps_geo_y, limit_n_rms_y)
91+
)
92+
if limit_n_rms_z:
93+
distribution_z = cut_distribution(
94+
distribution=distribution_z,
95+
is_accepted=make_is_accepted_within_n_sigma(
96+
eps_geo_z, limit_n_rms_z)
97+
)
4598
return ParticleGenerator(
4699
macroparticlenumber, intensity, charge, mass, circumference, gamma,
47-
gaussian2D(eps_geo_x), alpha_x, beta_x, dispersion_x,
48-
gaussian2D(eps_geo_y), alpha_y, beta_y, dispersion_y,
49-
gaussian2D(eps_geo_z), Qs, eta
100+
distribution_x, alpha_x, beta_x, dispersion_x,
101+
distribution_y, alpha_y, beta_y, dispersion_y,
102+
distribution_z, Qs, eta
50103
).generate()
51104

52105

@@ -205,30 +258,41 @@ def _cut_distribution(n_particles):
205258
return [z, dp]
206259
return _cut_distribution
207260

208-
def make_is_accepted_within_n_sigma(epsn_rms, limit_n_rms, twiss_beta=1):
261+
def make_is_accepted_within_n_sigma(rms_amplitude=None, limit_n_rms=None,
262+
epsn_rms=None):
209263
'''Closure creating an is_accepted function (e.g. for
210264
cut_distribution). The is_accepted function will return whether
211265
the canonical coordinate and momentum pair lies within the phase
212-
space region limited by the action value limit_n_rms * epsn_rms.
213-
214-
Coordinate u and momentum up are assumed to be connected to the
215-
amplitude J via the twiss_beta value,
216-
J = sqrt(u^2 + twiss_beta^2 up^2) .
266+
space region limited by the action value
267+
limit_n_rms * rms_amplitude.
268+
The closure acts on normalised Floquet space, i.e. do apply this
269+
function to the particles before matching to the optics values.
270+
271+
Coordinate u and momentum up are squared to give the action
272+
amplitude
273+
J = u^2 + up^2 .
217274
The amplitude is required to be below the limit to be accepted,
218-
J < limit_n_rms * epsn_rms.
275+
J < limit_n_rms * rms_amplitude.
219276
The usual use case will be generating u and up in normalised Floquet
220277
space (i.e. before the normalised phase space coordinates
221278
get matched to the optics or longitudinal eta and Qs).
222-
In this case, twiss_beta takes the default value 1 in normalised
223-
Floquet space. Consequently, the 1 sigma RMS reference value
279+
Consequently, the 1 sigma RMS reference value
224280
epsn_rms corresponds to the normalised 1 sigma RMS emittance
225281
(i.e. amounting to beam.epsn_x() and beam.epsn_y() in the transverse
226282
plane, and beam.epsn_z()/4 in the longitudinal plane).
227283
'''
228-
threshold_amplitude_squared = (limit_n_rms * epsn_rms)**2
284+
if epsn_rms:
285+
# backwards compatibility (it was bad naming):
286+
limit_n_rms *= limit_n_rms
287+
assert rms_amplitude == None, \
288+
("epsn_rms is for backwards compatibility, it has been "
289+
"replaced by its sqrt-value rms_amplitude. Please do not "
290+
"use both at the same time!")
291+
rms_amplitude = epsn_rms**2
292+
threshold_amplitude = limit_n_rms * rms_amplitude
229293
def is_accepted(u, up):
230-
Jsq = u**2 + (twiss_beta * up)**2
231-
return Jsq < threshold_amplitude_squared
294+
Jsq = u**2 + up**2
295+
return Jsq < threshold_amplitude
232296
return is_accepted
233297

234298

PyHEADTAIL/testing/unittests/testsuite.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
TestDispatch,
4545
TestMonitor]
4646

47+
test_list = sorted(test_list, key=str)
4748

4849
if __name__ == '__main__':
4950
test_load = unittest.TestLoader()

README.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ And there you go, start using PyHEADTAIL!
5757
5858
In [1]: import PyHEADTAIL
5959
60-
PyHEADTAIL v1.13.1
60+
PyHEADTAIL v1.13.3
6161
6262
For a single installation of PyHEADTAIL we recommended to add
6363
the PyHEADTAIL path to your PYTHONPATH.

0 commit comments

Comments
 (0)