Skip to content

Commit

Permalink
Merge pull request #6 from T-Nicholls/cagetless
Browse files Browse the repository at this point in the history
SoftIOC changes
  • Loading branch information
T-Nicholls committed May 31, 2019
2 parents c458bb8 + b0c68f6 commit 0ccfe14
Show file tree
Hide file tree
Showing 14 changed files with 531 additions and 268 deletions.
5 changes: 4 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ install:
fi
- pip install -r requirements.txt
- pip install git+https://github.com/dls-controls/pytac.git
- pip install -e "git+https://github.com/atcollab/at.git#egg=at-python&subdirectory=pyat"
- pip install -e "git+https://github.com/atcollab/at.git#egg=accelerator-toolbox&subdirectory=pyat"
- cd venv/src/accelerator-toolbox/pyat
- python setup.py develop
- cd ../../../../

script:
- python -m pytest test --cov-report term-missing --cov=atip
Expand Down
2 changes: 1 addition & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ element fields that the simulator doesn't.
Virtual Accelerator:
--------------------

ATIP can be used as a virtual accelerator, see ``ioc/SOFT-IOC.rst`` for further
ATIP can be used as a virtual accelerator, see ``ioc/README.rst`` for further
information.

Implementation:
Expand Down
97 changes: 77 additions & 20 deletions atip/at_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import at
import numpy
import cothread
from scipy.constants import speed_of_light


class ATSimulator(object):
Expand Down Expand Up @@ -68,13 +69,21 @@ def __init__(self, at_lattice, callback=None):
self._lindata = self._at_lat.linopt(refpts=self._rp, get_chrom=True,
coupled=False)
# Threading stuff initialisation.
self.queue = cothread.ThreadedEventQueue()
self.queue = cothread.EventQueue()
self.up_to_date = cothread.Event()
self.up_to_date.Signal()
self._paused = cothread.Event()
self._calculation_thread = cothread.Spawn(self._recalculate_phys_data,
callback)

def _gather_one_sample(self):
"""If the queue is empty Wait() yields until an item is added. When the
queue is not empty the oldest change will be removed and applied to the
AT lattice.
"""
data_source, field, value = self.queue.Wait()
data_source.make_change(field, value)

def _recalculate_phys_data(self, callback):
"""Target function for the Cothread thread. Recalculates the physics
data dependant on the status of the '_paused' flag and the length of
Expand All @@ -95,24 +104,21 @@ def _recalculate_phys_data(self, callback):
but as a warning.
"""
while True:
if len(self.queue) != 0:
for i in range(len(self.queue)):
data_source, field, value = self.queue.Wait()
data_source.make_change(field, value)
if bool(self._paused) is False:
try:
self._at_lat.radiation_on()
self._emittance = self._at_lat.ohmi_envelope(self._rp)
self._at_lat.radiation_off()
self._lindata = self._at_lat.linopt(refpts=self._rp,
get_chrom=True,
coupled=False)
except Exception as e:
warn(at.AtWarning(e))
if callback is not None:
callback()
self.up_to_date.Signal()
cothread.Yield()
self._gather_one_sample()
for i in range(len(self.queue)):
self._gather_one_sample()
if bool(self._paused) is False:
try:
self._at_lat.radiation_on()
self._emittance = self._at_lat.ohmi_envelope(self._rp)
self._at_lat.radiation_off()
self._lindata = self._at_lat.linopt(0.0, self._rp, True,
coupled=False)
except Exception as e:
warn(at.AtWarning(e))
if callback is not None:
callback()
self.up_to_date.Signal()

def toggle_calculations(self):
"""Pause or unpause the physics calculations by setting or clearing the
Expand Down Expand Up @@ -183,7 +189,7 @@ def get_emit(self, cell):
Returns:
float: The x or y emittance for the AT lattice.
"""
return self._emittance[2]['emitXY'][:, cell][0]
return self._emittance[2]['emitXY'][0, cell]

def get_orbit(self, cell):
"""Return the specified cell of the closed orbit for the AT lattice.
Expand Down Expand Up @@ -266,3 +272,54 @@ def get_mu(self):
numpy.array: The mu array for each element.
"""
return self._lindata[3]['mu']

def get_mcf(self):
"""Return the linear momentum compaction factor for the AT lattice.
returns:
float: The linear momentum compaction factor of the AT lattice.
"""
return self._at_lat.get_mcf()

def get_energy_loss(self):
"""Return the energy loss per turn for the AT lattice. Taken from the
AT lattice property.
returns:
float: The energy loss of the AT lattice.
"""
return self._at_lat.energy_loss

def get_damping_times(self):
"""Return the damping times for the 3 normal modes.
returns:
numpy.array: The damping times of the AT lattice.
"""
T0 = self.get_s()[-1] / speed_of_light
return T0 / self._emittance[1][1]

def get_total_bend_angle(self):
"""Return the total bending angle of all the dipoles in the AT lattice.
returns:
float: The total bending angle for the AT lattice.
"""
thetas = []
for elem in self._at_lat:
if isinstance(elem, at.lattice.elements.Dipole):
thetas.append(elem.BendingAngle)
return numpy.degrees(sum(thetas))

def get_total_absolute_bend_angle(self):
"""Return the total absolute bending angle of all the dipoles in the
AT lattice.
returns:
float: The total absolute bending angle for the AT lattice.
"""
thetas = []
for elem in self._at_lat:
if isinstance(elem, at.lattice.elements.Dipole):
thetas.append(elem.BendingAngle)
return numpy.degrees(sum(numpy.abs(thetas)))
19 changes: 10 additions & 9 deletions ioc/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@
Running ATIP as a Virtual Accelerator using Python Soft IOC
===========================================================

Using Python Soft IOC ATIP can emulate machine PVs so that the ATIP simulator
can be addressed in the same manner as the live machine. This is useful for
testing high level applications as it can update PVs in a physically correct
way in response to changes by the user. The virtual accelerator runs on EPICS
port 6064 to avoid conflict with the same PVs on the live machine.
Using `PythonSoftIOC <https://github.com/Araneidae/pythonIoc>`_ ATIP can
emulate machine PVs so that the ATIP simulator can be addressed in the same
manner as the live machine. This is useful for testing high level applications
as it can update PVs in a physically correct way in response to changes by the
user. The virtual accelerator runs on EPICS port 6064 to avoid conflict with
the same PVs on the live machine.

Initialisation:
---------------
Expand Down Expand Up @@ -50,7 +51,7 @@ Feedback Records:
A number of PVs related to the feedback systems are supported. They can be read
from in the same way as any other PV, but for testing and debugging there is a
special method for setting them. This is done on the ATIP server object, inside
the server terminal (the one you ran `start-ioc` in initially). As arguments,
the server terminal (the one you ran ``start-ioc`` in initially). As arguments,
it takes the element's index in the ring (starting from 1, 0 is used to set on
the lattice), the field (possible element fields are: ``'x_fofb_disabled',
'x_sofb_disabled', 'y_fofb_disabled', 'y_sofb_disabled', 'h_fofb_disabled',
Expand All @@ -68,9 +69,9 @@ Ring Mode:

You can run the virtual accelerator in any ring mode that is supported by
Pytac; currently 'VMX', 'VMXSP', and 'DIAD'. The ring mode can be passed as an
command line argument to `start-ioc`, if it is not passed in that manner then
it can be configured by changing the `RINGMODE` environment variable, if that
is not set then the ring mode PV `SR-CS-RING-01:MODE` is checked, if that is
command line argument to ``start-ioc``, if it is not passed in that manner then
it can be configured by changing the ``RINGMODE`` environment variable, if that
is not set then the ring mode PV ``SR-CS-RING-01:MODE`` is checked, if that is
also not set then the virtual accelerator will default to 'DIAD'. For example::

$ ./start-ioc DIAD
Expand Down
4 changes: 1 addition & 3 deletions ioc/atip_ioc_entry.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@
require('epicsdbbuilder==1.0')
require('numpy>=1.10')
require('scipy>=0.16')


here = os.path.realpath('.')
here = os.path.realpath(os.path.dirname(__file__))
sys.path.append(os.path.split(here)[0])

import atip_server # noqa: E402
Expand Down

0 comments on commit 0ccfe14

Please sign in to comment.