|
| 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