Cavities reference phase
Definitions
There are three ways to define the reference phase of a cavity:
by its relative entry phase \(\phi_{0,\,\mathrm{rel}}\),
its absolute entry phase \(\phi_{0,\,\mathrm{abs}}\),
its synchronous phase \(\phi_s\).
A cavity reference phase controls its behavior when the beam phase differs from the baseline, which happens downstream of a failure if the beam absolute phase was not recovered.
"phi_0_abs": the cavity is not rephased."phi_0_rel": \(\phi_{0,\,\mathrm{rel}}\) is preserved, so that the beam always “sees” the same RF phase."phi_s": \(\phi_s\) is preserved, so that the cavity acceptance stays the same.
The relation between relative and entry phases is: \begin{equation} E_0\cos{\phi_{0,\,\mathrm{abs}}} = E_0\cos{(\phi_{0,\,\mathrm{rel}} + \phi_\mathrm{in})} \end{equation} where \(\phi_\mathrm{in}\) is the phase at which the synchronous particle enters the cavity, \(E_0\) is the amplitude of the electric field.
There is no analytic relation between \(phi_s\) and the RF phases \(\phi_{0,\,\mathrm{rel}}\), \(\phi_{0,\,\mathrm{abs}}\). Hereby, when the reference phase of a cavity is \(phi_s\), calculations are performed to find the actual RF phase allowing to retrieve \(phi_s\). Those calculations can take some time.
The cavities reference phases are set by the reference_phase_policy entry in the beam_calculator section of the TOML file:
"phi_0_rel","phi_0_abs"or"phi_s": use this phase as reference"as_in_original_dat": keep the reference stated in theDATfile, following theSET_SYNC_PHASEand theFLAG_PHI_ABSvalue of theFIELD_MAPcommands.Different cavities can have different reference phase!
In the rest of this notebook, we illustrate these differences and their impact on the beam dynamics.
Preparation
Load libraries
[1]:
import logging
from pathlib import Path
import matplotlib.pyplot as plt
from lightwin.beam_calculation.beam_calculator import BeamCalculator
from lightwin.beam_calculation.factory import BeamCalculatorsFactory
from lightwin.beam_calculation.simulation_output.simulation_output import SimulationOutput
from lightwin.config.config_manager import process_config
from lightwin.constants import example_config, example_results
from lightwin.core.accelerator.accelerator import Accelerator
from lightwin.failures.fault_scenario import FaultScenario, FaultScenarioFactory
from lightwin.util.log_manager import set_up_logging
from lightwin.ui.workflow_setup import set_up_accelerators
from lightwin.visualization import plot
plt.rcParams["figure.figsize"] = (15, 5)
Set the different solvers
[2]:
CONFIG_KEYS = {
'files': 'files',
'beam_calculator': 'generic_envelope1d',
'beam': 'beam',
'plots': 'plots_minimal',
'wtf': 'generic_wtf',
'design_space': 'generic_design_space',
}
override = {
'plots': {'energy': True, "add_objectives": False, "twiss": False},
'wtf': {'objective_preset': 'EnergyPhaseMismatch'},
}
config_common = process_config(example_config, CONFIG_KEYS, warn_mismatch=True, override=override)
beam_calculators_factory = BeamCalculatorsFactory(**config_common)
[INFO ] [log_manager.py ] Starting log for LightWin - Version: 0.16.3.dev1+g4c96aaf, Commit: 4c96aaf2a12e1dbfd514865321331c13ad57cc82
[INFO ] [files_specs.py ] Setting project_path = PosixPath('/home/docs/checkouts/readthedocs.org/user_builds/lightwin/envs/latest/lib/python3.12/site-packages/lightwin/data/ads/lw_results')
Setting log_file = 'lightwin.log'
[INFO ] [factory.py ] Creating new BeamCalculatorsFactory instance.
Linac with absolute reference phase
[3]:
override['beam_calculator'] = {'reference_phase_policy': "phi_0_abs"}
config_abs = process_config(example_config, CONFIG_KEYS, warn_mismatch=True, override=override)
# We will use this for plots representing cavity settings, phase
plots_phase = config_abs['plots']
[INFO ] [table_spec.py ] .toml table [files] loaded!
[INFO ] [table_spec.py ] .toml table [beam] loaded!
[INFO ] [table_spec.py ] .toml table [generic_envelope1d] loaded!
[INFO ] [table_spec.py ] .toml table [plots_minimal] loaded!
[INFO ] [table_spec.py ] .toml table [generic_design_space] loaded!
[INFO ] [table_spec.py ] .toml table [generic_wtf] loaded!
[INFO ] [log_manager.py ] Starting log for LightWin - Version: 0.16.3.dev1+g4c96aaf, Commit: 4c96aaf2a12e1dbfd514865321331c13ad57cc82
[INFO ] [files_specs.py ] Setting project_path = PosixPath('/home/docs/checkouts/readthedocs.org/user_builds/lightwin/envs/latest/lib/python3.12/site-packages/lightwin/data/ads/lw_results')
Setting log_file = 'lightwin.log'
Linac with relative reference phase
[4]:
override['beam_calculator'] = {'reference_phase_policy': "phi_0_rel"}
config_rel = process_config(example_config, CONFIG_KEYS, warn_mismatch=True, override=override)
# We will use this for plots representing cavity settings, phase, energy
plots_complete = config_rel['plots']
[INFO ] [table_spec.py ] .toml table [files] loaded!
[INFO ] [table_spec.py ] .toml table [beam] loaded!
[INFO ] [table_spec.py ] .toml table [generic_envelope1d] loaded!
[INFO ] [table_spec.py ] .toml table [plots_minimal] loaded!
[INFO ] [table_spec.py ] .toml table [generic_design_space] loaded!
[INFO ] [table_spec.py ] .toml table [generic_wtf] loaded!
[INFO ] [log_manager.py ] Starting log for LightWin - Version: 0.16.3.dev1+g4c96aaf, Commit: 4c96aaf2a12e1dbfd514865321331c13ad57cc82
[INFO ] [files_specs.py ] Setting project_path = PosixPath('/home/docs/checkouts/readthedocs.org/user_builds/lightwin/envs/latest/lib/python3.12/site-packages/lightwin/data/ads/lw_results')
Setting log_file = 'lightwin.log'
Linac with synchronous reference phase
[5]:
override['beam_calculator'] = {'reference_phase_policy': "phi_s"}
config_sync = process_config(example_config, CONFIG_KEYS, warn_mismatch=True, override=override)
[INFO ] [table_spec.py ] .toml table [files] loaded!
[INFO ] [table_spec.py ] .toml table [beam] loaded!
[INFO ] [table_spec.py ] .toml table [generic_envelope1d] loaded!
[INFO ] [table_spec.py ] .toml table [plots_minimal] loaded!
[INFO ] [table_spec.py ] .toml table [generic_design_space] loaded!
[INFO ] [table_spec.py ] .toml table [generic_wtf] loaded!
[INFO ] [log_manager.py ] Starting log for LightWin - Version: 0.16.3.dev1+g4c96aaf, Commit: 4c96aaf2a12e1dbfd514865321331c13ad57cc82
[INFO ] [files_specs.py ] Setting project_path = PosixPath('/home/docs/checkouts/readthedocs.org/user_builds/lightwin/envs/latest/lib/python3.12/site-packages/lightwin/data/ads/lw_results')
Setting log_file = 'lightwin.log'
Create all BeamCalculators at once
[6]:
policies = ("abs", "rel", "sync")
solvers = beam_calculators_factory.run_all(
(
config_abs["beam_calculator"],
config_rel["beam_calculator"],
config_sync["beam_calculator"],
)
)
solvers = {
policy: solver for policy, solver in zip(policies, solvers, strict=True)
}
# solver_abs.id += r' (absolute $\phi_0$)'
# solver_rel.id += r' (relative $\phi_0$)'
# solver_sync.id += r' ($\phi_s$)'
[INFO ] [factory.py ] Creating new BeamCalculator: 0_Envelope1D
[INFO ] [factory.py ] Creating new BeamCalculator: 1_Envelope1D
[INFO ] [factory.py ] Creating new BeamCalculator: 2_Envelope1D
Set Accelerator objects
Reduce logging level to keep only the warnings.
[7]:
logging.getLogger().setLevel(logging.WARNING)
Note that LightWin warns us about the different reference phases.
[8]:
accelerators = {
policy: set_up_accelerators(config_common, solver)
for policy, solver in solvers.items()
}
Every value in accelerators links a FaultScenario index with corresponding Accelerator(s):
[9]:
accelerators["abs"]
[9]:
{0: [<lightwin.core.accelerator.accelerator.Accelerator at 0x78f5c8a5c2c0>],
1: [<lightwin.core.accelerator.accelerator.Accelerator at 0x78f5c7d00c20>]}
To ease manipulation in this notebook, we also group Reference and Broken/Fixed Accelerators:
[10]:
reference_accelerators = {
policy: accelerator[0][0]
for policy, accelerator in accelerators.items()
}
broken_accelerators = {
policy: accelerator[1][0]
for policy, accelerator in accelerators.items()
}
Propagate the beam
Nominal linac
[11]:
for solver, ref in zip(solvers.values(), reference_accelerators.values(), strict=True):
simulation_output = solver.compute(ref)
Note
In Figure below, the reference accelerator calculated with solver_abs is showed twice (Reference 0_Envelope1D).
[12]:
refs_for_plot = {
0: [reference_accelerators["abs"]], # <- will be plotted twice, that's because plot faactory was not designed for
1: [reference_accelerators["rel"]], # this kind of plots where the 3 Accelerator are a reference
2: [reference_accelerators["sync"]]
}
figs = plot.factory(refs_for_plot, plots_phase, save_fig=False, clean_fig=False)
In the nominal linac, there is no difference between the different reference phases.
The reference \(\phi_0\) that is used is the one defined in the DAT. As a matter of a fact, we need to propagate the beam a first time to know the entry phase of the synchronous particle in every cavity. This quantity is necessary to link \(\phi_{0,\,\mathrm{abs}}\) with \(\phi_{0,\,\mathrm{rel}}\).
Linac with one broken cavity
We create the one FaultScenario per solver; internally, the reference phase of cavities downstream the failure will follow the BeamCalculator setting “reference_phase_policy”.
[13]:
design_space = config_common["design_space"]
fault_scenario_factories = {
policy: FaultScenarioFactory(accelerators[policy], solvers[policy], design_space)
for policy in policies
}
[14]:
fault_scenarios = {
policy: factory.create(**config_common["wtf"])
for policy, factory in fault_scenario_factories.items()
}
[15]:
for policy in policies:
solver = solvers[policy]
broken = broken_accelerators[policy]
simulation_output = solver.compute(broken)
Absolute reference phase
Every cavity downstream the failure will keep its absolute phase. In other words, cavities are not rephased at all.
[16]:
figs = plot.factory(accelerators["abs"], plots_complete, save_fig=False, clean_fig=False)
Cavities in their nominal tuning are green, and the failed cavity is represented in red. Here the beam energy and phase remain very close to the baseline. However, the positive synchronous phases show that high losses would be to expect.
Relative reference phase
Every cavity downstream the failure will keep its relative phase. In other words, if the beams is 20 degrees late at the exit of the failed cavity, all downstream cavities are rephased of 20 degrees.
[17]:
figs = plot.factory(accelerators["rel"], plots_complete, save_fig=False, clean_fig=False)
In this example, the error on the beam energy is progressively damped. In LightWin, rephased cavities are represented with olive color.
Synchronous reference phase
Every cavity downstream the failure will keep its synchronous phase. In other words, all cavities downstream the failure are rephased to maintain the nominal synchronous phases.
[18]:
figs = plot.factory(accelerators["sync"], plots_complete, save_fig=False, clean_fig=False)
Notice that the synchronous phases are unaffected by failed cavity.
Fixing the fault
[19]:
for fault_scenario in fault_scenarios.values():
for scenario in fault_scenario:
scenario.fix_all()
[WARNING ] [objective.py ] key = 'to_deg' is recommended to avoid undetermined behavior but was not found.
Objective(name=$M_{z\delta}$, weight=1.0, get_key=twiss, get_kwargs={'elt': Drift # 80 | DRIFT 150 100 0 0 0, 'pos': 'out', 'to_numpy': True, 'phase_space_name': 'zdelta'}, ideal_value=0.0, descriptor=Minimize mismatch factor in the [z-delta] plane.)
[WARNING ] [objective.py ] key = 'to_deg' is recommended to avoid undetermined behavior but was not found.
Objective(name=$W_{kin}$ [MeV], weight=1.0, get_key=w_kin, get_kwargs={'elt': Drift # 80 | DRIFT 150 100 0 0 0, 'pos': 'out', 'to_numpy': False}, ideal_value=25.771551199731583, descriptor=Minimize diff. of w_kin between ref and fix at the end of the compensation zone.)
[WARNING ] [objective.py ] key = 'to_deg' is recommended to avoid undetermined behavior but was not found.
Objective(name=$M_{z\delta}$, weight=1.0, get_key=twiss, get_kwargs={'elt': Drift # 80 | DRIFT 150 100 0 0 0, 'pos': 'out', 'to_numpy': True, 'phase_space_name': 'zdelta'}, ideal_value=0.0, descriptor=Minimize mismatch factor in the [z-delta] plane.)
[WARNING ] [objective.py ] key = 'to_deg' is recommended to avoid undetermined behavior but was not found.
Objective(name=$W_{kin}$ [MeV], weight=1.0, get_key=w_kin, get_kwargs={'elt': Drift # 80 | DRIFT 150 100 0 0 0, 'pos': 'out', 'to_numpy': False}, ideal_value=25.771551199731583, descriptor=Minimize diff. of w_kin between ref and fix at the end of the compensation zone.)
[WARNING ] [objective.py ] key = 'to_deg' is recommended to avoid undetermined behavior but was not found.
Objective(name=$M_{z\delta}$, weight=1.0, get_key=twiss, get_kwargs={'elt': Drift # 80 | DRIFT 150 100 0 0 0, 'pos': 'out', 'to_numpy': True, 'phase_space_name': 'zdelta'}, ideal_value=0.0, descriptor=Minimize mismatch factor in the [z-delta] plane.)
Absolute phase linac
For the absolute phase linac, we try to retrieve the energy and the phase at the end of the compensation zone, as well as to minimize the mismatch factor.
[20]:
figs = plot.factory(accelerators["abs"], plots_complete, save_fig=False, clean_fig=False)
Relative phase linac
When downstream cavities are rephased, it is no longer mandatory to retrieve nominal beam phase at the exit of the compensation zone. Here, we try to retrieve the kinetic energy and to minimize the mismatch factor.
[21]:
figs = plot.factory(accelerators["rel"], plots_complete, save_fig=False, clean_fig=False)
Synchronous phase linac
Results will be very similar to the relative phase linac.
[22]:
figs = plot.factory(accelerators["sync"], plots_complete, save_fig=False, clean_fig=False)