"""
Accelerator elements, including methods for their manipulation and conversion to
the representation expected by Dynac.
"""
from Pynac.DataClasses import Param
from collections import defaultdict
import abc # Abstract Base Class
_dynac2pynac = defaultdict(lambda: 'NotImplemented', {
'QUADRUPO': 'Quad',
'CAVMC': 'CavityAnalytic',
'DRIFT': 'Drift',
'CAVSC': 'AccGap',
'REJECT': 'Set4DAperture',
'BUNCHER': 'Buncher',
'FIELD': 'AccFieldFromFile',
'STEER': 'Steerer',
})
[docs]class PynacElement(metaclass=abc.ABCMeta):
@abc.abstractclassmethod
[docs] def from_dynacRepr(cls, pynacRepr):
pass
@abc.abstractclassmethod
[docs] def dynacRepresentation(self):
pass
[docs]class Quad(PynacElement):
"""
A Pynac representation of a quadrupole magnet.
Before the simulation is run, any changes made to elements using this class have to
be put back into the ``lattice`` attribute of `Pynac` using the ``dynacRepresentation``
method.
"""
def __init__(self, L, B, aperRadius):
self.L = Param(val = L, unit = 'cm')
self.B = Param(val = B, unit = 'kG')
self.aperRadius = Param(val = aperRadius, unit = 'cm')
@classmethod
[docs] def from_dynacRepr(cls, pynacRepr):
"""
Construct a ``Quad`` instance from the Pynac lattice element
"""
L = float(pynacRepr[1][0][0])
B = float(pynacRepr[1][0][1])
aperRadius = float(pynacRepr[1][0][2])
return cls(L, B, aperRadius)
[docs] def scaleField(self, scalingFactor):
"""
Adjust the field of the magnet by the value of ``scalingFactor``. The adjustment
is multiplicative, so a value of ``scalingFactor = 1.0`` will result in no change
of the field.
"""
self.B = self.B._replace(val=self.B.val * scalingFactor)
[docs] def setField(self, new_value):
"""
Adjust the field of the magnet by the value of ``scalingFactor``. The adjustment
is multiplicative, so a value of ``scalingFactor = 1.0`` will result in no change
of the field.
"""
self.B = self.B._replace(val=new_value)
[docs] def dynacRepresentation(self):
"""
Return the Pynac representation of this quadrupole instance.
"""
return ['QUADRUPO', [[self.L.val, self.B.val, self.aperRadius.val]]]
def __repr__(self):
s = 'QUAD:'
s += ' | L = ' + self.L.__repr__()
s += ' | B = ' + self.B.__repr__()
s += ' | aperRadius = ' + self.aperRadius.__repr__()
return s
[docs]class CavityAnalytic(PynacElement):
"""
A Pynac representation of a resonant EM cavity (i.e., the ``CAVMC`` model used by Dynac
to do analytic calculations).
Before the simulation is run, any changes made to elements using this class have to
be put back into the ``lattice`` attribute of ``Pynac`` using the ``dynacRepresentation``
method.
"""
def __init__(self, phase, fieldReduction, cavID=0, xesln=0, isec=0):
self.cavID = Param(val = cavID, unit = None)
self.xesln = Param(val = xesln, unit = 'cm')
self.phase = Param(val = phase, unit = 'deg')
self.fieldReduction = Param(val = fieldReduction, unit = 'percent')
self.isec = Param(val = isec, unit = None)
@classmethod
[docs] def from_dynacRepr(cls, pynacRepr):
"""
Construct a ``CavityAnalytic`` instance from the Pynac lattice element
"""
cavID = int(pynacRepr[1][0][0])
xesln = float(pynacRepr[1][1][0])
phase = float(pynacRepr[1][1][1])
fieldReduction = float(pynacRepr[1][1][2])
isec = int(pynacRepr[1][1][3])
return cls(phase, fieldReduction, cavID, xesln, isec)
[docs] def adjustPhase(self, adjustment):
"""
Adjust the accelerating phase of the cavity by the value of ``adjustment``.
The adjustment is additive, so a value of ``scalingFactor = 0.0`` will result
in no change of the phase.
"""
self.phase = self.phase._replace(val = self.phase.val + adjustment)
[docs] def scaleField(self, scalingFactor):
"""
Adjust the accelerating field of the cavity by the value of ``scalingFactor``.
The adjustment is multiplicative, so a value of ``scalingFactor = 1.0`` will result
in no change of the field.
"""
oldField = self.fieldReduction.val
newField = 100.0 * (scalingFactor * (1.0 + oldField/100.0) - 1.0)
self.fieldReduction = self.fieldReduction._replace(val = newField)
[docs] def dynacRepresentation(self):
"""
Return the Dynac representation of this cavity instance.
"""
return ['CAVMC', [
[self.cavID.val],
[self.xesln.val, self.phase.val, self.fieldReduction.val, self.isec.val, 1],
]]
def __repr__(self):
s = 'CavityAnalytic:'
s += ' | phase = ' + self.phase.__repr__()
s += ' | fieldReduction = ' + self.fieldReduction.__repr__()
return s
[docs]class Drift(PynacElement):
"""
A Pynac representation of a drift.
Before the simulation is run, any changes made to elements using this class have to
be put back into the ``lattice`` attribute of `Pynac` using the ``dynacRepresentation``
method.
"""
def __init__(self, L):
self.L = Param(val = L, unit = 'cm')
@classmethod
[docs] def from_dynacRepr(cls, pynacRepr):
"""
Construct a ``Drift`` instance from the Pynac lattice element
"""
L = float(pynacRepr[1][0][0])
return cls(L)
[docs] def dynacRepresentation(self):
"""
Return the Dynac representation of this drift instance.
"""
return ['DRIFT', [[self.L.val]]]
def __repr__(self):
s = 'DRIFT:'
s += ' | L = ' + self.L.__repr__()
return s
[docs]class AccGap(PynacElement):
"""
A Pynac representation of an accelerating gap.
Before the simulation is run, any changes made to elements using this class have to
be put back into the ``lattice`` attribute of `Pynac` using the ``dynacRepresentation``
method.
"""
def __init__(self, L, TTF, TTFprime, TTFprimeprime, EField, phase, F, atten):
self.L = Param(val = L, unit = 'cm')
self.TTF = Param(val = TTF, unit = None)
self.TTFprime = Param(val = TTFprime, unit = None)
self.TTFprimeprime = Param(val = TTFprimeprime, unit = None)
self.EField = Param(val = EField, unit = 'MV/m')
self.phase = Param(val = phase, unit = 'deg')
self.F = Param(val = F, unit = 'MHz')
self.atten = Param(val = atten, unit = None)
# The following are dummy variables, not used by Dynac
self.gapID = Param(val = 0, unit = None)
self.energy = Param(val = 0, unit = 'MeV')
self.beta = Param(val = 0, unit = None)
self.S = Param(val = 0, unit = None)
self.SP = Param(val = 0, unit = None)
self.quadLength = Param(val = 0, unit = 'cm')
self.quadStrength = Param(val = 0, unit = 'kG/cm')
self.accumLen = Param(val = 0, unit = 'cm')
@classmethod
[docs] def from_dynacRepr(cls, pynacRepr):
"""
Construct a ``AccGap`` instance from the Pynac lattice element
"""
pynacList = pynacRepr[1][0]
L = float(pynacList[3])
TTF = float(pynacList[4])
TTFprime = float(pynacList[5])
TTFprimeprime = float(pynacList[13])
EField = float(pynacList[10])
phase = float(pynacList[11])
F = float(pynacList[14])
atten = float(pynacList[15])
gap = cls(L, TTF, TTFprime, TTFprimeprime, EField, phase, F, atten)
gap.gapID = Param(val = int(pynacList[0]), unit = None)
gap.energy = Param(val = float(pynacList[1]), unit = 'MeV')
gap.beta = Param(val = float(pynacList[2]), unit = None)
gap.S = Param(val = float(pynacList[6]), unit = None)
gap.SP = Param(val = float(pynacList[7]), unit = None)
gap.quadLength = Param(val = float(pynacList[8]), unit = 'cm')
gap.quadStrength = Param(val = float(pynacList[9]), unit = 'kG/cm')
gap.accumLen = Param(val = float(pynacList[12]), unit = 'cm')
return gap
[docs] def dynacRepresentation(self):
"""
Return the Dynac representation of this accelerating gap instance.
"""
details = [
self.gapID.val,
self.energy.val,
self.beta.val,
self.L.val,
self.TTF.val,
self.TTFprime.val,
self.S.val,
self.SP.val,
self.quadLength.val,
self.quadStrength.val,
self.EField.val,
self.phase.val,
self.accumLen.val,
self.TTFprimeprime.val,
self.F.val,
self.atten.val,
]
return ['CAVSC', [details]]
def __repr__(self):
s = 'AccGap:'
s += ' | L = ' + self.L.__repr__()
s += ' | Field = ' + self.EField.__repr__()
s += ' | phase = ' + self.phase.__repr__()
s += ' | freq = ' + self.F.__repr__()
return s
[docs]class Set4DAperture(PynacElement):
def __init__(self, energy, phase, x, y, radius, energyDefnFlag = 0):
if energyDefnFlag == 1 or energyDefnFlag == 11:
energyUnit = 'MeV'
else:
energyUnit = '%'
self.energy = Param(val = energy, unit = energyUnit)
self.phase = Param(val = phase, unit = 'deg')
self.x = Param(val = x, unit = 'cm')
self.y = Param(val = y, unit = 'cm')
self.radius = Param(val = radius, unit = 'cm')
self.energyDefnFlag = Param(val = energyDefnFlag, unit = None)
@classmethod
[docs] def from_dynacRepr(cls, pynacRepr):
"""
Construct a ``Set4DAperture`` instance from the Pynac lattice element
"""
energyDefnFlag = int(pynacRepr[1][0][0])
energy = float(pynacRepr[1][0][1])
phase = float(pynacRepr[1][0][2])
x = float(pynacRepr[1][0][3])
y = float(pynacRepr[1][0][4])
radius = float(pynacRepr[1][0][5])
return cls(energy, phase, x, y, radius, energyDefnFlag)
[docs] def dynacRepresentation(self):
"""
Return the Pynac representation of this Set4DAperture instance.
"""
details = [
self.energyDefnFlag.val,
self.energy.val,
self.phase.val,
self.x.val,
self.y.val,
self.radius.val,
]
return ['REJECT', [details]]
def __repr__(self):
s = 'Set4DAperture:'
s += ' | E: ' + self.energy.__repr__()
s += ' | Phase: ' + self.phase.__repr__()
s += ' | x: ' + self.x.__repr__()
s += ' | y: ' + self.y.__repr__()
s += ' | radius: ' + self.radius.__repr__()
return s
[docs]class Buncher(PynacElement):
def __init__(self, voltage, phase, harmonicNum, apertureRadius):
self.voltage = Param(val = voltage, unit = 'MV')
self.phase = Param(val = phase, unit = 'deg')
self.harmonicNum = Param(val = harmonicNum, unit = None)
self.apertureRadius = Param(val = apertureRadius, unit = 'cm')
@classmethod
[docs] def from_dynacRepr(cls, pynacRepr):
voltage = float(pynacRepr[1][0][0])
phase = float(pynacRepr[1][0][1])
harmonicNum = float(pynacRepr[1][0][2])
apertureRadius = float(pynacRepr[1][0][3])
return cls(voltage, phase, harmonicNum, apertureRadius)
[docs] def dynacRepresentation(self):
"""
Return the Pynac representation of this Set4DAperture instance.
"""
details = [
self.voltage.val,
self.phase.val,
self.harmonicNum.val,
self.apertureRadius.val,
]
return ['BUNCHER', [details]]
def __repr__(self):
s = 'BUNCHER:'
s += ' | V: ' + self.voltage.__repr__()
s += ' | Phase: ' + self.phase.__repr__()
s += ' | Harmonic: ' + self.harmonicNum.__repr__()
s += ' | Aperture: ' + self.apertureRadius.__repr__()
return s
[docs]class AccFieldFromFile(PynacElement):
def __init__(self, filename, scaleFactor):
self.filename = filename
self.scaleFactor = Param(val = scaleFactor, unit = None)
@classmethod
[docs] def from_dynacRepr(cls, pynacRepr):
filename = pynacRepr[1][0][0]
scaleFactor = float(pynacRepr[1][1][0])
return cls(filename, scaleFactor)
[docs] def dynacRepresentation(self):
"""
Return the Pynac representation of this AccFieldFromFile instance.
"""
return ['FIELD', [[self.filename], [self.scaleFactor.val]]]
def __repr__(self):
s = 'AccFieldFile: '
s += self.filename
s += ' | Scale: ' + self.scaleFactor.__repr__()
return s
[docs]class Steerer(PynacElement):
def __init__(self, field_strength, plane):
self.field_strength = Param(
val=field_strength,
unit='T.m'
)
self.plane = Param(
val=plane,
unit='None'
)
@classmethod
[docs] def from_dynacRepr(cls, pynacRepr):
"""
Construct a ``Steerer`` instance from the Pynac lattice element
"""
f = float(pynacRepr[1][0][0])
p = 'HV'[int(pynacRepr[1][0][1])]
return cls(f, p)
[docs] def dynacRepresentation(self):
"""
Return the Dynac representation of this steerer instance.
"""
if self.plane.val == 'H':
p = 0
elif self.plane.val == 'V':
p = 1
return ['STEER', [[self.field_strength.val], [p]]]
[docs] def scaleField(self, scalingFactor):
"""
Adjust the field of the magnet by the value of ``scalingFactor``. The adjustment
is multiplicative, so a value of ``scalingFactor = 1.0`` will result in no change
of the field.
"""
self.field_strength = self.field_strength._replace(
val=self.field_strength.val * scalingFactor
)
[docs] def setField(self, new_value):
"""
Adjust the field of the magnet by the value of ``scalingFactor``. The adjustment
is multiplicative, so a value of ``scalingFactor = 1.0`` will result in no change
of the field.
"""
self.field_strength = self.field_strength._replace(
val=new_value
)
def __repr__(self):
s = 'STEER:'
s += ' | f = ' + self.field_strength.__repr__()
s += ' | plane = ' + self.plane.__repr__()
return s