|
| 1 | +# copyright ############################### # |
| 2 | +# This file is part of the Xcoll Package. # |
| 3 | +# Copyright (c) CERN, 2024. # |
| 4 | +# ######################################### # |
| 5 | + |
| 6 | +import numpy as np |
| 7 | +import matplotlib.pyplot as plt |
| 8 | +import time |
| 9 | +start_time = time.time() |
| 10 | + |
| 11 | +import xobjects as xo |
| 12 | +import xtrack as xt |
| 13 | +import xpart as xp |
| 14 | +import xcoll as xc |
| 15 | + |
| 16 | +# This example is a more realistic example of the ADT in the LHC. Each |
| 17 | +# particle in a bunch gets the same kick (this is the default for a BlowUp). |
| 18 | +# Note that this realistic behaviour shakes the beam around at every pass: |
| 19 | +# the distribution becomes unmatched as it is no longer centered around |
| 20 | +# the closed orbit. After the blow-up, the beam needs some relaxation time |
| 21 | +# for the particles to decohere again. |
| 22 | +# This is investigated in this example: the emittance is calculated by an |
| 23 | +# emittance monitor (based on the covariance matrix of physical coordinates), |
| 24 | +# and by a particles monitor that calculates the emittance based on normalised |
| 25 | +# coordinates (which implicitly assumes a matched beam). In the resulting |
| 26 | +# plots, the emittance based on normalised coordinates directly reflects its |
| 27 | +# final value once the blow-up is stopped, while the emittance based on physical |
| 28 | +# coordinates needs some relaxation time to reach this final value. |
| 29 | + |
| 30 | +beam = 1 |
| 31 | +plane = 'V' |
| 32 | +adt_turns = 1000 |
| 33 | +total_turns = 3500 |
| 34 | +num_particles = 5000 |
| 35 | +nemitt_x = 3.5e-6 |
| 36 | +nemitt_y = 2.5e-6 |
| 37 | + |
| 38 | + |
| 39 | +# Import a Run 3 LHC lattice without apertures |
| 40 | +line = xt.Line.from_json(xc._pkg_root.parent / 'examples' / 'machines' / f'lhc_run3_b{beam}_no_aper.json') |
| 41 | + |
| 42 | + |
| 43 | +# Create the ADT |
| 44 | +pos = 'b5l4' if f'{beam}' == '1' and plane == 'H' else 'b5r4' |
| 45 | +pos = 'b5l4' if f'{beam}' == '2' and plane == 'V' else pos |
| 46 | +name = f'adtk{plane.lower()}.{pos}.b{beam}' |
| 47 | +tank_start = f'adtk{plane.lower()}.{pos}.a.b{beam}' |
| 48 | +tank_end = f'adtk{plane.lower()}.{pos}.d.b{beam}' |
| 49 | +adt_pos = 0.5*line.get_s_position(tank_start) + 0.5*line.get_s_position(tank_end) |
| 50 | +adt = xc.BlowUp.install(line, name=name, at_s=adt_pos, need_apertures=False, plane=plane, |
| 51 | + stop_at_turn=adt_turns) |
| 52 | + |
| 53 | + |
| 54 | +# Add an emittance monitor |
| 55 | +mon = xc.EmittanceMonitor.install(line, name="emittance monitor", at_s=adt_pos, stop_at_turn=total_turns) |
| 56 | + |
| 57 | + |
| 58 | +# We also add a particles monitor, to calculate the emittance based on normalised coordinates. |
| 59 | +# This will allow us to see how strongly the beam is mismatched. |
| 60 | +mon2 = xt.ParticlesMonitor(start_at_turn=0, stop_at_turn=total_turns, num_particles=num_particles) |
| 61 | +line.insert_element(element=mon2, name="particle monitor", at_s=adt_pos) |
| 62 | + |
| 63 | + |
| 64 | +# Build the tracker and optimise |
| 65 | +line.build_tracker() |
| 66 | +line.optimize_for_tracking() |
| 67 | +twiss = line.twiss() |
| 68 | + |
| 69 | + |
| 70 | +# This will calibrate the ADT such that we push the full beam beyond 5 sigma over 1000 turns. |
| 71 | +# In this example we won't blow up this aggressively (as the emittance calculation tends to |
| 72 | +# get noisy at large emittances); hence we put the amplitude at 10%. |
| 73 | +if plane == 'H': |
| 74 | + adt.calibrate_by_emittance(nemitt=nemitt_x, twiss=twiss) |
| 75 | +else: |
| 76 | + adt.calibrate_by_emittance(nemitt=nemitt_y, twiss=twiss) |
| 77 | +adt.amplitude = 0.1 |
| 78 | + |
| 79 | + |
| 80 | +# Generate a matched Gaussian bunch |
| 81 | +part = xp.generate_matched_gaussian_bunch(num_particles=num_particles, total_intensity_particles=1.6e11, |
| 82 | + nemitt_x=nemitt_x, nemitt_y=nemitt_y, sigma_z=7.55e-2, line=line) |
| 83 | + |
| 84 | + |
| 85 | +# Move the tracker to a multi-core context |
| 86 | +line.discard_tracker() |
| 87 | +line.build_tracker(_context=xo.ContextCpu(omp_num_threads=12)) |
| 88 | + |
| 89 | + |
| 90 | +# Activate the ADT |
| 91 | +adt.activate() |
| 92 | + |
| 93 | + |
| 94 | +# Track |
| 95 | +line.track(part, num_turns=total_turns, with_progress=1) |
| 96 | + |
| 97 | + |
| 98 | +# Get the normalised emittance from the particles monitor |
| 99 | +part_norm = twiss.get_normalized_coordinates(mon2) |
| 100 | +mon2_nemitt_x = np.array([np.mean(x[~np.isnan(x)])/2 for x in (part_norm.x_norm**2 + part_norm.px_norm**2).T]) |
| 101 | +mon2_nemitt_y = np.array([np.mean(y[~np.isnan(y)])/2 for y in (part_norm.y_norm**2 + part_norm.py_norm**2).T]) |
| 102 | +mon2_x_mean = np.mean(mon2.x, axis=0) |
| 103 | +mon2_y_mean = np.mean(mon2.y, axis=0) |
| 104 | +mon2_x_std = np.std(mon2.x, axis=0) |
| 105 | +mon2_y_std = np.std(mon2.y, axis=0) |
| 106 | +mon2_turns = list(range(total_turns)) |
| 107 | + |
| 108 | + |
| 109 | +# Plot the results |
| 110 | +_, ax = plt.subplots(figsize=(6,4)) |
| 111 | +ax.plot(mon.turns, 1.e6*mon.nemitt_x, label='H') |
| 112 | +ax.plot(mon.turns, 1.e6*mon.nemitt_I, label='I') |
| 113 | +ax.plot(mon2_turns, 1.e6 * mon2_nemitt_x * mon.beta0 * mon.gamma0, label='<xN^2 + pxN^2>/2') |
| 114 | +ax.axvline(adt_turns, c='r', ls='--', label='stop blow-up') |
| 115 | +ax.set_ylabel(r"$\epsilon\; [\mu\mathrm{m}]$") |
| 116 | +ax.set_xlabel("Turn number") |
| 117 | +ax.legend() |
| 118 | +ax.set_title("Horizontal emittance growth by ADT blow-up in the LHC") |
| 119 | +print(f"Total calculation time {time.time()-start_time}s") |
| 120 | +plt.savefig("adt_horizontal_emittance.png", dpi=300) |
| 121 | +plt.show() |
| 122 | + |
| 123 | +_, ax = plt.subplots(figsize=(6,4)) |
| 124 | +ax.fill_between(mon2_turns, 1e3*mon2_x_mean + 1e3*mon2_x_std, 1e3*mon2_x_mean - 1e3*mon2_x_std, alpha=0.4) |
| 125 | +ax.plot(mon2_turns, 1e3*mon2_x_mean, label=r'$<x>$') |
| 126 | +ax.axvline(adt_turns, c='r', ls='--', label='stop blow-up') |
| 127 | +ax.set_ylabel(r"normalised amplitude $[mm]$") |
| 128 | +ax.set_xlabel("Turn number") |
| 129 | +ax.legend() |
| 130 | +ax.set_title("Average amplitude growth by ADT blow-up in the LHC") |
| 131 | +plt.savefig("adt_horizontal_amplitude.png", dpi=300) |
| 132 | +plt.show() |
| 133 | + |
| 134 | +_, ax = plt.subplots(figsize=(6,4)) |
| 135 | +ax.plot(mon.turns, 1.e6*mon.nemitt_y, label='V') |
| 136 | +ax.plot(mon.turns, 1.e6*mon.nemitt_II, label='II') |
| 137 | +ax.plot(mon2_turns, 1.e6 * mon2_nemitt_y * mon.beta0 * mon.gamma0, label='<yN^2 + pyN^2>/2') |
| 138 | +ax.axvline(adt_turns, c='r', ls='--', label='stop blow-up') |
| 139 | +ax.set_ylabel(r"$\epsilon\; [\mu\mathrm{m}]$") |
| 140 | +ax.set_xlabel("Turn number") |
| 141 | +ax.legend() |
| 142 | +ax.set_title("Vertical emittance growth by ADT blow-up in the LHC") |
| 143 | +plt.savefig("adt_vertical_emittance.png", dpi=300) |
| 144 | +plt.show() |
| 145 | + |
| 146 | +_, ax = plt.subplots(figsize=(6,4)) |
| 147 | +ax.fill_between(mon2_turns, 1e3*mon2_y_mean + 1e3*mon2_y_std, 1e3*mon2_y_mean - 1e3*mon2_y_std, alpha=0.4) |
| 148 | +ax.plot(mon2_turns, 100*mon2_y_mean, label=r'$<y>$') |
| 149 | +ax.axvline(adt_turns, c='r', ls='--', label='stop blow-up') |
| 150 | +ax.set_ylabel(r"normalised amplitude $[mm]$") |
| 151 | +ax.set_xlabel("Turn number") |
| 152 | +ax.legend() |
| 153 | +ax.set_title("Average amplitude growth by ADT blow-up in the LHC") |
| 154 | +plt.savefig("adt_vertical_amplitude.png", dpi=300) |
| 155 | +plt.show() |
| 156 | + |
| 157 | +_, ax = plt.subplots(figsize=(6,4)) |
| 158 | +ax.plot(mon.turns, 1.e2*mon.nemitt_zeta, label='L') |
| 159 | +ax.plot(mon.turns, 1.e2*mon.nemitt_III, label='III') |
| 160 | +ax.axvline(adt_turns, c='r', ls='--', label='stop blow-up') |
| 161 | +ax.set_ylabel(r"$\epsilon\; [\mathrm{cm}]$") |
| 162 | +ax.set_xlabel("Turn number") |
| 163 | +ax.legend() |
| 164 | +ax.set_title("Longitudinal emittance growth by ADT blow-up in the LHC") |
| 165 | +plt.savefig("adt_longitudinal_emittance.png", dpi=300) |
| 166 | +plt.show() |
0 commit comments