syned 1.0.47__py3-none-any.whl → 1.0.49__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- syned/beamline/__init__.py +1 -1
- syned/beamline/beamline.py +155 -155
- syned/beamline/beamline_element.py +76 -76
- syned/beamline/element_coordinates.py +199 -199
- syned/beamline/optical_element.py +47 -47
- syned/beamline/optical_element_with_surface_shape.py +126 -126
- syned/beamline/optical_elements/__init__.py +1 -1
- syned/beamline/optical_elements/absorbers/absorber.py +21 -21
- syned/beamline/optical_elements/absorbers/beam_stopper.py +63 -63
- syned/beamline/optical_elements/absorbers/filter.py +61 -61
- syned/beamline/optical_elements/absorbers/holed_filter.py +67 -67
- syned/beamline/optical_elements/absorbers/slit.py +80 -80
- syned/beamline/optical_elements/crystals/__init__.py +1 -1
- syned/beamline/optical_elements/crystals/crystal.py +70 -70
- syned/beamline/optical_elements/gratings/__init__.py +1 -1
- syned/beamline/optical_elements/gratings/grating.py +279 -279
- syned/beamline/optical_elements/ideal_elements/__init__.py +1 -1
- syned/beamline/optical_elements/ideal_elements/ideal_element.py +15 -15
- syned/beamline/optical_elements/ideal_elements/ideal_fzp.py +183 -183
- syned/beamline/optical_elements/ideal_elements/ideal_lens.py +54 -54
- syned/beamline/optical_elements/ideal_elements/screen.py +15 -15
- syned/beamline/optical_elements/mirrors/__init__.py +1 -1
- syned/beamline/optical_elements/mirrors/mirror.py +39 -39
- syned/beamline/optical_elements/multilayers/__init__.py +46 -46
- syned/beamline/optical_elements/multilayers/multilayer.py +45 -45
- syned/beamline/optical_elements/refractors/__init__.py +1 -1
- syned/beamline/optical_elements/refractors/crl.py +79 -79
- syned/beamline/optical_elements/refractors/interface.py +60 -60
- syned/beamline/optical_elements/refractors/lens.py +105 -105
- syned/beamline/shape.py +2884 -2803
- syned/storage_ring/__init__.py +1 -1
- syned/storage_ring/electron_beam.py +804 -804
- syned/storage_ring/empty_light_source.py +40 -40
- syned/storage_ring/light_source.py +90 -90
- syned/storage_ring/magnetic_structure.py +8 -8
- syned/storage_ring/magnetic_structures/__init__.py +1 -1
- syned/storage_ring/magnetic_structures/bending_magnet.py +329 -329
- syned/storage_ring/magnetic_structures/insertion_device.py +169 -169
- syned/storage_ring/magnetic_structures/undulator.py +413 -413
- syned/storage_ring/magnetic_structures/wiggler.py +27 -27
- syned/syned_object.py +273 -264
- syned/util/__init__.py +21 -21
- syned/util/json_tools.py +196 -198
- syned/widget/widget_decorator.py +66 -66
- {syned-1.0.47.dist-info → syned-1.0.49.dist-info}/METADATA +88 -88
- syned-1.0.49.dist-info/RECORD +52 -0
- {syned-1.0.47.dist-info → syned-1.0.49.dist-info}/WHEEL +1 -1
- {syned-1.0.47.dist-info → syned-1.0.49.dist-info}/licenses/LICENSE +20 -20
- syned/__test/__init__.py +0 -46
- syned/__test/test.py +0 -28
- syned-1.0.47.dist-info/RECORD +0 -54
- {syned-1.0.47.dist-info → syned-1.0.49.dist-info}/top_level.txt +0 -0
|
@@ -1,413 +1,413 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Implement an undulator with vertical and horizontal magnetic fields.
|
|
3
|
-
"""
|
|
4
|
-
import numpy
|
|
5
|
-
import scipy.constants as codata
|
|
6
|
-
|
|
7
|
-
cte = codata.e/(2*numpy.pi*codata.electron_mass*codata.c)
|
|
8
|
-
|
|
9
|
-
from syned.storage_ring.magnetic_structures.insertion_device import InsertionDevice
|
|
10
|
-
|
|
11
|
-
class Undulator(InsertionDevice):
|
|
12
|
-
"""
|
|
13
|
-
Constructor.
|
|
14
|
-
|
|
15
|
-
Parameters
|
|
16
|
-
----------
|
|
17
|
-
K_vertical : float, optional
|
|
18
|
-
The deflection K parameter corresponding to magnetic field in the vertical direction.
|
|
19
|
-
K_horizontal : float, optional
|
|
20
|
-
The deflection K parameter corresponding to magnetic field in the horizontal direction.
|
|
21
|
-
period_length : float, optional
|
|
22
|
-
The ID period in m.
|
|
23
|
-
number_of_periods : float, optional
|
|
24
|
-
The number of periods. It may be a float, considering that number_of_periods = ID_length / period_length.
|
|
25
|
-
|
|
26
|
-
"""
|
|
27
|
-
|
|
28
|
-
def __init__(self,
|
|
29
|
-
K_vertical = 0.0,
|
|
30
|
-
K_horizontal = 0.0,
|
|
31
|
-
period_length = 0.0,
|
|
32
|
-
number_of_periods = 1.0):
|
|
33
|
-
InsertionDevice.__init__(self, K_vertical, K_horizontal, period_length, number_of_periods)
|
|
34
|
-
|
|
35
|
-
def resonance_wavelength(self, gamma, theta_x=0.0, theta_z=0.0, harmonic=1.0):
|
|
36
|
-
"""
|
|
37
|
-
Returns the resonant wavelength in m.
|
|
38
|
-
|
|
39
|
-
Parameters
|
|
40
|
-
----------
|
|
41
|
-
gamma : float
|
|
42
|
-
The gamma or Lorentz factor of the electron beam.
|
|
43
|
-
theta_x : float, optional
|
|
44
|
-
The angle along the horizontal direction in rad.
|
|
45
|
-
theta_z : float, optional
|
|
46
|
-
The angle along the vertical direction in rad.
|
|
47
|
-
harmonic : float, optional
|
|
48
|
-
The harmonic number (1,2,3...) to be considered.
|
|
49
|
-
|
|
50
|
-
Returns
|
|
51
|
-
-------
|
|
52
|
-
float
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
"""
|
|
56
|
-
wavelength = (self.period_length() / (2.0 * gamma **2)) * \
|
|
57
|
-
(1 + self.K_vertical()**2 / 2.0 + self.K_horizontal()**2 / 2.0 + \
|
|
58
|
-
gamma**2 * (theta_x**2 + theta_z**2))
|
|
59
|
-
|
|
60
|
-
return wavelength / harmonic
|
|
61
|
-
|
|
62
|
-
def resonance_frequency(self, gamma, theta_x=0.0, theta_z=0.0, harmonic=1.0):
|
|
63
|
-
"""
|
|
64
|
-
Returns the resonant frequency in Hz.
|
|
65
|
-
|
|
66
|
-
Parameters
|
|
67
|
-
----------
|
|
68
|
-
gamma : float
|
|
69
|
-
The gamma or Lorentz factor of the electron beam.
|
|
70
|
-
theta_x : float, optional
|
|
71
|
-
The angle along the horizontal direction in rad.
|
|
72
|
-
theta_z : float, optional
|
|
73
|
-
The angle along the vertical direction in rad.
|
|
74
|
-
harmonic : float, optional
|
|
75
|
-
The harmonic number (1,2,3...) to be considered.
|
|
76
|
-
|
|
77
|
-
Returns
|
|
78
|
-
-------
|
|
79
|
-
float
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
"""
|
|
83
|
-
frequency = codata.c / self.resonance_wavelength(gamma, theta_x, theta_z)
|
|
84
|
-
|
|
85
|
-
return frequency * harmonic
|
|
86
|
-
|
|
87
|
-
def resonance_energy(self, gamma, theta_x=0.0, theta_z=0.0, harmonic=1.0):
|
|
88
|
-
"""
|
|
89
|
-
Returns the resonant energy in eV.
|
|
90
|
-
|
|
91
|
-
Parameters
|
|
92
|
-
----------
|
|
93
|
-
gamma : float
|
|
94
|
-
The gamma or Lorentz factor of the electron beam.
|
|
95
|
-
theta_x : float, optional
|
|
96
|
-
The angle along the horizontal direction in rad.
|
|
97
|
-
theta_z : float, optional
|
|
98
|
-
The angle along the vertical direction in rad.
|
|
99
|
-
harmonic : float, optional
|
|
100
|
-
The harmonic number (1,2,3...) to be considered.
|
|
101
|
-
|
|
102
|
-
Returns
|
|
103
|
-
-------
|
|
104
|
-
float
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
"""
|
|
108
|
-
energy_in_ev = codata.h * self.resonance_frequency(gamma, theta_x, theta_z) / codata.e
|
|
109
|
-
|
|
110
|
-
return energy_in_ev*harmonic
|
|
111
|
-
|
|
112
|
-
def gaussian_central_cone_aperture(self, gamma, n=1.0):
|
|
113
|
-
"""
|
|
114
|
-
Returns the approximated angular width (sigma) in rad of the central cone.
|
|
115
|
-
|
|
116
|
-
Parameters
|
|
117
|
-
----------
|
|
118
|
-
gamma : float
|
|
119
|
-
The gamma or Lorentz factor of the electron beam.
|
|
120
|
-
n : float, optional
|
|
121
|
-
The harmonic number (1,2,3...) to be considered.
|
|
122
|
-
|
|
123
|
-
Returns
|
|
124
|
-
-------
|
|
125
|
-
float
|
|
126
|
-
|
|
127
|
-
References
|
|
128
|
-
----------
|
|
129
|
-
Eq. 15 in https://xdb.lbl.gov/Section2/Sec_2-1.html
|
|
130
|
-
|
|
131
|
-
"""
|
|
132
|
-
return (1/gamma)*numpy.sqrt((1.0/(2.0*n*self.number_of_periods())) * (1.0 + self.K_horizontal()**2/2.0 + self.K_vertical()**2/2.0))
|
|
133
|
-
|
|
134
|
-
@classmethod
|
|
135
|
-
def initialize_as_vertical_undulator(cls, K = 0.0, period_length = 0.0, periods_number = 1.0, **params):
|
|
136
|
-
"""
|
|
137
|
-
Create an undulator with vertical magnetic field.
|
|
138
|
-
|
|
139
|
-
Parameters
|
|
140
|
-
----------
|
|
141
|
-
K : float, optional
|
|
142
|
-
The deflection K parameter corresponding to magnetic field in the vertical direction.
|
|
143
|
-
period_length : float, optional
|
|
144
|
-
The ID period in m.
|
|
145
|
-
periods_number : float, optional
|
|
146
|
-
The number of periods. It may be a float, considering that number_of_periods = ID_length / period_length.
|
|
147
|
-
params : other parameters accepted by Undulator.
|
|
148
|
-
|
|
149
|
-
Returns
|
|
150
|
-
-------
|
|
151
|
-
instance of Undulator
|
|
152
|
-
|
|
153
|
-
"""
|
|
154
|
-
return cls(K_vertical=K,
|
|
155
|
-
K_horizontal=0.0,
|
|
156
|
-
period_length=period_length,
|
|
157
|
-
number_of_periods=periods_number, **params)
|
|
158
|
-
#
|
|
159
|
-
#
|
|
160
|
-
#
|
|
161
|
-
|
|
162
|
-
def get_sigmas_radiation(self, gamma, harmonic=1.0):
|
|
163
|
-
"""
|
|
164
|
-
Returns the approximated radiation sigmas (size and divergence).
|
|
165
|
-
|
|
166
|
-
Parameters
|
|
167
|
-
----------
|
|
168
|
-
gamma : float
|
|
169
|
-
The gamma or Lorentz factor of the electron beam.
|
|
170
|
-
harmonic : float, optional
|
|
171
|
-
The harmonic number (1,2,3...) to be considered.
|
|
172
|
-
|
|
173
|
-
Returns
|
|
174
|
-
-------
|
|
175
|
-
tuple
|
|
176
|
-
(sigma_in_real_space [m], sigma_in_divergence_space [rad])
|
|
177
|
-
|
|
178
|
-
References
|
|
179
|
-
----------
|
|
180
|
-
|
|
181
|
-
See formulas 25 & 30 in Elleaume (Onuki & Elleaume) for the calculated sizes of the photon undulator beam.
|
|
182
|
-
Onuki & Elleaume "Undulators, Wigglers and Their Applications" (2002) CRC Press.
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
"""
|
|
186
|
-
# calculate sizes of the photon undulator beam
|
|
187
|
-
# see formulas 25 & 30 in Elleaume (Onuki & Elleaume)
|
|
188
|
-
photon_energy = self.resonance_energy(gamma,harmonic=harmonic)
|
|
189
|
-
lambdan = 1e-10 * codata.h * codata.c / codata.e * 1e10 / photon_energy # in m
|
|
190
|
-
sigma_r = 2.740 / 4 / numpy.pi * numpy.sqrt(lambdan * self.number_of_periods() * self.period_length())
|
|
191
|
-
sigma_r_prime = 0.69 * numpy.sqrt(lambdan / (self.length()))
|
|
192
|
-
return sigma_r, sigma_r_prime
|
|
193
|
-
|
|
194
|
-
def get_resonance_ring(self, gamma, harmonic=1.0, ring_order=1):
|
|
195
|
-
"""
|
|
196
|
-
Return the angular position of the rings at resonance.
|
|
197
|
-
|
|
198
|
-
Parameters
|
|
199
|
-
----------
|
|
200
|
-
gamma : float
|
|
201
|
-
The gamma or Lorentz factor of the electron beam.
|
|
202
|
-
harmonic : float, optional
|
|
203
|
-
The harmonic number (1,2,3...) to be considered.
|
|
204
|
-
ring_order : int, optional
|
|
205
|
-
The ring order (1,2,3...)
|
|
206
|
-
|
|
207
|
-
Returns
|
|
208
|
-
-------
|
|
209
|
-
float
|
|
210
|
-
The angular position in rad.
|
|
211
|
-
|
|
212
|
-
"""
|
|
213
|
-
K_value = numpy.sqrt( self.K_vertical()**2 + self.K_horizontal()**2)
|
|
214
|
-
return 1.0 / gamma * numpy.sqrt( ring_order / harmonic * (1 + 0.5 * K_value**2) )
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
def undulator_full_emitted_power(self, gamma, ring_current):
|
|
218
|
-
"""
|
|
219
|
-
Returns the total emitted power by an undulator.
|
|
220
|
-
|
|
221
|
-
Parameters
|
|
222
|
-
----------
|
|
223
|
-
gamma : float
|
|
224
|
-
The gamma or Lorentz factor of the electron beam.
|
|
225
|
-
ring_current : float
|
|
226
|
-
The current of the electron beam in A.
|
|
227
|
-
|
|
228
|
-
Returns
|
|
229
|
-
-------
|
|
230
|
-
float
|
|
231
|
-
The power in W.
|
|
232
|
-
|
|
233
|
-
References
|
|
234
|
-
----------
|
|
235
|
-
Eq. 18 in https://xdb.lbl.gov/Section2/Sec_2-1.html
|
|
236
|
-
|
|
237
|
-
"""
|
|
238
|
-
ptot = (self.number_of_periods() / 6) * codata.value('characteristic impedance of vacuum') * \
|
|
239
|
-
ring_current * codata.e * 2 * numpy.pi * codata.c * gamma**2 * \
|
|
240
|
-
(self.K_vertical()**2 + self.K_horizontal()**2) / self.period_length()
|
|
241
|
-
return ptot
|
|
242
|
-
|
|
243
|
-
def get_photon_sizes_and_divergences(self, syned_electron_beam, harmonic=1):
|
|
244
|
-
"""
|
|
245
|
-
Return the photon beam sizes and divergences.
|
|
246
|
-
|
|
247
|
-
They are calculates as a convolution of the radiation sigmas (get_sigmas_radiation()) and
|
|
248
|
-
the electron beam sizes.
|
|
249
|
-
|
|
250
|
-
Parameters
|
|
251
|
-
----------
|
|
252
|
-
syned_electron_beam : instance of ElectronBeam
|
|
253
|
-
The electron bean used to get the electron beam sizes and electron energy.
|
|
254
|
-
harmonic : int
|
|
255
|
-
The harmonic number (1,2,3...).
|
|
256
|
-
|
|
257
|
-
Returns
|
|
258
|
-
-------
|
|
259
|
-
tuple
|
|
260
|
-
(Sx [m], Sz [m], Sxp [rad], Szp [rad])
|
|
261
|
-
|
|
262
|
-
"""
|
|
263
|
-
sr,srp = self.get_sigmas_radiation(syned_electron_beam.gamma(), harmonic=harmonic)
|
|
264
|
-
sx,sxp,sz,szp = syned_electron_beam.get_sigmas_all()
|
|
265
|
-
|
|
266
|
-
Sx = numpy.sqrt( sx**2 + sr**2)
|
|
267
|
-
Sz = numpy.sqrt( sz**2 + sr**2)
|
|
268
|
-
Sxp = numpy.sqrt( sxp**2 + srp**2)
|
|
269
|
-
Szp = numpy.sqrt( szp**2 + srp**2)
|
|
270
|
-
|
|
271
|
-
return Sx,Sz,Sxp,Szp
|
|
272
|
-
|
|
273
|
-
def get_K_from_photon_energy(self, photon_energy, gamma, harmonic=1):
|
|
274
|
-
"""
|
|
275
|
-
Calculate K for a given resonance energy.
|
|
276
|
-
|
|
277
|
-
Parameters
|
|
278
|
-
----------
|
|
279
|
-
photon_energy : float
|
|
280
|
-
The photon energy in eV.
|
|
281
|
-
gamma : float
|
|
282
|
-
The electron gamma (Lorentz factor).
|
|
283
|
-
harmonic : int
|
|
284
|
-
The harmonic number.
|
|
285
|
-
|
|
286
|
-
Returns
|
|
287
|
-
-------
|
|
288
|
-
float
|
|
289
|
-
|
|
290
|
-
"""
|
|
291
|
-
m2ev = codata.c * codata.h / codata.e
|
|
292
|
-
wavelength = harmonic * m2ev / photon_energy
|
|
293
|
-
return numpy.sqrt(2 * (((wavelength * 2 * gamma**2) / self.period_length()) - 1))
|
|
294
|
-
|
|
295
|
-
def approximated_coherent_fraction_horizontal(self, syned_electron_beam, harmonic=1):
|
|
296
|
-
"""
|
|
297
|
-
Gets the approximated coherent fraction at resonance in the horizontal direction.
|
|
298
|
-
|
|
299
|
-
It is calculated as the ratio of the phase space of the coherent emission (zero emittance) over
|
|
300
|
-
the actual emission (finite emittance).
|
|
301
|
-
|
|
302
|
-
Parameters
|
|
303
|
-
----------
|
|
304
|
-
syned_electron_beam : instance of ElectronBeam
|
|
305
|
-
The electron bean used to get the electron beam sizes and electron energy.
|
|
306
|
-
harmonic : int
|
|
307
|
-
The harmonic number (1,2,3...).
|
|
308
|
-
|
|
309
|
-
Returns
|
|
310
|
-
-------
|
|
311
|
-
float
|
|
312
|
-
|
|
313
|
-
"""
|
|
314
|
-
Sx,Sy,Sxp,Syp = self.get_photon_sizes_and_divergences(syned_electron_beam, harmonic=harmonic)
|
|
315
|
-
srad,sradp = self.get_sigmas_radiation(syned_electron_beam.gamma(), harmonic=harmonic)
|
|
316
|
-
return srad * sradp / ( Sx * Sxp)
|
|
317
|
-
|
|
318
|
-
def approximated_coherent_fraction_vertical(self, syned_electron_beam,harmonic=1):
|
|
319
|
-
"""
|
|
320
|
-
Gets the approximated coherent fraction at resonance in the vertical direction.
|
|
321
|
-
|
|
322
|
-
It is calculated as the ratio of the phase space of the coherent emission (zero emittance) over
|
|
323
|
-
the actual emission (finite emittance).
|
|
324
|
-
|
|
325
|
-
Parameters
|
|
326
|
-
----------
|
|
327
|
-
syned_electron_beam : instance of ElectronBeam
|
|
328
|
-
The electron bean used to get the electron beam sizes and electron energy.
|
|
329
|
-
harmonic : int
|
|
330
|
-
The harmonic number (1,2,3...).
|
|
331
|
-
|
|
332
|
-
Returns
|
|
333
|
-
-------
|
|
334
|
-
float
|
|
335
|
-
|
|
336
|
-
"""
|
|
337
|
-
Sx,Sy,Sxp,Syp = self.get_photon_sizes_and_divergences(syned_electron_beam, harmonic=harmonic)
|
|
338
|
-
srad,sradp = self.get_sigmas_radiation(syned_electron_beam.gamma(), harmonic=harmonic)
|
|
339
|
-
return srad * sradp / ( Sy * Syp)
|
|
340
|
-
|
|
341
|
-
def approximated_coherent_fraction(self, syned_electron_beam, harmonic=1):
|
|
342
|
-
"""
|
|
343
|
-
Gets the approximated coherent fraction at resonance.
|
|
344
|
-
|
|
345
|
-
It is calculated as the product of the horizontal and vertical coherent fractions.
|
|
346
|
-
|
|
347
|
-
Parameters
|
|
348
|
-
----------
|
|
349
|
-
syned_electron_beam : instance of ElectronBeam
|
|
350
|
-
The electron bean used to get the electron beam sizes and electron energy.
|
|
351
|
-
harmonic : int
|
|
352
|
-
The harmonic number (1,2,3...).
|
|
353
|
-
|
|
354
|
-
Returns
|
|
355
|
-
-------
|
|
356
|
-
float
|
|
357
|
-
|
|
358
|
-
"""
|
|
359
|
-
return self.approximated_coherent_fraction_horizontal(syned_electron_beam, harmonic=harmonic) * \
|
|
360
|
-
self.approximated_coherent_fraction_vertical(syned_electron_beam, harmonic=harmonic)
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
if __name__ == "__main__":
|
|
364
|
-
|
|
365
|
-
a = Undulator(number_of_periods=61.5, period_length=0.057)
|
|
366
|
-
a.set_K_vertical_from_magnetic_field(0.187782)
|
|
367
|
-
|
|
368
|
-
print(a._K_vertical)
|
|
369
|
-
|
|
370
|
-
print (a.resonance_energy(gamma=5870.8540997356595))
|
|
371
|
-
|
|
372
|
-
fd = a.to_full_dictionary()
|
|
373
|
-
dict = a.to_dictionary()
|
|
374
|
-
|
|
375
|
-
print(dict)
|
|
376
|
-
|
|
377
|
-
for key in fd:
|
|
378
|
-
print(key,fd[key][0])
|
|
379
|
-
|
|
380
|
-
for key in fd:
|
|
381
|
-
print(key,dict[key])
|
|
382
|
-
|
|
383
|
-
print(a.keys())
|
|
384
|
-
print(a.info())
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
print("####### derived quantities ##########")
|
|
388
|
-
|
|
389
|
-
from syned.storage_ring.electron_beam import ElectronBeam
|
|
390
|
-
ebeam = ElectronBeam.initialize_as_pencil_beam(energy_in_GeV=2.0,energy_spread=0.0,current=0.5)
|
|
391
|
-
|
|
392
|
-
sigmas_radiation = a.get_sigmas_radiation(ebeam.gamma(),harmonic=1.0)
|
|
393
|
-
print("sigmas radiation [m rad]:",sigmas_radiation)
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
ring = a.get_resonance_ring(ebeam.gamma(), harmonic=1.0, ring_order=1)
|
|
397
|
-
print("first ring at [rad]:",ring)
|
|
398
|
-
|
|
399
|
-
pow = a.undulator_full_emitted_power(ebeam.gamma(), ebeam.current())
|
|
400
|
-
print("Total emission [W]",pow)
|
|
401
|
-
|
|
402
|
-
sizes = a.get_photon_sizes_and_divergences(ebeam,harmonic=1)
|
|
403
|
-
print("Sizes: ",sizes)
|
|
404
|
-
|
|
405
|
-
print("Resonance: ",a.resonance_energy(ebeam.gamma()))
|
|
406
|
-
|
|
407
|
-
print("K at 444 eV",a.get_K_from_photon_energy(444.0,ebeam.gamma()))
|
|
408
|
-
print("K at 200 eV", a.get_K_from_photon_energy(200.0, ebeam.gamma()))
|
|
409
|
-
|
|
410
|
-
print("CF H V HV: ",
|
|
411
|
-
a.approximated_coherent_fraction_horizontal(ebeam,harmonic=1),
|
|
412
|
-
a.approximated_coherent_fraction_vertical(ebeam, harmonic=1),
|
|
413
|
-
a.approximated_coherent_fraction(ebeam, harmonic=1),)
|
|
1
|
+
"""
|
|
2
|
+
Implement an undulator with vertical and horizontal magnetic fields.
|
|
3
|
+
"""
|
|
4
|
+
import numpy
|
|
5
|
+
import scipy.constants as codata
|
|
6
|
+
|
|
7
|
+
cte = codata.e/(2*numpy.pi*codata.electron_mass*codata.c)
|
|
8
|
+
|
|
9
|
+
from syned.storage_ring.magnetic_structures.insertion_device import InsertionDevice
|
|
10
|
+
|
|
11
|
+
class Undulator(InsertionDevice):
|
|
12
|
+
"""
|
|
13
|
+
Constructor.
|
|
14
|
+
|
|
15
|
+
Parameters
|
|
16
|
+
----------
|
|
17
|
+
K_vertical : float, optional
|
|
18
|
+
The deflection K parameter corresponding to magnetic field in the vertical direction.
|
|
19
|
+
K_horizontal : float, optional
|
|
20
|
+
The deflection K parameter corresponding to magnetic field in the horizontal direction.
|
|
21
|
+
period_length : float, optional
|
|
22
|
+
The ID period in m.
|
|
23
|
+
number_of_periods : float, optional
|
|
24
|
+
The number of periods. It may be a float, considering that number_of_periods = ID_length / period_length.
|
|
25
|
+
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def __init__(self,
|
|
29
|
+
K_vertical = 0.0,
|
|
30
|
+
K_horizontal = 0.0,
|
|
31
|
+
period_length = 0.0,
|
|
32
|
+
number_of_periods = 1.0):
|
|
33
|
+
InsertionDevice.__init__(self, K_vertical, K_horizontal, period_length, number_of_periods)
|
|
34
|
+
|
|
35
|
+
def resonance_wavelength(self, gamma, theta_x=0.0, theta_z=0.0, harmonic=1.0):
|
|
36
|
+
"""
|
|
37
|
+
Returns the resonant wavelength in m.
|
|
38
|
+
|
|
39
|
+
Parameters
|
|
40
|
+
----------
|
|
41
|
+
gamma : float
|
|
42
|
+
The gamma or Lorentz factor of the electron beam.
|
|
43
|
+
theta_x : float, optional
|
|
44
|
+
The angle along the horizontal direction in rad.
|
|
45
|
+
theta_z : float, optional
|
|
46
|
+
The angle along the vertical direction in rad.
|
|
47
|
+
harmonic : float, optional
|
|
48
|
+
The harmonic number (1,2,3...) to be considered.
|
|
49
|
+
|
|
50
|
+
Returns
|
|
51
|
+
-------
|
|
52
|
+
float
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
"""
|
|
56
|
+
wavelength = (self.period_length() / (2.0 * gamma **2)) * \
|
|
57
|
+
(1 + self.K_vertical()**2 / 2.0 + self.K_horizontal()**2 / 2.0 + \
|
|
58
|
+
gamma**2 * (theta_x**2 + theta_z**2))
|
|
59
|
+
|
|
60
|
+
return wavelength / harmonic
|
|
61
|
+
|
|
62
|
+
def resonance_frequency(self, gamma, theta_x=0.0, theta_z=0.0, harmonic=1.0):
|
|
63
|
+
"""
|
|
64
|
+
Returns the resonant frequency in Hz.
|
|
65
|
+
|
|
66
|
+
Parameters
|
|
67
|
+
----------
|
|
68
|
+
gamma : float
|
|
69
|
+
The gamma or Lorentz factor of the electron beam.
|
|
70
|
+
theta_x : float, optional
|
|
71
|
+
The angle along the horizontal direction in rad.
|
|
72
|
+
theta_z : float, optional
|
|
73
|
+
The angle along the vertical direction in rad.
|
|
74
|
+
harmonic : float, optional
|
|
75
|
+
The harmonic number (1,2,3...) to be considered.
|
|
76
|
+
|
|
77
|
+
Returns
|
|
78
|
+
-------
|
|
79
|
+
float
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
"""
|
|
83
|
+
frequency = codata.c / self.resonance_wavelength(gamma, theta_x, theta_z)
|
|
84
|
+
|
|
85
|
+
return frequency * harmonic
|
|
86
|
+
|
|
87
|
+
def resonance_energy(self, gamma, theta_x=0.0, theta_z=0.0, harmonic=1.0):
|
|
88
|
+
"""
|
|
89
|
+
Returns the resonant energy in eV.
|
|
90
|
+
|
|
91
|
+
Parameters
|
|
92
|
+
----------
|
|
93
|
+
gamma : float
|
|
94
|
+
The gamma or Lorentz factor of the electron beam.
|
|
95
|
+
theta_x : float, optional
|
|
96
|
+
The angle along the horizontal direction in rad.
|
|
97
|
+
theta_z : float, optional
|
|
98
|
+
The angle along the vertical direction in rad.
|
|
99
|
+
harmonic : float, optional
|
|
100
|
+
The harmonic number (1,2,3...) to be considered.
|
|
101
|
+
|
|
102
|
+
Returns
|
|
103
|
+
-------
|
|
104
|
+
float
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
"""
|
|
108
|
+
energy_in_ev = codata.h * self.resonance_frequency(gamma, theta_x, theta_z) / codata.e
|
|
109
|
+
|
|
110
|
+
return energy_in_ev*harmonic
|
|
111
|
+
|
|
112
|
+
def gaussian_central_cone_aperture(self, gamma, n=1.0):
|
|
113
|
+
"""
|
|
114
|
+
Returns the approximated angular width (sigma) in rad of the central cone.
|
|
115
|
+
|
|
116
|
+
Parameters
|
|
117
|
+
----------
|
|
118
|
+
gamma : float
|
|
119
|
+
The gamma or Lorentz factor of the electron beam.
|
|
120
|
+
n : float, optional
|
|
121
|
+
The harmonic number (1,2,3...) to be considered.
|
|
122
|
+
|
|
123
|
+
Returns
|
|
124
|
+
-------
|
|
125
|
+
float
|
|
126
|
+
|
|
127
|
+
References
|
|
128
|
+
----------
|
|
129
|
+
Eq. 15 in https://xdb.lbl.gov/Section2/Sec_2-1.html
|
|
130
|
+
|
|
131
|
+
"""
|
|
132
|
+
return (1/gamma)*numpy.sqrt((1.0/(2.0*n*self.number_of_periods())) * (1.0 + self.K_horizontal()**2/2.0 + self.K_vertical()**2/2.0))
|
|
133
|
+
|
|
134
|
+
@classmethod
|
|
135
|
+
def initialize_as_vertical_undulator(cls, K = 0.0, period_length = 0.0, periods_number = 1.0, **params):
|
|
136
|
+
"""
|
|
137
|
+
Create an undulator with vertical magnetic field.
|
|
138
|
+
|
|
139
|
+
Parameters
|
|
140
|
+
----------
|
|
141
|
+
K : float, optional
|
|
142
|
+
The deflection K parameter corresponding to magnetic field in the vertical direction.
|
|
143
|
+
period_length : float, optional
|
|
144
|
+
The ID period in m.
|
|
145
|
+
periods_number : float, optional
|
|
146
|
+
The number of periods. It may be a float, considering that number_of_periods = ID_length / period_length.
|
|
147
|
+
params : other parameters accepted by Undulator.
|
|
148
|
+
|
|
149
|
+
Returns
|
|
150
|
+
-------
|
|
151
|
+
instance of Undulator
|
|
152
|
+
|
|
153
|
+
"""
|
|
154
|
+
return cls(K_vertical=K,
|
|
155
|
+
K_horizontal=0.0,
|
|
156
|
+
period_length=period_length,
|
|
157
|
+
number_of_periods=periods_number, **params)
|
|
158
|
+
#
|
|
159
|
+
#
|
|
160
|
+
#
|
|
161
|
+
|
|
162
|
+
def get_sigmas_radiation(self, gamma, harmonic=1.0):
|
|
163
|
+
"""
|
|
164
|
+
Returns the approximated radiation sigmas (size and divergence).
|
|
165
|
+
|
|
166
|
+
Parameters
|
|
167
|
+
----------
|
|
168
|
+
gamma : float
|
|
169
|
+
The gamma or Lorentz factor of the electron beam.
|
|
170
|
+
harmonic : float, optional
|
|
171
|
+
The harmonic number (1,2,3...) to be considered.
|
|
172
|
+
|
|
173
|
+
Returns
|
|
174
|
+
-------
|
|
175
|
+
tuple
|
|
176
|
+
(sigma_in_real_space [m], sigma_in_divergence_space [rad])
|
|
177
|
+
|
|
178
|
+
References
|
|
179
|
+
----------
|
|
180
|
+
|
|
181
|
+
See formulas 25 & 30 in Elleaume (Onuki & Elleaume) for the calculated sizes of the photon undulator beam.
|
|
182
|
+
Onuki & Elleaume "Undulators, Wigglers and Their Applications" (2002) CRC Press.
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
"""
|
|
186
|
+
# calculate sizes of the photon undulator beam
|
|
187
|
+
# see formulas 25 & 30 in Elleaume (Onuki & Elleaume)
|
|
188
|
+
photon_energy = self.resonance_energy(gamma,harmonic=harmonic)
|
|
189
|
+
lambdan = 1e-10 * codata.h * codata.c / codata.e * 1e10 / photon_energy # in m
|
|
190
|
+
sigma_r = 2.740 / 4 / numpy.pi * numpy.sqrt(lambdan * self.number_of_periods() * self.period_length())
|
|
191
|
+
sigma_r_prime = 0.69 * numpy.sqrt(lambdan / (self.length()))
|
|
192
|
+
return sigma_r, sigma_r_prime
|
|
193
|
+
|
|
194
|
+
def get_resonance_ring(self, gamma, harmonic=1.0, ring_order=1):
|
|
195
|
+
"""
|
|
196
|
+
Return the angular position of the rings at resonance.
|
|
197
|
+
|
|
198
|
+
Parameters
|
|
199
|
+
----------
|
|
200
|
+
gamma : float
|
|
201
|
+
The gamma or Lorentz factor of the electron beam.
|
|
202
|
+
harmonic : float, optional
|
|
203
|
+
The harmonic number (1,2,3...) to be considered.
|
|
204
|
+
ring_order : int, optional
|
|
205
|
+
The ring order (1,2,3...)
|
|
206
|
+
|
|
207
|
+
Returns
|
|
208
|
+
-------
|
|
209
|
+
float
|
|
210
|
+
The angular position in rad.
|
|
211
|
+
|
|
212
|
+
"""
|
|
213
|
+
K_value = numpy.sqrt( self.K_vertical()**2 + self.K_horizontal()**2)
|
|
214
|
+
return 1.0 / gamma * numpy.sqrt( ring_order / harmonic * (1 + 0.5 * K_value**2) )
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def undulator_full_emitted_power(self, gamma, ring_current):
|
|
218
|
+
"""
|
|
219
|
+
Returns the total emitted power by an undulator.
|
|
220
|
+
|
|
221
|
+
Parameters
|
|
222
|
+
----------
|
|
223
|
+
gamma : float
|
|
224
|
+
The gamma or Lorentz factor of the electron beam.
|
|
225
|
+
ring_current : float
|
|
226
|
+
The current of the electron beam in A.
|
|
227
|
+
|
|
228
|
+
Returns
|
|
229
|
+
-------
|
|
230
|
+
float
|
|
231
|
+
The power in W.
|
|
232
|
+
|
|
233
|
+
References
|
|
234
|
+
----------
|
|
235
|
+
Eq. 18 in https://xdb.lbl.gov/Section2/Sec_2-1.html
|
|
236
|
+
|
|
237
|
+
"""
|
|
238
|
+
ptot = (self.number_of_periods() / 6) * codata.value('characteristic impedance of vacuum') * \
|
|
239
|
+
ring_current * codata.e * 2 * numpy.pi * codata.c * gamma**2 * \
|
|
240
|
+
(self.K_vertical()**2 + self.K_horizontal()**2) / self.period_length()
|
|
241
|
+
return ptot
|
|
242
|
+
|
|
243
|
+
def get_photon_sizes_and_divergences(self, syned_electron_beam, harmonic=1):
|
|
244
|
+
"""
|
|
245
|
+
Return the photon beam sizes and divergences.
|
|
246
|
+
|
|
247
|
+
They are calculates as a convolution of the radiation sigmas (get_sigmas_radiation()) and
|
|
248
|
+
the electron beam sizes.
|
|
249
|
+
|
|
250
|
+
Parameters
|
|
251
|
+
----------
|
|
252
|
+
syned_electron_beam : instance of ElectronBeam
|
|
253
|
+
The electron bean used to get the electron beam sizes and electron energy.
|
|
254
|
+
harmonic : int
|
|
255
|
+
The harmonic number (1,2,3...).
|
|
256
|
+
|
|
257
|
+
Returns
|
|
258
|
+
-------
|
|
259
|
+
tuple
|
|
260
|
+
(Sx [m], Sz [m], Sxp [rad], Szp [rad])
|
|
261
|
+
|
|
262
|
+
"""
|
|
263
|
+
sr,srp = self.get_sigmas_radiation(syned_electron_beam.gamma(), harmonic=harmonic)
|
|
264
|
+
sx,sxp,sz,szp = syned_electron_beam.get_sigmas_all()
|
|
265
|
+
|
|
266
|
+
Sx = numpy.sqrt( sx**2 + sr**2)
|
|
267
|
+
Sz = numpy.sqrt( sz**2 + sr**2)
|
|
268
|
+
Sxp = numpy.sqrt( sxp**2 + srp**2)
|
|
269
|
+
Szp = numpy.sqrt( szp**2 + srp**2)
|
|
270
|
+
|
|
271
|
+
return Sx,Sz,Sxp,Szp
|
|
272
|
+
|
|
273
|
+
def get_K_from_photon_energy(self, photon_energy, gamma, harmonic=1):
|
|
274
|
+
"""
|
|
275
|
+
Calculate K for a given resonance energy.
|
|
276
|
+
|
|
277
|
+
Parameters
|
|
278
|
+
----------
|
|
279
|
+
photon_energy : float
|
|
280
|
+
The photon energy in eV.
|
|
281
|
+
gamma : float
|
|
282
|
+
The electron gamma (Lorentz factor).
|
|
283
|
+
harmonic : int
|
|
284
|
+
The harmonic number.
|
|
285
|
+
|
|
286
|
+
Returns
|
|
287
|
+
-------
|
|
288
|
+
float
|
|
289
|
+
|
|
290
|
+
"""
|
|
291
|
+
m2ev = codata.c * codata.h / codata.e
|
|
292
|
+
wavelength = harmonic * m2ev / photon_energy
|
|
293
|
+
return numpy.sqrt(2 * (((wavelength * 2 * gamma**2) / self.period_length()) - 1))
|
|
294
|
+
|
|
295
|
+
def approximated_coherent_fraction_horizontal(self, syned_electron_beam, harmonic=1):
|
|
296
|
+
"""
|
|
297
|
+
Gets the approximated coherent fraction at resonance in the horizontal direction.
|
|
298
|
+
|
|
299
|
+
It is calculated as the ratio of the phase space of the coherent emission (zero emittance) over
|
|
300
|
+
the actual emission (finite emittance).
|
|
301
|
+
|
|
302
|
+
Parameters
|
|
303
|
+
----------
|
|
304
|
+
syned_electron_beam : instance of ElectronBeam
|
|
305
|
+
The electron bean used to get the electron beam sizes and electron energy.
|
|
306
|
+
harmonic : int
|
|
307
|
+
The harmonic number (1,2,3...).
|
|
308
|
+
|
|
309
|
+
Returns
|
|
310
|
+
-------
|
|
311
|
+
float
|
|
312
|
+
|
|
313
|
+
"""
|
|
314
|
+
Sx,Sy,Sxp,Syp = self.get_photon_sizes_and_divergences(syned_electron_beam, harmonic=harmonic)
|
|
315
|
+
srad,sradp = self.get_sigmas_radiation(syned_electron_beam.gamma(), harmonic=harmonic)
|
|
316
|
+
return srad * sradp / ( Sx * Sxp)
|
|
317
|
+
|
|
318
|
+
def approximated_coherent_fraction_vertical(self, syned_electron_beam,harmonic=1):
|
|
319
|
+
"""
|
|
320
|
+
Gets the approximated coherent fraction at resonance in the vertical direction.
|
|
321
|
+
|
|
322
|
+
It is calculated as the ratio of the phase space of the coherent emission (zero emittance) over
|
|
323
|
+
the actual emission (finite emittance).
|
|
324
|
+
|
|
325
|
+
Parameters
|
|
326
|
+
----------
|
|
327
|
+
syned_electron_beam : instance of ElectronBeam
|
|
328
|
+
The electron bean used to get the electron beam sizes and electron energy.
|
|
329
|
+
harmonic : int
|
|
330
|
+
The harmonic number (1,2,3...).
|
|
331
|
+
|
|
332
|
+
Returns
|
|
333
|
+
-------
|
|
334
|
+
float
|
|
335
|
+
|
|
336
|
+
"""
|
|
337
|
+
Sx,Sy,Sxp,Syp = self.get_photon_sizes_and_divergences(syned_electron_beam, harmonic=harmonic)
|
|
338
|
+
srad,sradp = self.get_sigmas_radiation(syned_electron_beam.gamma(), harmonic=harmonic)
|
|
339
|
+
return srad * sradp / ( Sy * Syp)
|
|
340
|
+
|
|
341
|
+
def approximated_coherent_fraction(self, syned_electron_beam, harmonic=1):
|
|
342
|
+
"""
|
|
343
|
+
Gets the approximated coherent fraction at resonance.
|
|
344
|
+
|
|
345
|
+
It is calculated as the product of the horizontal and vertical coherent fractions.
|
|
346
|
+
|
|
347
|
+
Parameters
|
|
348
|
+
----------
|
|
349
|
+
syned_electron_beam : instance of ElectronBeam
|
|
350
|
+
The electron bean used to get the electron beam sizes and electron energy.
|
|
351
|
+
harmonic : int
|
|
352
|
+
The harmonic number (1,2,3...).
|
|
353
|
+
|
|
354
|
+
Returns
|
|
355
|
+
-------
|
|
356
|
+
float
|
|
357
|
+
|
|
358
|
+
"""
|
|
359
|
+
return self.approximated_coherent_fraction_horizontal(syned_electron_beam, harmonic=harmonic) * \
|
|
360
|
+
self.approximated_coherent_fraction_vertical(syned_electron_beam, harmonic=harmonic)
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
if __name__ == "__main__":
|
|
364
|
+
|
|
365
|
+
a = Undulator(number_of_periods=61.5, period_length=0.057)
|
|
366
|
+
a.set_K_vertical_from_magnetic_field(0.187782)
|
|
367
|
+
|
|
368
|
+
print(a._K_vertical)
|
|
369
|
+
|
|
370
|
+
print (a.resonance_energy(gamma=5870.8540997356595))
|
|
371
|
+
|
|
372
|
+
fd = a.to_full_dictionary()
|
|
373
|
+
dict = a.to_dictionary()
|
|
374
|
+
|
|
375
|
+
print(dict)
|
|
376
|
+
|
|
377
|
+
for key in fd:
|
|
378
|
+
print(key,fd[key][0])
|
|
379
|
+
|
|
380
|
+
for key in fd:
|
|
381
|
+
print(key,dict[key])
|
|
382
|
+
|
|
383
|
+
print(a.keys())
|
|
384
|
+
print(a.info())
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
print("####### derived quantities ##########")
|
|
388
|
+
|
|
389
|
+
from syned.storage_ring.electron_beam import ElectronBeam
|
|
390
|
+
ebeam = ElectronBeam.initialize_as_pencil_beam(energy_in_GeV=2.0,energy_spread=0.0,current=0.5)
|
|
391
|
+
|
|
392
|
+
sigmas_radiation = a.get_sigmas_radiation(ebeam.gamma(),harmonic=1.0)
|
|
393
|
+
print("sigmas radiation [m rad]:",sigmas_radiation)
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+
ring = a.get_resonance_ring(ebeam.gamma(), harmonic=1.0, ring_order=1)
|
|
397
|
+
print("first ring at [rad]:",ring)
|
|
398
|
+
|
|
399
|
+
pow = a.undulator_full_emitted_power(ebeam.gamma(), ebeam.current())
|
|
400
|
+
print("Total emission [W]",pow)
|
|
401
|
+
|
|
402
|
+
sizes = a.get_photon_sizes_and_divergences(ebeam,harmonic=1)
|
|
403
|
+
print("Sizes: ",sizes)
|
|
404
|
+
|
|
405
|
+
print("Resonance: ",a.resonance_energy(ebeam.gamma()))
|
|
406
|
+
|
|
407
|
+
print("K at 444 eV",a.get_K_from_photon_energy(444.0,ebeam.gamma()))
|
|
408
|
+
print("K at 200 eV", a.get_K_from_photon_energy(200.0, ebeam.gamma()))
|
|
409
|
+
|
|
410
|
+
print("CF H V HV: ",
|
|
411
|
+
a.approximated_coherent_fraction_horizontal(ebeam,harmonic=1),
|
|
412
|
+
a.approximated_coherent_fraction_vertical(ebeam, harmonic=1),
|
|
413
|
+
a.approximated_coherent_fraction(ebeam, harmonic=1),)
|