Source code for mbtrack2.tracking.excite
# -*- coding: utf-8 -*-
"""
Module to deal with different kinds of beam excitation.
"""
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.axes import Axes
from scipy.signal import chirp
from mbtrack2.tracking.element import Element
from mbtrack2.tracking.particles import Beam, Bunch
from mbtrack2.utilities.synchrotron import Synchrotron
[docs]
class Sweep(Element):
"""
Element which excite the beam in between two frequencies, i.e. apply
frequency sweep (chirp) on all or a given bunch in the chosen plane.
If applied to a full beam, the excitation is the same (and at the same time)
for all bunches, so it drives a growth of coupled bunch mode 0.
Parameters
----------
ring : Synchrotron
Synchrotron object.
f0 : float
Initial frequency of the sweep in [Hz].
f1 : float
Final frequency of the sweep in [Hz].
t1 : float
Time duration of the sweep in [s].
level : float
Kick level to apply in [V].
plane : "x", "y" or "tau"
Plane on which to apply the kick.
bunch_to_sweep : int, optional
Bunch number to which the sweeping is applied.
If None, the sweeping is applied for all bunches.
Default is None.
Methods
-------
track(bunch_or_beam)
Tracking method for the element.
plot()
Plot the sweep voltage applied.
"""
[docs]
def __init__(self,
ring: Synchrotron,
f0: float,
f1: float,
t1: float,
level: float,
plane: str,
bunch_to_sweep: int | None = None):
self.ring = ring
self.t = np.arange(0, t1, ring.T0)
self.N = len(self.t)
self.count = 0
self.level = level
self.sweep = chirp(self.t, f0, t1, f1)
if plane == "x":
self.apply = "xp"
elif plane == "y":
self.apply = "yp"
elif plane == "tau":
self.apply = "delta"
else:
raise ValueError("plane should be 'x', 'y' or 'tau'.")
self.bunch_to_sweep = bunch_to_sweep
[docs]
def track(self, bunch_or_beam: Bunch | Beam):
"""
Tracking method for this element.
Parameters
----------
bunch_or_beam : Bunch or Beam
"""
if isinstance(bunch_or_beam, Bunch):
bunch = bunch_or_beam
self._track_bunch(bunch)
elif isinstance(bunch_or_beam, Beam):
beam = bunch_or_beam
if beam.mpi_switch is True:
if self.bunch_to_sweep is not None:
if beam.mpi.bunch_num == self.bunch_to_sweep:
self._track_bunch(beam[beam.mpi.bunch_num])
else:
self._track_bunch(beam[beam.mpi.bunch_num])
else:
if self.bunch_to_sweep is not None:
self._track_bunch(beam[self.bunch_to_sweep])
else:
for bunch in beam.not_empty:
self._track_bunch(bunch, False)
self.count += 1
if self.count >= self.N:
self.count = 0
else:
raise TypeError("bunch_or_beam should be a Beam or Bunch object.")
[docs]
def _track_bunch(self, bunch: Bunch, count_step: bool = True):
"""
Tracking method for a bunch for this element.
Parameters
----------
bunch : Bunch
"""
sweep_val = self.sweep[self.count]
bunch[self.apply] += self.level / self.ring.E0 * sweep_val
if count_step:
self.count += 1
if self.count >= self.N:
self.count = 0
[docs]
def plot(self, ax: Axes | None = None) -> Axes:
"""Plot the sweep voltage applied."""
if ax is None:
_, ax = plt.subplots()
ax.plot(self.t, self.sweep)
ax.set_xlabel("Time [s]")
ax.set_ylabel("Sweep voltage [V]")
return ax