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]")