"""Define functions related to acceptance calculation.
.. todo::
Show equations and references in the docstrings.
"""
import logging
from math import cos, pi, sin, sqrt
import numpy as np
from lightwin.constants import c
from lightwin.physics.converters import energy
from lightwin.util.solvers import (
solve_scalar_equation_brent,
)
from lightwin.util.typing import BeamKwargs
[docs]
def compute_acceptances(
phi_s: float,
freq_cavity_mhz: float,
w_kin: float | None,
v_cav_mv: float,
length_m: float,
beam_kwargs: BeamKwargs,
) -> tuple[float, float]:
r"""Compute acceptances in phase and energy.
Handles off-limits synchronous phases.
Parameters
----------
phi_s
Synchronous phase; if it is outside :math:`(-\pi / 2,~0)`, both
acceptances are ``nan``.
freq_cavity_mhz
Cavity frequency in :unit:`MHz`.
w_kin
Beam energy at the cavity exit in :unit:`MeV`.
v_cav_mv
Cavity accelerating voltage in :unit:`MV`.
length_m
Cavity length in :unit:`m`.
beam_kwargs
Dict holding beam parameters, and in particular the adimensionned
charge ``q_adim`` and the rest energy ``e_rest_mev``.
Returns
-------
acceptance_phi
Acceptance in phase in :unit:`rad`.
acceptance_energy
Acceptance in energy in :unit:`MeV`.
"""
if not (-0.5 * pi <= phi_s <= 0.0):
return np.nan, np.nan
if w_kin is None:
logging.error(
"The kinetic energy of current cavity was not set. Is it a failed "
"cavity? Returning ``np.nan`` acceptances."
)
return np.nan, np.nan
return _compute_acceptance_phase(phi_s), _compute_acceptance_energy(
phi_s, freq_cavity_mhz, w_kin, v_cav_mv, length_m, **beam_kwargs
)
[docs]
def _compute_acceptance_phase(phi_s: float) -> float:
r"""Compute the acceptance in phase using Brent method.
Parameters
----------
phi_s
Synchronous phase, must be in :math:`(-\pi / 2,~0)`.
Returns
-------
Cavity acceptance in phase, in :unit:`rad`.
"""
return -(
phi_s
+ solve_scalar_equation_brent(
_compute_phi_2, phi_s, x_bounds=(-1.5 * pi, 0)
)
)
[docs]
def _compute_phi_2(phi_2: float, phi_s: float) -> float:
"""Compute the left boundary of the phase acceptance (phi_2).
Parameters
----------
phi_2
Phase value in radians to test as the boundary.
phi_s
Synchronous phase in radians.
Returns
-------
float
Function value to be used in root-finding (zero crossing corresponds to
phi_2).
"""
term1 = sin(phi_2) - phi_2 * cos(phi_s)
term2 = sin(phi_s) - phi_s * cos(phi_s)
return term1 + term2
[docs]
def _compute_acceptance_energy(
phi_s: float,
freq_cavity_mhz: float,
w_kin: float,
v_cav_mv: float,
length_m: float,
q_adim: float,
e_rest_mev: float,
**beam_kwargs,
) -> float:
r"""Compute the acceptance in energy.
Parameters
----------
phi_s
Synchronous phase, must be in :math:`(-\pi / 2,~0)`.
freq_cavity_mhz
Cavity frequency in :unit:`MHz`.
w_kin
Beam energy at the cavity exit in :unit:`MeV`.
v_cav_mv
Cavity accelerating voltage in :unit:`MV`.
length_m
Cavity length in :unit:`m`.
q_adim
Beam adimensionned charge.
e_rest_mev
Beam rest energy in :unit:`MeV`.
Returns
-------
Cavity acceptance in energy, in :unit:`MeV`.
"""
# q_over_m and m_over_q are not used... Even if they are mandatory args!
beta_kin = energy(
w_kin, "kin to beta", q_over_m=0.0, m_over_q=0, e_rest_mev=e_rest_mev
)
gamma_kin = energy(
w_kin, "kin to gamma", q_over_m=0, m_over_q=0, e_rest_mev=e_rest_mev
)
factor = (
q_adim * v_cav_mv * (beta_kin * gamma_kin) ** 3 * e_rest_mev * c
) / (pi * freq_cavity_mhz * 1e6 * length_m)
trig_term = phi_s * cos(phi_s) - sin(phi_s)
return sqrt(2 * factor * trig_term)