Skip to content

Commit 3e98c92

Browse files
SebastienJolySebastienJoly
authored andcommitted
Rewritten functions for phase advance correction, experimental
1 parent 30f86a7 commit 3e98c92

File tree

2 files changed

+185
-79
lines changed

2 files changed

+185
-79
lines changed

pySC/correction/tbt

Lines changed: 0 additions & 79 deletions
This file was deleted.

pySC/correction/tbt.py

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
import numpy as np
2+
import at
3+
from pySC.core.beam import _real_bpm_reading
4+
from pySC.lattice_properties.response_model import SCgetModelDispersion
5+
from pySC.core.constants import NUM_TO_AB
6+
from pySC.utils import sc_tools
7+
import copy
8+
9+
LOGGER = logging_tools.get_logger(__name__)
10+
11+
def phase_advance_correction(ring, bpm_indices, elements_indices, dkick, cut, Px=None, Py=None, Etax=None):
12+
"""
13+
Perform phase advance and horizontal dispersion correction on the given ring.
14+
15+
Parameters:
16+
dkick: Change in quadrupole strength for response matrix calculation.
17+
cut: number of kept singular values
18+
Px, Py, Etax: response matrices for horizontal and vertical phase advances and dispersion.
19+
If not provided, they will be calculated.
20+
21+
Returns:
22+
corrected ring
23+
"""
24+
# Initial Twiss parameters
25+
_, _, twiss_err0 = at.get_optics(ring, bpm_indices)
26+
elemdata0, beamdata, elemdata = at.get_optics(ring, bpm_indices)
27+
mux0 = elemdata.mu[:, 0] / (2 * np.pi)
28+
muy0 = elemdata.mu[:, 1] / (2 * np.pi)
29+
Eta_x0 = elemdata.dispersion[:, 0]
30+
31+
# Calculate Response Matrix if not provided
32+
if Px is None or Py is None or Etax is None:
33+
Px, Py, Etax = calculate_rm(dkick, ring, elements_indices, bpm_indices, mux0, muy0, Eta_x0)
34+
35+
response_matrix = np.hstack((Px, Py, Etax))
36+
37+
elemdata0, beamdata, elemdata = at.get_optics(ring, bpm_indices)
38+
mux = elemdata.mu[:, 0] / (2 * np.pi)
39+
muy = elemdata.mu[:, 1] / (2 * np.pi)
40+
Eta_xx = elemdata.dispersion[:, 0]
41+
42+
measurement = np.concatenate((mux - mux0, muy - muy0, Eta_xx - Eta_x0), axis=0)
43+
44+
s = np.linalg.svd(response_matrix.T, compute_uv=False)
45+
system_solution = np.linalg.pinv(response_matrix.T, rcond=s[cut - 1] / s[0]) @ -measurement
46+
ring = apply_correction(ring, system_solution, elements_indices)
47+
48+
return ring
49+
50+
51+
def calculate_rm(dkick, ring, elements_indices, bpm_indices, mux0, muy0, Eta_x0):
52+
"""
53+
Returns:
54+
Px, Py, Etax: Response matrices for horizontal and vertical phase advances and dispersion.
55+
"""
56+
px =[]
57+
py =[]
58+
etax = []
59+
60+
for index in elements_indices:
61+
original_setting = ring[index].PolynomB[1]
62+
63+
ring[index].PolynomB[1] += dkick
64+
_, _, elemdata = at.get_optics(ring, bpm_indices)
65+
66+
mux = elemdata.mu[:, 0] / (2 * np.pi)
67+
muy = elemdata.mu[:, 1] / (2 * np.pi)
68+
Eta_x = elemdata.dispersion[:, 0]
69+
70+
px.append((mux - mux0) / dkick)
71+
py.append((muy - muy0) / dkick)
72+
etax.append((Eta_x - Eta_x0) / dkick)
73+
74+
ring[index].PolynomB[1] = original_setting
75+
76+
Px = np.squeeze(np.array(px))
77+
Py = np.squeeze(np.array(py))
78+
Etax = np.squeeze(np.array(etax))
79+
80+
return Px, Py, Etax
81+
82+
83+
def apply_correction(ring, corrections, elements_indices):
84+
for i, index in enumerate(elements_indices):
85+
ring[index].PolynomB[1] += corrections[i]
86+
return ring
87+
88+
def SCgetModelPhaseAdvanceRM(SC, BPMords, ELEMords, dkick=1e-5, skewness=False, order=1, useIdealRing=True):
89+
"""
90+
(to be rewritten)
91+
Returns:
92+
Px, Py, Etax: Response matrices for horizontal and vertical phase advances and dispersion.
93+
"""
94+
LOGGER.info('Calculating model phase advance response matrix')
95+
ring = SC.IDEALRING.deepcopy() if useIdealRing else SCgetModelRING(SC)
96+
97+
nBPM = len(BPMords)
98+
nELEM = len(ELEMords)
99+
RM = np.full((3 * nBPM, nELEM), np.nan) # (Px+Py+Etax, Elem)
100+
101+
_, _, elemdata0 = at.get_optics(ring, BPMords)
102+
mux0 = elemdata0.mu[:, 0] / (2 * np.pi)
103+
muy0 = elemdata0.mu[:, 1] / (2 * np.pi)
104+
Eta_x0 = elemdata0.dispersion[:, 0]
105+
Ta = np.hstack((mux0, muy0, Eta_x0))
106+
107+
for i, ELEMord in enumerate(ELEMords):
108+
109+
PolynomNominal = getattr(ring[ELEMord], f"Polynom{NUM_TO_AB[int(skewness)]}")
110+
changed_polynom = copy.deepcopy(PolynomNominal[:])
111+
changed_polynom[order] += dkick
112+
setattr(ring[ELEMord], f"Polynom{NUM_TO_AB[int(skewness)]}", changed_polynom[:])
113+
114+
_, _, elemdata = at.get_optics(ring, BPMords)
115+
mux = elemdata.mu[:, 0] / (2 * np.pi)
116+
muy = elemdata.mu[:, 1] / (2 * np.pi)
117+
Eta_x = elemdata.dispersion[:, 0]
118+
TdB = np.hstack((mux, muy, Eta_x))
119+
120+
setattr(ring[ELEMord], f"Polynom{NUM_TO_AB[int(skewness)]}", PolynomNominal[:])
121+
dTdB = (TdB - Ta) / dkick
122+
RM[:, i] = dTdB
123+
124+
return RM
125+
126+
def phase_advance_correction2(SC, BPMords, ELEMords, dkick=1e-5, nturns=64, skewness=False,
127+
order=1, dipole_compensation=True, alpha=1e-3, RM=None):
128+
"""
129+
(To be rewritten)
130+
Perform phase advance and horizontal dispersion correction on the given ring.
131+
132+
Parameters:
133+
dkick: Change in quadrupole strength for response matrix calculation.
134+
cut: number of kept singular values
135+
Px, Py, Etax: response matrices for horizontal and vertical phase advances and dispersion.
136+
If not provided, they will be calculated.
137+
138+
Returns:
139+
corrected ring
140+
"""
141+
# Ideal Twiss parameters and tunes
142+
elemdata0, beamdata, elemdata = at.get_optics(SC.IDEALRING, BPMords)
143+
mux0 = elemdata.mu[:, 0] / (2 * np.pi)
144+
muy0 = elemdata.mu[:, 1] / (2 * np.pi)
145+
Eta_x0 = elemdata.dispersion[:, 0]
146+
Qx0 = beamdata.tune[0]
147+
Qy0 = beamdata.tune[1]
148+
149+
# Calculate Response Matrix if not provided
150+
if RM is None:
151+
RM = SCgetModelPhaseAdvanceRM(SC, BPMords, ELEMords, dkick=dkick, skewness=skewness, order=order, useIdealRing=True)
152+
153+
#track two particles, one with amplitude in x and one in y
154+
Z0=np.asfortranarray(np.vstack(([dkick, 0, 0, 0, 0, 0],
155+
[0, 0, dkick, 0, 0, 0])).T)
156+
Z=at.lattice_pass(SC.RING, Z0, nturns, refpts=SC.ORD.BPM)
157+
bpm_readings = Z[[0, 2], [0, 1], :, :]
158+
real_bpm_readings = _real_bpm_reading(SC, bpm_readings[:, np.newaxis, :, :])[0] # advanced indexing to match _real_bpm_reading input
159+
160+
# get tunes and phases using NAFF algorithm
161+
tune_x, amp_x, phase_x = at.physics.harmonic_analysis.get_main_harmonic(real_bpm_readings[0], fmin=0.5, fmax=0.95)
162+
tune_y, amp_y, phase_y = at.physics.harmonic_analysis.get_main_harmonic(real_bpm_readings[1], fmin=0.05, fmax=0.5)
163+
mux = np.unwrap(phase_x) / (2 * np.pi)
164+
mux *= np.sign(mux) # sign correction needed to fit at.get_optics() phase convention
165+
muy = np.unwrap(phase_y) / (2 * np.pi)
166+
muy *= np.sign(muy) # sign correction needed to fit at.get_optics() phase convention
167+
168+
# dispersion calculation
169+
# I advise to use useIdealRing=True here, otherwise the dispersion is quite noisy
170+
Eta_x = SCgetModelDispersion(SC, SC.ORD.BPM, CAVords=SC.ORD.RF, useIdealRing=True, rfStep=2e1)[:SC.ORD.BPM.size]
171+
Eta_x *= -SC.RING.get_rf_frequency() * SC.RING.disable_6d(copy=True).get_mcf() # need proper normalization
172+
# SCgetModelDispersion does not return the dispersion in [m]
173+
174+
Qx = np.mean(tune_x, keepdims=True)
175+
Qy = np.mean(tune_y, keepdims=True)
176+
177+
# measurement contains all the observables to minimize
178+
measurement = np.concatenate((mux - mux0, muy - muy0, Eta_x - Eta_x0, Qx - Qx0, Qy - Qy0), axis=0)
179+
# weights can be changed to emphasize some observables
180+
weights = np.hstack((np.ones(mux.size), np.ones(muy.size), np.ones(Eta_x.size), np.ones(1), np.ones(1)))
181+
182+
inverse_RM = sc_tools.pinv(RM, alpha=alpha, plot=False)
183+
system_solution = -np.dot(inverse_RM, measurement*weights)
184+
185+
return SC, system_solution

0 commit comments

Comments
 (0)