Skip to content

Commit

Permalink
Resolve conflicts between readme.rst and readme.md
Browse files Browse the repository at this point in the history
  • Loading branch information
razvanvasile committed May 25, 2017
2 parents 9afce37 + 162d912 commit eaea617
Show file tree
Hide file tree
Showing 12 changed files with 75 additions and 105 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,6 @@ pytac/#*

# documentation
docs/_build

# jupyter
.ipynb_checkpoints
2 changes: 1 addition & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ It is simplest to work with a virtualenv. Then:
>>> export PYTHONPATH=.
>>> py.test test

To see a coverage report:
To see a coverage report::

>>> py.test --cov=pytac test

Expand Down
23 changes: 15 additions & 8 deletions docs/index.rst
Original file line number Diff line number Diff line change
@@ -1,20 +1,28 @@
.. docs documentation master file, created by
sphinx-quickstart on Mon May 8 13:02:08 2017.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive. include:: intro.rst
Python Toolkit for Accelerator Controls
=======================================

Python Toolkit for Accelerator Controls (Pytac) is a Python library intended to make it easy to work with particle accelerators.

It is a framework that implements high level configure/run behaviour of control system components. Pytac includes a set of API (Application Programming Interface) which is used for writing further scripts and interactive controls. The function of Pytac is to provide a scripting language for on-line control.
It is a framework that allows working with control system components at a high level, providing a set of APIs used for writing further scripts and interactive controls.

Two pieces of software influenced its design:

* Matlab Middlelayer, used widely by accelerator physicists
* APHLA, high-level applications written in Python by the NSLS-II accelerator physics group


Software Overview
=================
Pytac provides a library of software that communicates with machine hardware for online applications. It is important to note that although the online get/set routines originally communicated via EPICS Channel Access they can now communicate with a variety of control systems. There are only two core functions that need to be re-programmed to work with the new control system - get and put inside the ControlSystem class.

Pytac provides a Python library ``pytac`` that makes it easier to communicate with machine hardware for online applications. Although it currently works with EPICS, the `cs.ControlSystem` class may be sub-classed to allow other control systems to be used.

The design is based around a ``Lattice`` object that contains a sequence of ``Element`` s. Each element may have zero or more 'fields' (examples x, y, a1 or b2), associated with which are ``Device`` objects. These devices contain the necessary information to request live data from the control system.

Data may be requested in ``ENG`` or ``PHYS`` units and will be converted as appropriate. Two types of unit conversion are available: Polynomial (often used for linear conversion) and Piecewise Cubic Hermite Interpolating Polynomial (Pchip; often used for magnet data where field may not be linear with current). In the case that measurement data (used to set up the conversion objects) is not in the same units as the physical models, further functions may be given to these objects to complete the conversion correctly.

Elements may be grouped into families (an element may be in more than one family) and requested from the lattice object in those families.

Machines are defined using a set of ``.csv`` files, located by default in the ``data`` directory.


Contents:
Expand All @@ -23,7 +31,6 @@ Contents:
.. toctree::
:maxdepth: 4

intro
examples
pytac

Expand Down
12 changes: 0 additions & 12 deletions docs/intro.rst

This file was deleted.

26 changes: 0 additions & 26 deletions pytac/cs.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,29 +31,3 @@ def put(self, pv, value):
value(Number): The value to be set.
"""
raise NotImplementedError()


class NullControlSystem(ControlSystem):
""" Dummy control system to set the value of a pv."""
def __init__(self):
pass

def get(self, pv):
""" Get the value of the given pv.
Args:
pv(string): The Pv to get the value of.
Returns:
Number: The numeric value of the pv.
"""
pass

def put(self, pv, value):
""" Put the value of a given pv.
Args:
pv(string): The string to put the value for.
value(Number): The value to be set.
"""
pass
19 changes: 18 additions & 1 deletion pytac/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ def __init__(self, cs, rb_pv=None, sp_pv=None):
Contains a control system, readback and setpoint pvs. A readback
or setpoint pv is required when creating a device otherwise a
PvException is raised.
PvException is raised. The device is enabled by default.
Args:
cs (ControlSystem): Control system object used to get and set
Expand All @@ -20,13 +20,30 @@ def __init__(self, cs, rb_pv=None, sp_pv=None):
self.rb_pv = rb_pv
self.sp_pv = sp_pv
self._cs = cs
self._enabled = True
if rb_pv is not None:
self.name = rb_pv.split(':')[0]
elif sp_pv is not None:
self.name = sp_pv.split(':')[0]
else:
raise PvException("Readback or setpoint pvs need to be given")

def is_enabled(self):
"""Check whether an device is enabled or disabled.
Returns:
boolean: Represents whether an device is enabled or disabled.
"""
return self._enabled

def set_enabled(self, enabled=True):
"""Enable or disable an device.
Args:
enabled (boolean): Set whether an device is disabled or enabled.
"""
self._enabled = enabled

def put_value(self, value):
"""Set the value of a pv.
Expand Down
25 changes: 4 additions & 21 deletions pytac/element.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,16 @@ def __init__(self, name, length, element_type):
self.families = set()
self._uc = dict()
self._devices = dict()
self._enabled = True

def __repr__(self):
def __str__(self):
"""Auxiliary function to print out an element.
Prints out the set of families an element is part of, as a string.
Return a representation of an element, as a string.
Returns:
string: Set of families the element is part of.
string: A representation of an element.
"""
return str(self.families)
return 'Element: {0}, length: {1}, families: {2}'.format(self._name, self._length, self.families)

def get_fields(self):
"""Get the fields defined on an element.
Expand All @@ -53,22 +52,6 @@ def get_length(self):

return self._length

def is_enabled(self):
"""Check whether an element is enabled or disabled.
Returns:
boolean: Represents whether an element is enabled or disabled.
"""
return self._enabled

def set_enabled(self, enabled=True):
"""Enable or disable an element.
Args:
enabled (boolean): Set whether an element is disabled or enabled.
"""
self._enabled = enabled

def add_device(self, field, device, uc):
"""Add device and unit conversion objects to a given field.
Expand Down
8 changes: 4 additions & 4 deletions pytac/units.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import numpy as np
import numpy
from scipy.interpolate import PchipInterpolator
from pytac.exceptions import UniqueSolutionException

Expand Down Expand Up @@ -37,7 +37,7 @@ def __init__(self, coef, post_eng_to_phys=unit_function, pre_phys_to_eng=unit_fu
coef(array_like): The polynomial's coefficients, in decreasing powers.
"""
super(self.__class__, self).__init__(post_eng_to_phys, pre_phys_to_eng)
self.p = np.poly1d(coef)
self.p = numpy.poly1d(coef)

def _raw_eng_to_phys(self, eng_value):
"""Convert between engineering and physics units.
Expand Down Expand Up @@ -91,8 +91,8 @@ def __init__(self, x, y, post_eng_to_phys=unit_function, pre_phys_to_eng=unit_fu
self.y = y
self.pp = PchipInterpolator(x, y)

diff = np.diff(y)
if not ((np.all(diff > 0)) or (np.all((diff < 0)))):
diff = numpy.diff(y)
if not ((numpy.all(diff > 0)) or (numpy.all((diff < 0)))):
raise ValueError("Given coefficients must be monotonically"
"decreasing.")

Expand Down
6 changes: 0 additions & 6 deletions test/test_cs.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,3 @@ def test_InvalidControlSystem_throws_NotImplementedError():
ics.get('dummy')
with pytest.raises(NotImplementedError):
ics.put('dummy', 1)


def test_NullControlSystem_throws_no_errors():
ncs = cs.NullControlSystem()
ncs.get('dummy')
ncs.put('dummy', 1)
36 changes: 21 additions & 15 deletions test/test_device.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,33 +4,39 @@
import mock


SP_PV = 'SR01A-PC-SQUAD-01:SETI'
RB_PV = 'SR01A-PC-SQUAD-01:I'

@pytest.fixture
def create_device(readback, setpoint):
def create_device(readback=RB_PV, setpoint=SP_PV):
_rb = readback
_sp = setpoint
device = pytac.device.Device(rb_pv=_rb, sp_pv=_sp, cs=mock.MagicMock())
return device


def test_set_device_value():
rb_pv = 'SR01A-PC-SQUAD-01:I'
sp_pv = 'SR01A-PC-SQUAD-01:SETI'
def test_set_device_value(create_device):
create_device.put_value(40)
create_device._cs.put.assert_called_with(SP_PV, 40)

device1 = create_device(rb_pv, sp_pv)
device1.put_value(40)
device1._cs.put.assert_called_with(sp_pv, 40)

device2 = create_device(rb_pv, None)
def test_device_invalid_sp_raise_exception():
device2 = create_device(RB_PV, None)
with pytest.raises(PvException):
device2.put_value(40)
with pytest.raises(PvException):
create_device(None, None)


def test_get_device_value():
sp_pv = 'SR01A-PC-SQUAD-01:SETI'

device = create_device(None, sp_pv)
def test_get_device_value(create_device):
with pytest.raises(PvException):
device.get_value('non_existent')
create_device.get_value('non_existent')

with pytest.raises(PvException):
create_device(None, None)

def test_is_enabled(create_device):
assert create_device.is_enabled() == True


def test_set_enabled(create_device):
create_device.set_enabled(False)
assert create_device.is_enabled() == False
14 changes: 6 additions & 8 deletions test/test_element.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,11 +88,9 @@ def test_identity_conversion():
def test_get_fields(test_element):
assert set(test_element.get_fields()) == set(['y', 'x'])


def test_is_enabled(test_element):
assert test_element.is_enabled() == True


def test_set_enabled(test_element):
test_element.set_enabled(False)
assert test_element.is_enabled() == False
def test_element_representation(test_element):
s = str(test_element)
assert test_element._name in s
assert str(test_element._length) in s
for f in test_element.families:
assert f in s
6 changes: 3 additions & 3 deletions test/test_units.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import pytest
from pytac.units import PolyUnitConv, PchipUnitConv
import numpy as np
import numpy


def f1(value):
Expand Down Expand Up @@ -44,9 +44,9 @@ def test_ppconversion_to_physics_2_points():
def test_pp_conversion_to_physics_3_points():
pchip_uc = PchipUnitConv([1, 3, 5], [1, 3, 6])
assert pchip_uc.eng_to_phys(1) == 1
assert np.round(pchip_uc.eng_to_phys(2), 4) == 1.8875
assert numpy.round(pchip_uc.eng_to_phys(2), 4) == 1.8875
assert pchip_uc.eng_to_phys(3) == 3
assert np.round(pchip_uc.eng_to_phys(4), 4) == 4.3625
assert numpy.round(pchip_uc.eng_to_phys(4), 4) == 4.3625
assert pchip_uc.eng_to_phys(5) == 6


Expand Down

0 comments on commit eaea617

Please sign in to comment.