Skip to content

Commit 700204d

Browse files
authored
Merge pull request #88 from xsuite/release/v0.5.0
Release 0.5.0
2 parents df83ff4 + 9eac1f4 commit 700204d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+2620
-1154
lines changed

examples/adt.py

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
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()

examples/adt_simplified.py

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
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 simplified example of the ADT in the LHC. Each
17+
# particle gets a different kick, randomly sampled. This allows for
18+
# a quicker and more controlled emittance growth (though less realistic).
19+
20+
beam = 1
21+
plane = 'V'
22+
adt_turns = 1000
23+
total_turns = 2000
24+
num_particles = 5000
25+
nemitt_x = 3.5e-6
26+
nemitt_y = 2.5e-6
27+
28+
29+
# Import a Run 3 LHC lattice without apertures
30+
line = xt.Line.from_json(xc._pkg_root.parent / 'examples' / 'machines' / f'lhc_run3_b{beam}_no_aper.json')
31+
32+
33+
# Create the ADT
34+
pos = 'b5l4' if f'{beam}' == '1' and plane == 'H' else 'b5r4'
35+
pos = 'b5l4' if f'{beam}' == '2' and plane == 'V' else pos
36+
name = f'adtk{plane.lower()}.{pos}.b{beam}'
37+
tank_start = f'adtk{plane.lower()}.{pos}.a.b{beam}'
38+
tank_end = f'adtk{plane.lower()}.{pos}.d.b{beam}'
39+
adt_pos = 0.5*line.get_s_position(tank_start) + 0.5*line.get_s_position(tank_end)
40+
adt = xc.BlowUp.install(line, name=name, at_s=adt_pos, need_apertures=False, plane=plane,
41+
stop_at_turn=adt_turns, use_individual_kicks=True)
42+
43+
44+
# Add an emittance monitor
45+
mon = xc.EmittanceMonitor.install(line, name="emittance monitor", at_s=adt_pos, stop_at_turn=total_turns)
46+
47+
48+
# We also add a particles monitor, to calculate the emittance based on normalised coordinates.
49+
# This will allow us to see how strongly the beam is mismatched.
50+
mon2 = xt.ParticlesMonitor(start_at_turn=0, stop_at_turn=total_turns, num_particles=num_particles)
51+
line.insert_element(element=mon2, name="particle monitor", at_s=adt_pos)
52+
53+
54+
# Build the tracker and optimise
55+
line.build_tracker()
56+
line.optimize_for_tracking()
57+
twiss = line.twiss()
58+
59+
60+
# This will calibrate the ADT such that we push the full beam beyond 5 sigma over 1000 turns.
61+
# In this example we won't blow up this aggressively (as the emittance calculation tends to
62+
# get noisy at large emittances); hence we put the amplitude at 10%.
63+
if plane == 'H':
64+
adt.calibrate_by_emittance(nemitt=nemitt_x, twiss=twiss)
65+
else:
66+
adt.calibrate_by_emittance(nemitt=nemitt_y, twiss=twiss)
67+
adt.amplitude = 0.1
68+
69+
70+
# Generate a matched Gaussian bunch
71+
part = xp.generate_matched_gaussian_bunch(num_particles=num_particles, total_intensity_particles=1.6e11,
72+
nemitt_x=nemitt_x, nemitt_y=nemitt_y, sigma_z=7.55e-2, line=line)
73+
74+
75+
# Move the tracker to a multi-core context
76+
line.discard_tracker()
77+
line.build_tracker(_context=xo.ContextCpu(omp_num_threads=12))
78+
79+
80+
# Activate the ADT
81+
adt.activate()
82+
83+
84+
# Track
85+
line.track(part, num_turns=total_turns, with_progress=1)
86+
87+
88+
# Get the normalised emittance from the particles monitor
89+
part_norm = twiss.get_normalized_coordinates(mon2)
90+
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])
91+
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])
92+
mon2_x_mean = np.mean(mon2.x, axis=0)
93+
mon2_y_mean = np.mean(mon2.y, axis=0)
94+
mon2_x_std = np.std(mon2.x, axis=0)
95+
mon2_y_std = np.std(mon2.y, axis=0)
96+
mon2_turns = list(range(total_turns))
97+
98+
99+
# Plot the results
100+
_, ax = plt.subplots(figsize=(6,4))
101+
ax.plot(mon.turns, 1.e6*mon.nemitt_x, label='H')
102+
ax.plot(mon.turns, 1.e6*mon.nemitt_I, label='I')
103+
ax.plot(mon2_turns, 1.e6 * mon2_nemitt_x * mon.beta0 * mon.gamma0, label='<xN^2 + pxN^2>/2')
104+
ax.axvline(adt_turns, c='r', ls='--', label='stop blow-up')
105+
ax.set_ylabel(r"$\epsilon\; [\mu\mathrm{m}]$")
106+
ax.set_xlabel("Turn number")
107+
ax.legend()
108+
ax.set_title("Horizontal emittance growth by ADT blow-up in the LHC")
109+
print(f"Total calculation time {time.time()-start_time}s")
110+
plt.savefig("adt_simplified_horizontal_emittance.png", dpi=300)
111+
plt.show()
112+
113+
_, ax = plt.subplots(figsize=(6,4))
114+
ax.fill_between(mon2_turns, 1e3*mon2_x_mean + 1e3*mon2_x_std, 1e3*mon2_x_mean - 1e3*mon2_x_std, alpha=0.4)
115+
ax.plot(mon2_turns, 1e3*mon2_x_mean, label=r'$<x>$')
116+
ax.axvline(adt_turns, c='r', ls='--', label='stop blow-up')
117+
ax.set_ylabel(r"normalised amplitude $[mm]$")
118+
ax.set_xlabel("Turn number")
119+
ax.legend()
120+
ax.set_title("Average amplitude growth by ADT blow-up in the LHC")
121+
plt.savefig("adt_simplified_horizontal_amplitude.png", dpi=300)
122+
plt.show()
123+
124+
_, ax = plt.subplots(figsize=(6,4))
125+
ax.plot(mon.turns, 1.e6*mon.nemitt_y, label='V')
126+
ax.plot(mon.turns, 1.e6*mon.nemitt_II, label='II')
127+
ax.plot(mon2_turns, 1.e6 * mon2_nemitt_y * mon.beta0 * mon.gamma0, label='<yN^2 + pyN^2>/2')
128+
ax.axvline(adt_turns, c='r', ls='--', label='stop blow-up')
129+
ax.set_ylabel(r"$\epsilon\; [\mu\mathrm{m}]$")
130+
ax.set_xlabel("Turn number")
131+
ax.legend()
132+
ax.set_title("Vertical emittance growth by ADT blow-up in the LHC")
133+
plt.savefig("adt_simplified_vertical_emittance.png", dpi=300)
134+
plt.show()
135+
136+
_, ax = plt.subplots(figsize=(6,4))
137+
ax.fill_between(mon2_turns, 1e3*mon2_y_mean + 1e3*mon2_y_std, 1e3*mon2_y_mean - 1e3*mon2_y_std, alpha=0.4)
138+
ax.plot(mon2_turns, 100*mon2_y_mean, label=r'$<y>$')
139+
ax.axvline(adt_turns, c='r', ls='--', label='stop blow-up')
140+
ax.set_ylabel(r"normalised amplitude $[mm]$")
141+
ax.set_xlabel("Turn number")
142+
ax.legend()
143+
ax.set_title("Average amplitude growth by ADT blow-up in the LHC")
144+
plt.savefig("adt_simplified_vertical_amplitude.png", dpi=300)
145+
plt.show()
146+
147+
_, ax = plt.subplots(figsize=(6,4))
148+
ax.plot(mon.turns, 1.e2*mon.nemitt_zeta, label='L')
149+
ax.plot(mon.turns, 1.e2*mon.nemitt_III, label='III')
150+
ax.axvline(adt_turns, c='r', ls='--', label='stop blow-up')
151+
ax.set_ylabel(r"$\epsilon\; [\mathrm{cm}]$")
152+
ax.set_xlabel("Turn number")
153+
ax.legend()
154+
ax.set_title("Longitudinal emittance growth by ADT blow-up in the LHC")
155+
plt.savefig("adt_simplified_longitudinal_emittance.png", dpi=300)
156+
plt.show()

examples/everest_block.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,7 @@
1717
block.track(part)
1818

1919
plt.hist(part.x, bins=200, density=True)
20+
plt.show()
21+
2022
plt.hist(part.px*part.rpp, bins=200, density=True)
2123
plt.show()

examples/everest_collimator.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44
# ######################################### #
55

66
import numpy as np
7-
from pathlib import Path
8-
import sys, os, contextlib
97
import matplotlib.pyplot as plt
108

119
import xobjects as xo

0 commit comments

Comments
 (0)