Files
dev/script/__Lib/diffcalc-2.1/diffcalc/hardware.py
2019-03-20 13:52:00 +01:00

383 lines
13 KiB
Python
Executable File

###
# Copyright 2008-2011 Diamond Light Source Ltd.
# This file is part of Diffcalc.
#
# Diffcalc is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Diffcalc is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Diffcalc. If not, see <http://www.gnu.org/licenses/>.
###
from __future__ import absolute_import
from diffcalc.util import DiffcalcException
from diffcalc import settings
SMALL = 1e-8
from diffcalc.util import command
__all__ = ['hardware', 'setcut', 'setmin', 'setmax']
def getNameFromScannableOrString(o):
try: # it may be a scannable
return o.getName()
except AttributeError:
return str(o)
@command
def hardware():
"""hardware -- show diffcalc limits and cuts"""
print settings.hardware.repr_sector_limits_and_cuts() # @UndefinedVariable
@command
def setcut(scannable_or_string=None, val=None):
"""setcut {name {val}} -- sets cut angle
"""
if scannable_or_string is None and val is None:
print settings.hardware.repr_sector_limits_and_cuts() # @UndefinedVariable
else:
name = getNameFromScannableOrString(scannable_or_string)
if val is None:
print '%s: %f' % (name, settings.hardware.get_cuts()[name]) # @UndefinedVariable
else:
oldcut = settings.hardware.get_cuts()[name] # @UndefinedVariable
settings.hardware.set_cut(name, float(val)) # @UndefinedVariable
newcut = settings.hardware.get_cuts()[name] # @UndefinedVariable
@command
def setmin(name=None, val=None):
"""setmin {axis {val}} -- set lower limits used by auto sector code (None to clear)""" #@IgnorePep8
_setMinOrMax(name, val, settings.hardware.set_lower_limit) # @UndefinedVariable
@command
def setmax(name=None, val=None):
"""setmax {name {val}} -- sets upper limits used by auto sector code (None to clear)""" #@IgnorePep8
_setMinOrMax(name, val, settings.hardware.set_upper_limit) # @UndefinedVariable
@command
def setrange(name=None, lower=None, upper=None):
"""setrange {axis {min} {max}} -- set lower and upper limits used by auto sector code (None to clear)""" #@IgnorePep8
_setMinOrMax(name, lower, settings.hardware.set_lower_limit) # @UndefinedVariable
_setMinOrMax(name, upper, settings.hardware.set_upper_limit) # @UndefinedVariable
def _setMinOrMax(name, val, setMethod):
if name is None:
print settings.hardware.repr_sector_limits_and_cuts() # @UndefinedVariable
else:
name = getNameFromScannableOrString(name)
if val is None:
print settings.hardware.repr_sector_limits_and_cuts(name) # @UndefinedVariable
else:
setMethod(name, float(val))
commands_for_help = ['Hardware',
hardware,
setcut,
setmin,
setmax]
class HardwareAdapter(object):
def __init__(self, diffractometerAngleNames, defaultCuts={},
energyScannableMultiplierToGetKeV=1):
self._diffractometerAngleNames = diffractometerAngleNames
self._upperLimitDict = {}
self._lowerLimitDict = {}
self._cut_angles = {}
self._configure_cuts(defaultCuts)
self.energyScannableMultiplierToGetKeV = \
energyScannableMultiplierToGetKeV
self._name = 'base'
@property
def name(self):
return self._name
def get_axes_names(self):
return tuple(self._diffractometerAngleNames)
def get_position(self):
"""pos = get_position() -- returns the current physical diffractometer
position as a diffcalc.util object in degrees
"""
raise NotImplementedError()
def get_wavelength(self):
"""wavelength = get_wavelength() -- returns wavelength in Angstroms
"""
return 12.39842 / self.get_energy()
def get_energy(self):
"""energy = get_energy() -- returns energy in kEv """
raise NotImplementedError()
def __str__(self):
s = self.name + ":\n"
s += " energy : " + str(self.get_energy()) + " keV\n"
s += " wavelength : " + str(self.get_wavelength()) + " Angstrom\n"
names = self._diffractometerAngleNames
for name, pos in zip(names, self.get_position()):
s += " %s : %r deg\n" % (name, pos)
return s
def __repr__(self):
return self.__str__()
def get_position_by_name(self, angleName):
names = list(self._diffractometerAngleNames)
return self.get_position()[names.index(angleName)]
### Limits ###
def get_lower_limit(self, name):
'''returns lower limits by axis name. Limit may be None if not set
'''
if name not in self._diffractometerAngleNames:
raise ValueError("No angle called %s. Try one of: %s" %
(name, self._diffractometerAngleNames))
return self._lowerLimitDict.get(name)
def get_upper_limit(self, name):
'''returns upper limit by axis name. Limit may be None if not set
'''
if name not in self._diffractometerAngleNames:
raise ValueError("No angle called %s. Try one of: %s" %
name, self._diffractometerAngleNames)
return self._upperLimitDict.get(name)
def set_lower_limit(self, name, value):
"""value may be None to remove limit"""
if name not in self._diffractometerAngleNames:
raise ValueError(
"Cannot set lower Diffcalc limit: No angle called %s. Try one "
"of: %s" % (name, self._diffractometerAngleNames))
if value is None:
try:
del self._lowerLimitDict[name]
except KeyError:
print ("WARNING: There was no lower Diffcalc limit %s set to "
"clear" % name)
else:
self._lowerLimitDict[name] = value
def set_upper_limit(self, name, value):
"""value may be None to remove limit"""
if name not in self._diffractometerAngleNames:
raise ValueError(
"Cannot set upper Diffcalc limit: No angle called %s. Try one "
"of: %s" % (name, self._diffractometerAngleNames))
if value is None:
try:
del self._upperLimitDict[name]
except KeyError:
print ("WARNING: There was no upper Diffcalc limit %s set to "
"clear" % name)
else:
self._upperLimitDict[name] = value
def is_position_within_limits(self, positionArray):
"""
where position array is in degrees and cut to be between -180 and 180
"""
names = self._diffractometerAngleNames
for axis_name, value in zip(names, positionArray):
if not self.is_axis_value_within_limits(axis_name, value):
return False
return True
def is_axis_value_within_limits(self, axis_name, value):
if axis_name in self._upperLimitDict:
if value > self._upperLimitDict[axis_name]:
return False
if axis_name in self._lowerLimitDict:
if value < self._lowerLimitDict[axis_name]:
return False
return True
def repr_sector_limits_and_cuts(self, name=None):
if name is None:
s = ''
for name in self.get_axes_names():
s += self.repr_sector_limits_and_cuts(name) + '\n'
s += "Note: When auto sector/transforms are used,\n "
s += " cuts are applied before checking limits."
return s
# limits:
low = self.get_lower_limit(name)
high = self.get_upper_limit(name)
s = ' '
if low is not None:
s += "% 6.1f <= " % low
else:
s += ' ' * 10
s += '%5s' % name
if high is not None:
s += " <= % 6.1f" % high
else:
s += ' ' * 10
# cuts:
try:
if self.get_cuts()[name] is not None:
s += " (cut: % 6.1f)" % self.get_cuts()[name]
except KeyError:
pass
return s
### Cutting Stuff ###
def _configure_cuts(self, defaultCutsDict):
# 1. Set default cut angles
self._cut_angles = dict.fromkeys(self._diffractometerAngleNames, -180.)
if 'phi' in self._cut_angles:
self._cut_angles['phi'] = 0.
# 2. Overide with user-specified cuts
for name, val in defaultCutsDict.iteritems():
self.set_cut(name, val)
def set_cut(self, name, value):
if name in self._cut_angles:
self._cut_angles[name] = value
else:
raise KeyError("Diffractometer has no angle %s. Try: %s." %
(name, self._diffractometerAngleNames))
def get_cuts(self):
return self._cut_angles
def cut_angles(self, positionArray):
'''Assumes each angle in positionArray is between -360 and 360
'''
cutArray = []
names = self._diffractometerAngleNames
for axis_name, value in zip(names, positionArray):
cutArray.append(self.cut_angle(axis_name, value))
return tuple(cutArray)
def cut_angle(self, axis_name, value):
cut_angle = self._cut_angles[axis_name]
if cut_angle is None:
return value
return cut_angle_at(cut_angle, value)
def cut_angle_at(cut_angle, value):
if (cut_angle == 0 and (abs(value - 360) < SMALL) or
(abs(value + 360) < SMALL) or
(abs(value) < SMALL)):
value = 0.
if value < (cut_angle - SMALL):
return value + 360.
elif value >= cut_angle + 360. + SMALL:
return value - 360.
else:
return value
class DummyHardwareAdapter(HardwareAdapter):
def __init__(self, diffractometerAngleNames):
super(self.__class__, self).__init__(diffractometerAngleNames)
# HardwareAdapter.__init__(self, diffractometerAngleNames)
self._position = [0.] * len(diffractometerAngleNames)
self._wavelength = 1.
self.energyScannableMultiplierToGetKeV = 1
self._name = "Dummy"
# Required methods
def get_position(self):
"""
pos = getDiffractometerPosition() -- returns the current physical
diffractometer position as a list in degrees
"""
return self._position
def _set_position(self, pos):
assert len(pos) == len(self.get_axes_names()), \
"Wrong length of input list"
self._position = pos
position = property(get_position, _set_position)
def get_energy(self):
"""energy = get_energy() -- returns energy in kEv """
if self._wavelength is None:
raise DiffcalcException(
"Energy or wavelength have not been set")
return (12.39842 /
(self._wavelength * self.energyScannableMultiplierToGetKeV))
def _set_energy(self, energy):
self._wavelength = 12.39842 / energy
energy = property(get_energy, _set_energy)
def get_wavelength(self):
"""wavelength = get_wavelength() -- returns wavelength in Angstroms"""
if self._wavelength is None:
raise DiffcalcException(
"Energy or wavelength have not been set")
return self._wavelength
def _set_wavelength(self, wavelength):
self._wavelength = wavelength
wavelength = property(get_wavelength, _set_wavelength)
class ScannableHardwareAdapter(HardwareAdapter):
def __init__(self, diffractometerScannable, energyScannable,
energyScannableMultiplierToGetKeV=1):
input_names = diffractometerScannable.getInputNames()
super(self.__class__, self).__init__(input_names)
# HardwareAdapter.__init__(self, input_names)
self.diffhw = diffractometerScannable
self.energyhw = energyScannable
self.energyScannableMultiplierToGetKeV = \
energyScannableMultiplierToGetKeV
self._name = "ScannableHarwdareMonitor"
# Required methods
def get_position(self):
"""
pos = getDiffractometerPosition() -- returns the current physical
diffractometer position as a list in degrees
"""
return self.diffhw.getPosition()
def get_energy(self):
"""energy = get_energy() -- returns energy in kEv (NOT eV!) """
multiplier = self.energyScannableMultiplierToGetKeV
energy = self.energyhw.getPosition() * multiplier
if energy is None:
raise DiffcalcException("Energy has not been set")
return energy
def get_wavelength(self):
"""wavelength = get_wavelength() -- returns wavelength in Angstroms"""
energy = self.get_energy()
return 12.39842 / energy
@property
def name(self):
return self.diffhw.getName()