Files
2019-08-16 14:54:56 +02:00

481 lines
17 KiB
Python

###
# 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 diffcalc.util import command
from copy import copy
from math import pi
from diffcalc.hkl.vlieg.geometry import VliegPosition as P
SMALL = 1e-10
class Transform(object):
def transform(self, pos):
raise RuntimeError('Not implemented')
### Transforms, currently for definition and testing the theory only
class TransformC(Transform):
'''Flip omega, invert chi and flip phi
'''
def transform(self, pos):
pos = pos.clone()
pos.omega -= 180
pos.chi *= -1
pos.phi -= 180
return pos
class TransformB(Transform):
'''Flip chi, and invert and flip omega
'''
def transform(self, pos):
pos = pos.clone()
pos.chi -= 180
pos.omega = 180 - pos.omega
return pos
class TransformA(Transform):
'''Invert scattering plane: invert delta and omega and flip chi'''
def transform(self, pos):
pos = pos.clone()
pos.delta *= -1
pos.omega *= -1
pos.chi -= 180
return pos
class TransformCInRadians(Transform):
'''
Flip omega, invert chi and flip phi. Using radians and keeping
-pi<omega<=pi and 0<=phi<=360
'''
def transform(self, pos):
pos = pos.clone()
if pos.omega > 0:
pos.omega -= pi
else:
pos.omega += pi
pos.chi *= -1
pos.phi += pi
return pos
###
transformsFromSector = {
0: (),
1: ('c',),
2: ('a',),
3: ('a', 'c'),
4: ('b', 'c'),
5: ('b',),
6: ('a', 'b', 'c'),
7: ('a', 'b')
}
sectorFromTransforms = {}
for k, v in transformsFromSector.iteritems():
sectorFromTransforms[v] = k
class VliegPositionTransformer(object):
def __init__(self, geometry, hardware, solution_transformer):
self._geometry = geometry
self._hardware = hardware
self._solution_transformer = solution_transformer
solution_transformer.limitCheckerFunction = self.is_position_within_limits
def transform(self, pos):
# 1. Choose the correct sector/transforms
return self._solution_transformer.transformPosition(pos)
def is_position_within_limits(self, position):
'''where position is Position object in degrees'''
angleTuple = self._geometry.internal_position_to_physical_angles(position)
angleTuple = self._hardware.cut_angles(angleTuple)
return self._hardware.is_position_within_limits(angleTuple)
class VliegTransformSelector(object):
'''All returned angles are between -180. and 180. -180.<=angle<180.
'''
### basic sector selection
def __init__(self):
self.transforms = []
self.autotransforms = []
self.autosectors = []
self.limitCheckerFunction = None # inject
self.sector = None
self.setSector(0)
def setSector(self, sector):
if not 0 <= sector <= 7:
raise ValueError('%i must between 0 and 7.' % sector)
self.sector = sector
self.transforms = list(transformsFromSector[sector])
def setTransforms(self, transformList):
transformList = list(transformList)
transformList.sort()
self.sector = sectorFromTransforms[tuple(transformList)]
self.transforms = transformList
def addTransorm(self, transformName):
if transformName not in ('a', 'b', 'c'):
raise ValueError('%s is not a recognised transform. Try a, b or c'
% transformName)
if transformName in self.transforms:
print "WARNING, transform %s is already selected"
else:
self.setTransforms(self.transforms + [transformName])
def removeTransorm(self, transformName):
if transformName not in ('a', 'b', 'c'):
raise ValueError('%s is not a recognised transform. Try a, b or c'
% transformName)
if transformName in self.transforms:
new = copy(self.transforms)
new.remove(transformName)
self.setTransforms(new)
else:
print "WARNING, transform %s was not selected" % transformName
def addAutoTransorm(self, transformOrSector):
'''
If input is a string (letter), tags one of the transofrms as being a
candidate for auto application. If a number, tags a sector as being a
candidate for auto application, and removes similar tags for any
transforms (as the two are incompatable).
'''
if type(transformOrSector) == str:
transform = transformOrSector
if transform not in ('a', 'b', 'c'):
raise ValueError(
'%s is not a recognised transform. Try a, b or c' %
transform)
if transform not in self.autotransforms:
self.autosectors = []
self.autotransforms.append(transform)
else:
print "WARNING: %s is already set to auto apply" % transform
elif type(transformOrSector) == int:
sector = transformOrSector
if not 0 <= sector <= 7:
raise ValueError('%i must between 0 and 7.' % sector)
if sector not in self.autosectors:
self.autotransforms = []
self.autosectors.append(sector)
else:
print "WARNING: %i is already set to auto apply" % sector
else:
raise ValueError("Input must be 'a', 'b' or 'c', "
"or 1,2,3,4,5,6 or 7.")
def removeAutoTransform(self, transformOrSector):
if type(transformOrSector) == str:
transform = transformOrSector
if transform not in ('a', 'b', 'c'):
raise ValueError("%s is not a recognised transform. "
"Try a, b or c" % transform)
if transform in self.autotransforms:
self.autotransforms.remove(transform)
else:
print "WARNING: %s is not set to auto apply" % transform
elif type(transformOrSector) == int:
sector = transformOrSector
if not 0 <= sector <= 7:
raise ValueError('%i must between 0 and 7.' % sector)
if sector in self.autosectors:
self.autosectors.remove(sector)
else:
print "WARNING: %s is not set to auto apply" % sector
else:
raise ValueError("Input must be 'a', 'b' or 'c', "
"or 1,2,3,4,5,6 or 7.")
def setAutoSectors(self, sectorList):
for sector in sectorList:
if not 0 <= sector <= 7:
raise ValueError('%i must between 0 and 7.' % sector)
self.autosectors = list(sectorList)
def transformPosition(self, pos):
pos = self.transformNWithoutCut(self.sector, pos)
cutpos = self.cutPosition(pos)
# -180 <= cutpos < 180, NOT the externally applied cuts
if len(self.autosectors) > 0:
if self.is_position_within_limits(cutpos):
return cutpos
else:
return self.autoTransformPositionBySector(cutpos)
if len(self.autotransforms) > 0:
if self.is_position_within_limits(cutpos):
return cutpos
else:
return self.autoTransformPositionByTransforms(pos)
#else
return cutpos
def transformNWithoutCut(self, n, pos):
if n == 0:
return P(pos.alpha, pos.delta, pos.gamma,
pos.omega, pos.chi, pos.phi)
if n == 1:
return P(pos.alpha, pos.delta, pos.gamma,
pos.omega - 180., -pos.chi, pos.phi - 180.)
if n == 2:
return P(pos.alpha, -pos.delta, pos.gamma,
-pos.omega, pos.chi - 180., pos.phi)
if n == 3:
return P(pos.alpha, -pos.delta, pos.gamma,
180. - pos.omega, 180. - pos.chi, pos.phi - 180.)
if n == 4:
return P(pos.alpha, pos.delta, pos.gamma,
-pos.omega, 180. - pos.chi, pos.phi - 180.)
if n == 5:
return P(pos.alpha, pos.delta, pos.gamma,
180. - pos.omega, pos.chi - 180., pos.phi)
if n == 6:
return P(pos.alpha, -pos.delta, pos.gamma,
pos.omega, -pos.chi, pos.phi - 180.)
if n == 7:
return P(pos.alpha, -pos.delta, pos.gamma,
pos.omega - 180., pos.chi, pos.phi)
else:
raise Exception("sector must be between 0 and 7")
### autosector
def hasAutoSectorsOrTransformsToApply(self):
return len(self.autosectors) > 0 or len(self.autotransforms) > 0
def autoTransformPositionBySector(self, pos):
okaysectors = []
okaypositions = []
for sector in self.autosectors:
newpos = self.transformNWithoutCut(sector, pos)
if self.is_position_within_limits(newpos):
okaysectors.append(sector)
okaypositions.append(newpos)
if len(okaysectors) == 0:
raise Exception(
"Autosector could not find a sector (from %s) to move %s into "
"limits." % (self.autosectors, str(pos)))
if len(okaysectors) > 1:
print ("WARNING: Autosector found multiple sectors that would "
"move %s to move into limits: %s" % (str(pos), okaysectors))
print ("INFO: Autosector changed sector from %i to %i" %
(self.sector, okaysectors[0]))
self.sector = okaysectors[0]
return okaypositions[0]
def autoTransformPositionByTransforms(self, pos):
possibleTransforms = self.createListOfPossibleTransforms()
okaytransforms = []
okaypositions = []
for transforms in possibleTransforms:
sector = sectorFromTransforms[tuple(transforms)]
newpos = self.cutPosition(self.transformNWithoutCut(sector, pos))
if self.is_position_within_limits(newpos):
okaytransforms.append(transforms)
okaypositions.append(newpos)
if len(okaytransforms) == 0:
raise Exception(
"Autosector could not find a sector (from %r) to move %r into "
"limits." % (self.autosectors, pos))
if len(okaytransforms) > 1:
print ("WARNING: Autosector found multiple sectors that would "
"move %s to move into limits: %s" %
(repr(pos), repr(okaytransforms)))
print ("INFO: Autosector changed selected transforms from %r to %r" %
(self.transforms, okaytransforms[0]))
self.setTransforms(okaytransforms[0])
return okaypositions[0]
def createListOfPossibleTransforms(self):
def vary(possibleTransforms, name):
result = []
for transforms in possibleTransforms:
# add the original.
result.append(transforms)
# add a modified one
toadd = list(copy(transforms))
if name in transforms:
toadd.remove(name)
else:
toadd.append(name)
toadd.sort()
result.append(toadd)
return result
# start with the currently selected list of transforms
if len(self.transforms) == 0:
possibleTransforms = [()]
else:
possibleTransforms = copy(self.transforms)
for name in self.autotransforms:
possibleTransforms = vary(possibleTransforms, name)
return possibleTransforms
def is_position_within_limits(self, pos):
'''where pos os a poistion object in degrees'''
return self.limitCheckerFunction(pos)
def __repr__(self):
def createPrefix(transform):
if transform in self.transforms:
s = '*on* '
else:
s = 'off '
if len(self.autotransforms) > 0:
if transform in self.autotransforms:
s += '*auto*'
else:
s += ' '
return s
s = 'Transforms/sector:\n'
s += (' %s (a transform) Invert scattering plane: invert delta and '
'omega and flip chi\n' % createPrefix('a'))
s += (' %s (b transform) Flip chi, and invert and flip omega\n' %
createPrefix('b'))
s += (' %s (c transform) Flip omega, invert chi and flip phi\n' %
createPrefix('c'))
s += ' Current sector: %i (Spec fourc equivalent)\n' % self.sector
if len(self.autosectors) > 0:
s += ' Auto sectors: %s\n' % self.autosectors
return s
def cutPosition(self, position):
'''Cuts angles at -180.; moves each argument between -180. and 180.
'''
def cut(a):
if a is None:
return None
else:
if a < (-180. - SMALL):
return a + 360.
if a > (180. + SMALL):
return a - 360.
return a
return P(cut(position.alpha), cut(position.delta), cut(position.gamma),
cut(position.omega), cut(position.chi), cut(position.phi))
def getNameFromScannableOrString(o):
try: # it may be a scannable
return o.getName()
except AttributeError:
return str(o)
class TransformCommands(object):
def __init__(self, sector_selector):
self._sectorSelector = sector_selector
@command
def transform(self):
"""transform -- show transform configuration"""
print self._sectorSelector.__repr__()
@command
def transforma(self, *args):
"""transforma {on|off|auto|manual} -- configure transform A application
"""
self._transform('transforma', 'a', args)
@command
def transformb(self, *args):
"""transformb {on|off|auto|manual} -- configure transform B application
"""
self._transform('transformb', 'b', args)
@command
def transformc(self, *args):
"""transformc {on|off|auto|manual} -- configure transform C application
"""
self._transform('transformc', 'c', args)
def _transform(self, commandName, transformName, args):
if len(args) == 0:
print self._sectorSelector.__repr__()
return
# get name
if len(args) != 1:
raise TypeError()
if type(args[0]) is not str:
raise TypeError()
ss = self._sectorSelector
if args[0] == 'on':
ss.addTransorm(transformName)
elif args[0] == 'off':
ss.removeTransorm(transformName)
elif args[0] == 'auto':
ss.addAutoTransorm(transformName)
elif args[0] == 'manual':
ss.removeAutoTransform(transformName)
else:
raise TypeError()
print self._sectorSelector.__repr__()
@command
def sector(self, sector=None):
"""sector {0-7} -- Select or display sector (a la Spec)
"""
if sector is None:
print self._sectorSelector.__repr__()
else:
if type(sector) is not int and not (0 <= sector <= 7):
raise TypeError()
self._sectorSelector.setSector(sector)
print self._sectorSelector.__repr__()
@command
def autosector(self, *args):
"""autosector [None] [0-7] [0-7]... -- Set sectors that might be automatically applied""" #@IgnorePep8
if len(args) == 0:
print self._sectorSelector.__repr__()
elif len(args) == 1 and args[0] is None:
self._sectorSelector.setAutoSectors([])
print self._sectorSelector.__repr__()
else:
sectorList = []
for arg in args:
if type(arg) is not int:
raise TypeError()
sectorList.append(arg)
self._sectorSelector.setAutoSectors(sectorList)
print self._sectorSelector.__repr__()