Skip to content

Commit feed52a

Browse files
authored
Merge pull request #25 from lhcopt/pymaskdoclumi
Pymaskdoclumi
2 parents a963aca + 7557781 commit feed52a

File tree

13 files changed

+1300
-63
lines changed

13 files changed

+1300
-63
lines changed

.gitignore

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,14 @@
1+
*.parquet
2+
fort.2
3+
fort.3
4+
fort.8
5+
fort.16
6+
fort.34
7+
fc.2
8+
fc.3
9+
fc.8
10+
fc.16
11+
fc.34
112
beambeam_macros
213
db5
314
error_table.tfs

pymask/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@
22
from .madpoint import *
33
from .madxp import *
44
from .pymasktools import *
5+
from .luminosity import *

pymask/luminosity.py

Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
1+
import numpy as np
2+
from scipy import integrate
3+
from scipy.constants import c as clight
4+
from scipy.constants import e as qe
5+
6+
7+
def beta(z, beta0, alpha_z0):
8+
'''Beta function in drift space'''
9+
return beta0-2*alpha_z0*z+(1+alpha_z0**2)/beta0*z**2
10+
11+
def dispersion(z, d0, dp0):
12+
'''Dispersion in drift space'''
13+
return d0+z*dp0
14+
15+
def sigma(beta, epsilon0, betagamma):
16+
'''Betatronic sigma'''
17+
return np.sqrt(beta*epsilon0/betagamma)
18+
19+
def L(f, nb,
20+
N1, N2,
21+
x_1, x_2,
22+
y_1, y_2,
23+
px_1, px_2,
24+
py_1, py_2,
25+
energy_tot1, energy_tot2,
26+
deltap_p0_1, deltap_p0_2,
27+
epsilon_x1, epsilon_x2,
28+
epsilon_y1, epsilon_y2,
29+
sigma_z1, sigma_z2,
30+
beta_x1, beta_x2,
31+
beta_y1, beta_y2,
32+
alpha_x1, alpha_x2,
33+
alpha_y1, alpha_y2,
34+
dx_1, dx_2,
35+
dy_1, dy_2,
36+
dpx_1, dpx_2,
37+
dpy_1, dpy_2,
38+
CC_V_x_1=0, CC_f_x_1=0, CC_phase_x_1=0,
39+
CC_V_x_2=0, CC_f_x_2=0, CC_phase_x_2=0,
40+
CC_V_y_1=0, CC_f_y_1=0, CC_phase_y_1=0,
41+
CC_V_y_2=0, CC_f_y_2=0, CC_phase_y_2=0,
42+
R12_1=0, R22_1=0, R34_1=0, R44_1=0,
43+
R12_2=0, R22_2=0, R34_2=0, R44_2=0,
44+
verbose=False, sigma_integration=3):
45+
'''
46+
Returns luminosity in Hz/cm^2.
47+
f: revolution frequency
48+
nb: number of colliding bunch per beam in the specific Interaction Point (IP).
49+
N1,2: B1,2 number of particle per bunch
50+
x,y,1,2: horizontal/vertical position at the IP of B1,2, as defined in MADX [m]
51+
px,y,1,2: px,py at the IP of B1,2, as defined in MADX
52+
energy_tot1,2: total energy of the B1,2 [GeV]
53+
deltap_p0_1,2: rms momentum spread of B1,2 (formulas assume Gaussian off-momentum distribution)
54+
epsilon_x,y,1,2: horizontal/vertical normalized emittances of B1,2 [m rad]
55+
sigma_z1,2: rms longitudinal spread in z of B1,2 [m]
56+
beta_x,y,1,2: horizontal/vertical beta-function at IP of B1,2 [m]
57+
alpha_x,y,1,2: horizontal/vertical alpha-function at IP of B1,2
58+
dx,y_1,2: horizontal/vertical dispersion-function at IP of B1,2, as defined in MADX [m]
59+
dpx,y_1,2: horizontal/vertical differential-dispersion-function IP of B1,2, as defined in MADX
60+
CC_V_x,y,1,2: B1,2 H/V CC total of the cavities that the beam sees before reaching the IP [V]
61+
CC_f_x,y,1,2: B1,2 H/V CC frequency of cavities that the beam sees before reaching the IP [Hz]
62+
CC_phase_1,2: B1,2 H/V CC phase of cavities that the beam sees before reaching the IP.
63+
Sinusoidal function with respect to the center of the bunch is assumed.
64+
Therefore 0 rad means no kick for the central longitudinal slice [rad]
65+
RAB_1,2: B1,2 equivalent H/V linear transfer matrix coefficients between the CC
66+
that the beam sees before reaching the IP and IP itself [SI units]
67+
verbose: to have verbose output
68+
sigma_integration: the number of sigma consider for the integration
69+
(taken into account only if CC(s) is/are present)
70+
In MAD-X px is p_x/p_0 (p_x is the x-component of the momentum and p_0 is the design momentum).
71+
In our approximation we use the paraxial approximation: p_0~p_z so px is an angle.
72+
Similar arguments holds for py.
73+
In MAD-X, dx and dpx are the literature dispersion and is derivative in s divided by the relatistic beta.
74+
In fact, since pt=beta*deltap, where beta is the relativistic Lorentz factor,
75+
those functions given by MAD-X must be multiplied by beta a number of times equal to the order of
76+
the derivative to find the functions given in the literature.
77+
To note that dpx is normalized by the reference momentum (p_s) and not the design momentum (p_0),
78+
ps = p0(1+deltap). We assume that dpx is the z-derivative of the px.
79+
'''
80+
81+
gamma1 = energy_tot1 / 0.93827231 # To be generalized for ions
82+
br_1 = np.sqrt(1-1/gamma1**2)
83+
betagamma_1 = br_1*gamma1
84+
85+
gamma2 = energy_tot2 / 0.93827231 # To be generalized for ions
86+
br_2 = np.sqrt(1-1/gamma2**2)
87+
betagamma_2 = br_2*gamma2
88+
89+
q = qe # To be generalized for ions
90+
91+
# module of B1 speed
92+
v0_1=br_1*clight
93+
# paraxial hypothesis
94+
vx_1=v0_1*px_1
95+
vy_1=v0_1*py_1
96+
vz_1=v0_1*np.sqrt(1-px_1**2-py_1**2)
97+
v_1=np.array([vx_1, vy_1, vz_1])
98+
99+
v0_2=br_2*clight # module of B2 speed
100+
# Assuming counter rotating B2 ('-' sign)
101+
vx_2=-v0_2*px_2
102+
vy_2=-v0_2*py_2
103+
# assuming px_2**2+py_2**2 < 1
104+
vz_2=-v0_2*np.sqrt(1-px_2**2-py_2**2)
105+
v_2=np.array([vx_2, vy_2, vz_2])
106+
107+
if verbose:
108+
print(f'B1 velocity vector:{v_1}')
109+
print(f'B2 velocity vector:{v_2}')
110+
111+
diff_v = v_1-v_2
112+
cross_v= np.cross(v_1, v_2)
113+
114+
# normalized to get 1 for the ideal case
115+
# NB we assume px_1 and py_1 constant along the z-slices
116+
# NOT TRUE FOR CC! In any case the Moeller efficiency is almost equal to 1 in most cases...
117+
Moeller_efficiency=np.sqrt(clight**2*np.dot(diff_v,diff_v)-np.dot(cross_v,cross_v))/clight**2/2
118+
119+
def sx1(z):
120+
'''The sigma_x of B1, quadratic sum of betatronic and dispersive sigma'''
121+
return np.sqrt(sigma(beta(z, beta_x1, alpha_x1), epsilon_x1, betagamma_1)**2 \
122+
+ (dispersion(z, br_1*dx_1, br_1*dpx_1)*deltap_p0_1)**2)
123+
124+
def sy1(z):
125+
'''The sigma_y of B1, quadratic sum of betatronic and dispersive sigma'''
126+
return np.sqrt(sigma(beta(z, beta_y1, alpha_y1), epsilon_y1, betagamma_1)**2 \
127+
+ (dispersion(z, br_1*dy_1, br_1*dpy_1)*deltap_p0_1)**2)
128+
129+
def sx2(z):
130+
'''The sigma_x of B2, quadratic sum of betatronic and dispersive sigma'''
131+
return np.sqrt(sigma(beta(z, beta_x2, alpha_x2), epsilon_x2, betagamma_2)**2 \
132+
+ (dispersion(z, br_2*dx_2, br_2*dpx_2)*deltap_p0_2)**2)
133+
134+
def sy2(z):
135+
'''The sigma_y of B2, quadratic sum of betatronic and dispersive sigma'''
136+
return np.sqrt(sigma(beta(z, beta_y2, alpha_y2), epsilon_y2, betagamma_2)**2 \
137+
+ (dispersion(z, br_2*dy_2, br_2*dpy_2)*deltap_p0_2)**2)
138+
139+
sigma_z=np.max([sigma_z1, sigma_z2])
140+
141+
if not [CC_V_x_1, CC_V_y_1, CC_V_x_2, CC_V_y_2]==[0,0,0,0]:
142+
def theta_x_1(delta_z):
143+
# Eq. 3 of https://espace.cern.ch/acc-tec-sector/Chamonix/Chamx2012/papers/RC_9_04.pdf
144+
return CC_V_x_1/energy_tot1/1e9*np.sin(CC_phase_x_1 + 2*np.pi*CC_f_x_1/c*delta_z)
145+
146+
def theta_y_1(delta_z):
147+
return CC_V_y_1/energy_tot1/1e9*np.sin(CC_phase_y_1 + 2*np.pi*CC_f_y_1/c*delta_z)
148+
149+
def theta_x_2(delta_z):
150+
return CC_V_x_2/energy_tot2/1e9*np.sin(CC_phase_x_2 + 2*np.pi*CC_f_x_2/c*delta_z)
151+
152+
def theta_y_2(delta_z):
153+
return CC_V_y_2/energy_tot2/1e9*np.sin(CC_phase_y_2 + 2*np.pi*CC_f_y_2/c*delta_z)
154+
155+
def mx1(z, t):
156+
'''The mu_x of B1 as straight line'''
157+
return x_1 + R12_1*theta_x_1(z-c*t) + (px_1+R22_1*theta_x_1(z-c*t))*z
158+
159+
def my1(z, t):
160+
'''The mu_y of B1 as straight line'''
161+
return y_1 + R34_1*theta_y_1(z-c*t) + (py_1+R44_1*theta_y_1(z-c*t))*z
162+
163+
def mx2(z, t):
164+
'''The mu_x of B2 as straight line'''
165+
return x_2 + R12_2*theta_x_2(z+c*t) + (px_2+R22_2*theta_x_2(z+c*t))*z
166+
167+
def my2(z, t):
168+
'''The mu_y of B2 as straight line'''
169+
return y_2 + R34_2*theta_y_2(z+c*t) + (py_2+R44_2*theta_y_2(z+c*t))*z
170+
171+
def kernel_double_integral(t, z):
172+
return np.exp(0.5*(-(mx1(z, t) - mx2(z, t))**2/(sx1(z)**2 + sx2(z)**2) \
173+
-(my1(z, t) - my2(z, t))**2/(sy1(z)**2 + sy2(z)**2) \
174+
-(-br_1*c*t+z)**2/(sigma_z1**2) \
175+
-( br_2*c*t+z)**2/(sigma_z2**2))) \
176+
/np.sqrt((sx1(z)**2 + sx2(z)**2)*(sy1(z)**2 + sy2(z)**2))/sigma_z1/sigma_z2
177+
178+
integral=integrate.dblquad((lambda t, z: kernel_double_integral(t, z)),
179+
-sigma_integration*sigma_z, sigma_integration*sigma_z,-sigma_integration*sigma_z/c, sigma_integration*sigma_z/c)
180+
L0=f*N1*N2*nb*c/2/np.pi**(2)*integral[0]
181+
else:
182+
def mx1(z):
183+
'''The mu_x of B1 as straight line'''
184+
return x_1 + px_1*z
185+
186+
def my1(z):
187+
'''The mu_y of B1 as straight line'''
188+
return y_1 + py_1*z
189+
190+
def mx2(z):
191+
'''The mu_x of B2 as straight line'''
192+
return x_2 + px_2*z
193+
194+
def my2(z):
195+
'''The mu_y of B2 as straight line'''
196+
return y_2 + py_2*z
197+
198+
def kernel_single_integral(z):
199+
return np.exp(0.5*(-(mx1(z) - mx2(z))**2/(sx1(z)**2 + sx2(z)**2) \
200+
-(my1(z) - my2(z))**2/(sy1(z)**2 + sy2(z)**2) \
201+
-((br_1+br_2)**2*z**2)/(br_2**2*sigma_z1**2 + br_1**2*sigma_z2**2))) \
202+
/np.sqrt((sx1(z)**2 + sx2(z)**2)*(sy1(z)**2 + sy2(z)**2)*(sigma_z1**2 + sigma_z2**2))
203+
204+
integral=integrate.quad(lambda z: kernel_single_integral(z), -20*sigma_z, 20*sigma_z)
205+
L0=f*N1*N2*nb/np.sqrt(2)/np.pi**(3/2)*integral[0]
206+
result= L0*Moeller_efficiency/1e4
207+
if verbose:
208+
print(f'Moeller efficiency: {Moeller_efficiency}')
209+
print(f'Integral Relative Error: {integral[1]/integral[0]}')
210+
print(f'==> Luminosity [Hz/cm^2]: {result}')
211+
return result
212+
213+
def get_luminosity_dict(mad, twiss_dfs, ip_name, number_of_ho_collisions):
214+
b1=mad.sequence.lhcb1.beam
215+
b2=mad.sequence.lhcb2.beam
216+
assert b1.freq0==b2.freq0
217+
ip_b1=twiss_dfs['lhcb1'].loc[f'{ip_name}:1']
218+
ip_b2=twiss_dfs['lhcb2'].loc[f'{ip_name}:1']
219+
return {
220+
'f':b1.freq0*1e6, 'nb':number_of_ho_collisions,
221+
'N1':b1.npart, 'N2':b2.npart,
222+
'energy_tot1':b1.energy, 'energy_tot2':b2.energy,
223+
'deltap_p0_1':b1.sige, 'deltap_p0_2':b2.sige,
224+
'epsilon_x1':b1.exn, 'epsilon_x2':b2.exn,
225+
'epsilon_y1':b1.eyn, 'epsilon_y2':b2.eyn,
226+
'sigma_z1':b1.sigt, 'sigma_z2':b2.sigt,
227+
'beta_x1':ip_b1.betx, 'beta_x2':ip_b2.betx,
228+
'beta_y1':ip_b1.bety, 'beta_y2':ip_b2.bety,
229+
'alpha_x1':ip_b1.alfx, 'alpha_x2':ip_b2.alfx,
230+
'alpha_y1':ip_b1.alfy, 'alpha_y2':ip_b2.alfy,
231+
'dx_1':ip_b1.dx, 'dx_2':ip_b2.dx,
232+
'dpx_1':ip_b1.dpx, 'dpx_2':ip_b2.dpx,
233+
'dy_1':ip_b1.dy, 'dy_2':ip_b2.dy,
234+
'dpy_1':ip_b1.dpy, 'dpy_2':ip_b2.dpy,
235+
'x_1':ip_b1.x, 'x_2':ip_b2.x,
236+
'px_1':ip_b1.px, 'px_2':ip_b2.px,
237+
'y_1':ip_b1.y, 'y_2':ip_b2.y,
238+
'py_1':ip_b1.py, 'py_2':ip_b2.py, 'verbose':False}
239+
240+
def compute_luminosity(mad, twiss_dfs, ip_name, number_of_ho_collisions):
241+
return L(**get_luminosity_dict(mad, twiss_dfs, ip_name, number_of_ho_collisions))
242+
243+
def print_luminosity(mad, twiss_dfs, nco_IP1, nco_IP2, nco_IP5, nco_IP8):
244+
for ip, number_of_ho_collisions in zip(['ip1', 'ip2', 'ip5', 'ip8'],
245+
[nco_IP1, nco_IP2, nco_IP5, nco_IP8]):
246+
myL=compute_luminosity(mad, twiss_dfs, ip, number_of_ho_collisions)
247+
print(f'L in {ip} is {myL} Hz/cm^2')

0 commit comments

Comments
 (0)