Source code for wake_t.beamline_elements.field_element

from typing import Optional, Union, Callable, List, Literal

import scipy.constants as ct

from wake_t.diagnostics import OpenPMDDiagnostics
from wake_t.tracking.tracker import Tracker
from wake_t.fields.base import Field
from wake_t.particles.particle_bunch import ParticleBunch


[docs] class FieldElement: """ Generic class for any beamline element based on field tracking. Parameters ---------- length : float or str Length of the plasma stage in m. dt_bunch : float, str or list of float and str The time step for evolving the particle bunches. An adaptive time step can be used if this parameter is set to ``'auto'`` and a ``auto_dt_bunch`` function is provided. A list of values can also be provided. In this case, the list should have the same order as the list of bunches given to the ``track`` method. bunch_pusher : str The pusher used to evolve the particle bunches in time within the specified fields. Possible values are ``'rk4'`` (Runge-Kutta method of 4th order) or ``'boris'`` (Boris method). n_out : int Number of times along the stage in which the particle distribution should be returned (A list with all output bunches is returned after tracking). name : str Name of the plasma stage. This is only used for displaying the progress bar during tracking. By default, ``'field element'``. fields : list List of Fields that will be applied to the particle bunches. auto_dt_bunch : callable, optional Function used to determine the adaptive time step for bunches in which the time step is set to ``'auto'``. The function should take solely a ``ParticleBunch`` as argument. push_bunches_before_diags : bool, optional Whether to push the bunches before saving them to the diagnostics. Since the time step of the diagnostics can be different from that of the bunches, it could happen that the bunches appear in the diagnostics as they were at the last push, but not at the actual time of the diagnostics. Setting this parameter to ``True`` (default) ensures that an additional push is given to all bunches to evolve them to the diagnostics time before saving. This additional push will always have a time step smaller than the the time step of the bunch, so it has no detrimental impact on the accuracy of the simulation. However, it could make convergence studies more difficult to interpret, since the number of pushes will depend on `n_diags`. Therefore, it is exposed as an option so that it can be disabled if needed. """ def __init__( self, length: float, dt_bunch: Union[float, str, List[Union[float, str]]], bunch_pusher: Optional[Literal["boris", "rk4"]] = "boris", n_out: Optional[int] = 1, name: Optional[str] = "field element", fields: Optional[List[Field]] = [], auto_dt_bunch: Optional[Callable[[ParticleBunch], float]] = None, push_bunches_before_diags: Optional[bool] = True, ) -> None: self.length = length self.bunch_pusher = bunch_pusher self.dt_bunch = dt_bunch self.n_out = n_out self.name = name self.fields = fields self.auto_dt_bunch = auto_dt_bunch self.push_bunches_before_diags = push_bunches_before_diags
[docs] def track( self, bunches: Optional[Union[ParticleBunch, List[ParticleBunch]]] = [], opmd_diag: Optional[Union[bool, OpenPMDDiagnostics]] = False, diag_dir: Optional[str] = None, show_progress_bar: Optional[bool] = True, ) -> Union[List[ParticleBunch], List[List[ParticleBunch]]]: """ Track bunch through element. Parameters ---------- bunches : ParticleBunch or list of ParticleBunch Particle bunches to be tracked. opmd_diag : bool or OpenPMDDiagnostics Determines whether to write simulation diagnostics to disk (i.e. particle distributions and fields). The output is written to HDF5 files following the openPMD standard. The number of outputs the `n_out` value. It is also possible to provide an already existing OpenPMDDiagnostics instance instead of a boolean value. diag_dir : str Directory into which the openPMD output will be written. By default this is a 'diags' folder in the current directory. Only needed if `opmd_diag=True`. show_progress_bar : bool, optional Whether to show a progress bar of the tracking. By default ``True``. Returns ------- A list of size 'n_out' containing the bunch distribution at each step. """ # Make sure `bunches` and `dt_bunch` are lists. if not isinstance(bunches, list): bunches = [bunches] if not isinstance(self.dt_bunch, list): dt_bunch = [self.dt_bunch] * len(bunches) else: n_dt, n_bunches = len(self.dt_bunch), len(bunches) if n_dt != n_bunches: raise ValueError( f"The number of time steps in `dt_bunch` ({n_dt}) " f"does not match the number of bunches ({n_bunches})." ) dt_bunch = self.dt_bunch # Create diagnostics instance. if type(opmd_diag) is not OpenPMDDiagnostics and opmd_diag: opmd_diag = OpenPMDDiagnostics(write_dir=diag_dir) elif not opmd_diag: opmd_diag = None # Create tracker. tracker = Tracker( t_final=self.length / ct.c, bunches=bunches, dt_bunches=dt_bunch, fields=self.fields, n_diags=self.n_out, opmd_diags=opmd_diag, bunch_pusher=self.bunch_pusher, auto_dt_bunch_f=self.auto_dt_bunch, push_bunches_before_diags=self.push_bunches_before_diags, show_progress_bar=show_progress_bar, section_name=self.name, ) # Do tracking. bunch_list = tracker.do_tracking() # If only tracking one bunch, do not return list of lists. if len(bunch_list) == 1: bunch_list = bunch_list[0] return bunch_list