Skip to content

Commit 52e38a3

Browse files
committed
feature/linear_normal_form added computation of linear normal form
1 parent 22dcb69 commit 52e38a3

File tree

2 files changed

+123
-0
lines changed

2 files changed

+123
-0
lines changed

pysixtrack/line.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from .loader_mad import iter_from_madx_sequence
99
from .closed_orbit import linearize_around_closed_orbit
1010
from .closed_orbit import healy_symplectify
11+
from .linear_normal_form import _linear_normal_form
1112

1213

1314
# missing access to particles._m:
@@ -195,6 +196,9 @@ def get_elements_of_type(self, types):
195196

196197
return elements, names
197198

199+
def linear_normal_form(self, M):
200+
return _linear_normal_form(M)
201+
198202
def find_closed_orbit_and_linear_OTM(
199203
self, p0c, guess=None, d=1.e-7, tol=1.e-10, max_iterations=20, longitudinal_coordinate='zeta'
200204
):

pysixtrack/linear_normal_form.py

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
import numpy as np
2+
3+
S = np.array([[0., 1., 0., 0., 0., 0.],
4+
[-1., 0., 0., 0., 0., 0.],
5+
[ 0., 0., 0., 1., 0., 0.],
6+
[ 0., 0.,-1., 0., 0., 0.],
7+
[ 0., 0., 0., 0., 0., 1.],
8+
[ 0., 0., 0., 0.,-1., 0.]])
9+
10+
######################################################
11+
### Implement Normalization of fully coupled motion ##
12+
######################################################
13+
14+
def Rot2D(mu):
15+
return np.array([[ np.cos(mu), np.sin(mu)],
16+
[-np.sin(mu), np.cos(mu)]])
17+
18+
def _linear_normal_form(M):
19+
w0, v0 = np.linalg.eig(M)
20+
21+
a0 = np.real(v0)
22+
b0 = np.imag(v0)
23+
24+
index_list = [0,5,1,2,3,4]
25+
26+
##### Sort modes in pairs of conjugate modes #####
27+
28+
conj_modes = np.zeros([3,2], dtype=np.int)
29+
for j in [0,1]:
30+
conj_modes[j,0] = index_list[0]
31+
del index_list[0]
32+
33+
min_index = 0
34+
min_diff = abs(np.imag(w0[conj_modes[j,0]] + w0[index_list[min_index]]))
35+
for i in range(1,len(index_list)):
36+
diff = abs(np.imag(w0[conj_modes[j,0]] + w0[index_list[i]]))
37+
if min_diff > diff:
38+
min_diff = diff
39+
min_index = i
40+
41+
conj_modes[j,1] = index_list[min_index]
42+
del index_list[min_index]
43+
44+
conj_modes[2,0] = index_list[0]
45+
conj_modes[2,1] = index_list[1]
46+
47+
##################################################
48+
#### Select mode from pairs with positive (real @ S @ imag) #####
49+
50+
modes = np.empty(3, dtype=np.int)
51+
for ii,ind in enumerate(conj_modes):
52+
if np.matmul(np.matmul(a0[:,ind[0]], S), b0[:,ind[0]]) > 0:
53+
modes[ii] = ind[0]
54+
else:
55+
modes[ii] = ind[1]
56+
57+
##################################################
58+
#### Sort modes such that (1,2,3) is close to (x,y,zeta) ####
59+
for i in [1,2]:
60+
if abs(v0[:,modes[0]])[0] < abs(v0[:,modes[i]])[0]:
61+
modes[0], modes[i] = modes[i], modes[0]
62+
63+
if abs(v0[:,modes[1]])[2] < abs(v0[:,modes[2]])[2]:
64+
modes[2], modes[1] = modes[1], modes[2]
65+
66+
##################################################
67+
#### Rotate eigenvectors to the Courant-Snyder parameterization ####
68+
phase0 = np.log(v0[0,modes[0]]).imag
69+
phase1 = np.log(v0[2,modes[1]]).imag
70+
phase2 = np.log(v0[4,modes[2]]).imag
71+
72+
v0[:,modes[0]] *= np.exp(-1.j*phase0)
73+
v0[:,modes[1]] *= np.exp(-1.j*phase1)
74+
v0[:,modes[2]] *= np.exp(-1.j*phase2)
75+
76+
##################################################
77+
#### Construct W #################################
78+
79+
a1 = v0[:,modes[0]].real
80+
a2 = v0[:,modes[1]].real
81+
a3 = v0[:,modes[2]].real
82+
b1 = v0[:,modes[0]].imag
83+
b2 = v0[:,modes[1]].imag
84+
b3 = v0[:,modes[2]].imag
85+
86+
n1 = 1./np.sqrt(np.matmul(np.matmul(a1, S), b1))
87+
n2 = 1./np.sqrt(np.matmul(np.matmul(a2, S), b2))
88+
n3 = 1./np.sqrt(np.matmul(np.matmul(a3, S), b3))
89+
90+
a1 *= n1
91+
a2 *= n2
92+
a3 *= n3
93+
94+
b1 *= n1
95+
b2 *= n2
96+
b3 *= n3
97+
98+
W = np.array([a1,b1,a2,b2,a3,b3]).T
99+
W[abs(W) < 1.e-14] = 0. # Set very small numbers to zero.
100+
invW = np.matmul(np.matmul(S.T, W.T), S)
101+
102+
##################################################
103+
#### Get tunes and rotation matrix in the normalized coordinates ####
104+
105+
mu1 = np.log(w0[modes[0]]).imag
106+
mu2 = np.log(w0[modes[1]]).imag
107+
mu3 = np.log(w0[modes[2]]).imag
108+
109+
#q1 = mu1/(2.*np.pi)
110+
#q2 = mu2/(2.*np.pi)
111+
#q3 = mu3/(2.*np.pi)
112+
113+
R = np.zeros_like(W)
114+
R[0:2,0:2] = Rot2D(mu1)
115+
R[2:4,2:4] = Rot2D(mu2)
116+
R[4:6,4:6] = Rot2D(mu3)
117+
##################################################
118+
119+
return W, invW, R

0 commit comments

Comments
 (0)