Skip to content

Commit

Permalink
Merge pull request #104 from bluesky/98-sim-geometries
Browse files Browse the repository at this point in the history
Create simulators for common geometries
  • Loading branch information
mrakitin authored Jan 13, 2021
2 parents e0b792b + ed91b67 commit 9c36128
Show file tree
Hide file tree
Showing 6 changed files with 110 additions and 142 deletions.
66 changes: 24 additions & 42 deletions docs/source/geometries.rst
Original file line number Diff line number Diff line change
Expand Up @@ -39,42 +39,19 @@ the actual motors provided by the diffractometer.
.. [#] The ``ophyd.SoftPositioner`` is such a software simulation:
https://blueskyproject.io/ophyd/positioners.html#ophyd.positioner.SoftPositioner
Create the custom 6-circle subclass:
Load the simulated 6-circle geometry from *hklpy*:

.. code-block:: python
:linenos:
import gi
gi.require_version('Hkl', '5.0')
gi.require_version("Hkl", "5.0")
# MUST come before `import hkl`
import hkl.geometries
from ophyd import Component, PseudoSingle, SoftPositioner
class SimulatedE6C(hkl.geometries.E6C):
"""E6C: Simulated (soft) 6-circle diffractometer"""
h = Component(PseudoSingle, '')
k = Component(PseudoSingle, '')
l = Component(PseudoSingle, '')
mu = Component(SoftPositioner)
omega = Component(SoftPositioner)
chi = Component(SoftPositioner)
phi = Component(SoftPositioner)
gamma = Component(SoftPositioner)
delta = Component(SoftPositioner)
def __init__(self, *args, **kwargs):
"""
start the SoftPositioner objects with initial values
"""
super().__init__(*args, **kwargs)
for axis in self.real_positioners:
axis.move(0)
from hkl.geometries import SimulatedE6C
Create an instance of this diffractometer with::

sim6c = SimulatedE6C('', name='sim6c')
sim6c = SimulatedE6C("", name="sim6c")

.. _geometries.k4cv:

Expand All @@ -100,23 +77,26 @@ tth sky:m4
.. [#] ``ophyd.EpicsMotor``:
https://blueskyproject.io/ophyd/builtin-devices.html?highlight=epicsmotor#ophyd.epics_motor.EpicsMotor
Create the custom kappa 4-circle subclass:
In this case, we must first create a custom kappa 4-circle subclass and
connect our motor PVs to the positioners for the *real axes*:

.. code-block:: python
:linenos:
import gi
gi.require_version('Hkl', '5.0')
gi.require_version("Hkl", "5.0")
# MUST come before `import hkl`
import hkl.geometries
from ophyd import Component, PseudoSingle, EpicsMotor
from ophyd import Component
from ophyd import EpicsMotor
from ophyd import PseudoSingle
class KappaK4CV(hkl.geometries.K4CV):
"""K4CV: kappa diffractometer in 4-circle geometry"""
h = Component(PseudoSingle, '')
k = Component(PseudoSingle, '')
l = Component(PseudoSingle, '')
h = Component(PseudoSingle, "")
k = Component(PseudoSingle, "")
l = Component(PseudoSingle, "")
komega = Component(EpicsMotor, "sky:m1")
kappa = Component(EpicsMotor, "sky:m2")
Expand All @@ -125,7 +105,7 @@ Create the custom kappa 4-circle subclass:
Create an instance of this diffractometer with::

k4cv = KappaK4CV('', name='k4cv')
k4cv = KappaK4CV("", name="k4cv")

.. _geometries.k4cve:

Expand Down Expand Up @@ -165,29 +145,31 @@ which then sets the wavelength:

(:math:`h\nu=` 12.39842 angstrom :math:`\cdot` keV) and account for the
units of the control system *energy*. To combine all this, we define a
new python class starting similar to `KappaK4CV` above, and adding the
new python class starting similar to ``KappaK4CV`` above, and adding the
energy signals. Create the custom kappa 4-circle subclass with energy:

.. code-block:: python
:linenos:
import gi
gi.require_version('Hkl', '5.0')
gi.require_version("Hkl", "5.0")
# MUST come before `import hkl`
import hkl.geometries
from ophyd import Component
from ophyd import PseudoSingle
from ophyd import EpicsSignal, EpicsMotor, Signal
from ophyd import EpicsMotor
from ophyd import EpicsSignal
from ophyd import Signal
import pint
class KappaK4CV_Energy(hkl.geometries.K4CV):
"""
K4CV: kappa diffractometer in 4-circle geometry with energy
"""
h = Component(PseudoSingle, '')
k = Component(PseudoSingle, '')
l = Component(PseudoSingle, '')
h = Component(PseudoSingle, "")
k = Component(PseudoSingle, "")
l = Component(PseudoSingle, "")
komega = Component(EpicsMotor, "sky:m1")
kappa = Component(EpicsMotor, "sky:m2")
Expand All @@ -203,7 +185,7 @@ energy signals. Create the custom kappa 4-circle subclass with energy:
Create an instance of this diffractometer with::

k4cve = KappaK4CV_Energy('', name='k4cve')
k4cve = KappaK4CV_Energy("", name="k4cve")

.. note::

Expand All @@ -215,7 +197,7 @@ Create an instance of this diffractometer with::
PVs are connected. The following code, will create the object, wait
for all PVs to connect, then update the `calc` engine::

k4cve = KappaK4CV_Energy('', name='k4cve')
k4cve = KappaK4CV_Energy("", name="k4cve")
k4cve.wait_for_connection()
k4cve._energy_changed(k4cve.energy.get())

Expand Down
64 changes: 64 additions & 0 deletions hkl/geometries.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@
~K6C
~TwoC
~Zaxis
~SimulatedE4CV
~SimulatedE6C
~SimulatedK4CV
~SimulatedK6C
SPECIAL-USE DIFFRACTOMETER GEOMETRIES
Expand All @@ -33,6 +37,10 @@

from . import calc
from .diffract import Diffractometer
from ophyd import Component as Cpt
from ophyd import Device
from ophyd import PseudoSingle
from ophyd import SoftPositioner
import logging


Expand Down Expand Up @@ -127,3 +135,59 @@ class Zaxis(Diffractometer):
"""Z-axis geometry"""

calc_class = calc.CalcZaxis


class SimMixin(Device):
"""Common setup for simulated geometries."""

h = Cpt(PseudoSingle, "")
k = Cpt(PseudoSingle, "")
l = Cpt(PseudoSingle, "")

def __init__(self, *args, **kwargs):
"""
start the SoftPositioner objects with initial values
"""
super().__init__(*args, **kwargs)
for axis in self.real_positioners:
axis.move(0)


class SimulatedE4CV(SimMixin, E4CV):
"""SimulatedE4CV: Eulerian 4-circle diffractometer, vertical"""

omega = Cpt(SoftPositioner, limits=(-180, 180))
chi = Cpt(SoftPositioner, limits=(-180, 180))
phi = Cpt(SoftPositioner, limits=(-180, 180))
tth = Cpt(SoftPositioner, limits=(-180, 180))


class SimulatedE6C(SimMixin, E6C):
"""SimulatedE6C: Eulerian 6-circle diffractometer"""

mu = Cpt(SoftPositioner, limits=(-180, 180))
omega = Cpt(SoftPositioner, limits=(-180, 180))
chi = Cpt(SoftPositioner, limits=(-180, 180))
phi = Cpt(SoftPositioner, limits=(-180, 180))
gamma = Cpt(SoftPositioner, limits=(-180, 180))
delta = Cpt(SoftPositioner, limits=(-180, 180))


class SimulatedK4CV(SimMixin, K4CV):
"""SimulatedK4CV: Kappa 4-circle diffractometer, vertical"""

komega = Cpt(SoftPositioner, limits=(-180, 180))
kappa = Cpt(SoftPositioner, limits=(-180, 180))
kphi = Cpt(SoftPositioner, limits=(-180, 180))
tth = Cpt(SoftPositioner, limits=(-180, 180))


class SimulatedK6C(SimMixin, K6C):
"""SimulatedK6C: Kappa 6-circle diffractometer"""

mu = Cpt(SoftPositioner, limits=(-180, 180))
komega = Cpt(SoftPositioner, limits=(-180, 180))
kappa = Cpt(SoftPositioner, limits=(-180, 180))
kphi = Cpt(SoftPositioner, limits=(-180, 180))
gamma = Cpt(SoftPositioner, limits=(-180, 180))
delta = Cpt(SoftPositioner, limits=(-180, 180))
28 changes: 5 additions & 23 deletions hkl/tests/test_diffract.py
Original file line number Diff line number Diff line change
@@ -1,34 +1,16 @@
import pytest
import gi
import numpy.testing

from ophyd import Component as Cpt
from ophyd import PseudoSingle, SoftPositioner

import pint

import gi
import pytest

gi.require_version("Hkl", "5.0")
# NOTE: MUST call gi.require_version() BEFORE import hkl
from hkl.calc import A_KEV
from hkl.geometries import E4CV


class Fourc(E4CV):
h = Cpt(PseudoSingle, "")
k = Cpt(PseudoSingle, "")
l = Cpt(PseudoSingle, "")

omega = Cpt(SoftPositioner)
chi = Cpt(SoftPositioner)
phi = Cpt(SoftPositioner)
tth = Cpt(SoftPositioner)
from hkl.geometries import SimulatedE4CV

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

for p in self.real_positioners:
p._set_position(0) # give each a starting position
class Fourc(SimulatedE4CV):
...


@pytest.fixture(scope="function")
Expand Down
46 changes: 9 additions & 37 deletions hkl/tests/test_extra_motor.py
Original file line number Diff line number Diff line change
@@ -1,52 +1,24 @@
from ophyd import PseudoSingle, SoftPositioner
from ophyd import Component as Cpt

import gi
from ophyd import Component as Cpt
from ophyd import PseudoSingle

gi.require_version("Hkl", "5.0")
from hkl.geometries import E4CV


class Fourc(E4CV):
h = Cpt(PseudoSingle, "")
k = Cpt(PseudoSingle, "")
l = Cpt(PseudoSingle, "")
from hkl.geometries import SimulatedE4CV

omega = Cpt(SoftPositioner)
chi = Cpt(SoftPositioner)
phi = Cpt(SoftPositioner)
tth = Cpt(SoftPositioner)

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

for p in self.real_positioners:
p._set_position(0) # give each a starting position
class Fourc(SimulatedE4CV):
...


FOURC_SETUP_CODE = """
from ophyd import PseudoSingle, SoftPositioner
from ophyd import Component as Cpt
from ophyd import SoftPositioner
import gi
gi.require_version('Hkl', '5.0')
from hkl.geometries import E4CV
class Fourc(E4CV):
h = Cpt(PseudoSingle, '')
k = Cpt(PseudoSingle, '')
l = Cpt(PseudoSingle, '')
omega = Cpt(SoftPositioner)
chi = Cpt(SoftPositioner)
phi = Cpt(SoftPositioner)
tth = Cpt(SoftPositioner)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
from hkl.geometries import SimulatedE4CV
for p in self.real_positioners:
p._set_position(0) # give each a starting position
class Fourc(SimulatedE4CV):
...
"""


Expand Down
23 changes: 4 additions & 19 deletions hkl/tests/test_fourc.py
Original file line number Diff line number Diff line change
@@ -1,34 +1,19 @@
from bluesky import plans as bp
from bluesky.simulators import check_limits
from ophyd import PseudoSingle, SoftPositioner
from ophyd import Component as Cpt
from ophyd.positioner import LimitError
import gi
import numpy as np
import numpy.testing
import pytest

import gi

gi.require_version("Hkl", "5.0")
# NOTE: MUST call gi.require_version() BEFORE import hkl
from hkl.geometries import E4CV


class Fourc(E4CV):
h = Cpt(PseudoSingle, "")
k = Cpt(PseudoSingle, "")
l = Cpt(PseudoSingle, "")

omega = Cpt(SoftPositioner, limits=(-180, 180))
chi = Cpt(SoftPositioner, limits=(-180, 180))
phi = Cpt(SoftPositioner, limits=(-180, 180))
tth = Cpt(SoftPositioner, limits=(-180, 180))
from hkl.geometries import SimulatedE4CV

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

for p in self.real_positioners:
p._set_position(0) # give each a starting position
class Fourc(SimulatedE4CV):
...


@pytest.fixture(scope="function")
Expand Down
Loading

0 comments on commit 9c36128

Please sign in to comment.