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