Skip to content

Commit 82b9386

Browse files
authored
Merge pull request #56 from lhcopt/dev_1.1.0
Version 1.1.0
2 parents 6a587bf + b13191e commit 82b9386

21 files changed

+1166
-529
lines changed

pymask/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
__version__ = "v0.3.1"
1+
__version__ = "v1.1.0"
22

33
from .beambeam import *
44
from .madpoint import *

pymask/linear_normal_form.py

Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
import numpy as np
2+
from scipy.optimize import fsolve
3+
4+
import xline as xl
5+
import xtrack as xt
6+
7+
def _one_turn_map(p, particle_on_madx_co, tracker):
8+
xl_part = particle_on_madx_co.copy()
9+
xl_part.x = p[0]
10+
xl_part.px = p[1]
11+
xl_part.y = p[2]
12+
xl_part.py = p[3]
13+
xl_part.zeta = p[4]
14+
xl_part.delta = p[5]
15+
16+
part = xt.Particles(**xl_part.to_dict())
17+
tracker.track(part)
18+
p_res = np.array([
19+
part.x[0],
20+
part.px[0],
21+
part.y[0],
22+
part.py[0],
23+
part.zeta[0],
24+
part.delta[0]])
25+
return p_res
26+
27+
def find_closed_orbit_from_tracker(tracker, particle_co_guess_dict):
28+
particle_co_guess = xl.Particles.from_dict(particle_co_guess_dict)
29+
print('Start CO search')
30+
res = fsolve(lambda p: p - _one_turn_map(p, particle_co_guess, tracker),
31+
x0=np.array([particle_co_guess.x, particle_co_guess.px,
32+
particle_co_guess.y, particle_co_guess.py,
33+
particle_co_guess.zeta, particle_co_guess.delta]))
34+
print('Done CO search')
35+
particle_on_co = particle_co_guess.copy()
36+
particle_on_co.x = res[0]
37+
particle_on_co.px = res[1]
38+
particle_on_co.y = res[2]
39+
particle_on_co.py = res[3]
40+
particle_on_co.zeta = res[4]
41+
particle_on_co.delta = res[5]
42+
43+
return particle_on_co
44+
45+
def compute_R_matrix_finite_differences(
46+
particle_on_co, tracker,
47+
dx=1e-9, dpx=1e-12, dy=1e-9, dpy=1e-12,
48+
dzeta=1e-9, ddelta=1e-9,
49+
symplectify=True):
50+
51+
# Find R matrix
52+
p0 = np.array([
53+
particle_on_co.x,
54+
particle_on_co.px,
55+
particle_on_co.y,
56+
particle_on_co.py,
57+
particle_on_co.zeta,
58+
particle_on_co.delta])
59+
II = np.eye(6)
60+
RR = np.zeros((6, 6), dtype=np.float64)
61+
for jj, dd in enumerate([dx, dpx, dy, dpy, dzeta, ddelta]):
62+
pd=p0+II[jj]*dd
63+
RR[:,jj]=(_one_turn_map(pd, particle_on_co, tracker)-p0)/dd
64+
65+
66+
if symplectify:
67+
return healy_symplectify(RR)
68+
else:
69+
return RR
70+
71+
def healy_symplectify(M):
72+
# https://accelconf.web.cern.ch/e06/PAPERS/WEPCH152.PDF
73+
print("Symplectifying linear One-Turn-Map...")
74+
75+
print("Before symplectifying: det(M) = {}".format(np.linalg.det(M)))
76+
I = np.identity(6)
77+
78+
S = np.array(
79+
[
80+
[0.0, 1.0, 0.0, 0.0, 0.0, 0.0],
81+
[-1.0, 0.0, 0.0, 0.0, 0.0, 0.0],
82+
[0.0, 0.0, 0.0, 1.0, 0.0, 0.0],
83+
[0.0, 0.0, -1.0, 0.0, 0.0, 0.0],
84+
[0.0, 0.0, 0.0, 0.0, 0.0, 1.0],
85+
[0.0, 0.0, 0.0, 0.0, -1.0, 0.0],
86+
]
87+
)
88+
89+
V = np.matmul(S, np.matmul(I - M, np.linalg.inv(I + M)))
90+
W = (V + V.T) / 2
91+
if np.linalg.det(I - np.matmul(S, W)) != 0:
92+
M_new = np.matmul(I + np.matmul(S, W),
93+
np.linalg.inv(I - np.matmul(S, W)))
94+
else:
95+
print("WARNING: det(I - SW) = 0!")
96+
V_else = np.matmul(S, np.matmul(I + M, np.linalg.inv(I - M)))
97+
W_else = (V_else + V_else.T) / 2
98+
M_new = -np.matmul(
99+
I + np.matmul(S, W_else), np.linalg(I - np.matmul(S, W_else))
100+
)
101+
102+
print("After symplectifying: det(M) = {}".format(np.linalg.det(M_new)))
103+
return M_new
104+
105+
S = np.array([[0., 1., 0., 0., 0., 0.],
106+
[-1., 0., 0., 0., 0., 0.],
107+
[ 0., 0., 0., 1., 0., 0.],
108+
[ 0., 0.,-1., 0., 0., 0.],
109+
[ 0., 0., 0., 0., 0., 1.],
110+
[ 0., 0., 0., 0.,-1., 0.]])
111+
112+
######################################################
113+
### Implement Normalization of fully coupled motion ##
114+
######################################################
115+
116+
def Rot2D(mu):
117+
return np.array([[ np.cos(mu), np.sin(mu)],
118+
[-np.sin(mu), np.cos(mu)]])
119+
120+
def compute_linear_normal_form(M):
121+
w0, v0 = np.linalg.eig(M)
122+
123+
a0 = np.real(v0)
124+
b0 = np.imag(v0)
125+
126+
index_list = [0,5,1,2,3,4]
127+
128+
##### Sort modes in pairs of conjugate modes #####
129+
130+
conj_modes = np.zeros([3,2], dtype=np.int64)
131+
for j in [0,1]:
132+
conj_modes[j,0] = index_list[0]
133+
del index_list[0]
134+
135+
min_index = 0
136+
min_diff = abs(np.imag(w0[conj_modes[j,0]] + w0[index_list[min_index]]))
137+
for i in range(1,len(index_list)):
138+
diff = abs(np.imag(w0[conj_modes[j,0]] + w0[index_list[i]]))
139+
if min_diff > diff:
140+
min_diff = diff
141+
min_index = i
142+
143+
conj_modes[j,1] = index_list[min_index]
144+
del index_list[min_index]
145+
146+
conj_modes[2,0] = index_list[0]
147+
conj_modes[2,1] = index_list[1]
148+
149+
##################################################
150+
#### Select mode from pairs with positive (real @ S @ imag) #####
151+
152+
modes = np.empty(3, dtype=np.int64)
153+
for ii,ind in enumerate(conj_modes):
154+
if np.matmul(np.matmul(a0[:,ind[0]], S), b0[:,ind[0]]) > 0:
155+
modes[ii] = ind[0]
156+
else:
157+
modes[ii] = ind[1]
158+
159+
##################################################
160+
#### Sort modes such that (1,2,3) is close to (x,y,zeta) ####
161+
for i in [1,2]:
162+
if abs(v0[:,modes[0]])[0] < abs(v0[:,modes[i]])[0]:
163+
modes[0], modes[i] = modes[i], modes[0]
164+
165+
if abs(v0[:,modes[1]])[2] < abs(v0[:,modes[2]])[2]:
166+
modes[2], modes[1] = modes[1], modes[2]
167+
168+
##################################################
169+
#### Rotate eigenvectors to the Courant-Snyder parameterization ####
170+
phase0 = np.log(v0[0,modes[0]]).imag
171+
phase1 = np.log(v0[2,modes[1]]).imag
172+
phase2 = np.log(v0[4,modes[2]]).imag
173+
174+
v0[:,modes[0]] *= np.exp(-1.j*phase0)
175+
v0[:,modes[1]] *= np.exp(-1.j*phase1)
176+
v0[:,modes[2]] *= np.exp(-1.j*phase2)
177+
178+
##################################################
179+
#### Construct W #################################
180+
181+
a1 = v0[:,modes[0]].real
182+
a2 = v0[:,modes[1]].real
183+
a3 = v0[:,modes[2]].real
184+
b1 = v0[:,modes[0]].imag
185+
b2 = v0[:,modes[1]].imag
186+
b3 = v0[:,modes[2]].imag
187+
188+
n1 = 1./np.sqrt(np.matmul(np.matmul(a1, S), b1))
189+
n2 = 1./np.sqrt(np.matmul(np.matmul(a2, S), b2))
190+
n3 = 1./np.sqrt(np.matmul(np.matmul(a3, S), b3))
191+
192+
a1 *= n1
193+
a2 *= n2
194+
a3 *= n3
195+
196+
b1 *= n1
197+
b2 *= n2
198+
b3 *= n3
199+
200+
W = np.array([a1,b1,a2,b2,a3,b3]).T
201+
W[abs(W) < 1.e-14] = 0. # Set very small numbers to zero.
202+
invW = np.matmul(np.matmul(S.T, W.T), S)
203+
204+
##################################################
205+
#### Get tunes and rotation matrix in the normalized coordinates ####
206+
207+
mu1 = np.log(w0[modes[0]]).imag
208+
mu2 = np.log(w0[modes[1]]).imag
209+
mu3 = np.log(w0[modes[2]]).imag
210+
211+
#q1 = mu1/(2.*np.pi)
212+
#q2 = mu2/(2.*np.pi)
213+
#q3 = mu3/(2.*np.pi)
214+
215+
R = np.zeros_like(W)
216+
R[0:2,0:2] = Rot2D(mu1)
217+
R[2:4,2:4] = Rot2D(mu2)
218+
R[4:6,4:6] = Rot2D(mu3)
219+
##################################################
220+
221+
return W, invW, R

0 commit comments

Comments
 (0)