Source code for at.lattice.cavity_access

"""Access to properties of RF cavities"""
from enum import Enum
import numpy
from typing import Optional
import functools
from .elements import RFCavity
from .utils import AtError, make_copy, Refpts, get_bool_index
from .lattice_object import Lattice

__all__ = ['frequency_control',
           'get_rf_frequency', 'get_rf_voltage', 'set_rf_voltage',
           'get_rf_timelag', 'set_rf_timelag', 'set_cavity', 'Frf']


[docs]def frequency_control(func): r""" Function to be used as decorator for :pycode:`func(ring, *args, **kwargs)` If :pycode:`ring.is_6d` is :py:obj:`True` **and** *dp*, *dct* or *df* is specified in *kwargs*, make a copy of *ring* with a modified RF frequency, remove *dp*, *dct* or *df* from *kwargs* and call *func* with the modified *ring*. If :pycode:`ring.is_6d` is :py:obj:`False` **or** no *dp*, *dct* or *df* is specified in *kwargs*, *func* is called unchanged. Examples: .. code-block:: python @frequency_control def func(ring, *args, dp=None, dct=None, **kwargs): pass """ @functools.wraps(func) def wrapper(ring, *args, **kwargs): if ring.is_6d: momargs = {} for key in ['dp', 'dct', 'df']: v = kwargs.pop(key, None) if v is not None: momargs[key] = v if len(momargs) > 0: frequency = ring.get_revolution_frequency(**momargs) \ * ring.harmonic_number ring = set_cavity(ring, Frequency=frequency, copy=True) return func(ring, *args, **kwargs) return wrapper
[docs]class Frf(Enum): """Enum class for frequency setting""" NOMINAL = 'nominal'
def _select_cav(ring: Lattice, cavpts): """Select the cavities""" if cavpts is None: try: cavpts = ring.cavpts except AttributeError: cavpts = get_bool_index(ring, RFCavity) return cavpts def _singlev(values, attr): """Return the single value""" vals = numpy.unique(values) if len(vals) > 1: raise AtError('{0} not equal for all cavities'.format(attr)) return vals[0] # noinspection PyUnusedLocal def _sumv(values, attr): """Return the sum of values""" return numpy.sum(values) def _fundmask(ring: Lattice, cavpts): freqs = numpy.array([cav.Frequency for cav in ring.select(cavpts)]) if len(freqs) < 1: raise AtError('No cavity found in the lattice') mask = (freqs == min(freqs)) return mask def _get_cavity(ring: Lattice, attr, fsingle, cavpts=None, array=False): cavpts = _select_cav(ring, cavpts) vcell = numpy.array([getattr(cav, attr) for cav in ring.select(cavpts)]) if array: return vcell else: fundmask = _fundmask(ring, cavpts) if fundmask is not None: vcell = vcell[fundmask] return fsingle(vcell, attr)
[docs]def get_rf_frequency(ring: Lattice, **kwargs) -> float: """Return the RF frequency Parameters: ring: Lattice description Keyword Arguments: cavpts=None: Cavity location. If None, look for ring.cavpts, otherwise take all cavities. array=False: If False, return the frequency of the selected cavities with the lowest frequency. If True, return the frequency of all selected cavities Returns: rf_freq: RF frequency """ return _get_cavity(ring, 'Frequency', _singlev, **kwargs)
[docs]def get_rf_voltage(ring: Lattice, **kwargs) -> float: """Return the total RF voltage (full ring) Parameters: ring: Lattice description Keyword Arguments: cavpts=None: Cavity location. If None, look for ring.cavpts, otherwise take all cavities. array=False: If False, return the frequency of the selected cavities with the lowest frequency. If True, return the frequency of all selected cavities Returns: rf_v: Total RF voltage (full ring) """ vcell = _get_cavity(ring, 'Voltage', _sumv, **kwargs) return ring.periodicity * vcell
[docs]def get_rf_timelag(ring: Lattice, **kwargs) -> float: """Return the RF time lag Parameters: ring: Lattice description Keyword Arguments: cavpts=None: Cavity location. If None, look for ring.cavpts, otherwise take all cavities. array=False: If False, return the frequency of the selected cavities with the lowest frequency. If True, return the frequency of all selected cavities Returns: rf_timelag: RF time lag """ return _get_cavity(ring, 'TimeLag', _singlev, **kwargs)
[docs]def set_rf_voltage(ring: Lattice, voltage: float, **kwargs): """Set the RF voltage for the full ring Parameters: ring: Lattice description voltage: Total RF voltage (full ring) Keyword Arguments: cavpts=None: Cavity location. If None, look for ring.cavpts, otherwise take all cavities. array=False: If False, return the frequency of the selected cavities with the lowest frequency. If True, return the frequency of all selected cavities copy=False: If True, returns a shallow copy of ``ring`` with new cavity elements. Otherwise, modify ``ring`` in-place. """ return set_cavity(ring, Voltage=voltage, **kwargs)
[docs]def set_rf_timelag(ring: Lattice, timelag: float, **kwargs): """Set the RF time lag Parameters: ring: Lattice description timelag: RF time lag Keyword Arguments: cavpts=None: Cavity location. If None, look for ring.cavpts, otherwise take all cavities. array=False: If False, return the frequency of the selected cavities with the lowest frequency. If True, return the frequency of all selected cavities copy=False: If True, returns a shallow copy of ``ring`` with new cavity elements. Otherwise, modify ``ring`` in-place. """ return set_cavity(ring, TimeLag=timelag, **kwargs)
# noinspection PyPep8Naming
[docs]def set_cavity(ring: Lattice, Voltage: Optional[float] = None, Frequency: Optional[float] = None, TimeLag: Optional[float] = None, cavpts: Optional[Refpts] = None, copy: Optional[bool] = False, array: Optional[bool] = False): """ Set the parameters of the RF cavities Parameters: ring: lattice description Frequency: RF frequency [Hz] Voltage: RF voltage [V] TimeLag: RF time shift [-ct] cavpts: Cavity location. If None, look for ring.cavpts, or otherwise take all cavities array: If False, the value is applied as described for set_rf_voltage, set_rf_timelag and set_rf_frequency If True, directly apply the value to the selected cavities. The value must be broadcastable to the number of cavities. copy: If True, returns a shallow copy of ring with new cavity elements. Otherwise, modify ring in-place """ # noinspection PyShadowingNames def getv(ring: Lattice, attr, refpts): values = numpy.array([getattr(c, attr) for c in ring.select(refpts)]) valfund = values[fundmask] return values, valfund cavpts = _select_cav(ring, cavpts) fundmask = None if array else _fundmask(ring, cavpts) modif = {} if Frequency is not None: if Frequency is Frf.NOMINAL: Frequency = ring.revolution_frequency * ring.harmonic_number if fundmask is not None: vall, vmain = getv(ring, 'Frequency', cavpts) vmain = vmain[0] if vmain == 0.0: vall[fundmask] = Frequency Frequency = vall else: Frequency *= vall/vmain modif['Frequency'] = Frequency if TimeLag is not None: if fundmask is not None: vall, vmain = getv(ring, 'TimeLag', cavpts) TimeLag += vall-_singlev(vmain, 'TimeLag') modif['TimeLag'] = TimeLag if Voltage is not None: vcell = Voltage / ring.periodicity if fundmask is not None: vall, vmain = getv(ring, 'Voltage', cavpts) vmain = numpy.sum(vmain) if vmain == 0.0: vall[fundmask] = vcell/numpy.count_nonzero(fundmask) vcell = vall else: vcell *= vall/vmain modif['Voltage'] = vcell # noinspection PyShadowingNames @make_copy(copy) def apply(ring: Lattice, cavpts, modif): ncavs = ring.refcount(cavpts) for attr in modif.keys(): try: values = numpy.broadcast_to(modif[attr], (ncavs,)) except ValueError: raise AtError('set_cavity args should be either scalar or ' 'a ({0},) vector'.format(ncavs)) for val, cavity in zip(values, ring.select(cavpts)): cavity.update({attr: val}) return apply(ring, cavpts, modif)
Lattice.get_rf_voltage = get_rf_voltage Lattice.get_rf_frequency = get_rf_frequency Lattice.get_rf_timelag = get_rf_timelag Lattice.set_rf_voltage = set_rf_voltage Lattice.set_rf_timelag = set_rf_timelag Lattice.set_cavity = set_cavity Lattice.rf_voltage = property(get_rf_voltage, set_rf_voltage, doc="RF voltage of the full ring [V]") Lattice.rf_timelag = property(get_rf_timelag, set_rf_timelag, doc="Time lag of the fundamental mode [m]")