Interact and run this jupyter notebook online:
Also find this lecture rendered as HTML slides on github $\nearrow$ along with the source repository $\nearrow$.
Imports and modules:
from config import (np, plt, Madx, interp1d,
set_correctors, )
%matplotlib inline
We use the lattice definition of the SIS18 synchrotron (Schwerionensynchrotron) at GSI.
For a sufficient accelerator description we need the following elements ($\implies$ what do they do?):
The 216.72m long SIS18 consists of 12 symmetric cells, where each cell basically features:
image by S. Mirza et al.
We use once again MAD-X for the calculation of optics:
madx = Madx(stdout=True)
madx.options.echo = False
madx.options.info = False
madx.options.warn = False
madx.set(format_="13.5f") # reduce significant figures
# load the lattice definition (magnet SEQuence)
madx.call('sis18.seq')
# define the beam
madx.beam()
# (normalised multipoles k and optics functions are independent of energy)
# activate lattice
madx.use('sis18ring')
++++++++++++++++++++++++++++++++++++++++++++ + MAD-X 5.09.00 (64 bit, Linux) + + Support: mad@cern.ch, http://cern.ch/mad + + Release date: 2023.05.05 + + Execution date: 2024.01.09 22:53:08 + ++++++++++++++++++++++++++++++++++++++++++++
Define the quadrupole magnet strengths for the focusing:
madx.input('''
kqf = 0.387; // focusing k
kqd = -0.368; // defocusing k
k1nl_GS01QS1F := kqf;
k1nl_GS01QS2D := kqd;
k1nl_GS12QS1F := kqf;
k1nl_GS12QS2D := kqd;
''')
True
Compute the optics for this SIS18 lattice configuration:
# output the Twiss parameters every 0.1m
madx.command.select(flag="interpolate", sequence="sis18ring", step=0.1)
# compute optics
twiss = madx.twiss();
enter Twiss module iteration: 1 error: 0.000000E+00 deltap: 0.000000E+00 orbit: 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 ++++++ table: summ length orbit5 alfa gammatr 216.72000 -0.00000 0.03172 5.61519 q1 dq1 betxmax dxmax 4.30215 -7.33943 34.29041 3.39951 dxrms xcomax xcorms q2 1.99458 0.00000 0.00000 4.19932 dq2 betymax dymax dyrms -6.84397 28.30370 0.00000 0.00000 ycomax ycorms deltap synch_1 0.00000 0.00000 0.00000 0.00000 synch_2 synch_3 synch_4 synch_5 0.00000 0.00000 0.00000 0.00000 synch_6 synch_8 nflips dqmin 0.00000 0.00000 0.00000 0.00000 dqmin_phase 0.00000
How do the beta functions look like around the ring? ($\implies$ what do they represent?)
plt.plot(twiss['s'], twiss['betx'], c='k', label=r'$\beta_x$')
plt.plot(twiss['s'], twiss['bety'], c='r', label=r'$\beta_y$')
plt.xlabel('$s$ [m]')
plt.ylabel(r'$\beta_{x,y}$ [m/rad]')
plt.legend(bbox_to_anchor=(1.05, 1));
How does the closed orbit look like with respect to the reference orbit?
plt.plot(twiss['s'], twiss['x'])
plt.xlabel('$s$ [m]')
plt.ylabel(r'$x_{co}$ [m]');
Collect the locations around the ring where BPMs and corrector magnets are located:
s_bpm = 17.813 + np.linspace(0, twiss.summary.length, 12, endpoint=False)
corrector_names = ['GS01MU1A', 'GS02MU1A', 'GS03MU1A', 'GS04MU2A',
'GS05MU1A', 'GS06MU2A', 'GS07MU1A', 'GS08MU1A',
'GS09MU1A', 'GS10MU1A', 'GS11MU1A', 'GS12MU1A']
s_corr = np.array(
[twiss['s'][list(twiss['name']).index((cn + ':1').lower())]
for cn in corrector_names]
)
l1, = plt.plot(twiss['s'], twiss['betx'], c='black')
for s in s_bpm:
l2 = plt.axvline(s, c='cornflowerblue', lw=2)
for s in s_corr:
l3 = plt.axvline(s, c='orange', ls='--', lw=2)
plt.xlabel('$s$ [m]')
plt.ylabel(r'$\beta_x$ [m/rad]')
plt.legend([l1, l2, l3], [r'$\beta_x$', 'BPM', 'corrector'], loc=0, framealpha=1)
# comment this line to see the whole ring:
plt.xlim(0, twiss.summary.length / 12);
Extract the Twiss $\beta_x(s)$ and phase advance $\psi_x(s)$ functions from the MAD-X TWISS table:
beta_x = interp1d(twiss['s'], twiss['betx'], kind='linear')
psi_x = interp1d(twiss['s'], 2 * np.pi * twiss['mux'], kind='linear')
And the horizontal tune $Q_x$:
Qx = twiss.summary.q1
The distortion of the equilibrium orbit at $s$ due to a kick $\theta$ at location $s_0$ is given by
$$x_\mathrm{COD}(s) = \theta \cdot \sqrt{\beta_x(s_0) \cdot \beta_x(s)} \cdot \cfrac{\cos(|\Delta \psi_x| - \pi Q_x)}{2\sin(\pi Q_x)}$$def x_cod(theta, s_source, s_target):
sq_betxs = np.sqrt(beta_x(s_source) * beta_x(s_target))
delta_psi = psi_x(s_target) - psi_x(s_source)
return theta * sq_betxs / (2 * np.sin(np.pi * Qx)) * np.cos(np.abs(delta_psi) - np.pi * Qx)
Let us consider a dipole "error" of $\theta=0.01\,$rad induced at the location of the first corrector magnet:
plt.plot(twiss['s'], x_cod(0.01, s_corr[0], twiss['s']))
plt.xlabel('$s$ [m]')
plt.ylabel(r'$x_{co}$ [m]')
Text(0, 0.5, '$x_{co}$ [m]')
Let us see what MAD-X computes for the closed orbit distortion:
set_correctors([0.01] + [0] * 11, madx)
twiss = madx.twiss();
enter Twiss module iteration: 1 error: 4.815962E-02 deltap: 0.000000E+00 orbit: 2.415323E-02 -5.963771E-03 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 iteration: 2 error: 1.926643E-05 deltap: 0.000000E+00 orbit: 2.415184E-02 -5.967624E-03 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 iteration: 3 error: 1.842674E-10 deltap: 0.000000E+00 orbit: 2.415184E-02 -5.967624E-03 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 ++++++ table: summ length orbit5 alfa gammatr 216.72000 -0.00000 0.03266 5.53319 q1 dq1 betxmax dxmax 4.30249 -7.34282 35.03731 4.84185 dxrms xcomax xcorms q2 2.11433 0.07333 0.03275 4.19952 dq2 betymax dymax dyrms -6.84785 28.99330 0.00000 0.00000 ycomax ycorms deltap synch_1 0.00000 0.00000 0.00000 0.00000 synch_2 synch_3 synch_4 synch_5 0.00000 0.00000 0.00000 0.00000 synch_6 synch_8 nflips dqmin 0.00000 0.00000 0.00000 0.00000 dqmin_phase 0.00000
plt.plot(twiss['s'], x_cod(0.01, s_corr[0], twiss['s']), c='darkblue', label='analytic')
plt.plot(twiss['s'], twiss['x'], c='orange', ls='--', label='MAD-X')
plt.xlabel('$s$ [m]')
plt.ylabel(r'$x_{co}$ [m]')
plt.legend();
def reset_correctors():
set_correctors([0] * 12, madx)
reset_correctors()
A dipole error $\Delta x' = \theta$ at $s_0$ is propagated to $s_1$ using the top right entry of the Twiss transfer matrix, $\left(\mathcal{M}_\mathrm{tw,x}|_{s_1\leftarrow s_0}\right)_{12}$:
$$x_\mathrm{prop}|_{s_1\leftarrow s_0} = \theta \cdot \sqrt{\beta_x(s_0)\cdot \beta_x(s_1)}\cdot \sin(\Delta \psi_x)$$Let us shift the closed orbit at $s=10\,$m by $\Delta x=-0.01\,$m:
dx_target = -0.01
# phase advances:
psi1s = psi_x(10) - psi_x(s_corr[0])
psi12 = psi_x(s_corr[1]) - psi_x(s_corr[0])
psi23 = psi_x(s_corr[2]) - psi_x(s_corr[1])
# beta functions:
betas = beta_x(10)
beta1 = beta_x(s_corr[0])
beta2 = beta_x(s_corr[1])
beta3 = beta_x(s_corr[2])
The first corrector strength is simply $$\theta_1=\cfrac{\Delta x_\mathrm{target}}{\sqrt{\beta_1 \cdot \beta_x(10\,\mathrm{m})} \cdot \sin(\psi_x(s_1)-\psi_x(10\,\mathrm{m}))}$$
theta1 = dx_target / (np.sqrt(beta1 * betas) * np.sin(psi1s))
theta1
-0.0011845798797791632
The second corrector strength was calculated to be $$\theta_2 = -\theta_1\cdot \sqrt{\cfrac{\beta_1}{\beta_2}}\cdot \cfrac{\sin(\psi_{12}+\psi_{23})}{\sin(\psi_{23})}$$
theta2 = -theta1 * np.sqrt(beta1 / beta2) * np.sin(psi12 + psi23) / np.sin(psi23)
theta2
-0.0014930330679946973
And the third corrector closes the bump with $$\theta_3 = \theta_1 \cdot \sqrt{\cfrac{\beta_1}{\beta_3}}\cdot \cfrac{\sin(\psi_{12})}{\sin(\psi_{23})}$$
theta3 = theta1 * np.sqrt(beta1 / beta3) * np.sin(psi12) / np.sin(psi23)
theta3
-0.0011845798797791636
We apply these computed angles to the 3 first correctors:
set_correctors([theta1, theta2, theta3] + [0] * 9, madx)
Recompute the optics (mainly for the closed orbit):
twiss = madx.twiss();
enter Twiss module iteration: 1 error: 4.096098E-07 deltap: 0.000000E+00 orbit: -5.207033E-07 -1.440725E-07 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 ++++++ table: summ length orbit5 alfa gammatr 216.72000 -0.00000 0.03158 5.62735 q1 dq1 betxmax dxmax 4.30200 -7.33916 34.32811 3.45112 dxrms xcomax xcorms q2 1.99307 0.01411 0.00296 4.19923 dq2 betymax dymax dyrms -6.84393 28.33192 0.00000 0.00000 ycomax ycorms deltap synch_1 0.00000 0.00000 0.00000 0.00000 synch_2 synch_3 synch_4 synch_5 0.00000 0.00000 0.00000 0.00000 synch_6 synch_8 nflips dqmin 0.00000 0.00000 0.00000 0.00000 dqmin_phase 0.00000
Let's plot the 3-corrector bump:
plt.plot(twiss['s'], twiss['x'])
plt.scatter([10], [dx_target], marker='o', c='red')
plt.xlabel('$s$ [m]')
plt.ylabel(r'$x_{co}$ [m]');
reset_correctors()
The orbit response matrix (ORM) $\Omega_{ij}$ is a discrete table describing the linear orbit offset at the $i$th BPM induced by the $j$th dipole corrector magnet, i.e. its angle $\Theta_j$.
$\Omega$ is, therefore, defined by the relation
$$(\Delta x)_i = \Omega_{ij}\Theta_j$$For a one-pass transfer line, $\Omega_{ij}$ is built from the $(\mathcal{M}_\mathrm{tw,x}|_{s_1\leftarrow s_0})_{12}$ values for all $i$ correctors and $j$ BPMs, which is not necessarily periodic.
For an accelerator ring, on the other hand, the closed orbit is a periodic equilibrium solution and the ORM is built from the $x_{COD}\propto \cfrac{\cos(\Delta\psi_x-\pi Q_x)}{\sin(\pi Q_x)}$ values.
s_mat_corr = np.meshgrid(s_corr, np.ones_like(s_bpm))[0]
s_mat_bpm = np.meshgrid(np.ones_like(s_corr), s_bpm)[1]
Define the ORM:
omega = x_cod(1, s_mat_corr, s_mat_bpm)
Singular Value Decomposition: factorise $\Omega$ into
$$\Omega=U\cdot S\cdot V^T$$with the (non-uniquely defined) rectangular orthogonal matrices $U$ and $V$ and the diagonal matrix $S$ listing the (unique, non-negative) singular values.
$U$ and $V$ contain orthonormal vectors along the rows/columns.
$\implies$ SVD constructs the (approximate) null space and provides orthogonal modes in the orbit response matrix to move the orbit!
U, S, Vt = np.linalg.svd(omega)
$U$ and $V$ are orthogonal, i.e. $U\cdot U^T = \mathbb{1} = V \cdot V^T$:
matrix = U
plt.spy(matrix.dot(matrix.T), precision=1e-10)
<matplotlib.image.AxesImage at 0x7f4838a71b70>
$S$ contains finite singular values and the (approximately vanishing) null space entries if the system is under-/overdetermined:
plt.plot(S, ls='none', marker='.')
plt.yscale('log')
plt.xlabel('#entry')
plt.ylabel('singular value');
Consider a horizontal shift $\Delta x_q$ of the magnetic centre of the quadrupole magnets.
$\implies$ What happens to a beam centroid entering a quadrupole field off-centre?
reset_correctors()
Random Gaussian normal distribution of the misalignments with a standard deviation of $\sigma_{\Delta x_q} = 1\,$mm:
madx.input('''
sigmadx = 0.001; // 1mm
select, flag=error, clear;
select, flag=error, class=quadrupole;
eoption, add=false, seed=12345;
ealign, dx := sigmadx * tgauss(2);
''')
True
Let us plot the distribution of horizontal misalignments for the quadrupoles around the ring:
quads = [el for el in madx.sequence.sis18ring.expanded_elements if 'quad_long' in str(el)]
quad_s = np.array([twiss['s'][list(twiss['name']).index(q.name.lower() + ':1')] for q in quads])
quad_dx = np.array([q.align_errors.dx for q in quads])
plt.bar(quad_s[::2], quad_dx[::2], width=3,
facecolor='darkblue', edgecolor='none', label='focusing')
plt.bar(quad_s[1::2], quad_dx[1::2], width=3,
facecolor='orange', edgecolor='none', alpha=0.6, label='defocusing')
plt.xlabel('$s$ [m]')
plt.ylabel('$\Delta x_{q}$ [m]')
plt.title('Quadrupole misalignments', y=1.04)
plt.legend(loc='upper left', bbox_to_anchor=(1.05, 1));
# recompute optics
twiss = madx.twiss();
enter Twiss module iteration: 1 error: 1.142975E-02 deltap: 0.000000E+00 orbit: -3.120383E-03 1.762989E-03 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 iteration: 2 error: 2.679426E-06 deltap: 0.000000E+00 orbit: -3.119209E-03 1.762633E-03 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 iteration: 3 error: 1.317965E-12 deltap: 0.000000E+00 orbit: -3.119209E-03 1.762633E-03 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 ++++++ table: summ length orbit5 alfa gammatr 216.72000 -0.00000 0.03177 5.61014 q1 dq1 betxmax dxmax 4.30226 -7.33998 34.43140 3.92505 dxrms xcomax xcorms q2 2.00303 0.03432 0.00981 4.19942 dq2 betymax dymax dyrms -6.84447 28.47205 0.00000 0.00000 ycomax ycorms deltap synch_1 0.00000 0.00000 0.00000 0.00000 synch_2 synch_3 synch_4 synch_5 0.00000 0.00000 0.00000 0.00000 synch_6 synch_8 nflips dqmin 0.00000 0.00000 0.00000 0.00000 dqmin_phase 0.00000
beta_x = interp1d(twiss['s'], twiss['betx'], kind='linear')
psi_x = interp1d(twiss['s'], 2 * np.pi * twiss['mux'], kind='linear')
x_co = interp1d(twiss['s'], twiss['x'], kind='linear')
x_co_bpm = x_co(s_bpm)
plt.plot(twiss['s'], twiss['x'])
plt.scatter(s_bpm, x_co_bpm, marker='o', c='r', label='BPM')
plt.xlabel('$s$ [m]')
plt.ylabel(r'$x_{co}$ [m]')
plt.legend();
Construct pseudo-inverse of ORM:
$$\Omega^{-1} = (U\cdot \mathrm{diag}(S_{ii}) \cdot V^T)^{-1} = (V^T)^{-1} \cdot S^{-1} \cdot U^{-1} = V \cdot \mathrm{diag}\left(\frac{1}{S_{ii}}\right) \cdot U^T$$where $S_{ii}$ refers to the finite singular values (i.e. excluding the null space).
S_mat = np.diag(S)
S_inv_mat = np.diag(1/S)
The goal is to induce a shift $(\Delta x_\mathrm{target})_i$ at each $i$th BPM: this can be
The corrector angles are then given by $\Omega^{-1} \Delta x_\mathrm{target}$,
omega_inv = Vt.T.dot(
(S_inv_mat).dot(U.T))
We set $\Delta x_\mathrm{target}$ to the negative values of the observed horizontal positions at the BPMs, this will move the closed orbit (in the BPMs) back towards the reference orbit (zero):
theta_vec = omega_inv.dot(-x_co_bpm)
Set the corrector strengths $\Theta_j$:
set_correctors(theta_vec, madx)
And recompute the optics (mainly the closed orbit):
twiss = madx.twiss();
enter Twiss module iteration: 1 error: 1.175661E-03 deltap: 0.000000E+00 orbit: -5.930454E-05 -2.317281E-04 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 iteration: 2 error: 5.877478E-09 deltap: 0.000000E+00 orbit: -5.929467E-05 -2.317264E-04 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 ++++++ table: summ length orbit5 alfa gammatr 216.72000 -0.00000 0.03163 5.62258 q1 dq1 betxmax dxmax 4.30217 -7.33961 34.33156 3.54981 dxrms xcomax xcorms q2 1.99376 0.01224 0.00464 4.19944 dq2 betymax dymax dyrms -6.84455 28.35124 0.00000 0.00000 ycomax ycorms deltap synch_1 0.00000 0.00000 0.00000 0.00000 synch_2 synch_3 synch_4 synch_5 0.00000 0.00000 0.00000 0.00000 synch_6 synch_8 nflips dqmin 0.00000 0.00000 0.00000 0.00000 dqmin_phase 0.00000
Let us plot the closed orbit after correction now!
x_co = interp1d(twiss['s'], twiss['x'], kind='linear')
x_co_bpm = x_co(s_bpm)
plt.plot(twiss['s'], twiss['x'])
plt.scatter(s_bpm, x_co_bpm, marker='o', c='r', label='BPM')
plt.xlabel('$s$ [m]')
plt.ylabel(r'$x_{co}$ [m]')
plt.legend();
$\implies$ Can you describe and explain what you observe?
Let us plot the used corrector strengths $\Theta_j$:
plt.bar(s_corr, theta_vec, width=3, facecolor='darkred', edgecolor='none')
plt.xlabel('$s$ [m]')
plt.ylabel('$\Theta_j$ [rad]')
plt.title('Corrector strengths', y=1.04);
reset_correctors()
s_save = np.array(twiss['s']).copy()
x_save = np.array(twiss['x']).copy()
Let MAD-X do the job for us:
madx.input('''
select, flag=twiss, clear;
select, flag=twiss, class=GS00DX5H;
twiss, file="bpm.tsv";
''')
enter Twiss module iteration: 1 error: 1.142975E-02 deltap: 0.000000E+00 orbit: -3.120383E-03 1.762989E-03 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 iteration: 2 error: 2.679426E-06 deltap: 0.000000E+00 orbit: -3.119209E-03 1.762633E-03 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 iteration: 3 error: 1.317965E-12 deltap: 0.000000E+00 orbit: -3.119209E-03 1.762633E-03 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 ++++++ table: summ length orbit5 alfa gammatr 216.72000 -0.00000 0.03177 5.61014 q1 dq1 betxmax dxmax 4.30226 -7.33998 34.43140 3.92505 dxrms xcomax xcorms q2 2.00303 0.03432 0.00981 4.19942 dq2 betymax dymax dyrms -6.84447 28.47205 0.00000 0.00000 ycomax ycorms deltap synch_1 0.00000 0.00000 0.00000 0.00000 synch_2 synch_3 synch_4 synch_5 0.00000 0.00000 0.00000 0.00000 synch_6 synch_8 nflips dqmin 0.00000 0.00000 0.00000 0.00000 dqmin_phase 0.00000
True
madx.input('''
readmytable, file="bpm.tsv", table="twiss_bpm";
''')
Want to make named table: twiss_bpm
True
Here comes the correction command, using also the SVD algorithm:
madx.input('''
correct, flag=ring, mode=svd, plane=x, error=1.0e-10, extern, orbit=twiss_bpm, clist="corr.tab";
''')
Want to correct orbit of a single ring Want to use orbit from: twiss_bpm 20 monitors and 12 correctors found in input 12 monitors and 12 correctors enabled start SVD correction using 12 correctors CORRECTION SUMMARY: average [mm] std.dev. [mm] RMS [mm] peak-to-peak [mm] before correction: 0.760000 6.654684 6.697941 23.720000 after correction: 0.000000 0.000000 0.000000 0.000000 Max strength: 1.948182e+00 should be less than corrector strength limit: 1.000000e+00
True
twiss = madx.twiss();
enter Twiss module iteration: 1 error: 1.265436E-03 deltap: 0.000000E+00 orbit: -4.551246E-05 -2.470152E-04 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 iteration: 2 error: 6.045699E-09 deltap: 0.000000E+00 orbit: -4.550017E-05 -2.470137E-04 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 ++++++ table: summ length orbit5 alfa gammatr 216.72000 -0.00000 0.03163 5.62277 q1 dq1 betxmax dxmax 4.30217 -7.33960 34.33280 3.55307 dxrms xcomax xcorms q2 1.99375 0.01242 0.00467 4.19944 dq2 betymax dymax dyrms -6.84454 28.35251 0.00000 0.00000 ycomax ycorms deltap synch_1 0.00000 0.00000 0.00000 0.00000 synch_2 synch_3 synch_4 synch_5 0.00000 0.00000 0.00000 0.00000 synch_6 synch_8 nflips dqmin 0.00000 0.00000 0.00000 0.00000 dqmin_phase 0.00000
Plot the MAD-X result for the corrected closed orbit. Compare to our self-implemented SVD algorithm with the orbit response matrix defined by the $x_\mathrm{COD}(s)$ expression:
x_co = interp1d(twiss['s'], twiss['x'], kind='linear')
x_co_bpm = x_co(s_bpm)
plt.plot(twiss['s'], twiss['x'], label='MAD-X')
plt.plot(s_save, x_save, c='lightblue', ls='--', lw=2, label='manual ORM')
plt.scatter(s_bpm, x_co_bpm, marker='o', c='r', label='BPM')
plt.xlabel('$s$ [m]')
plt.ylabel(r'$x_{co}$ [m]')
plt.legend(loc='upper left', bbox_to_anchor=(1.05, 1));
Let us compare the obtained kick strengths from MAD-X to our SVD implementation:
theta_vec_madx = [el.chkick for el in madx.sequence.sis18ring.expanded_elements if 'gs00mu1a' in str(el)]
plt.bar(s_corr - 1.5, theta_vec_madx, width=3, facecolor='C0', edgecolor='none', label='MAD-X')
plt.bar(s_corr + 1.5, theta_vec, width=3, facecolor='lightblue', edgecolor='none', label='manual ORM')
plt.xlabel('$s$ [m]')
plt.ylabel('$\Theta_j$ [rad]')
plt.title('Corrector strengths', y=1.04)
plt.legend(loc='upper left', bbox_to_anchor=(1.05, 1));
$\implies$ global orbit correction will correct the orbit at the BPMs to zero and usually reduce the overall rms closed orbit distortion! Local orbit correction can help on top to bring down excessive peaks in between BPMs (e.g. when the aperture (vacuum tube around the beam) is hit and beam loss monitors indicate the location in the ring).