-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsynchrotron.py
More file actions
382 lines (324 loc) · 12.4 KB
/
synchrotron.py
File metadata and controls
382 lines (324 loc) · 12.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
"""
This module defines a Synchrotron class which includes the parameters
useful for stability simulations. Some basic parameters such as the beam
energy or the RF voltage are loaded from a YAML configuration file
and used to compute all the needed parameters such as the synchrotron tune
or the revolution frequency.
Some of these parameters can be modified. The values derived from this will
then be recomputed accordingly.
@copyright: CERN
@date: 08/03/2018
@author: David Amorim
"""
from __future__ import division
import warnings
import yaml
import numpy as np
import particle_parameters
class Synchrotron(object):
"""
Define the parameters of a synchrotron
The parameters can be loaded from a YAML file. If no file is provided,
a default file is used.
Methods are defined to compute various parameters from the ones in the file
Parameters
----------
parameter_file: str, default './beams/Basic_synchrotron.yaml'
Path to the YAML file containing the beam and ring parameters
A default synchrotron based on the PS is provided
Attributes
----------
circumference, radius: floats
Accelerator circumference and radius. One of the value is taken from
the YAML file and the other one is computed from it. Can be
overwritten by the user.
particle: object of class Particle from particle_parameters
Object storing the particle properties and physical constants
E_kinetic, gamma: floats
Beam kinetic energy and Lorentz factor. The kinetic energy is given
in MeV. One of the value is taken from the YAML file and the other one
is computed from it. They can be overwritten by the user.
f0, omega0: float
Beam revolution frequency and angular revolution frequency derived
from the beam energy and the machine circumference.
Qs: float
Synchrotron tune. Use the value given in the YAML file if
it is present. Otherwise it is computed from RF voltage, harmonic
number, beam kinetic parameters, synchronous phase and slippage factor.
It can be overwritten by the user.
omegas: float
Synchrotron angular frequency. Computed from Qs and beam angluar
revolution frequency.
alphap: float
Momentum compaction factor of the machine. It is taken from the
YAML file.
eta: float
Slippage factor of the machine. It is taken from the
YAML file.
Qx, Qy: floats
Transverse betatron tunes taken from the YAML file. They can be
overwritten by the user.
Qxfrac, Qyfrac: floats
Fractionnal part of the transverse betatron tunes computed from
the transverse tunes
beta_x, beta_y: floats
Transverse beta functions taken from the YAML file. They can be
overwritten by the user. Smooth optics approximation is used if they
are not given.
Example
-------
>>> import synchrotron
>>> SPS = synchrotron.Synchrotron(
parameter_file=('./machine_configuration/
'SPS/SPS_Q26_injection_MOSES.yaml'))
>>> print SPS.Qs
0.004179
>>> SPS.Qs = 0.0043
"""
def __init__(self,
parameter_file=('./machine_configuration/'
'Basic_synchrotron.yaml')):
# Warn the user if the Basic synchrotron is loaded
if parameter_file is './machine_configuration/Basic_synchrotron.yaml':
warnings.warn('Using the example synchrotron', UserWarning)
self._parameters = self._read_parameters_file(parameter_file)
self.particle = getattr(particle_parameters,
self._parameters['Beam Parameters']
['particle_name'])()
self._compute_ring_geometry()
self._compute_kinetic_parameters()
self._set_alphap()
self._set_beta_function()
self._set_synchrotron_tune()
self._set_bunch_length()
@property
def circumference(self):
return self._circumference
@circumference.setter
def circumference(self, value):
self._circumference = value
self._radius = self.circumference / (2*np.pi)
@property
def radius(self):
return self._radius
@radius.setter
def radius(self, value):
self._radius = value
self._circumference = 2*np.pi*self.radius
@property
def gamma(self):
return self._gamma
@gamma.setter
def gamma(self, value):
""" Compute the beam Lorentz factor and derived parameters
such as the relativistic beta, the kinetic and total energy.
"""
self._gamma = value
self._beta = np.sqrt(1. - 1./(self.gamma**2))
self._p0 = (self._beta * self.gamma
* self.particle.mass * self.particle.c)
self._E_kinetic = (self.gamma - 1)*self.particle.E_rest
self._E_total = self.E_kinetic + self.particle.E_rest
try:
self._eta = self.alphap - 1/self.gamma**2
except AttributeError:
print('Momentum compaction factor not attributed yet, '
'pass slippage factor computation')
pass
@property
def beta(self):
return self._beta
@property
def E_kinetic(self):
return self._E_kinetic
@E_kinetic.setter
def E_kinetic(self, value):
E_rest = self.particle.E_rest
self.gamma = (value + E_rest) / E_rest
@property
def p0(self):
return self._p0
@p0.setter
def p0(self, value):
self.gamma = np.sqrt(1 + (value / (self.particle.mass
* self.particle.c)) ** 2)
@property
def alphap(self):
return self._alphap
@alphap.setter
def alphap(self, value):
self._alphap = value
self._eta = self.alphap - 1/self._gamma**2
@property
def Qs(self):
""" Compute the synchrotron tune from the beam parameters,
the RF voltage, harmonic number and synchronous phase.
If the parameter has been overwitten, the computation is ignored
"""
if hasattr(self, '_Qs'):
return self._Qs
else:
beta_rel = self._beta
c = self.particle.c
RF_voltage = self._parameters['Beam Parameters']['RF_voltage']
harmonic_number = self._parameters['Beam Parameters']['harmonic']
phi_s = self._parameters['Beam Parameters']['synchrotron_phase']
Q_s = np.sqrt(self.particle.e
* RF_voltage
* np.abs(self._eta)
* harmonic_number
* np.cos(phi_s)
/ (2*np.pi*beta_rel*c*self.p0))
return Q_s
@Qs.setter
def Qs(self, value):
warnings.warn('Overwriting the synchrotron tune parameters: '
'RF voltage will not be updated', UserWarning)
self._Qs = value
@property
def f0(self):
return self._beta * self.particle.c / self.circumference
@property
def omega0(self):
return self.f0 * 2 * np.pi
@property
def omegas(self):
return self.Qs * self.omega0
@property
def Qx(self):
if hasattr(self, '_Qx'):
return self._Qx
else:
return self._parameters['Beam Parameters']['Qx']
@Qx.setter
def Qx(self, value):
warnings.warn('Overwriting the H transverse tune', UserWarning)
self._Qx = value
self.beta_x = self.circumference / 2 / np.pi / value
@property
def Qy(self):
if hasattr(self, '_Qy'):
return self._Qy
else:
return self._parameters['Beam Parameters']['Qy']
@Qy.setter
def Qy(self, value):
warnings.warn('Overwriting the V transverse tune', UserWarning)
self._Qy = value
self.beta_y = self.circumference / 2 / np.pi / value
@property
def Qxfrac(self):
return self.Qx - np.floor(self.Qx)
@property
def Qyfrac(self):
return self.Qy - np.floor(self.Qy)
@property
def beta_x(self):
if hasattr(self, '_beta_x'):
return self._beta_x
else:
return self._parameters['Beam Parameters']['beta_x']
@beta_x.setter
def beta_x(self, value):
self._beta_x = value
@property
def beta_y(self):
if hasattr(self, '_beta_y'):
return self._beta_y
else:
return self._parameters['Beam Parameters']['beta_y']
@beta_y.setter
def beta_y(self, value):
self._beta_y = value
@property
def taub(self):
if hasattr(self, '_taub'):
return self._taub
else:
return self._parameters['Beam Parameters']['taub']
@taub.setter
def taub(self, value):
self._taub = value
@property
def sigmaz(self):
return self.taub * self._beta * self.particle.c / 4
@sigmaz.setter
def sigmaz(self, value):
self.taub = 4 * value / (self._beta * self.particle.c)
@property
def eta(self):
return self.alphap - 1/self.gamma**2
@eta.setter
def eta(self, value):
self._eta = value
def _read_parameters_file(self, file_path):
with open(file_path) as yaml_file:
machine_parameters = yaml.load(yaml_file)
return machine_parameters
def _compute_ring_geometry(self):
try:
self.circumference = (self._parameters['Ring Parameters']
['circumference'])
except KeyError:
print('No circumference parameter, using radius')
try:
self.radius = self._parameters['Ring Parameters']['radius']
except KeyError:
raise ValueError('No radius or circumference given')
def _compute_kinetic_parameters(self):
try:
self.E_kinetic = self._parameters['Beam Parameters']['E_kinetic']
except KeyError:
print('No kinetic energy parameter, using gamma')
try:
self.gamma = self._parameters['Beam Parameters']['gamma']
except KeyError:
raise ValueError('No kinetic energy or gamma given')
def _set_alphap(self):
"""Check if momentum compaction factor is present at initialization"""
try:
self.alphap = self._parameters['Beam Parameters']['alphap']
except KeyError:
raise ValueError('No momentum compaction factor found')
def _set_beta_function(self):
"""Check if beta functions are present at initialization"""
try:
self.beta_x = self._parameters['Beam Parameters']['beta_x']
except KeyError:
print('No beta function found for H plane, smooth optics approximation '
'used instead')
try:
self.Qx = self._parameters['Beam Parameters']['Qx']
except KeyError:
raise ValueError('No beta function or tune given for H plane')
try:
self.beta_y = self._parameters['Beam Parameters']['beta_y']
except KeyError:
print('No beta function found for V plane, smooth optics approximation '
'used instead')
try:
self.Qy = self._parameters['Beam Parameters']['Qy']
except KeyError:
raise ValueError('No beta function or tune given for V plane')
def _set_synchrotron_tune(self):
"""Set the synchrotron tune at the object initialization."""
try:
self.Qs = self._parameters['Beam Parameters']['Qs']
except KeyError:
print('Synchrotron tune will be computed from RF voltage, '
'harmonic number...')
pass
def _set_bunch_length(self):
"""Set the bunch length at the object initialization."""
try:
self.taub = self._parameters['Beam Parameters']['taub']
except KeyError:
print('No full bunch length (in seconds) given in '
'the paramters file, trying with '
'RMS bunch length (in meters)')
try:
self.sigmaz = self._parameters['Beam Parameters']['sigmaz']
except KeyError:
raise ValueError('No full bunch length (in seconds) or '
'RMS bunch length (in meters)'
'given in parameters')