Source code for at.physics.ring_parameters

from __future__ import annotations

__all__ = ["RingParameters", "radiation_parameters", "envelope_parameters"]

from math import pi, sqrt, asin, cos

import numpy as np
from numpy import nan

from .energy_loss import get_energy_loss
from .radiation import get_radiation_integrals, ohmi_envelope
from ..constants import clight, Cgamma, Cq
from ..lattice import Lattice, Orbit, AtError


[docs] class RingParameters: """Class for pretty printing the ring properties""" _props = { "tunes": " Frac. tunes: {0}", "tunes6": " Frac. tunes (6D motion): {0}", "fulltunes": " Tunes: {0}", "chromaticities": " Chromaticities: {0}", "alphac": " Momentum compact. factor: {0:e}", "etac": " Slip factor: {0:e}", "E0": " Energy: {0:e} eV", "U0": " Energy loss / turn: {0:e} eV", "i1": " Radiation integrals - I1: {0} m", "i2": " I2: {0} m^-1", "i3": " I3: {0} m^-2", "i4": " I4: {0} m^-1", "i5": " I5: {0} m^-1", "emittances": " Mode emittances: {0}", "J": "Damping partition numbers: {0}", "Tau": " Damping times: {0} s", "sigma_e": " Energy spread: {0:g}", "sigma_l": " Bunch length: {0:g} m", "voltage": " Cavities voltage: {0} V", "phi_s": " Synchrotron phase: {0:g} rd", "f_s": " Synchrotron frequency: {0:g} Hz", } def __init__(self, **kwargs): for key, value in kwargs.items(): setattr(self, key, value) def __str__(self): vrs = vars(self).copy() vals = [(self._props[k], vrs.pop(k, None)) for k in self._props] # Process predefined attributes first lines = [f.format(v) for f, v in vals if v is not None] # Process other attributes lines += [f"{k:>25}: {getattr(self, k)}" for k in vrs] return "\n".join(lines)
# noinspection PyPep8Naming
[docs] def radiation_parameters( ring: Lattice, params: RingParameters | None = None, **kwargs, ) -> RingParameters: r"""Compute ring parameters from the radiation integrals Valid with or without longitudinal motion. Parameters: ring: Lattice description. params: :py:class:`.RingParameters` object to be updated. Default: create a new one Keyword Args: dp: Momentum deviation. dct (float): Path lengthening. Defaults to :py:obj:`None` df (float): Deviation of RF frequency. Defaults to :py:obj:`None` method (Callable): Method for linear optics: :py:obj:`~.linear.linopt2`: no longitudinal motion, no H/V coupling, :py:obj:`~.linear.linopt6` (default): with or without longitudinal motion, normal mode analysis orbit (Orbit): Avoids looking for the closed orbit if it is already known ((6,) array) XYStep (float): Step size. Default: :py:data:`DConstant.XYStep <.DConstant>` DPStep (float): Momentum step size. Default: :py:data:`DConstant.DPStep <.DConstant>` Returns: params: :py:class:`.RingParameters` object. **params** is a :py:class:`.RingParameters` object with the following attributes: ================== ======================================== **tunes** (3,) fractional (H, V, Long.) tunes **fulltunes** (3,) full tunes **chromaticities** (2,) H, V Chromaticities **alphac** Momentum compaction factor **etac** Frequency slip factor **E0** Energy [eV] **U0** Energy loss / turn [eV] **i1** Radiation integrals - :math:`I_1 \quad [m]` **i2** :math:`I_2 \quad [m^{-1}]` **i3** :math:`I_3 \quad [m^{-2}]` **i4** :math:`I_4 \quad [m^{-1}]` **i5** :math:`I_5 \quad [m^{-1}]` **emittances** (3,) Mode emittances **J** (3,) Damping partition numbers **Tau** (3,) Damping times [s] **sigma_e** Energy spread **sigma_l** Bunch length [m] **voltage** Total accelerating voltage [V] **phi_s** Synchrotron phase [rad] **f_s** Synchrotron frequency [Hz] ================== ======================================== """ rp = RingParameters() if params is None else params _, ringdata, twiss = ring.get_optics( refpts=range(len(ring) + 1), get_chrom=True, **kwargs ) rp.chromaticities = ringdata.chromaticity * ring.periodicity integs = get_radiation_integrals(ring, twiss=twiss) rp.i1, rp.i2, rp.i3, rp.i4, rp.i5 = np.array(integs) * ring.periodicity circumference = ring.circumference E0 = ring.energy gamma = ring.gamma gamma2 = gamma * gamma beta = sqrt(1.0 - 1.0 / gamma2) U0 = Cgamma / 2.0 / pi * E0**4 * rp.i2 Jx = 1.0 - rp.i4 / rp.i2 Jz = 1.0 Je = 2.0 + rp.i4 / rp.i2 damping_partition_numbers = np.array([Jx, Jz, Je]) revolution_period = circumference / clight / beta ct = 2.0 * E0 / U0 * revolution_period rp.E0 = E0 rp.U0 = U0 emitx = Cq * gamma2 * rp.i5 / Jx / rp.i2 alphac = rp.i1 / circumference etac = 1.0 / gamma2 - alphac rp.sigma_e = sqrt(Cq * gamma2 * rp.i3 / Je / rp.i2) try: voltage = ring.rf_voltage except AtError: rp.voltage = nan rp.phi_s = nan nu_s = nan rp.f_s = nan rp.sigma_l = nan else: rp.voltage = voltage rp.phi_s = (pi - asin(U0 / voltage)) if U0 <= voltage else nan nu_s = sqrt(abs(etac * ring.harmonic_number * voltage * cos(rp.phi_s) / E0 / 2.0 / pi)) / beta rp.f_s = nu_s / revolution_period rp.sigma_l = beta * abs(etac) * circumference / 2.0 / pi / nu_s * rp.sigma_e rp.Tau = ct / damping_partition_numbers rp.J = damping_partition_numbers rp.emittances = np.array([emitx, nan, rp.sigma_e * rp.sigma_l]) ringtunes, _ = np.modf(ring.periodicity * ringdata.tune) if len(ringtunes) < 3: rp.tunes = np.concatenate((ringtunes, (nu_s,))) else: rp.tunes6 = ringtunes rp.fulltunes = ring.periodicity * twiss[-1].mu / 2.0 / pi rp.alphac = alphac rp.etac = etac return rp
# noinspection PyPep8Naming
[docs] def envelope_parameters( ring: Lattice, params: RingParameters | None = None, orbit: Orbit = None, keep_lattice: bool = False, ) -> RingParameters: r"""Compute ring parameters from ohmi_envelope Parameters: ring: Lattice description. params: :py:class:`.RingParameters` object to be updated. Default: create a new one orbit: Avoids looking for the closed orbit if it is already known ((6,) array) keep_lattice: Assume no lattice change since the previous tracking. Returns: params: :py:class:`.RingParameters` object. **params** is a :py:class:`.RingParameters` object with the following attributes: ================== ======================================== **tunes6** (3,) fractional (H, V, Long.) tunes (6D motion) **emittances** (3,) Mode emittances **J** (3,) Damping partition numbers **Tau** (3,) Damping times [s] **sigma_e** Energy spread **sigma_l** Bunch length [m] **voltage** Total accelerating voltage [V] **phi_s** Synchrotron phase [rad] **f_s** Synchrotron frequency [Hz] ================== ======================================== """ rp = RingParameters() if params is None else params emit0, beamdata, emit = ohmi_envelope(ring, orbit=orbit, keep_lattice=keep_lattice) voltage = ring.rf_voltage rp.E0 = ring.energy rp.U0 = get_energy_loss(ring) rev_freq = ring.revolution_frequency rp.Tau = 1.0 / rev_freq / beamdata.damping_rates / ring.periodicity alpha = 1.0 / rp.Tau rp.J = 4.0 * alpha / np.sum(alpha) rp.tunes6, _ = np.modf(ring.periodicity * beamdata.tunes) rp.phi_s = pi - np.arcsin(rp.U0 / voltage) rp.voltage = voltage rp.f_s = rp.tunes6[2] * rev_freq rp.emittances = beamdata.mode_emittances rp.sigma_e = sqrt(emit0.r66[4, 4]) rp.sigma_l = sqrt(emit0.r66[5, 5]) return rp
Lattice.radiation_parameters = radiation_parameters Lattice.envelope_parameters = envelope_parameters