diff --git a/docs/p/CavityControl.md b/docs/p/CavityControl.md
index 67fbf19c9..94c88b148 100644
--- a/docs/p/CavityControl.md
+++ b/docs/p/CavityControl.md
@@ -2,73 +2,107 @@
title: Controlling the RF cavities
---
A lattice may contain multiple RF cavities, grouped according to different RF systems:
-main cavities, harmonic cavities…
-
+main cavities, harmonic cavities…\
AT provides simple tools to tune them, with methods and properties of the
`Lattice` object.
### Lattice methods for cavity control
-The values handled by these methods concern the full ring (`periodicity` $$\times$$ superperiod).
+All methods have `array` and `cavpts` keyword arguments, used to select the cavities concerned by the command.
-All methods have a `cavpts` argument, used to select the cavities concerned by the command.
-The `cavpts` argument is used as follows:
-- `cavpts` is a "refpts" type (integer, integer or boolean array, callable): it is used to select the cavities,
-- `cavpts` is `None` (default value), and the `Lattice` object has a `cavpts` attribute: the lattice attribute is used to select the cavities,
-- `cavpts` is `None`, and the lattice has no `cavpts` attribute (or it is `None`): all cavities are selected.
-
-So the easier way to use it is:
-- **single RF system** (main cavities): forget the `cavpts` argument. The default is to use all cavities,
-- **main RF system + harmonic system**: set the `Lattice` `cavpts` attribute to the location of the main cavities,
- so that the default behaviour is to drive the main system. Use the method's `cavpts` argument to drive the harmonic cavities.
+- if `array is True`, the output attribute value is an array as long as the number of selected cavities.
+ The input argument must be an array as long as the number of selected cavities
+ or a scalar which will be broadcasted to the number of cavities,
+- if `array is False` (default), the input and output are scalars. The scalar value
+applies to the set of cavities with the lowest frequency within the selection.
+The other cavities are ignored in `get_*` methods. For `set_*` methods, the other
+cavities are scaled as explained if the specific method description.
-All methods also have a `copy` argument to select either in-place modification
+The `cavpts` argument is used as follows:
+- `cavpts` is a "refpts" type (integer, integer or boolean array, callable): it is used to select the
+ cavities.
+- `cavpts is None` (default value), and the `Lattice` object has a `cavpts` attribute:
+ the lattice attribute is used to select the cavities.
+- `cavpts is None`, and the lattice has no `cavpts` attribute (or it is `None`):
+ all cavities are taken into account.
+
+{% include tip.html content="
+- **single RF system:** you can forget the `cavpts` argument: by default the methods
+address all cavities. However, using the `Lattice.cavpts` attribute makes calls significantly
+faster by skipping the search for cavities.
+- **complex system:** an easy way is to have the `Lattice.cavpts` address the accelerating
+cavities so that they will be driven by default. Harmonic cavities may be driven
+using the `cavpts` argument.
+" %}
+
+{% include tip.html content="
+Adding to the Lattice \"refpts\" type attributes addressing the different cavity sets make
+them available everywhere the lattice is visible.
+" %}
+
+All `set_*` methods also have a `copy` argument to select either in-place modification
of the lattice, or creation of a shallow copy with modified cavities.
#### Voltage:
-```voltage = Lattice.get_rf_voltage(cavpts=None)```
+```voltage = Lattice.get_rf_voltage(cavpts=None, array=False)```
+
+The scalar voltage is the sum of the cavity voltages of the cavities with the
+lowest frequency within the selection, multiplied by the periodicity.
-```Lattice.set_rf_voltage(voltage, cavpts=None, copy=False)```
+```Lattice.set_rf_voltage(voltage, cavpts=None, array=False, copy=False)```
-The specified voltage is equally distributed among all selected cavities in all superperiods.
+For array == False, all the existing voltages are scaled to reach the specified
+value on the fundamental mode.
#### Frequency:
-`frequency = Lattice.get_rf_frequency(cavpts=None)`
+`frequency = Lattice.get_rf_frequency(cavpts=None, array=False)`
-`Lattice.get_rf_frequency(frequency=None, dp=None, dct=None, cavpts=None, copy=False)`
+The frequency of the fundamental mode is returned.
-If the frequency in `None`, the method will set the frequency to the nominal value,
+`Lattice.get_rf_frequency(frequency=None, dp=None, dct=None, cavpts=None, array=False, copy=False)`
+
+If the frequency is None, the method will set the frequency to the nominal value,
according to the revolution frequency and harmonic number. An optional
off-momentum may be applied using the `dp` or `dct` arguments. The frequency
shift is then computed using the linear slip factor $$\eta_c = 1/\gamma^2 - \alpha_c$$ ,
-so that the resulting `dp` may slighly differ from the specified value.
+so that the resulting `dp` may slightly differ from the specified value.
+
+For array == False, the value is applied to the fundamental mode cavities and
+the frequency of all other cavities is scaled by the same ratio.
#### Time lag
-`time_lag = Lattice.get_rf_timelag(cavpts=None)`
+The time lag is expressed in values of path lengthening "cτ", the 6th particle coordinate [m].
-`Lattice.set_rf_timelag(time_lag, cavpts=None, copy=False)`
+`time_lag = Lattice.get_rf_timelag(cavpts=None, array=False)`
+
+The time lag of the fundamental mode is returned.
+
+`Lattice.set_rf_timelag(time_lag, cavpts=None, array=False, copy=False)`
+
+For array == False, the time lag is applied to the fundamental mode cavities and
+the time lag of all the other selected cavities is shifted by the same amount.
-The time lag is expressed in values of path lengthening "cτ", the 6th particle coordinate [m].
#### All-in-one method
`Lattice.set_cavity(ring, Voltage=None, Frequency=None, TimeLag=None,
- cavpts=None, copy=False)`
+ cavpts=None, array=False, copy=False)`
-This method sets only the explicity provided values, default ones are left unchanged.
+This method sets only the explicitly provided values, the other ones are left unchanged.
For the frequency, a special value `at.Frf.NOMINAL` means nominal frequency,
according to the revolution frequency and harmonic number.
+The behaviour of the `cavpts` and `array` keywords is the same as for individual methods.
+
### Lattice properties
The properties provide an even easier way to control the cavities, but are restricted
to the default behaviour of the equivalent Lattice method:
-- cavities are selected by the `Lattice` `cavpts` argument (all cavities by default),
+- cavities are selected by the `Lattice.cavpts` attribute (all cavities by default),
- Setting a property modifies the ring in-place (no copy).
`Lattice.rf_voltage`
`Lattice.rf_frequency`
-The special value `at.Frf.NOMINAL` means nominal frequency,
-according to the revolution frequency and harmonic number.
+The special value `at.Frf.NOMINAL` means nominal frequency.
`Lattice.harmonic_number`
diff --git a/pyat/at/lattice/cavity_access.py b/pyat/at/lattice/cavity_access.py
index 094b69fc9..a6a9b09d2 100644
--- a/pyat/at/lattice/cavity_access.py
+++ b/pyat/at/lattice/cavity_access.py
@@ -1,7 +1,7 @@
from enum import Enum
import numpy
from .elements import RFCavity
-from .utils import AtError, checktype, make_copy
+from .utils import AtError, checktype, make_copy, get_cells
from .lattice_object import Lattice
__all__ = ['get_rf_frequency', 'get_rf_voltage', 'set_rf_voltage',
@@ -13,92 +13,137 @@ class Frf(Enum):
NOMINAL = 'nominal'
-def _get_rf_attr(ring, attr, cavpts=None):
+def _select_cav(ring, cavpts):
+ """Select the cavities"""
if cavpts is None:
- cavpts = getattr(ring, 'cavpts', checktype(RFCavity))
- cavities = ring.select(cavpts)
- return numpy.array([getattr(cavity, attr) for cavity in cavities])
+ try:
+ cavpts = ring.cavpts
+ except AttributeError:
+ cavpts = get_cells(ring, checktype(RFCavity))
+ return cavpts
-def _get_rf_unique_attr(ring, attr, cavpts=None):
- freq = numpy.unique(_get_rf_attr(ring, attr, cavpts=cavpts))
- if len(freq) == 0:
- raise AtError('No cavity found in the lattice')
- elif len(freq) > 1:
+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, 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 None if numpy.all(mask) else mask
+
+
+def _get_cavity(ring, 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:
- return freq[0]
+ fundmask = _fundmask(ring, cavpts)
+ if fundmask is not None:
+ vcell = vcell[fundmask]
+ return fsingle(vcell, attr)
-def get_rf_frequency(ring, cavpts=None):
+def get_rf_frequency(ring, **kwargs):
"""Return the RF frequency
KEYWORDS
- cavpts=None Cavity location. If None, look for ring.cavpts,
- otherwise take all cavities. This allows to ignore
- harmonic cavities.
+ cavpts=None Cavity location.
+ If None, look for ring.cavpts, or 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
"""
- return _get_rf_unique_attr(ring, 'Frequency', cavpts=cavpts)
+ return _get_cavity(ring, 'Frequency', _singlev, **kwargs)
-def get_rf_voltage(ring, cavpts=None):
- """Return the total RF voltage for the full ring
+def get_rf_voltage(ring, **kwargs):
+ """Return the total RF voltage (full ring)
KEYWORDS
- cavpts=None Cavity location. If None, look for ring.cavpts,
- otherwise take all cavities. This allows to ignore
- harmonic cavities.
+ cavpts=None Cavity location.
+ If None, look for ring.cavpts, or otherwise take all
+ cavities.
+ array=False If False, return the sum of the voltage of the selected
+ cavities with the lowest frequency.
+ If True, return the voltage of all the selected cavities.
"""
- _ = get_rf_frequency(ring, cavpts=cavpts) # check single frequency
- vcell = sum(_get_rf_attr(ring, 'Voltage', cavpts=cavpts))
+ vcell = _get_cavity(ring, 'Voltage', _sumv, **kwargs)
return ring.periodicity * vcell
-def get_rf_timelag(ring, cavpts=None):
+def get_rf_timelag(ring, **kwargs):
"""Return the RF time lag
KEYWORDS
- cavpts=None Cavity location. If None, look for ring.cavpts,
- otherwise take all cavities. This allows to ignore
- harmonic cavities.
+ cavpts=None Cavity location.
+ If None, look for ring.cavpts, or otherwise take all
+ cavities.
+ array=False If False, return the time lag of the cavities with the
+ lowest frequency.
+ If True, return the time lag of all the selected cavities.
"""
- return _get_rf_unique_attr(ring, 'TimeLag', cavpts=cavpts)
+ return _get_cavity(ring, 'TimeLag', _singlev, **kwargs)
-def set_rf_voltage(ring, voltage, cavpts=None, copy=False):
- """Set the RF voltage
+def set_rf_voltage(ring, voltage, **kwargs):
+ """Set the RF voltage for the full ring
PARAMETERS
- ring lattice description
- voltage Total RF voltage for the full ring
+ ring lattice description
+ voltage RF voltage [V]
KEYWORDS
- cavpts=None Cavity location. If None, look for ring.cavpts,
- otherwise take all cavities. This allows to ignore
- harmonic cavities.
- copy=False If True, returns a shallow copy of ring with new
- cavity elements. Otherwise, modify ring in-place
+ cavpts=None If None, look for ring.cavpts, or otherwise take all
+ cavities.
+ array=False If False, the voltages of all cavities are scaled to
+ reach the specified value on the selected cavities with
+ the lowest frequency.
+ If True, directly apply voltage to the selected
+ cavities. The value must be broadcastable to the number
+ of 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, cavpts=cavpts, copy=copy)
+ return set_cavity(ring, Voltage=voltage, **kwargs)
-def set_rf_timelag(ring, timelag, cavpts=None, copy=False):
+def set_rf_timelag(ring, timelag, **kwargs):
"""Set the RF time lag
PARAMETERS
- ring lattice description
- timelag
+ ring lattice description
+ timelag RF time shift (-ct) [m]
KEYWORDS
- cavpts=None Cavity location. If None, look for ring.cavpts,
- otherwise take all cavities. This allows to ignore
- harmonic cavities.
- copy=False If True, returns a shallow copy of ring with new
- cavity elements. Otherwise, modify ring in-place
+ cavpts=None If None, look for ring.cavpts, or otherwise take all
+ cavities.
+ array=False If False, timelag is applied to the selected cavities
+ with the lowest frequency. The timelag of all the
+ other selected cavities is shifted by the same amount.
+ If True, directly apply timelag to the selected
+ cavities. The value must be broadcastable to the number
+ of 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, cavpts=cavpts, copy=copy)
+ return set_cavity(ring, TimeLag=timelag, **kwargs)
# noinspection PyPep8Naming
def set_cavity(ring, Voltage=None, Frequency=None, TimeLag=None, cavpts=None,
- copy=False):
+ copy=False, array=False):
"""
Set the parameters of the RF cavities
@@ -106,38 +151,72 @@ def set_cavity(ring, Voltage=None, Frequency=None, TimeLag=None, cavpts=None,
ring lattice description
KEYWORDS
- Frequency=None RF frequency. The special enum value Frf.NOMINAL
- sets the frequency to the nominal value, given
- ring length and harmonic number.
- Voltage=None RF voltage for the full ring.
- TimeLag=None
- cavpts=None Cavity location. If None, look for ring.cavpts,
- otherwise take all cavities. This allows to ignore
- harmonic cavities.
- copy=False If True, returns a shallow copy of ring with new
- cavity elements. Otherwise, modify ring in-place
+ Frequency=None RF frequency [Hz]
+ Voltage=None RF voltage [V]
+ TimeLag=None RF time shift [-ct]
+ cavpts=None Cavity location. If None, look for ring.cavpts, or
+ otherwise take all cavities
+ array=False 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=False If True, returns a shallow copy of ring with new
+ cavity elements. Otherwise, modify ring in-place
"""
- if cavpts is None:
- cavpts = getattr(ring, 'cavpts', checktype(RFCavity))
- n_cavities = ring.refcount(cavpts)
- if n_cavities < 1:
- raise AtError('No cavity found in the lattice')
+ # noinspection PyShadowingNames
+ def getv(ring, 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:
- modif['Voltage'] = Voltage / ring.periodicity / n_cavities
+ 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, cavpts, modif):
- for cavity in ring.select(cavpts):
- cavity.update(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)
@@ -149,4 +228,6 @@ def apply(ring, cavpts, modif):
Lattice.set_rf_timelag = set_rf_timelag
Lattice.set_cavity = set_cavity
Lattice.rf_voltage = property(get_rf_voltage, set_rf_voltage,
- doc="Total RF voltage of the full ring [V]")
+ 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]")
diff --git a/pyat/at/lattice/lattice_object.py b/pyat/at/lattice/lattice_object.py
index a5d973562..e4179cb81 100644
--- a/pyat/at/lattice/lattice_object.py
+++ b/pyat/at/lattice/lattice_object.py
@@ -369,6 +369,14 @@ def circumference(self):
"""Ring circumference (full ring) [m]"""
return self.periodicity * self.get_s_pos(len(self))[0]
+ @property
+ def revolution_frequency(self):
+ """Revolution frequency (fullring) [Hz]"""
+ # gamma = self.gamma
+ # beta = math.sqrt(1.0 - 1.0 / gamma / gamma)
+ # return beta * clight / self.circumference
+ return clight / self.circumference
+
@property
def particle(self):
"""Circulating particle"""
@@ -408,6 +416,11 @@ def beta(self):
gamma = float(self.energy / self.particle.rest_energy)
return math.sqrt(1.0 - 1.0/gamma/gamma)
+ # noinspection PyPep8Naming
+ @property
+ def BRho(self):
+ return math.sqrt(self.energy**2 - self.particle.rest_energy**2)/clight
+
@property
def radiation(self):
"""If True, at least one element modifies the beam energy"""
diff --git a/pyat/at/physics/energy_loss.py b/pyat/at/physics/energy_loss.py
index c4ec2fea8..f473fd964 100644
--- a/pyat/at/physics/energy_loss.py
+++ b/pyat/at/physics/energy_loss.py
@@ -1,12 +1,12 @@
from enum import Enum
from warnings import warn
-from math import sqrt, pi
+from math import pi
import numpy
from scipy.optimize import least_squares
from at.lattice import Lattice, Dipole, Wiggler, RFCavity
from at.lattice import check_radiation, AtError
from at.lattice import checktype, set_value_refpts, get_cells, refpts_len
-from at.lattice.constants import clight, e_mass, Cgamma
+from at.lattice.constants import clight, Cgamma
from at.tracking import lattice_pass
__all__ = ['get_energy_loss', 'set_cavity_phase', 'ELossMethod',
@@ -34,12 +34,14 @@ def get_energy_loss(ring, method=ELossMethod.INTEGRAL):
TRACKING: The losses are obtained by tracking without cavities.
Needs radiation ON, takes into account all radiating elements.
"""
+
+ # noinspection PyShadowingNames
def integral(ring):
"""Losses = Cgamma / 2pi * EGeV^4 * i2
"""
def wiggler_i2(wiggler):
- rhoinv = wiggler.Bmax / Brho
+ rhoinv = wiggler.Bmax / ring.BRho
coefh = wiggler.By[1, :]
coefv = wiggler.Bx[1, :]
return wiggler.Length * (numpy.sum(coefh * coefh) + numpy.sum(
@@ -48,7 +50,6 @@ def wiggler_i2(wiggler):
def dipole_i2(dipole):
return dipole.BendingAngle ** 2 / dipole.Length
- Brho = sqrt(ring.energy**2 - e_mass**2) / clight
i2 = 0.0
for el in ring:
if isinstance(el, Dipole):
@@ -58,6 +59,7 @@ def dipole_i2(dipole):
e_loss = Cgamma / 2.0 / pi * ring.energy ** 4 * i2
return e_loss
+ # noinspection PyShadowingNames
@check_radiation(True)
def tracking(ring):
"""Losses from tracking
@@ -83,6 +85,7 @@ def tracking(ring):
raise AtError('Invalid method: {}'.format(method))
+# noinspection PyPep8Naming
def get_timelag_fromU0(ring, method=ELossMethod.INTEGRAL, cavpts=None):
"""
Get the TimeLag attribute of RF cavities based on frequency,
@@ -95,24 +98,29 @@ def get_timelag_fromU0(ring, method=ELossMethod.INTEGRAL, cavpts=None):
KEYWORDS
method=ELossMethod.INTEGRAL
- method for energy loss computation.
- See "get_energy_loss".
- cavpts=None Cavity location. If None, use all cavities.
- This allows to ignore harmonic cavities.
+ method for energy loss computation. See "get_energy_loss".
+ cavpts=None Cavity location. If None, use all cavities.
+ This allows to ignore harmonic cavities.
RETURN
- timelag
+ timelag Timelag
+ ts Time difference with the present value
"""
+ def singlev(values):
+ vals = numpy.unique(values)
+ if len(vals) > 1:
+ raise AtError('values not equal for all cavities')
+ return vals[0]
+
if cavpts is None:
cavpts = get_cells(ring, checktype(RFCavity))
- u0 = get_energy_loss(ring, method=method)
+ u0 = get_energy_loss(ring, method=method) / ring.periodicity
+ freq = numpy.array([cav.Frequency for cav in ring.select(cavpts)])
+ rfv = numpy.array([cav.Voltage for cav in ring.select(cavpts)])
+ tl0 = numpy.array([cav.TimeLag for cav in ring.select(cavpts)])
try:
- rfv = ring.get_rf_voltage(cavpts=cavpts)
- freq = ring.get_rf_frequency(cavpts=cavpts)
- tl0 = ring.get_rf_timelag(cavpts=cavpts)
+ frf = singlev(freq)
+ tml = singlev(tl0)
except AtError:
- freq = numpy.array([cav.Frequency for cav in ring.select(cavpts)])
- rfv = numpy.array([cav.Voltage for cav in ring.select(cavpts)])
- tl0 = numpy.array([cav.TimeLag for cav in ring.select(cavpts)])
if u0 > numpy.sum(rfv):
raise AtError('Not enough RF voltage: unstable ring')
ctmax = 1/numpy.amin(freq)*clight/2
@@ -129,10 +137,11 @@ def get_timelag_fromU0(ring, method=ELossMethod.INTEGRAL, cavpts=None):
bounds=(zero_diff, ctmax-tt0)).x[0]
timelag = ts+tl0
else:
- if u0 > rfv:
+ vrf = numpy.sum(rfv)
+ if u0 > vrf:
raise AtError('Not enough RF voltage: unstable ring')
- timelag = clight/(2*pi*freq)*numpy.arcsin(u0/rfv)
- ts = timelag - tl0
+ timelag = clight/(2*pi*frf)*numpy.arcsin(u0/vrf)
+ ts = timelag - tml
timelag *= numpy.ones(refpts_len(ring, cavpts))
return timelag, ts
@@ -165,7 +174,7 @@ def set_cavity_phase(ring, method=ELossMethod.INTEGRAL,
cavpts = refpts
elif cavpts is None:
cavpts = get_cells(ring, checktype(RFCavity))
- timelag, ts = get_timelag_fromU0(ring, method=method, cavpts=cavpts)
+ timelag, _ = get_timelag_fromU0(ring, method=method, cavpts=cavpts)
set_value_refpts(ring, cavpts, 'TimeLag', timelag, copy=copy)
diff --git a/pyat/at/physics/linear.py b/pyat/at/physics/linear.py
index 32715139d..31c5b2f6e 100644
--- a/pyat/at/physics/linear.py
+++ b/pyat/at/physics/linear.py
@@ -9,7 +9,6 @@
from ..lattice import DConstant, get_s_pos
from ..lattice import AtWarning, Lattice, check_radiation
from ..tracking import lattice_pass
-from .revolution import set_rf_frequency
from .orbit import find_orbit4, find_orbit6
from .matrix import find_m44, find_m66
from .amat import a_matrix, jmat, jmatswap
@@ -363,8 +362,8 @@ def unwrap(mu):
if get_chrom or get_w:
f0 = ring.get_rf_frequency(cavpts=cavpts)
df = dp_step * ring.radiation_off(copy=True).slip_factor * f0
- rgup = set_rf_frequency(ring, f0 + 0.5*df, cavpts=cavpts, copy=True)
- rgdn = set_rf_frequency(ring, f0 - 0.5*df, cavpts=cavpts, copy=True)
+ rgup = ring.set_rf_frequency(f0 + 0.5*df, cavpts=cavpts, copy=True)
+ rgdn = ring.set_rf_frequency(f0 - 0.5*df, cavpts=cavpts, copy=True)
o0up, _ = get_orbit(rgup, guess=orb0, **kwargs)
o0dn, _ = get_orbit(rgdn, guess=orb0, **kwargs)
if get_w:
@@ -1042,10 +1041,10 @@ def get_chrom(ring, method='linopt', dp=0, dct=None, cavpts=None, **kwargs):
if ring.radiation:
f0 = ring.get_rf_frequency(cavpts=cavpts)
df = dp_step * ring.radiation_off(copy=True).slip_factor * f0
- rgup = set_rf_frequency(ring, f0 + 0.5 * df, cavpts=cavpts, copy=True)
+ rgup = ring.set_rf_frequency(f0 + 0.5 * df, cavpts=cavpts, copy=True)
o0up, _ = find_orbit6(rgup, **kwargs)
tune_up = get_tune(rgup, method=method, orbit=o0up, **kwargs)
- rgdn = set_rf_frequency(ring, f0 - 0.5 * df, cavpts=cavpts, copy=True)
+ rgdn = ring.set_rf_frequency(f0 - 0.5 * df, cavpts=cavpts, copy=True)
o0dn, _ = find_orbit6(rgdn, **kwargs)
tune_down = get_tune(rgdn, method=method, orbit=o0dn, **kwargs)
dp_step = o0up[4] - o0dn[4]
diff --git a/pyat/at/physics/orbit.py b/pyat/at/physics/orbit.py
index d6b2129b1..5d5a43990 100644
--- a/pyat/at/physics/orbit.py
+++ b/pyat/at/physics/orbit.py
@@ -3,8 +3,8 @@
"""
import numpy
from at.lattice.constants import clight
-from at.lattice import AtWarning, AtError, check_radiation, DConstant
-from at.lattice import Lattice, get_s_pos, elements, uint32_refpts
+from at.lattice import AtWarning, check_radiation, DConstant
+from at.lattice import Lattice, get_s_pos, uint32_refpts
from at.tracking import lattice_pass
from at.physics import ELossMethod, get_timelag_fromU0
import warnings
@@ -261,11 +261,6 @@ def find_sync_orbit(ring, dct=0.0, refpts=None, dp=None, orbit=None,
def _orbit6(ring, cavpts=None, guess=None, keep_lattice=False, **kwargs):
"""Solver for 6D motion"""
-
- def iscavity(elem):
- return isinstance(elem, elements.RFCavity) and \
- elem.PassMethod.endswith('CavityPass')
-
convergence = kwargs.pop('convergence', DConstant.OrbConvergence)
max_iterations = kwargs.pop('max_iterations', DConstant.OrbMaxIter)
xy_step = kwargs.pop('XYStep', DConstant.XYStep)
@@ -273,14 +268,7 @@ def iscavity(elem):
method = kwargs.pop('method', ELossMethod.TRACKING)
l0 = get_s_pos(ring, len(ring))[0]
- # Get the main RF frequency (the lowest)
- try:
- f_rf = min(elm.Frequency for elm in ring if iscavity(elm))
- except ValueError:
- raise AtError('No cavity found in the lattice.')
- # gamma = self.energy / self.particle.mass
- # beta = math.sqrt(1.0 - 1.0 / gamma / gamma)
- # h = round(fmin*l0/beta/clight)
+ f_rf = ring.get_rf_frequency()
harm_number = round(f_rf*l0/clight)
if guess is None:
diff --git a/pyat/at/physics/revolution.py b/pyat/at/physics/revolution.py
index 696091429..d8e34e437 100644
--- a/pyat/at/physics/revolution.py
+++ b/pyat/at/physics/revolution.py
@@ -1,6 +1,6 @@
from ..lattice import Lattice, get_rf_frequency, check_radiation, get_s_pos
from ..lattice import DConstant
-from ..lattice.constants import clight, e_mass
+from ..lattice.constants import clight
from ..tracking import lattice_pass
from .orbit import find_orbit4
import numpy
@@ -45,7 +45,7 @@ def get_slip_factor(ring, **kwargs):
Defaults to False
dp_step=1.0E-6 momentum deviation used for differentiation
"""
- gamma = ring.energy / e_mass
+ gamma = ring.gamma
etac = (1.0/gamma/gamma - get_mcf(ring, **kwargs))
return etac
@@ -63,7 +63,7 @@ def get_revolution_frequency(ring, dp=None, dct=None, **kwargs):
Defaults to False
dp_step=1.0E-6 momentum deviation used for differentiation
"""
- frev = clight / ring.circumference
+ frev = ring.revolution_frequency
if dct is not None:
frev -= frev * frev / clight * ring.periodicity * dct
elif dp is not None:
@@ -73,35 +73,38 @@ def get_revolution_frequency(ring, dp=None, dct=None, **kwargs):
return frev
-def set_rf_frequency(ring, frequency=None, dp=None, dct=None, cavpts=None,
- copy=False):
+def set_rf_frequency(ring, frequency=None, dp=None, dct=None, **kwargs):
"""Set the RF frequency
PARAMETERS
- ring lattice description
- frequency RF frequency. Default: nominal frequency.
+ ring lattice description
+ frequency RF frequency [Hz]. Default: nominal frequency.
KEYWORDS
- dp=0.0 momentum deviation.
+ dp=0.0 Momentum deviation.
dct=0.0 Path length deviation
- cavpts=None Cavity location. If None, use all cavities.
- This allows to ignore harmonic cavities.
+ cavpts=None If None, look for ring.cavpts, or otherwise take all
+ cavities.
+ array=False If False, frequency is applied to the selected cavities
+ with the lowest frequency. The frequency of all the
+ other selected cavities is scaled by the same ratio.
+ If True, directly apply frequency to the selected
+ cavities. The value must be broadcastable to the number
+ of cavities.
copy=False If True, returns a shallow copy of ring with new
cavity elements. Otherwise, modify ring in-place
"""
if frequency is None:
frequency = ring.get_revolution_frequency(dp=dp, dct=dct) \
* ring.harmonic_number
- return ring.set_cavity(Frequency=frequency, cavpts=cavpts, copy=copy)
+ return ring.set_cavity(Frequency=frequency, **kwargs)
Lattice.mcf = property(get_mcf, doc="Momentum compaction factor")
Lattice.slip_factor = property(get_slip_factor, doc="Slip factor")
Lattice.get_revolution_frequency = get_revolution_frequency
-Lattice.revolution_frequency = property(get_revolution_frequency,
- doc="Revolution frequency of on-momentum particles (full ring) [Hz]")
Lattice.get_mcf = get_mcf
Lattice.get_slip_factor = get_slip_factor
Lattice.set_rf_frequency = set_rf_frequency
Lattice.rf_frequency = property(get_rf_frequency, set_rf_frequency,
- doc="RF frequency [Hz]")
+ doc="Fundamental RF frequency [Hz]")