Multisetters
When the number of beam elements to be changed is very large, this can introduce
a significant overhead on the simulation time. For these cases it is possible to use
the xtrack.MultiSetter
class, to perform the changes in a more efficient way,
bypassing the expressions and acting directly on the element properties.
Furthermore, the MultiSetter
stores the
memory addresses of the quantities to be changed and performs the changes with a single
compiled kernel, using multithreading when allowed by the context.
The following example shows how to use the MultiSetter
to apply a sinusoidal
ripple to the strength of several quadropoles of a synchrotron.
See also: xtrack.MultiSetter
import numpy as np
from cpymad.madx import Madx
import xobjects as xo
import xtrack as xt
import xpart as xp
# Choose a context
ctx = xo.ContextCpu()
# Import SPS lattice and build a tracker
mad = Madx()
seq_name = 'sps'
mad.call('../../test_data/sps_w_spacecharge/sps_thin.seq')
mad.use(seq_name)
madtw = mad.twiss()
line = xt.Line.from_madx_sequence(mad.sequence[seq_name])
line.particle_ref = xt.Particles(p0c=400e9, mass0=xt.PROTON_MASS_EV)
line.build_tracker(_context=ctx)
# Switch on RF and twiss
line['acta.31637'].voltage = 7e6
line['acta.31637'].lag = 180.
twxt = line.twiss()
# Get revolution period
T_rev = twxt['T_rev0']
# Extract list of elements to trim (all focusing quads)
tt = line.get_table()
elements_to_trim = tt.rows[tt.element_type == 'Multipole'].rows['qf.*'].name
# => contains ['qf.52010', 'qf.52210', 'qf.52410', 'qf.52610', 'qf.52810',
# 'qf.53010', 'qf.53210', 'qf.53410', 'qf.60010', 'qf.60210', ...]
# Build a custom setter
qf_setter = xt.MultiSetter(line, elements_to_trim,
field='knl', index=1 # we want to change knl[1]
)
# Get the initial values of the quad strength
k1l_0 = qf_setter.get_values()
# Generate particles to be tracked
# (we choose to match the distribution without accounting for spacecharge)
particles = xp.generate_matched_gaussian_bunch(_context=ctx,
num_particles=100, total_intensity_particles=1e10,
nemitt_x=3e-6, nemitt_y=3e-6, sigma_z=15e-2,
line=line)
# Define amplitude and phase of the quadrupole ripple
f_quad = 50. # Hz
A_quad = 0.01 # relative amplitude
# Track the particles and apply turn-by-turn change to all selected quads
num_turns = 2000
check_trim = []
for ii in range(num_turns):
if ii % 100 == 0: print(f'Turn {ii} of {num_turns}')
# Change the strength of the quads
k1l = k1l_0 * (1 + A_quad * np.sin(2*np.pi*f_quad*ii*T_rev))
qf_setter.set_values(k1l)
# Track one turn
line.track(particles)
# Log the strength of one quad to check
check_trim.append(ctx.nparray_from_context_array(line['qf.52010'].knl)[1])
# Plot the evolution of the quad strength
import matplotlib.pyplot as plt
plt.plot(check_trim)
plt.show()
# Complete source: xtrack/examples/multisetter/000_sps_50hz_ripple.py