Startup
This commit is contained in:
0
script/__Lib/diffcalc-2.1/diffcalc/__init__.py
Executable file
0
script/__Lib/diffcalc-2.1/diffcalc/__init__.py
Executable file
0
script/__Lib/diffcalc-2.1/diffcalc/dc/__init__.py
Executable file
0
script/__Lib/diffcalc-2.1/diffcalc/dc/__init__.py
Executable file
24
script/__Lib/diffcalc-2.1/diffcalc/dc/common.py
Executable file
24
script/__Lib/diffcalc-2.1/diffcalc/dc/common.py
Executable file
@@ -0,0 +1,24 @@
|
||||
from diffcalc.util import allnum, command, DiffcalcException
|
||||
|
||||
|
||||
def sim(scn, hkl):
|
||||
"""sim hkl scn -- simulates moving scannable (not all)
|
||||
"""
|
||||
if not isinstance(hkl, (tuple, list)):
|
||||
raise TypeError()
|
||||
|
||||
if not allnum(hkl):
|
||||
raise TypeError()
|
||||
|
||||
try:
|
||||
print scn.simulateMoveTo(hkl)
|
||||
except AttributeError:
|
||||
raise TypeError(
|
||||
"The first argument does not support simulated moves")
|
||||
|
||||
def energy_to_wavelength(energy):
|
||||
try:
|
||||
return 12.39842 / energy
|
||||
except ZeroDivisionError:
|
||||
raise DiffcalcException(
|
||||
"Cannot calculate hkl position as Energy is set to 0")
|
||||
76
script/__Lib/diffcalc-2.1/diffcalc/dc/dcvlieg.py
Executable file
76
script/__Lib/diffcalc-2.1/diffcalc/dc/dcvlieg.py
Executable file
@@ -0,0 +1,76 @@
|
||||
from diffcalc.dc.common import energy_to_wavelength
|
||||
|
||||
from diffcalc import settings
|
||||
from diffcalc.hkl.vlieg.transform import VliegTransformSelector,\
|
||||
TransformCommands, VliegPositionTransformer
|
||||
from diffcalc.dc.help import compile_extra_motion_commands_for_help
|
||||
import diffcalc.hkl.vlieg.calc
|
||||
|
||||
|
||||
# reload to aid testing only
|
||||
import diffcalc.ub.ub as _ub
|
||||
reload(_ub)
|
||||
from diffcalc import hardware as _hardware
|
||||
#reload(_hardware)
|
||||
import diffcalc.hkl.vlieg.hkl as _hkl
|
||||
reload(_hkl)
|
||||
|
||||
from diffcalc.ub.ub import * # @UnusedWildImport
|
||||
from diffcalc.hardware import * # @UnusedWildImport
|
||||
from diffcalc.hkl.vlieg.hkl import * # @UnusedWildImport
|
||||
from diffcalc.gdasupport.scannable.sim import sim
|
||||
|
||||
_transform_selector = VliegTransformSelector()
|
||||
_transform_commands = TransformCommands(_transform_selector)
|
||||
_transformer = VliegPositionTransformer(settings.geometry, settings.hardware,
|
||||
_transform_selector)
|
||||
|
||||
transform = _transform_commands.transform
|
||||
transforma = _transform_commands.transforma
|
||||
transformb = _transform_commands.transformb
|
||||
transformc = _transform_commands.transformc
|
||||
|
||||
|
||||
on = 'on'
|
||||
off = 'off'
|
||||
auto = 'auto'
|
||||
manual = 'manual'
|
||||
|
||||
def hkl_to_angles(h, k, l, energy=None):
|
||||
"""Convert a given hkl vector to a set of diffractometer angles"""
|
||||
if energy is None:
|
||||
energy = settings.hardware.get_energy() # @UndefinedVariable
|
||||
|
||||
position, params = hklcalc.hklToAngles(h, k, l, energy_to_wavelength(energy))
|
||||
position = _transformer.transform(position)
|
||||
angle_tuple = settings.geometry.internal_position_to_physical_angles(position) # @UndefinedVariable
|
||||
angle_tuple = settings.hardware.cut_angles(angle_tuple) # @UndefinedVariable
|
||||
|
||||
return angle_tuple, params
|
||||
|
||||
|
||||
def angles_to_hkl(angleTuple, energy=None):
|
||||
"""Converts a set of diffractometer angles to an hkl position
|
||||
((h, k, l), paramDict)=angles_to_hkl(self, (a1, a2,aN), energy=None)"""
|
||||
if energy is None:
|
||||
energy = settings.hardware.get_energy() # @UndefinedVariable
|
||||
|
||||
i_pos = settings.geometry.physical_angles_to_internal_position(angleTuple) # @UndefinedVariable
|
||||
return hklcalc.anglesToHkl(i_pos, energy_to_wavelength(energy))
|
||||
|
||||
|
||||
settings.ubcalc_strategy = diffcalc.hkl.vlieg.calc.VliegUbCalcStrategy()
|
||||
settings.angles_to_hkl_function = diffcalc.hkl.vlieg.calc.vliegAnglesToHkl
|
||||
settings.include_sigtau = True
|
||||
|
||||
ub_commands_for_help = _ub.commands_for_help
|
||||
|
||||
hkl_commands_for_help = (_hkl.commands_for_help +
|
||||
_hardware.commands_for_help +
|
||||
['Transform',
|
||||
transform,
|
||||
transforma,
|
||||
transformb,
|
||||
transformc] +
|
||||
compile_extra_motion_commands_for_help())
|
||||
|
||||
47
script/__Lib/diffcalc-2.1/diffcalc/dc/dcwillmot.py
Executable file
47
script/__Lib/diffcalc-2.1/diffcalc/dc/dcwillmot.py
Executable file
@@ -0,0 +1,47 @@
|
||||
# This file differs from dcyou in only two places
|
||||
|
||||
from diffcalc import settings
|
||||
from diffcalc.dc.common import energy_to_wavelength
|
||||
from diffcalc.dc.help import compile_extra_motion_commands_for_help
|
||||
import diffcalc.hkl.willmott.calc
|
||||
|
||||
|
||||
# reload to aid testing only
|
||||
from diffcalc.ub import ub as _ub
|
||||
reload(_ub)
|
||||
from diffcalc import hardware as _hardware
|
||||
#reload(_hardware)
|
||||
from diffcalc.hkl.you import hkl as _hkl
|
||||
reload(_hkl)
|
||||
|
||||
from diffcalc.ub.ub import * # @UnusedWildImport
|
||||
from diffcalc.hardware import * # @UnusedWildImport
|
||||
from diffcalc.hkl.willmot.hkl import * # @UnusedWildImport
|
||||
from diffcalc.gdasupport.scannable.sim import sim
|
||||
|
||||
def hkl_to_angles(h, k, l, energy=None):
|
||||
"""Convert a given hkl vector to a set of diffractometer angles"""
|
||||
if energy is None:
|
||||
energy = settings.hardware.get_energy() # @UndefinedVariable
|
||||
|
||||
(pos, params) = hklcalc.hklToAngles(h, k, l, energy_to_wavelength(energy))
|
||||
angle_tuple = settings.geometry.internal_position_to_physical_angles(pos) # @UndefinedVariable
|
||||
angle_tuple = settings.hardware.cut_angles(angle_tuple) # @UndefinedVariable
|
||||
|
||||
return angle_tuple, params
|
||||
|
||||
def angles_to_hkl(angleTuple, energy=None):
|
||||
"""Converts a set of diffractometer angles to an hkl position
|
||||
((h, k, l), paramDict)=angles_to_hkl(self, (a1, a2,aN), energy=None)"""
|
||||
if energy is None:
|
||||
energy = settings.hardware.get_energy() # @UndefinedVariable
|
||||
i_pos = settings.geometry.physical_angles_to_internal_position(angleTuple) # @UndefinedVariable
|
||||
return hklcalc.anglesToHkl(i_pos, energy_to_wavelength(energy))
|
||||
|
||||
settings.ubcalc_strategy = diffcalc.hkl.willmott.calc.WillmottHorizontalUbCalcStrategy()
|
||||
settings.angles_to_hkl_function = diffcalc.hkl.willmott.calc.angles_to_hkl
|
||||
|
||||
|
||||
ub_commands_for_help = _ub.commands_for_help
|
||||
|
||||
hkl_commands_for_help = _hkl.commands_for_help + _hardware.commands_for_help + compile_extra_motion_commands_for_help()
|
||||
56
script/__Lib/diffcalc-2.1/diffcalc/dc/dcyou.py
Executable file
56
script/__Lib/diffcalc-2.1/diffcalc/dc/dcyou.py
Executable file
@@ -0,0 +1,56 @@
|
||||
from diffcalc import settings
|
||||
from diffcalc.dc.common import energy_to_wavelength
|
||||
from diffcalc.dc.help import compile_extra_motion_commands_for_help
|
||||
|
||||
import diffcalc.hkl.you.calc
|
||||
settings.ubcalc_strategy = diffcalc.hkl.you.calc.YouUbCalcStrategy()
|
||||
settings.angles_to_hkl_function = diffcalc.hkl.you.calc.youAnglesToHkl
|
||||
settings.include_reference = True
|
||||
|
||||
# reload to aid testing only
|
||||
from diffcalc.ub import ub as _ub
|
||||
|
||||
reload(_ub)
|
||||
from diffcalc import hardware as _hardware
|
||||
#reload(_hardware)
|
||||
from diffcalc.hkl.you import hkl as _hkl
|
||||
reload(_hkl)
|
||||
|
||||
from diffcalc.ub.ub import * # @UnusedWildImport
|
||||
from diffcalc.hardware import * # @UnusedWildImport
|
||||
from diffcalc.hkl.you.hkl import * # @UnusedWildImport
|
||||
|
||||
|
||||
def hkl_to_angles(h, k, l, energy=None):
|
||||
"""Convert a given hkl vector to a set of diffractometer angles
|
||||
|
||||
return angle tuple and params dictionary
|
||||
|
||||
"""
|
||||
if energy is None:
|
||||
energy = settings.hardware.get_energy() # @UndefinedVariable
|
||||
|
||||
(pos, params) = hklcalc.hklToAngles(h, k, l, energy_to_wavelength(energy))
|
||||
angle_tuple = settings.geometry.internal_position_to_physical_angles(pos) # @UndefinedVariable
|
||||
angle_tuple = settings.hardware.cut_angles(angle_tuple) # @UndefinedVariable
|
||||
|
||||
return angle_tuple, params
|
||||
|
||||
|
||||
def angles_to_hkl(angleTuple, energy=None):
|
||||
"""Converts a set of diffractometer angles to an hkl position
|
||||
|
||||
Return hkl tuple and params dictionary
|
||||
|
||||
"""
|
||||
if energy is None:
|
||||
energy = settings.hardware.get_energy() # @UndefinedVariable
|
||||
i_pos = settings.geometry.physical_angles_to_internal_position(angleTuple) # @UndefinedVariable
|
||||
return hklcalc.anglesToHkl(i_pos, energy_to_wavelength(energy))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
ub_commands_for_help = _ub.commands_for_help
|
||||
hkl_commands_for_help = _hkl.commands_for_help + _hardware.commands_for_help + compile_extra_motion_commands_for_help()
|
||||
161
script/__Lib/diffcalc-2.1/diffcalc/dc/help.py
Executable file
161
script/__Lib/diffcalc-2.1/diffcalc/dc/help.py
Executable file
@@ -0,0 +1,161 @@
|
||||
'''
|
||||
Created on 6 May 2016
|
||||
|
||||
@author: walton
|
||||
'''
|
||||
from diffcalc import settings
|
||||
from diffcalc.gdasupport.scannable.sim import sim
|
||||
import textwrap
|
||||
from diffcalc.util import bold
|
||||
|
||||
|
||||
class ExternalCommand(object):
|
||||
"""Instances found in a command_list by format_command_help will
|
||||
result in documentation for a command without there actually being one.
|
||||
"""
|
||||
def __init__(self, docstring):
|
||||
"""Set the docstring that will be pulled off by format_command_help.
|
||||
"""
|
||||
self.__doc__ = docstring
|
||||
self.__name__ = ''
|
||||
|
||||
|
||||
WIDTH = 27
|
||||
INDENT = 3
|
||||
|
||||
|
||||
def format_command_help(command_list):
|
||||
|
||||
row_list = _command_list_to_table_cells(command_list)
|
||||
lines = []
|
||||
for row_cells in row_list:
|
||||
if len(row_cells) == 1:
|
||||
heading = row_cells[0]
|
||||
lines.append('')
|
||||
lines.append(bold(heading))
|
||||
lines.append('')
|
||||
elif len(row_cells) == 2:
|
||||
cell1, cell2 = row_cells
|
||||
|
||||
cell1_lines = textwrap.wrap(cell1, WIDTH, subsequent_indent=' ')
|
||||
cell2_lines = textwrap.wrap(cell2, 79 - INDENT - 3 - WIDTH)
|
||||
|
||||
first_line = True
|
||||
while cell1_lines or cell2_lines:
|
||||
line = ' ' * INDENT
|
||||
if cell1_lines:
|
||||
line += cell1_lines.pop(0).ljust(WIDTH)
|
||||
else:
|
||||
line += ' ' * (WIDTH)
|
||||
line += ' : ' if first_line else ' '
|
||||
if cell2_lines:
|
||||
line += cell2_lines.pop(0)
|
||||
lines.append(line)
|
||||
first_line = False
|
||||
|
||||
return '\n'.join(lines)
|
||||
|
||||
|
||||
def format_commands_for_rst_table(title, command_list):
|
||||
W1 = WIDTH # internal width
|
||||
W2 = 79 - W1 - 3 # internal width
|
||||
HORIZ_LINE = '+-' + '-' * W1 + '-+-' + '-' * W2 + '-+'
|
||||
|
||||
row_list = _command_list_to_table_cells(command_list)
|
||||
|
||||
lines = []
|
||||
|
||||
lines.append(HORIZ_LINE) # Top line
|
||||
for row_cells in row_list:
|
||||
if len(row_cells) == 1:
|
||||
lines.append('| ' + ('**' + row_cells[0] + '**').ljust(W1 + W2 + 3) + ' |')
|
||||
|
||||
elif len(row_cells) == 2:
|
||||
cmd_and_args = row_cells[0].split(' ', 1)
|
||||
cmd = cmd_and_args[0]
|
||||
args = cmd_and_args[1] if len(cmd_and_args) == 2 else ''
|
||||
cell1 = '**-- %s** %s' % (cmd, args)
|
||||
cell1_lines = textwrap.wrap(cell1, W1) #, subsequent_indent=' ')
|
||||
cell2_lines = textwrap.wrap(row_cells[1], W2)
|
||||
|
||||
while cell1_lines or cell2_lines:
|
||||
line = '| '
|
||||
line += (cell1_lines.pop(0) if cell1_lines else '').ljust(W1)
|
||||
line += ' | '
|
||||
line += (cell2_lines.pop(0) if cell2_lines else '').ljust(W2)
|
||||
line += ' |'
|
||||
lines.append(line)
|
||||
|
||||
else:
|
||||
assert False
|
||||
|
||||
lines.append(HORIZ_LINE)
|
||||
return lines
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def _command_list_to_table_cells(command_list):
|
||||
row_list = []
|
||||
for obj in command_list:
|
||||
|
||||
if isinstance(obj, basestring): # group heading
|
||||
row_list.append([obj.upper()])
|
||||
|
||||
else: # individual command
|
||||
doc_before_empty_line = obj.__doc__.split('\n\n')[0]
|
||||
doc_lines = [s.strip() for s in doc_before_empty_line.split('\n')]
|
||||
for doc_line in doc_lines:
|
||||
if doc_line == '':
|
||||
continue
|
||||
if obj.__name__ in ('ub', 'hkl'):
|
||||
continue
|
||||
name, args, desc = _split_doc_line(doc_line)
|
||||
desc = desc.strip()
|
||||
args = args.strip()
|
||||
if desc and desc[-1] == '.':
|
||||
desc = desc[:-1]
|
||||
|
||||
row_list.append([name + (' ' if args else '') + args, desc])
|
||||
|
||||
return row_list
|
||||
|
||||
|
||||
def _split_doc_line(docLine):
|
||||
name, _, right = docLine.partition(' ')
|
||||
args, _, desc = right.partition('-- ')
|
||||
return name, args, desc
|
||||
|
||||
|
||||
def compile_extra_motion_commands_for_help():
|
||||
|
||||
_hwname = settings.hardware.name # @UndefinedVariable
|
||||
_angles = ', '.join(settings.hardware.get_axes_names()) # @UndefinedVariable
|
||||
|
||||
commands = []
|
||||
|
||||
commands.append('Motion')
|
||||
commands.append(sim)
|
||||
commands.append(ExternalCommand(
|
||||
'%(_hwname)s -- show Eularian position' % vars()))
|
||||
commands.append(ExternalCommand(
|
||||
'pos %(_hwname)s [%(_angles)s] -- move to Eularian position'
|
||||
'(None holds an axis still)' % vars()))
|
||||
commands.append(ExternalCommand(
|
||||
'sim %(_hwname)s [%(_angles)s] -- simulate move to Eulerian position'
|
||||
'%(_hwname)s' % vars()))
|
||||
|
||||
commands.append(ExternalCommand(
|
||||
'hkl -- show hkl position'))
|
||||
commands.append(ExternalCommand(
|
||||
'pos hkl [h k l] -- move to hkl position'))
|
||||
commands.append(ExternalCommand(
|
||||
'pos {h | k | l} val -- move h, k or l to val'))
|
||||
commands.append(ExternalCommand(
|
||||
'sim hkl [h k l] -- simulate move to hkl position'))
|
||||
|
||||
# if engine_name != 'vlieg':
|
||||
# pass
|
||||
# # TODO: remove sigtau command and 'Surface' string
|
||||
return commands
|
||||
0
script/__Lib/diffcalc-2.1/diffcalc/gdasupport/__init__.py
Executable file
0
script/__Lib/diffcalc-2.1/diffcalc/gdasupport/__init__.py
Executable file
0
script/__Lib/diffcalc-2.1/diffcalc/gdasupport/minigda/__init__.py
Executable file
0
script/__Lib/diffcalc-2.1/diffcalc/gdasupport/minigda/__init__.py
Executable file
322
script/__Lib/diffcalc-2.1/diffcalc/gdasupport/minigda/command.py
Executable file
322
script/__Lib/diffcalc-2.1/diffcalc/gdasupport/minigda/command.py
Executable file
@@ -0,0 +1,322 @@
|
||||
###
|
||||
# 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/>.
|
||||
###
|
||||
|
||||
#try:
|
||||
# from gda.device import Scannable
|
||||
#except ImportError:
|
||||
# from diffcalc.gdasupport.minigda.scannable import Scannable
|
||||
from diffcalc.gdasupport.minigda.scannable import Scannable
|
||||
from diffcalc.util import getMessageFromException, allnum, bold
|
||||
import math
|
||||
|
||||
|
||||
ROOT_NAMESPACE_DICT = {}
|
||||
|
||||
class Pos(object):
|
||||
|
||||
def __init__(self):
|
||||
self.__name__ = 'pos'
|
||||
|
||||
def __call__(self, *posargs):
|
||||
if len(posargs) == 0:
|
||||
|
||||
keys = dict(ROOT_NAMESPACE_DICT).keys()
|
||||
keys.sort()
|
||||
for key in keys:
|
||||
val = ROOT_NAMESPACE_DICT[key]
|
||||
if isinstance(val, Scannable):
|
||||
print self.posReturningReport(val)
|
||||
else:
|
||||
print self.posReturningReport(*posargs)
|
||||
|
||||
def posReturningReport(self, *posargs):
|
||||
# report position of this scannable
|
||||
if len(posargs) == 1:
|
||||
scannable = posargs[0]
|
||||
self._assert_scannable(scannable)
|
||||
return self._generatePositionReport(scannable)
|
||||
|
||||
# Move the scannable and report
|
||||
elif len(posargs) == 2:
|
||||
scannable = posargs[0]
|
||||
self._assert_scannable(scannable)
|
||||
# Move it
|
||||
scannable.asynchronousMoveTo(posargs[1])
|
||||
# TODO: minigda assumes all moves complete instantly, so no need
|
||||
# yet to check the move is complete
|
||||
return self._generatePositionReport(scannable)
|
||||
|
||||
else:
|
||||
raise ValueError(
|
||||
"Invlaid arguements: 'pos [ scannable [ value ] ]'")
|
||||
|
||||
def _assert_scannable(self, obj):
|
||||
if not isinstance(obj, Scannable):
|
||||
raise TypeError(
|
||||
"The first argument to the pos command must be scannable. "
|
||||
"Not: " + str(type(obj)))
|
||||
|
||||
def _generatePositionReport(self, scannable):
|
||||
fieldNames = (tuple(scannable.getInputNames()) +
|
||||
tuple(scannable.getExtraNames()))
|
||||
# All scannables
|
||||
result = "%s:" % scannable.getName()
|
||||
result = result.ljust(10)
|
||||
try:
|
||||
pos = scannable.getPosition()
|
||||
except Exception, e:
|
||||
return result + "Error: %s" % getMessageFromException(e)
|
||||
if pos is None:
|
||||
return result + "---"
|
||||
# Single field scannable:
|
||||
if len(fieldNames) == 1:
|
||||
try:
|
||||
result += "%s" % scannable.formatPositionFields(pos)[0]
|
||||
except AttributeError:
|
||||
result += str(scannable())
|
||||
# Multi field scannable:
|
||||
else:
|
||||
try:
|
||||
formatted = scannable.formatPositionFields(pos)
|
||||
for name, formattedValue in zip(fieldNames, formatted):
|
||||
result += "%s: %s " % (name, formattedValue)
|
||||
except AttributeError:
|
||||
result += str(scannable())
|
||||
|
||||
return result
|
||||
|
||||
|
||||
class ScanDataHandler:
|
||||
def __init__(self):
|
||||
self.scannables = None
|
||||
|
||||
def callAtScanStart(self, scannables):
|
||||
pass
|
||||
|
||||
def callWithScanPoint(self, PositionDictIndexedByScannable):
|
||||
pass
|
||||
|
||||
def callAtScanEnd(self):
|
||||
pass
|
||||
|
||||
|
||||
class ScanDataPrinter(ScanDataHandler):
|
||||
|
||||
def __init__(self):
|
||||
self.first_point_printed = False
|
||||
self.widths = []
|
||||
self.scannables = []
|
||||
|
||||
def callAtScanStart(self, scannables):
|
||||
self.first_point_printed = False
|
||||
self.scannables = scannables
|
||||
|
||||
def print_first_point(self, position_dict):
|
||||
# also sets self.widths
|
||||
header_strings = []
|
||||
for scn in self.scannables:
|
||||
field_names = list(scn.getInputNames()) + list(scn.getExtraNames())
|
||||
if len(field_names) == 1:
|
||||
header_strings.append(scn.getName())
|
||||
else:
|
||||
for field_name in field_names:
|
||||
header_strings.append(field_name)
|
||||
|
||||
first_row_strings = []
|
||||
for scn in self.scannables:
|
||||
pos = position_dict[scn]
|
||||
first_row_strings.extend(scn.formatPositionFields(pos))
|
||||
|
||||
self.widths = []
|
||||
for header, pos_string in zip(header_strings, first_row_strings):
|
||||
self.widths.append(max(len(header), len(pos_string)))
|
||||
|
||||
header_cells = []
|
||||
for heading, width in zip(header_strings, self.widths):
|
||||
header_cells.append(heading.rjust(width))
|
||||
|
||||
underline_cells = ['-' * w for w in self.widths]
|
||||
|
||||
first_row_cells = []
|
||||
for pos, width in zip(first_row_strings, self.widths):
|
||||
first_row_cells.append(pos.rjust(width))
|
||||
|
||||
#table_width = sum(self.widths) + len(self.widths * 2) - 2
|
||||
lines = []
|
||||
#lines.append('=' * table_width)
|
||||
lines.append(bold(' '.join(header_cells)))
|
||||
lines.append(' '.join(underline_cells))
|
||||
lines.append(' '.join(first_row_cells))
|
||||
print '\n'.join(lines)
|
||||
|
||||
def callWithScanPoint(self, position_dict):
|
||||
if not self.first_point_printed:
|
||||
self.print_first_point(position_dict)
|
||||
self.first_point_printed = True
|
||||
else:
|
||||
row_strings = []
|
||||
for scn in self.scannables:
|
||||
pos = position_dict[scn]
|
||||
row_strings.extend(scn.formatPositionFields(pos))
|
||||
|
||||
row_cells = []
|
||||
for pos, width in zip(row_strings, self.widths):
|
||||
row_cells.append(pos.rjust(width))
|
||||
|
||||
print ' '.join(row_cells)
|
||||
|
||||
def callAtScanEnd(self):
|
||||
#table_width = sum(self.widths) + len(self.widths * 2) - 2
|
||||
#print '=' * table_width
|
||||
pass
|
||||
|
||||
|
||||
class Scan(object):
|
||||
class Group:
|
||||
def __init__(self, scannable):
|
||||
self.scannable = scannable
|
||||
self.args = []
|
||||
|
||||
def __cmp__(self, other):
|
||||
return(self.scannable.getLevel() - other.scannable.getLevel())
|
||||
|
||||
def __repr__(self):
|
||||
return "Group(%s, %s)" % (self.scannable.getName(), str(self.args))
|
||||
|
||||
def shouldTriggerLoop(self):
|
||||
return len(self.args) == 3
|
||||
|
||||
def __init__(self, scanDataHandlers):
|
||||
# scanDataHandlers should be list
|
||||
if type(scanDataHandlers) not in (tuple, list):
|
||||
scanDataHandlers = (scanDataHandlers,)
|
||||
self.dataHandlers = scanDataHandlers
|
||||
|
||||
def __call__(self, *scanargs):
|
||||
groups = self._parseScanArgsIntoScannableArgGroups(scanargs)
|
||||
groups = self._reorderInnerGroupsAccordingToLevel(groups)
|
||||
# Configure data handlers for a new scan
|
||||
for handler in self.dataHandlers: handler.callAtScanStart(
|
||||
[grp.scannable for grp in groups])
|
||||
# Perform the scan
|
||||
self._performScan(groups, currentRecursionLevel=0)
|
||||
# Inform data handlers of scan completion
|
||||
for handler in self.dataHandlers: handler.callAtScanEnd()
|
||||
|
||||
def _parseScanArgsIntoScannableArgGroups(self, scanargs):
|
||||
"""
|
||||
-> [ Group(scnA, (a1, a2, a2)), Group((scnB), (b1)), ...
|
||||
... Group((scnC),()), Group((scnD),(d1))]
|
||||
"""
|
||||
result = []
|
||||
if not isinstance(scanargs[0], Scannable):
|
||||
raise TypeError("First scan argument must be a scannable")
|
||||
|
||||
# Parse out scannables followed by non-scannable args
|
||||
for arg in scanargs:
|
||||
if isinstance(arg, Scannable):
|
||||
result.append(Scan.Group(arg))
|
||||
else:
|
||||
result[-1].args.append(arg)
|
||||
return result
|
||||
|
||||
def _reorderInnerGroupsAccordingToLevel(self, groups):
|
||||
# Find the first group not to trigger a loop
|
||||
for idx, group in enumerate(groups):
|
||||
if not group.shouldTriggerLoop():
|
||||
break
|
||||
latter = groups[idx:]; latter.sort() # Horrible hack not needed in python 3!
|
||||
return groups[:idx] + latter
|
||||
|
||||
def _performScan(self, groups, currentRecursionLevel):
|
||||
# groups[currentRecursionLevel:] will start with either:
|
||||
# a) A loop triggering group
|
||||
# b) A number (possibly 0) of non-loop triggering groups
|
||||
unprocessedGroups = groups[currentRecursionLevel:]
|
||||
|
||||
# 1) If first remaining group should trigger a loop, perform this loop,
|
||||
# recursively calling this method on the remaining groups
|
||||
if len(unprocessedGroups) > 0:
|
||||
first = unprocessedGroups[0]
|
||||
# If groups starts with a request to loop:
|
||||
if first.shouldTriggerLoop():
|
||||
posList = self._frange(first.args[0], first.args[1], first.args[2])
|
||||
for pos in posList:
|
||||
first.scannable.asynchronousMoveTo(pos)
|
||||
# TODO: Should wait. minigda assumes all moves complete immediately
|
||||
self._performScan(groups, currentRecursionLevel + 1)
|
||||
return
|
||||
|
||||
# 2) Move all non-loop triggering groups (may be zero)
|
||||
self._moveNonLoopTriggeringGroups(unprocessedGroups)
|
||||
|
||||
# 3) Sample position of all scannables
|
||||
posDict = self._samplePositionsOfAllScannables(groups)
|
||||
|
||||
# 4) Inform the data handlers that this point has been recorded
|
||||
for handler in self.dataHandlers: handler.callWithScanPoint(posDict)
|
||||
|
||||
def _moveNonLoopTriggeringGroups(self, groups):
|
||||
# TODO: Should wait. minigda assumes all moves complete immediately. groups could be zero lengthed.
|
||||
for grp in groups:
|
||||
if len(grp.args) == 0:
|
||||
pass
|
||||
elif len(grp.args) == 1:
|
||||
grp.scannable.asynchronousMoveTo(grp.args[0])
|
||||
elif len(grp.args) == 2:
|
||||
raise Exception("Scannables followed by two args not supported by minigda's scan command ")
|
||||
else:
|
||||
raise Exception("Scannable: %s args%s" % (grp.scannable, str(grp.args)))
|
||||
|
||||
def _samplePositionsOfAllScannables(self, groups):
|
||||
posDict = {}
|
||||
for grp in groups:
|
||||
posDict[grp.scannable] = grp.scannable.getPosition()
|
||||
return posDict
|
||||
|
||||
def _frange(self, limit1, limit2, increment):
|
||||
"""Range function that accepts floats (and integers).
|
||||
"""
|
||||
# limit1 = float(limit1)
|
||||
# limit2 = float(limit2)
|
||||
try:
|
||||
increment = float(increment)
|
||||
except TypeError:
|
||||
raise TypeError(
|
||||
"Only scaler values are supported, not GDA format vectors.")
|
||||
count = int(math.ceil(((limit2 - limit1) + increment / 100.) / increment))
|
||||
result = []
|
||||
for n in range(count):
|
||||
result.append(limit1 + n * increment)
|
||||
return result
|
||||
|
||||
|
||||
def sim(scn, hkl):
|
||||
"""sim hkl scn -- simulates moving scannable (not all)
|
||||
"""
|
||||
if not isinstance(hkl, (tuple, list)):
|
||||
raise TypeError()
|
||||
|
||||
if not allnum(hkl):
|
||||
raise TypeError()
|
||||
|
||||
try:
|
||||
print scn.simulateMoveTo(hkl)
|
||||
except AttributeError:
|
||||
raise TypeError(
|
||||
"The first argument does not support simulated moves")
|
||||
511
script/__Lib/diffcalc-2.1/diffcalc/gdasupport/minigda/scannable.py
Executable file
511
script/__Lib/diffcalc-2.1/diffcalc/gdasupport/minigda/scannable.py
Executable file
@@ -0,0 +1,511 @@
|
||||
###
|
||||
# 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/>.
|
||||
###
|
||||
|
||||
import time
|
||||
|
||||
try:
|
||||
from gda.device.scannable import ScannableBase
|
||||
except ImportError:
|
||||
class Scannable(object):
|
||||
pass
|
||||
|
||||
class ScannableBase(Scannable):
|
||||
"""Implemtation of a subset of OpenGDA's Scannable interface
|
||||
"""
|
||||
|
||||
level = 5
|
||||
inputNames = []
|
||||
extraNames = []
|
||||
outputFormat = []
|
||||
|
||||
def isBusy(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
def rawGetPosition(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
def rawAsynchronousMoveTo(self, newpos):
|
||||
raise NotImplementedError()
|
||||
|
||||
def waitWhileBusy(self):
|
||||
while self.isBusy():
|
||||
time.sleep(.1)
|
||||
|
||||
def getPosition(self):
|
||||
return self.rawGetPosition()
|
||||
|
||||
def asynchronousMoveTo(self, newpos):
|
||||
self.rawAsynchronousMoveTo(newpos)
|
||||
|
||||
def atScanStart(self):
|
||||
pass
|
||||
|
||||
def atScanEnd(self):
|
||||
pass
|
||||
|
||||
def atCommandFailure(self):
|
||||
pass
|
||||
|
||||
###
|
||||
|
||||
def __repr__(self):
|
||||
pos = self.getPosition()
|
||||
formattedValues = self.formatPositionFields(pos)
|
||||
if len(tuple(self.getInputNames()) + tuple(self.getExtraNames())) > 1:
|
||||
result = self.getName() + ': '
|
||||
else:
|
||||
result = ''
|
||||
|
||||
names = tuple(self.getInputNames()) + tuple(self.getExtraNames())
|
||||
for name, val in zip(names, formattedValues):
|
||||
result += ' ' + name + ': ' + val
|
||||
return result
|
||||
###
|
||||
|
||||
def formatPositionFields(self, pos):
|
||||
"""Returns position as array of formatted strings"""
|
||||
# Make sure pos is a tuple or list
|
||||
if type(pos) not in (tuple, list):
|
||||
pos = tuple([pos])
|
||||
|
||||
# Sanity check
|
||||
if len(pos) != len(self.getOutputFormat()):
|
||||
raise Exception(
|
||||
"In scannable '%s':number of position fields differs from "
|
||||
"number format strings specified" % self.getName())
|
||||
|
||||
result = []
|
||||
for field, format in zip(pos, self.getOutputFormat()):
|
||||
if field is None:
|
||||
result.append('???')
|
||||
else:
|
||||
s = (format % field)
|
||||
## if width!=None:
|
||||
## s = s.ljust(width)
|
||||
result.append(s)
|
||||
|
||||
return result
|
||||
|
||||
def getName(self):
|
||||
return self.name
|
||||
|
||||
def setName(self, value):
|
||||
self.name = value
|
||||
|
||||
def getLevel(self):
|
||||
return self.level
|
||||
|
||||
def setLevel(self, value):
|
||||
self.level = value
|
||||
|
||||
def getInputNames(self):
|
||||
return self.inputNames
|
||||
|
||||
def setInputNames(self, value):
|
||||
self.inputNames = value
|
||||
|
||||
def getExtraNames(self):
|
||||
return self.extraNames
|
||||
|
||||
def setExtraNames(self, value):
|
||||
self.extraNames = value
|
||||
|
||||
def getOutputFormat(self):
|
||||
return self.outputFormat
|
||||
|
||||
def setOutputFormat(self, value):
|
||||
if type(value) not in (tuple, list):
|
||||
raise TypeError(
|
||||
"%s.setOutputFormat() expects tuple or list; not %s" %
|
||||
(self.getName(), str(type(value))))
|
||||
self.outputFormat = value
|
||||
|
||||
def __call__(self, newpos=None):
|
||||
if newpos is None:
|
||||
return self.getPosition()
|
||||
self.asynchronousMoveTo(newpos)
|
||||
|
||||
class ScannableAdapter(Scannable):
|
||||
'''Wrap up a Scannable and give it a new name and optionally an offset
|
||||
(added to the delegate when reading up and subtracting when setting down
|
||||
'''
|
||||
|
||||
def __init__(self, delegate_scn, name, offset=0):
|
||||
assert len(delegate_scn.getInputNames()) == 1
|
||||
assert len(delegate_scn.getExtraNames()) == 0
|
||||
self.delegate_scn = delegate_scn
|
||||
self.name = name
|
||||
self.offset = offset
|
||||
|
||||
def __getattr__(self, name):
|
||||
return getattr(self.delegate_scn, name)
|
||||
|
||||
def getName(self):
|
||||
return self.name
|
||||
|
||||
def getInputNames(self):
|
||||
return [self.name]
|
||||
|
||||
def getPosition(self):
|
||||
return self.delegate_scn.getPosition() + self.offset
|
||||
|
||||
def asynchronousMoveTo(self, newpos):
|
||||
self.delegate_scn.asynchronousMoveTo(newpos - self.offset)
|
||||
|
||||
def __repr__(self):
|
||||
pos = self.getPosition()
|
||||
formatted_values = self.delegate_scn.formatPositionFields(pos)
|
||||
return self.name + ': ' + formatted_values[0] + ' ' + self.get_hint()
|
||||
|
||||
def get_hint(self):
|
||||
if self.offset:
|
||||
offset_hint = ' + ' if self.offset >= 0 else ' - '
|
||||
offset_hint += str(self.offset)
|
||||
else:
|
||||
offset_hint = ''
|
||||
return '(%s%s)' % (self.delegate_scn.name, offset_hint)
|
||||
|
||||
def __call__(self, newpos=None):
|
||||
if newpos is None:
|
||||
return self.getPosition()
|
||||
self.asynchronousMoveTo(newpos)
|
||||
|
||||
class SingleFieldDummyScannable(ScannableBase):
|
||||
|
||||
def __init__(self, name, initial_position=0.):
|
||||
self.name = name
|
||||
self.inputNames = [name]
|
||||
self.outputFormat = ['% 6.4f']
|
||||
self.level = 3
|
||||
self._current_position = float(initial_position)
|
||||
|
||||
def isBusy(self):
|
||||
return False
|
||||
|
||||
def waitWhileBusy(self):
|
||||
return
|
||||
|
||||
def asynchronousMoveTo(self, new_position):
|
||||
self._current_position = float(new_position)
|
||||
|
||||
def getPosition(self):
|
||||
return self._current_position
|
||||
|
||||
|
||||
class DummyPD(SingleFieldDummyScannable):
|
||||
"""For compatability with the gda's dummy_pd module"""
|
||||
pass
|
||||
|
||||
|
||||
class MultiInputExtraFieldsDummyScannable(ScannableBase):
|
||||
'''Multi input Dummy PD Class supporting input and extra fields'''
|
||||
def __init__(self, name, inputNames, extraNames):
|
||||
self.setName(name)
|
||||
self.setInputNames(inputNames)
|
||||
self.setExtraNames(extraNames)
|
||||
self.setOutputFormat(['%6.4f'] * (len(inputNames) + len(extraNames)))
|
||||
self.setLevel(3)
|
||||
self.currentposition = [0.0] * len(inputNames)
|
||||
|
||||
def isBusy(self):
|
||||
return 0
|
||||
|
||||
def asynchronousMoveTo(self, new_position):
|
||||
if type(new_position) == type(1) or type(new_position) == type(1.0):
|
||||
new_position = [new_position]
|
||||
msg = "Wrong new_position size"
|
||||
assert len(new_position) == len(self.currentposition), msg
|
||||
for i in range(len(new_position)):
|
||||
if new_position[i] != None:
|
||||
self.currentposition[i] = float(new_position[i])
|
||||
|
||||
def getPosition(self):
|
||||
extraValues = range(100, 100 + (len(self.getExtraNames())))
|
||||
return self.currentposition + map(float, extraValues)
|
||||
|
||||
|
||||
class ZeroInputExtraFieldsDummyScannable(ScannableBase):
|
||||
'''Zero input/extra field dummy pd
|
||||
'''
|
||||
def __init__(self, name):
|
||||
self.setName(name)
|
||||
self.setInputNames([])
|
||||
self.setOutputFormat([])
|
||||
|
||||
def isBusy(self):
|
||||
return 0
|
||||
|
||||
def asynchronousMoveTo(self, new_position):
|
||||
pass
|
||||
|
||||
def getPosition(self):
|
||||
pass
|
||||
|
||||
|
||||
class ScannableGroup(ScannableBase):
|
||||
"""wraps up motors. Simulates motors if non given."""
|
||||
|
||||
def __init__(self, name, motorList):
|
||||
|
||||
self.setName(name)
|
||||
# Set input format
|
||||
motorNames = []
|
||||
for scn in motorList:
|
||||
motorNames.append(scn.getName())
|
||||
self.setInputNames(motorNames)
|
||||
# Set output format
|
||||
format = []
|
||||
for motor in motorList:
|
||||
format.append(motor.getOutputFormat()[0])
|
||||
self.setOutputFormat(format)
|
||||
self.__motors = motorList
|
||||
|
||||
def asynchronousMoveTo(self, position):
|
||||
# if input has any Nones, then replace these with the current positions
|
||||
if None in position:
|
||||
position = list(position)
|
||||
current = self.getPosition()
|
||||
for idx, val in enumerate(position):
|
||||
if val is None:
|
||||
position[idx] = current[idx]
|
||||
|
||||
for scn, pos in zip(self.__motors, position):
|
||||
scn.asynchronousMoveTo(pos)
|
||||
|
||||
def getPosition(self):
|
||||
return [scn.getPosition() for scn in self.__motors]
|
||||
|
||||
def isBusy(self):
|
||||
for scn in self.__motors:
|
||||
if scn.isBusy():
|
||||
return True
|
||||
return False
|
||||
|
||||
def configure(self):
|
||||
pass
|
||||
|
||||
|
||||
class ScannableMotionWithScannableFieldsBase(ScannableBase):
|
||||
'''
|
||||
This extended version of ScannableMotionBase contains a
|
||||
completeInstantiation() method which adds a dictionary of
|
||||
MotionScannableParts to an instance. Each part allows one of the
|
||||
instances fields to be interacted with like it itself is a scannable.
|
||||
Fields are dynamically added to the instance linking to these parts
|
||||
allowing dotted access from Jython. They may also be accessed using
|
||||
Jython container access methods (via the __getitem__() method). To acess
|
||||
them from Jave use the getComponent(name) method.
|
||||
|
||||
When moving a part (via either a pos or scan command), the part calls
|
||||
the parent to perform the actual task. The parts asynchronousMoveto
|
||||
command will call the parent with a list of None values except for the
|
||||
field it represents which will be passed the desired position value.
|
||||
|
||||
The asynchronousMoveTo method in class that inherats from this base
|
||||
class then must handle these Nones. In some cases the method may
|
||||
actually be able to move the underlying system assoiciated with one
|
||||
field individually from others. If this is not possible the best
|
||||
behaviour may be to simply not support this beahviour and exception or
|
||||
alternatively to substitute the None values with actual current position
|
||||
of parent's scannables associated fields.
|
||||
|
||||
ScannableMotionBaseWithMemory() inherats from this calss and provides a
|
||||
solution useful for some scenarious: it keeps track of the last position
|
||||
moved to, and replaces the Nones in an asynchronousMoveTo request with
|
||||
these values. There are a number of dangers associated with this which
|
||||
are addressed in that class's documentation, but it provides a way to
|
||||
move one axis within a group of non-orthogonal axis while keeping the
|
||||
others still.
|
||||
'''
|
||||
childrenDict = {}
|
||||
numInputFields = None
|
||||
numExtraFields = None
|
||||
|
||||
def completeInstantiation(self):
|
||||
'''This method should be called at the end of all user defined
|
||||
consructors'''
|
||||
# self.validate()
|
||||
self.numInputFields = len(self.getInputNames())
|
||||
self.numExtraFields = len(self.getExtraNames())
|
||||
self.addScannableParts()
|
||||
self.autoCompletePartialMoveToTargets = False
|
||||
self.positionAtScanStart = None
|
||||
|
||||
def setAutoCompletePartialMoveToTargets(self, b):
|
||||
self.autoCompletePartialMoveToTargets = b
|
||||
|
||||
def atScanStart(self):
|
||||
self.positionAtScanStart = self.getPosition()
|
||||
|
||||
def atCommandFailure(self):
|
||||
self.positionAtScanStart = None
|
||||
|
||||
def atScanEnd(self):
|
||||
self.positionAtScanStart = None
|
||||
|
||||
###
|
||||
|
||||
def __repr__(self):
|
||||
pos = self.getPosition()
|
||||
formattedValues = self.formatPositionFields(pos)
|
||||
if len(tuple(self.getInputNames()) + tuple(self.getExtraNames())) > 1:
|
||||
result = self.getName() + ': '
|
||||
else:
|
||||
result = ''
|
||||
|
||||
names = tuple(self.getInputNames()) + tuple(self.getExtraNames())
|
||||
for name, val in zip(names, formattedValues):
|
||||
result += ' ' + name + ': ' + val
|
||||
return result
|
||||
###
|
||||
|
||||
def formatPositionFields(self, pos):
|
||||
"""Returns position as array of formatted strings"""
|
||||
# Make sure pos is a tuple or list
|
||||
if type(pos) not in (tuple, list):
|
||||
pos = tuple([pos])
|
||||
|
||||
# Sanity check
|
||||
if len(pos) != len(self.getOutputFormat()):
|
||||
raise Exception(
|
||||
"In scannable '%s':number of position fields differs from "
|
||||
"number format strings specified" % self.getName())
|
||||
|
||||
result = []
|
||||
for field, format in zip(pos, self.getOutputFormat()):
|
||||
if field is None:
|
||||
result.append('???')
|
||||
else:
|
||||
s = (format % field)
|
||||
## if width!=None:
|
||||
## s = s.ljust(width)
|
||||
result.append(s)
|
||||
|
||||
return result
|
||||
|
||||
###
|
||||
|
||||
def addScannableParts(self):
|
||||
'''
|
||||
Creates an array of MotionScannableParts each of which allows access to
|
||||
the scannable's fields. See this class's documentation for more info.
|
||||
'''
|
||||
self.childrenDict = {}
|
||||
# Add parts to access the input fields
|
||||
for index in range(len(self.getInputNames())):
|
||||
scannableName = self.getInputNames()[index]
|
||||
self.childrenDict[scannableName] = self.MotionScannablePart(
|
||||
scannableName, index, self, isInputField=1)
|
||||
|
||||
# Add parts to access the extra fields
|
||||
for index in range(len(self.getExtraNames())):
|
||||
scannableName = self.getExtraNames()[index]
|
||||
self.childrenDict[scannableName] = self.MotionScannablePart(
|
||||
scannableName, index + len(self.getInputNames()),
|
||||
self, isInputField=0)
|
||||
|
||||
def asynchronousMoveTo(self, newpos):
|
||||
if self.autoCompletePartialMoveToTargets:
|
||||
newpos = self.completePosition(newpos)
|
||||
ScannableBase.asynchronousMoveTo(self, newpos)
|
||||
|
||||
def completePosition(self, position):
|
||||
'''
|
||||
If position contains any null or None values, these are replaced with
|
||||
the corresponding fields from the scannables current position and then
|
||||
returned.'''
|
||||
# Just return position if it does not need padding
|
||||
if None not in position:
|
||||
return position
|
||||
if self.positionAtScanStart is not None:
|
||||
basePosition = self.positionAtScanStart
|
||||
else:
|
||||
basePosition = self.getPosition()[:self.numInputFields]
|
||||
for i in range(self.numInputFields):
|
||||
if position[i] is None:
|
||||
position[i] = basePosition[i]
|
||||
return position
|
||||
|
||||
def __getattr__(self, name):
|
||||
try:
|
||||
return self.childrenDict[name]
|
||||
except:
|
||||
raise AttributeError("No child named:" + name)
|
||||
|
||||
def __getitem__(self, key):
|
||||
'''Provides container like access from Jython'''
|
||||
return self.childrenDict[key]
|
||||
|
||||
def getPart(self, name):
|
||||
'''Returns the a compnent scannable'''
|
||||
return self.childrenDict[name]
|
||||
|
||||
class MotionScannablePart(ScannableBase):
|
||||
'''
|
||||
A scannable to be placed in the parent's childrenDict that allows
|
||||
access to the parent's individual fields.'''
|
||||
|
||||
def __init__(self, scannableName, index, parentScannable,
|
||||
isInputField):
|
||||
self.setName(scannableName)
|
||||
if isInputField:
|
||||
self.setInputNames([scannableName])
|
||||
else:
|
||||
self.setExtraNames([scannableName])
|
||||
self.index = index
|
||||
self.parentScannable = parentScannable
|
||||
self.setOutputFormat(
|
||||
[self.parentScannable.getOutputFormat()[index]])
|
||||
|
||||
def isBusy(self):
|
||||
return self.parentScannable.isBusy()
|
||||
|
||||
def asynchronousMoveTo(self, new_position):
|
||||
if self.parentScannable.isBusy():
|
||||
raise Exception(
|
||||
self.parentScannable.getName() + "." + self.getName() +
|
||||
" cannot be moved because " +
|
||||
self.parentScannable.getName() + " is already moving")
|
||||
|
||||
toMoveTo = [None] * len(self.parentScannable.getInputNames())
|
||||
toMoveTo[self.index] = new_position
|
||||
self.parentScannable.asynchronousMoveTo(toMoveTo)
|
||||
|
||||
def moveTo(self, new_position):
|
||||
self.asynchronousMoveTo(new_position)
|
||||
self.waitWhileBusy()
|
||||
|
||||
def getPosition(self):
|
||||
return self.parentScannable.getPosition()[self.index]
|
||||
|
||||
def __str__(self):
|
||||
return self.__repr__()
|
||||
|
||||
def __repr__(self):
|
||||
# Get the name of this field
|
||||
# (assume its an input field first and correct if wrong)
|
||||
name = self.getInputNames()[0]
|
||||
|
||||
if name == 'value':
|
||||
name = self.getExtraNames()[0]
|
||||
parentName = self.parentScannable.getName()
|
||||
return parentName + "." + name + " : " + str(self.getPosition())
|
||||
|
||||
|
||||
|
||||
|
||||
0
script/__Lib/diffcalc-2.1/diffcalc/gdasupport/scannable/__init__.py
Executable file
0
script/__Lib/diffcalc-2.1/diffcalc/gdasupport/scannable/__init__.py
Executable file
62
script/__Lib/diffcalc-2.1/diffcalc/gdasupport/scannable/base.py
Executable file
62
script/__Lib/diffcalc-2.1/diffcalc/gdasupport/scannable/base.py
Executable file
@@ -0,0 +1,62 @@
|
||||
###
|
||||
# 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/>.
|
||||
###
|
||||
|
||||
try:
|
||||
from gda.device.scannable import PseudoDevice
|
||||
except ImportError:
|
||||
from diffcalc.gdasupport.minigda.scannable import \
|
||||
ScannableBase as PseudoDevice
|
||||
|
||||
|
||||
class ScannableGroup(PseudoDevice):
|
||||
|
||||
def __init__(self, name, motorList):
|
||||
|
||||
self.setName(name)
|
||||
# Set input format
|
||||
motorNames = []
|
||||
for scn in motorList:
|
||||
motorNames.append(scn.getName())
|
||||
self.setInputNames(motorNames)
|
||||
# Set output format
|
||||
format = []
|
||||
for motor in motorList:
|
||||
format.append(motor.getOutputFormat()[0])
|
||||
self.setOutputFormat(format)
|
||||
self.__motors = motorList
|
||||
|
||||
def asynchronousMoveTo(self, position):
|
||||
# if input has any Nones, then replace these with the current positions
|
||||
if None in position:
|
||||
position = list(position)
|
||||
current = self.getPosition()
|
||||
for idx, val in enumerate(position):
|
||||
if val is None:
|
||||
position[idx] = current[idx]
|
||||
|
||||
for scn, pos in zip(self.__motors, position):
|
||||
scn.asynchronousMoveTo(pos)
|
||||
|
||||
def getPosition(self):
|
||||
return [scn.getPosition() for scn in self.__motors]
|
||||
|
||||
def isBusy(self):
|
||||
for scn in self.__motors:
|
||||
if scn.isBusy():
|
||||
return True
|
||||
return False
|
||||
126
script/__Lib/diffcalc-2.1/diffcalc/gdasupport/scannable/diffractometer.py
Executable file
126
script/__Lib/diffcalc-2.1/diffcalc/gdasupport/scannable/diffractometer.py
Executable file
@@ -0,0 +1,126 @@
|
||||
###
|
||||
# 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/>.
|
||||
###
|
||||
|
||||
try:
|
||||
from gda.device.scannable import ScannableMotionBase
|
||||
except ImportError:
|
||||
from diffcalc.gdasupport.minigda.scannable import \
|
||||
ScannableBase as ScannableMotionBase
|
||||
|
||||
from diffcalc.util import getMessageFromException
|
||||
|
||||
# TODO: Split into a base class when making other scannables
|
||||
|
||||
|
||||
class DiffractometerScannableGroup(ScannableMotionBase):
|
||||
""""
|
||||
Wraps up a scannableGroup of axis to tweak the way the resulting
|
||||
object is displayed and to add a simulate move to method.
|
||||
|
||||
The scannable group should have the same geometry as that expected
|
||||
by the diffractometer hardware geometry used in the diffraction
|
||||
calculator.
|
||||
|
||||
The optional parameter slaveDriver can be used to provide a
|
||||
slave_driver. This is useful for triggering a move of an incidental
|
||||
axis whose position depends on that of the diffractometer, but whose
|
||||
position need not be included in the DiffractometerScannableGroup
|
||||
itself. This parameter is exposed as a field and can be set or
|
||||
cleared to null at will without effecting the core calculation code.
|
||||
"""
|
||||
|
||||
def __init__(self, name, diffcalc_module, scannableGroup,
|
||||
slave_driver=None, hint_generator=None):
|
||||
# if motorList is None, will create a dummy __group
|
||||
self.diffcalc_module = diffcalc_module
|
||||
self.__group = scannableGroup
|
||||
self.slave_driver = slave_driver
|
||||
self.setName(name)
|
||||
self.hint_generator = hint_generator
|
||||
|
||||
def getInputNames(self):
|
||||
return self.__group.getInputNames()
|
||||
|
||||
def getExtraNames(self):
|
||||
if self.slave_driver is None:
|
||||
return []
|
||||
else:
|
||||
return self.slave_driver.getScannableNames()
|
||||
|
||||
def getOutputFormat(self):
|
||||
if self.slave_driver is None:
|
||||
slave_formats = []
|
||||
else:
|
||||
slave_formats = self.slave_driver.getScannableNames()
|
||||
return list(self.__group.getOutputFormat()) + slave_formats
|
||||
|
||||
def asynchronousMoveTo(self, position):
|
||||
self.__group.asynchronousMoveTo(position)
|
||||
if self.slave_driver is not None:
|
||||
self.slave_driver.triggerAsynchronousMove(position)
|
||||
|
||||
def getPosition(self):
|
||||
if self.slave_driver is None:
|
||||
slave_positions = []
|
||||
else:
|
||||
slave_positions = self.slave_driver.getPositions()
|
||||
return list(self.__group.getPosition()) + list(slave_positions)
|
||||
|
||||
def isBusy(self):
|
||||
if self.slave_driver is None:
|
||||
return self.__group.isBusy()
|
||||
else:
|
||||
return self.__group.isBusy() or self.slave_driver.isBusy()
|
||||
|
||||
def waitWhileBusy(self):
|
||||
self.__group.waitWhileBusy()
|
||||
if self.slave_driver is not None:
|
||||
self.slave_driver.waitWhileBusy()
|
||||
|
||||
def simulateMoveTo(self, pos):
|
||||
if len(pos) != len(self.getInputNames()):
|
||||
raise ValueError('Wrong number of inputs')
|
||||
try:
|
||||
(hkl, params) = self.diffcalc_module.angles_to_hkl(pos)
|
||||
except Exception, e:
|
||||
return "Error: %s" % getMessageFromException(e)
|
||||
width = max(len(k) for k in params)
|
||||
|
||||
lines = ([' ' + 'hkl'.rjust(width) + ' : % 9.4f %.4f %.4f' %
|
||||
(hkl[0], hkl[1], hkl[2])])
|
||||
lines[-1] = lines[-1] + '\n'
|
||||
fmt = ' %' + str(width) + 's : % 9.4f'
|
||||
for k in sorted(params):
|
||||
lines.append(fmt % (k, params[k]))
|
||||
return '\n'.join(lines)
|
||||
|
||||
def __repr__(self):
|
||||
position = self.getPosition()
|
||||
names = list(self.getInputNames()) + list(self.getExtraNames())
|
||||
if self.hint_generator is None:
|
||||
hint_list = [''] * len(self.getInputNames())
|
||||
else:
|
||||
hint_list = self.hint_generator()
|
||||
|
||||
lines = [self.name + ':']
|
||||
width = max(len(k) for k in names)
|
||||
fmt = ' %' + str(width) + 's : % 9.4f %s'
|
||||
for name, pos, hint in zip(names, position, hint_list):
|
||||
lines.append(fmt % (name, pos, hint))
|
||||
lines[len(self.getInputNames())] += '\n'
|
||||
return '\n'.join(lines)
|
||||
135
script/__Lib/diffcalc-2.1/diffcalc/gdasupport/scannable/hkl.py
Executable file
135
script/__Lib/diffcalc-2.1/diffcalc/gdasupport/scannable/hkl.py
Executable file
@@ -0,0 +1,135 @@
|
||||
###
|
||||
# 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/>.
|
||||
###
|
||||
import platform
|
||||
|
||||
DEBUG = False
|
||||
|
||||
try:
|
||||
from gda.device.scannable.scannablegroup import \
|
||||
ScannableMotionWithScannableFieldsBase
|
||||
except ImportError:
|
||||
from diffcalc.gdasupport.minigda.scannable import \
|
||||
ScannableMotionWithScannableFieldsBase
|
||||
|
||||
from diffcalc.util import getMessageFromException, DiffcalcException
|
||||
|
||||
|
||||
class _DynamicDocstringMetaclass(type):
|
||||
|
||||
def _get_doc(self):
|
||||
return Hkl.dynamic_docstring
|
||||
|
||||
__doc__ = property(_get_doc) # @ReservedAssignment
|
||||
|
||||
|
||||
class Hkl(ScannableMotionWithScannableFieldsBase):
|
||||
|
||||
if platform.system() != 'Java':
|
||||
__metaclass__ = _DynamicDocstringMetaclass # TODO: Removed to fix Jython
|
||||
|
||||
dynamic_docstring = 'Hkl Scannable'
|
||||
|
||||
def _get_doc(self):
|
||||
return Hkl.dynamic_docstring
|
||||
|
||||
__doc__ = property(_get_doc) # @ReservedAssignment
|
||||
|
||||
def __init__(self, name, diffractometerObject, diffcalcObject,
|
||||
virtualAnglesToReport=None):
|
||||
self.diffhw = diffractometerObject
|
||||
self._diffcalc = diffcalcObject
|
||||
if type(virtualAnglesToReport) is str:
|
||||
virtualAnglesToReport = (virtualAnglesToReport,)
|
||||
self.vAngleNames = virtualAnglesToReport
|
||||
|
||||
self.setName(name)
|
||||
self.setInputNames(['h', 'k', 'l'])
|
||||
self.setOutputFormat(['%7.5f'] * 3)
|
||||
if self.vAngleNames:
|
||||
self.setExtraNames(self.vAngleNames)
|
||||
self.setOutputFormat(['%7.5f'] * (3 + len(self.vAngleNames)))
|
||||
|
||||
self.completeInstantiation()
|
||||
self.setAutoCompletePartialMoveToTargets(True)
|
||||
self.dynamic_class_doc = 'Hkl Scannable xyz'
|
||||
|
||||
def rawAsynchronousMoveTo(self, hkl):
|
||||
if len(hkl) != 3: raise ValueError('Hkl device expects three inputs')
|
||||
try:
|
||||
(pos, _) = self._diffcalc.hkl_to_angles(hkl[0], hkl[1], hkl[2])
|
||||
except DiffcalcException, e:
|
||||
if DEBUG:
|
||||
raise
|
||||
else:
|
||||
raise DiffcalcException(e.message)
|
||||
self.diffhw.asynchronousMoveTo(pos)
|
||||
|
||||
def rawGetPosition(self):
|
||||
pos = self.diffhw.getPosition() # a tuple
|
||||
(hkl , params) = self._diffcalc.angles_to_hkl(pos)
|
||||
result = list(hkl)
|
||||
if self.vAngleNames:
|
||||
for vAngleName in self.vAngleNames:
|
||||
result.append(params[vAngleName])
|
||||
return result
|
||||
|
||||
def getFieldPosition(self, i):
|
||||
return self.getPosition()[i]
|
||||
|
||||
def isBusy(self):
|
||||
return self.diffhw.isBusy()
|
||||
|
||||
def waitWhileBusy(self):
|
||||
return self.diffhw.waitWhileBusy()
|
||||
|
||||
def simulateMoveTo(self, hkl):
|
||||
if type(hkl) not in (list, tuple):
|
||||
raise ValueError('Hkl device expects three inputs')
|
||||
if len(hkl) != 3:
|
||||
raise ValueError('Hkl device expects three inputs')
|
||||
(pos, params) = self._diffcalc.hkl_to_angles(hkl[0], hkl[1], hkl[2])
|
||||
|
||||
width = max(len(k) for k in (params.keys() + list(self.diffhw.getInputNames())))
|
||||
fmt = ' %' + str(width) + 's : % 9.4f'
|
||||
|
||||
lines = [self.diffhw.getName() + ' would move to:']
|
||||
for idx, name in enumerate(self.diffhw.getInputNames()):
|
||||
lines.append(fmt % (name, pos[idx]))
|
||||
lines[-1] = lines[-1] + '\n'
|
||||
for k in sorted(params):
|
||||
lines.append(fmt % (k, params[k]))
|
||||
return '\n'.join(lines)
|
||||
|
||||
def __str__(self):
|
||||
return self.__repr__()
|
||||
|
||||
def __repr__(self):
|
||||
lines = ['hkl:']
|
||||
pos = self.diffhw.getPosition()
|
||||
try:
|
||||
(hkl, params) = self._diffcalc.angles_to_hkl(pos)
|
||||
except Exception, e:
|
||||
return "<hkl: %s>" % getMessageFromException(e)
|
||||
|
||||
width = max(len(k) for k in params)
|
||||
lines.append(' ' + 'hkl'.rjust(width) + ' : %9.4f %.4f %.4f' % (hkl[0], hkl[1], hkl[2]))
|
||||
lines[-1] = lines[-1] + '\n'
|
||||
fmt = ' %' + str(width) + 's : % 9.4f'
|
||||
for k in sorted(params):
|
||||
lines.append(fmt % (k, params[k]))
|
||||
return '\n'.join(lines)
|
||||
47
script/__Lib/diffcalc-2.1/diffcalc/gdasupport/scannable/mock.py
Executable file
47
script/__Lib/diffcalc-2.1/diffcalc/gdasupport/scannable/mock.py
Executable file
@@ -0,0 +1,47 @@
|
||||
###
|
||||
# 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/>.
|
||||
###
|
||||
|
||||
try:
|
||||
from gda.device.scannable import ScannableMotionBase
|
||||
except ImportError:
|
||||
from diffcalc.gdasupport.minigda.scannable import \
|
||||
ScannableBase as ScannableMotionBase
|
||||
|
||||
|
||||
class MockMotor(ScannableMotionBase):
|
||||
|
||||
def __init__(self, name='mock'):
|
||||
self.pos = 0.0
|
||||
self._busy = False
|
||||
self.name = name
|
||||
|
||||
def asynchronousMoveTo(self, pos):
|
||||
self._busy = True
|
||||
self.pos = float(pos)
|
||||
|
||||
def getPosition(self):
|
||||
return self.pos
|
||||
|
||||
def isBusy(self):
|
||||
return self._busy
|
||||
|
||||
def makeNotBusy(self):
|
||||
self._busy = False
|
||||
|
||||
def getOutputFormat(self):
|
||||
return ['%f']
|
||||
45
script/__Lib/diffcalc-2.1/diffcalc/gdasupport/scannable/parameter.py
Executable file
45
script/__Lib/diffcalc-2.1/diffcalc/gdasupport/scannable/parameter.py
Executable file
@@ -0,0 +1,45 @@
|
||||
###
|
||||
# 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/>.
|
||||
###
|
||||
|
||||
try:
|
||||
from gda.device.scannable import ScannableMotionBase
|
||||
except ImportError:
|
||||
from diffcalc.gdasupport.minigda.scannable import \
|
||||
ScannableBase as ScannableMotionBase
|
||||
|
||||
|
||||
class DiffractionCalculatorParameter(ScannableMotionBase):
|
||||
|
||||
def __init__(self, name, parameterName, parameter_manager):
|
||||
|
||||
self.parameter_manager = parameter_manager
|
||||
self.parameterName = parameterName
|
||||
|
||||
self.setName(name)
|
||||
self.setInputNames([parameterName])
|
||||
self.setOutputFormat(['%5.5f'])
|
||||
self.setLevel(3)
|
||||
|
||||
def asynchronousMoveTo(self, value):
|
||||
self.parameter_manager.set_constraint(self.parameterName, value)
|
||||
|
||||
def getPosition(self):
|
||||
return self.parameter_manager.get_constraint(self.parameterName)
|
||||
|
||||
def isBusy(self):
|
||||
return False
|
||||
21
script/__Lib/diffcalc-2.1/diffcalc/gdasupport/scannable/sim.py
Executable file
21
script/__Lib/diffcalc-2.1/diffcalc/gdasupport/scannable/sim.py
Executable file
@@ -0,0 +1,21 @@
|
||||
'''
|
||||
Created on 7 May 2016
|
||||
|
||||
@author: walton
|
||||
'''
|
||||
from diffcalc.util import allnum
|
||||
|
||||
def sim(scn, hkl):
|
||||
"""sim hkl scn -- simulates moving scannable (not all)
|
||||
"""
|
||||
if not isinstance(hkl, (tuple, list)):
|
||||
raise TypeError
|
||||
|
||||
if not allnum(hkl):
|
||||
raise TypeError()
|
||||
|
||||
try:
|
||||
print scn.simulateMoveTo(hkl)
|
||||
except AttributeError:
|
||||
raise TypeError(
|
||||
"The first argument does not support simulated moves")
|
||||
139
script/__Lib/diffcalc-2.1/diffcalc/gdasupport/scannable/simulation.py
Executable file
139
script/__Lib/diffcalc-2.1/diffcalc/gdasupport/scannable/simulation.py
Executable file
@@ -0,0 +1,139 @@
|
||||
###
|
||||
# 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/>.
|
||||
###
|
||||
|
||||
import time
|
||||
from math import sqrt, pi, exp
|
||||
|
||||
try:
|
||||
from gda.device.scannable import PseudoDevice
|
||||
except ImportError:
|
||||
from diffcalc.gdasupport.minigda.scannable import \
|
||||
ScannableBase as PseudoDevice
|
||||
|
||||
from diffcalc.ub.crystal import CrystalUnderTest
|
||||
from diffcalc.hkl.you.calc import youAnglesToHkl
|
||||
from diffcalc.hkl.vlieg.calc import vliegAnglesToHkl
|
||||
from diffcalc.hkl.you.geometry import calcCHI, calcPHI
|
||||
|
||||
TORAD = pi / 180
|
||||
TODEG = 180 / pi
|
||||
|
||||
|
||||
class Equation(object):
|
||||
|
||||
def __call__(self, dh, dk, dl):
|
||||
raise Exception('Abstract')
|
||||
|
||||
def __str__(self):
|
||||
"Abstract equation"
|
||||
|
||||
|
||||
class Gaussian(Equation):
|
||||
|
||||
def __init__(self, variance):
|
||||
self.variance = float(variance)
|
||||
|
||||
def __call__(self, dh, dk, dl):
|
||||
dr_squared = dh * dh + dk * dk + dl * dl
|
||||
return (1 / sqrt(2 * pi * self.variance) *
|
||||
exp(-dr_squared / (2 * self.variance)))
|
||||
|
||||
|
||||
class SimulatedCrystalCounter(PseudoDevice):
|
||||
|
||||
def __init__(self, name, diffractometerScannable, geometryPlugin,
|
||||
wavelengthScannable, equation=Gaussian(.01), engine='you'):
|
||||
self.setName(name)
|
||||
self.setInputNames([name + '_count'])
|
||||
self.setOutputFormat(['%7.5f'])
|
||||
self.exposureTime = 1
|
||||
self.pause = True
|
||||
self.diffractometerScannable = diffractometerScannable
|
||||
self.geometry = geometryPlugin
|
||||
self.wavelengthScannable = wavelengthScannable
|
||||
self.equation = equation
|
||||
self.engine = engine
|
||||
|
||||
self.cut = None
|
||||
self.UB = None
|
||||
self.chiMissmount = 0.
|
||||
self.phiMissmount = 0.
|
||||
self.setCrystal('cubic', 1, 1, 1, 90, 90, 90)
|
||||
|
||||
def setCrystal(self, name, a, b, c, alpha, beta, gamma):
|
||||
self.cut = CrystalUnderTest(name, a, b, c, alpha, beta, gamma)
|
||||
self.calcUB()
|
||||
|
||||
def setChiMissmount(self, chi):
|
||||
self.chiMissmount = chi
|
||||
self.calcUB()
|
||||
|
||||
def setPhiMissmount(self, phi):
|
||||
self.phiMissmount = phi
|
||||
self.calcUB()
|
||||
|
||||
def calcUB(self):
|
||||
CHI = calcCHI(self.chiMissmount * TORAD)
|
||||
PHI = calcPHI(self.phiMissmount * TORAD)
|
||||
self.UB = CHI * PHI * self.cut.B
|
||||
|
||||
def asynchronousMoveTo(self, exposureTime):
|
||||
self.exposureTime = exposureTime
|
||||
if self.pause:
|
||||
time.sleep(exposureTime) # Should not technically block!
|
||||
|
||||
def getPosition(self):
|
||||
h, k, l = self.getHkl()
|
||||
dh, dk, dl = h - round(h), k - round(k), l - round(l)
|
||||
count = self.equation(dh, dk, dl)
|
||||
#return self.exposureTime, count*self.exposureTime
|
||||
return count * self.exposureTime
|
||||
|
||||
def getHkl(self):
|
||||
pos = self.geometry.physical_angles_to_internal_position(
|
||||
self.diffractometerScannable.getPosition())
|
||||
pos.changeToRadians()
|
||||
wavelength = self.wavelengthScannable.getPosition()
|
||||
if self.engine.lower() == 'vlieg':
|
||||
return vliegAnglesToHkl(pos, wavelength, self.UB)
|
||||
elif self.engine.lower() == 'you':
|
||||
return youAnglesToHkl(pos, wavelength, self.UB)
|
||||
else:
|
||||
raise ValueError(self.engine)
|
||||
|
||||
def isBusy(self):
|
||||
return False
|
||||
|
||||
def __str__(self):
|
||||
return self.__repr__()
|
||||
|
||||
def __repr__(self):
|
||||
s = 'simulated crystal detector: %s\n' % self.getName()
|
||||
h, k, l = self.getHkl()
|
||||
s += ' h : %f\n' % h
|
||||
s += ' k : %f\n' % k
|
||||
s += ' l : %f\n' % l
|
||||
s += self.cut.__str__() + '\n'
|
||||
s += "chi orientation: %s\n" % self.chiMissmount
|
||||
s += "phi orientation: %s\n" % self.phiMissmount
|
||||
ub = self.UB.tolist()
|
||||
s += "UB:\n"
|
||||
s += " % 18.13f% 18.13f% 18.12f\n" % (ub[0][0], ub[0][1], ub[0][2])
|
||||
s += " % 18.13f% 18.13f% 18.12f\n" % (ub[1][0], ub[1][1], ub[1][2])
|
||||
s += " % 18.13f% 18.13f% 18.12f\n" % (ub[2][0], ub[2][1], ub[2][2])
|
||||
return s
|
||||
109
script/__Lib/diffcalc-2.1/diffcalc/gdasupport/scannable/slave_driver.py
Executable file
109
script/__Lib/diffcalc-2.1/diffcalc/gdasupport/scannable/slave_driver.py
Executable file
@@ -0,0 +1,109 @@
|
||||
###
|
||||
# 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 math import pi, tan, sin, atan, cos, atan2
|
||||
|
||||
TORAD = pi / 180
|
||||
TODEG = 180 / pi
|
||||
|
||||
|
||||
class SlaveScannableDriver(object):
|
||||
|
||||
def __init__(self, scannables):
|
||||
self.scannables = scannables
|
||||
|
||||
def isBusy(self):
|
||||
for scn in self.scannables:
|
||||
if scn.isBusy():
|
||||
return True
|
||||
return False
|
||||
|
||||
def waitWhileBusy(self):
|
||||
for scn in self.scannables:
|
||||
scn.waitWhileBusy()
|
||||
|
||||
def triggerAsynchronousMove(self, triggerPos):
|
||||
nu = self.slaveFromTriggerPos(triggerPos)
|
||||
for scn in self.scannables:
|
||||
scn.asynchronousMoveTo(nu)
|
||||
|
||||
def getPosition(self):
|
||||
return self.scannables[0].getPosition()
|
||||
|
||||
def slaveFromTriggerPos(self, triggerPos):
|
||||
raise Exception("Abstract")
|
||||
|
||||
def getScannableNames(self):
|
||||
return [scn.name for scn in self.scannables]
|
||||
|
||||
def getOutputFormat(self):
|
||||
return [list(scn.outputFormat)[0] for scn in self.scannables]
|
||||
|
||||
def getPositions(self):
|
||||
return [float(scn.getPosition()) for scn in self.scannables]
|
||||
|
||||
|
||||
"""
|
||||
Based on: Elias Vlieg, "A (2+3)-Type Surface Diffractometer: Mergence of the
|
||||
z-axis and (2+2)-Type Geometries", J. Appl. Cryst. (1998). 31. 198-203
|
||||
"""
|
||||
|
||||
|
||||
class NuDriverForSixCirclePlugin(SlaveScannableDriver):
|
||||
|
||||
def slaveFromTriggerPos(self, triggerPos):
|
||||
|
||||
alpha, delta, gamma, _, _, _ = triggerPos
|
||||
alpha = alpha * TORAD
|
||||
delta = delta * TORAD
|
||||
gamma = gamma * TORAD
|
||||
|
||||
### Equation16 RHS ###
|
||||
rhs = -1 * tan(gamma - alpha) * sin(delta)
|
||||
nu = atan(rhs) # -pi/2 <= nu <= pi/2
|
||||
return nu * TODEG
|
||||
|
||||
|
||||
class NuDriverForWillmottHorizontalGeometry(SlaveScannableDriver):
|
||||
|
||||
"""
|
||||
Based on: Phillip Willmott, "Angle calculations for a (2+3)-type
|
||||
diffractometer: focus on area detectors", J. Appl. Cryst. (2011). 44.
|
||||
73-83
|
||||
"""
|
||||
|
||||
def __init__(self, scannables, area_detector=False):
|
||||
SlaveScannableDriver.__init__(self, scannables)
|
||||
self.area_detector = area_detector
|
||||
|
||||
def slaveFromTriggerPos(self, triggerPos):
|
||||
|
||||
delta, gamma, omegah, _ = triggerPos
|
||||
delta *= TORAD
|
||||
gamma *= TORAD
|
||||
omegah *= TORAD
|
||||
if self.area_detector:
|
||||
nu = atan2(sin(delta - omegah), tan(gamma)) # (66)
|
||||
else:
|
||||
top = -sin(gamma) * sin(omegah)
|
||||
bot = (sin(omegah) * cos(gamma) * sin(delta) +
|
||||
cos(omegah) * cos(delta))
|
||||
nu = atan2(top, bot) # (61)
|
||||
|
||||
print 'nu:', nu * TODEG
|
||||
return nu * TODEG
|
||||
184
script/__Lib/diffcalc-2.1/diffcalc/gdasupport/scannable/vrmlanimator.py
Executable file
184
script/__Lib/diffcalc-2.1/diffcalc/gdasupport/scannable/vrmlanimator.py
Executable file
@@ -0,0 +1,184 @@
|
||||
###
|
||||
# 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/>.
|
||||
###
|
||||
|
||||
import time
|
||||
import threading
|
||||
import socket
|
||||
PORT = 4567
|
||||
|
||||
from gda.device.scannable import ScannableMotionWithScannableFieldsBaseTest
|
||||
|
||||
#import scannable.vrmlModelDriver
|
||||
#reload(scannable.vrmlModelDriver);from scannable.vrmlModelDriver import \
|
||||
# VrmlModelDriver, LinearProfile, MoveThread
|
||||
#fc=VrmlModelDriver(
|
||||
# 'fc',['alpha','delta','omega', 'chi','phi'], speed=30, host='diamrl5104')
|
||||
#alpha = fc.alpha
|
||||
#delta = fc.delta
|
||||
#omega = fc.omega
|
||||
#chi = fc.chi
|
||||
#phi = fc.phi
|
||||
|
||||
|
||||
def connect_to_socket(host, port):
|
||||
print "Connecting to %s on port %d" % (host, port)
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
sock.connect((host, port))
|
||||
print "Connected"
|
||||
socketfile = sock.makefile('rw', 0)
|
||||
return socketfile
|
||||
|
||||
|
||||
class LinearProfile(object):
|
||||
|
||||
def __init__(self, v, t_accel, startList, endList):
|
||||
assert len(startList) == len(endList)
|
||||
self.v = float(v)
|
||||
self.start = startList
|
||||
self.end = endList
|
||||
self.t_accel = t_accel
|
||||
|
||||
distances = [e - s for e, s in zip(self.end, self.start)]
|
||||
max_distance = max([abs(d) for d in distances])
|
||||
if max_distance == 0:
|
||||
self.delta_time = 0
|
||||
else:
|
||||
self.delta_time = abs(max_distance / self.v)
|
||||
self.speeds = [d / self.delta_time for d in distances]
|
||||
self.start_time = time.time()
|
||||
|
||||
def getPosition(self):
|
||||
if self.start_time is None:
|
||||
return self.start
|
||||
if not self.isMoving():
|
||||
return self.end
|
||||
t = abs(float(time.time() - self.start_time))
|
||||
if t > self.delta_time:
|
||||
# we are in the deceleration phase (i.e paused for now)
|
||||
return self.end
|
||||
return [s + v * t for s, v in zip(self.start, self.speeds)]
|
||||
|
||||
def isMoving(self):
|
||||
return time.time() < self.start_time + self.delta_time + self.t_accel
|
||||
|
||||
|
||||
class MoveThread(threading.Thread):
|
||||
|
||||
def __init__(self, profile, socketfile, axisNames):
|
||||
threading.Thread.__init__(self)
|
||||
self.profile = profile
|
||||
self.socketfile = socketfile
|
||||
self.axisNames = axisNames
|
||||
|
||||
def run(self):
|
||||
while self.profile.isMoving():
|
||||
self.update()
|
||||
time.sleep(.1)
|
||||
self.update()
|
||||
|
||||
def update(self):
|
||||
pos = self.profile.getPosition()
|
||||
d = dict(zip(map(str, self.axisNames), pos))
|
||||
if self.socketfile:
|
||||
self.socketfile.write(repr(d) + '\n')
|
||||
|
||||
|
||||
class VrmlModelDriver(ScannableMotionWithScannableFieldsBaseTest):
|
||||
|
||||
def __init__(self, name, axes_names, host=None, speed=60, t_accel=.1,
|
||||
format='%.3f'):
|
||||
self.name = name
|
||||
self.inputNames = list(axes_names)
|
||||
self.extraNames = []
|
||||
self.outputFormat = [format] * len(self.inputNames)
|
||||
self.completeInstantiation()
|
||||
self.__last_target = [0.] * len(self.inputNames)
|
||||
self.verbose = False
|
||||
self.move_thread = None
|
||||
self.speed = speed
|
||||
self.host = host
|
||||
self.t_accel = t_accel
|
||||
self.socketfile = None
|
||||
if self.host:
|
||||
try:
|
||||
self.connect()
|
||||
except socket.error:
|
||||
print "Failed to connect to %s:%r" % (self.host, PORT)
|
||||
print "Connect with: %s.connect()" % self.name
|
||||
|
||||
def connect(self):
|
||||
self.socketfile = connect_to_socket(self.host, PORT)
|
||||
self.rawAsynchronousMoveTo(self.__last_target)
|
||||
|
||||
def isBusy(self):
|
||||
if self.move_thread is None:
|
||||
return False
|
||||
return self.move_thread.profile.isMoving()
|
||||
|
||||
def rawGetPosition(self):
|
||||
if self.move_thread is None:
|
||||
return self.__last_target
|
||||
else:
|
||||
return self.move_thread.profile.getPosition()
|
||||
|
||||
def rawAsynchronousMoveTo(self, targetList):
|
||||
if self.isBusy():
|
||||
raise Exception(self.name + ' is already moving')
|
||||
if self.verbose:
|
||||
print self.name + ".rawAsynchronousMoveTo(%r)" % targetList
|
||||
|
||||
for i, target in enumerate(targetList):
|
||||
if target is None:
|
||||
targetList[i] = self.__last_target[i]
|
||||
profile = LinearProfile(
|
||||
self.speed, self.t_accel, self.__last_target, targetList)
|
||||
self.move_thread = MoveThread(
|
||||
profile, self.socketfile, self.inputNames)
|
||||
self.move_thread.start()
|
||||
self.__last_target = targetList
|
||||
|
||||
def getFieldPosition(self, index):
|
||||
return self.getPosition()[index]
|
||||
|
||||
def __del__(self):
|
||||
self.socketfile.close()
|
||||
|
||||
#class TrapezoidProfile(object):
|
||||
#
|
||||
# def __init__(self, t_accel, v_max, delta_x):
|
||||
# self.t_a = t_accel
|
||||
# self.v_m = v_max
|
||||
# self.delta_x = delta_x
|
||||
#
|
||||
# self.t_c = (self.X - self.v_m*self.t_a) / self.v_m
|
||||
#
|
||||
# def x(self, t):
|
||||
# if self.t_c <=0:
|
||||
# return self.__xshort(t)
|
||||
# else:
|
||||
# return self.__xlong(t)
|
||||
#
|
||||
# def __xshort(self, t):
|
||||
# delta_t = 2 * sqrt(self.delta_x*self.t_a/self.v_m)
|
||||
# if t <= .5*delta_t:
|
||||
# return (.5*self.v_m/self.t_a) * t**2
|
||||
# else:
|
||||
# v_peak = (self.v_m/self.t_a) * .5*delta_t
|
||||
# return (t-.5*delta_t)*v_peak - (t-.5*delta_t)**2 ####HERE, bugged
|
||||
# self.delta_x/2
|
||||
50
script/__Lib/diffcalc-2.1/diffcalc/gdasupport/scannable/wavelength.py
Executable file
50
script/__Lib/diffcalc-2.1/diffcalc/gdasupport/scannable/wavelength.py
Executable file
@@ -0,0 +1,50 @@
|
||||
###
|
||||
# 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/>.
|
||||
###
|
||||
|
||||
try:
|
||||
from gdascripts.pd.dummy_pds import DummyPD
|
||||
except ImportError:
|
||||
from diffcalc.gdasupport.minigda.scannable import DummyPD
|
||||
|
||||
|
||||
class Wavelength(DummyPD):
|
||||
|
||||
def __init__(self, name, energyScannable,
|
||||
energyScannableMultiplierToGetKeV=1):
|
||||
self.energyScannable = energyScannable
|
||||
self.energyScannableMultiplierToGetKeV = \
|
||||
energyScannableMultiplierToGetKeV
|
||||
|
||||
DummyPD.__init__(self, name)
|
||||
|
||||
def asynchronousMoveTo(self, pos):
|
||||
self.energyScannable.asynchronousMoveTo(
|
||||
(12.39842 / pos) / self.energyScannableMultiplierToGetKeV)
|
||||
|
||||
def getPosition(self):
|
||||
energy = self.energyScannable.getPosition()
|
||||
if energy == 0:
|
||||
raise Exception(
|
||||
"The energy is 0, so no wavelength could be calculated.run_All()")
|
||||
return 12.39842 / (energy * self.energyScannableMultiplierToGetKeV)
|
||||
|
||||
def isBusy(self):
|
||||
return self.energyScannable.isBusy()
|
||||
|
||||
def waitWhileBusy(self):
|
||||
return self.energyScannable.waitWhileBusy()
|
||||
113
script/__Lib/diffcalc-2.1/diffcalc/gdasupport/you.py
Executable file
113
script/__Lib/diffcalc-2.1/diffcalc/gdasupport/you.py
Executable file
@@ -0,0 +1,113 @@
|
||||
from diffcalc.gdasupport.scannable.diffractometer import DiffractometerScannableGroup
|
||||
from diffcalc.gdasupport.scannable.hkl import Hkl
|
||||
from diffcalc.gdasupport.scannable.simulation import SimulatedCrystalCounter
|
||||
from diffcalc.gdasupport.scannable.wavelength import Wavelength
|
||||
from diffcalc.gdasupport.scannable.parameter import DiffractionCalculatorParameter
|
||||
|
||||
|
||||
from diffcalc.dc import dcyou as _dc
|
||||
from diffcalc.dc.help import format_command_help
|
||||
reload(_dc)
|
||||
from diffcalc.dc.dcyou import * # @UnusedWildImport
|
||||
from diffcalc import settings
|
||||
|
||||
try:
|
||||
import gda # @UnusedImport @UnresolvedImport
|
||||
GDA = True
|
||||
except:
|
||||
GDA = False
|
||||
|
||||
if not GDA:
|
||||
from diffcalc.gdasupport.minigda import command
|
||||
_pos = command.Pos()
|
||||
_scan = command.Scan(command.ScanDataPrinter())
|
||||
|
||||
def pos(*args):
|
||||
"""
|
||||
pos show position of all Scannables
|
||||
pos scn show position of scn
|
||||
pos scn targetmove scn to target (a number)
|
||||
"""
|
||||
return _pos(*args)
|
||||
|
||||
def scan(*args):
|
||||
"""
|
||||
scan scn start stop step {scn {target}} {det t}
|
||||
"""
|
||||
return _scan(*args)
|
||||
|
||||
|
||||
from diffcalc.gdasupport.scannable.sim import sim # @UnusedImport
|
||||
|
||||
_scn_group = settings.axes_scannable_group
|
||||
_diff_scn_name = settings.geometry.name # @UndefinedVariable
|
||||
_energy_scannable = settings.energy_scannable
|
||||
|
||||
|
||||
# Create diffractometer scannable
|
||||
_diff_scn = DiffractometerScannableGroup(_diff_scn_name, _dc, _scn_group)
|
||||
globals()[_diff_scn_name] = _diff_scn
|
||||
|
||||
# Create hkl scannables
|
||||
hkl = Hkl('hkl', _scn_group, _dc)
|
||||
h = hkl.h
|
||||
k = hkl.k
|
||||
l = hkl.l
|
||||
|
||||
Hkl.dynamic_docstring = format_command_help(hkl_commands_for_help) # must be on the class
|
||||
ub.__doc__ = format_command_help(ub_commands_for_help)
|
||||
|
||||
_virtual_angles = ('theta', 'qaz', 'alpha', 'naz', 'tau', 'psi', 'beta')
|
||||
hklverbose = Hkl('hklverbose', _scn_group, _dc, _virtual_angles)
|
||||
|
||||
|
||||
# Create wavelength scannable
|
||||
wl = Wavelength(
|
||||
'wl', _energy_scannable, settings.energy_scannable_multiplier_to_get_KeV)
|
||||
if not GDA:
|
||||
wl.asynchronousMoveTo(1) # Angstrom
|
||||
_energy_scannable.level = 3
|
||||
wl.level = 3
|
||||
|
||||
|
||||
# Create simulated counter timer
|
||||
ct = SimulatedCrystalCounter('ct', _scn_group, settings.geometry, wl)
|
||||
ct.level = 10
|
||||
|
||||
|
||||
# Create constraint scannables
|
||||
def _create_constraint_scannable(con_name, scn_name=None):
|
||||
if not scn_name:
|
||||
scn_name = con_name
|
||||
return DiffractionCalculatorParameter(
|
||||
scn_name, con_name, _dc.constraint_manager)
|
||||
|
||||
# Detector constraints
|
||||
def isconstrainable(name):
|
||||
return not constraint_manager.is_constraint_fixed(name)
|
||||
|
||||
if isconstrainable('delta'): delta_con = _create_constraint_scannable('delta', 'delta_con')
|
||||
if isconstrainable('gam'): gam_con = _create_constraint_scannable('gam', 'gam_con')
|
||||
if isconstrainable('qaz'): qaz = _create_constraint_scannable('qaz')
|
||||
if isconstrainable('naz'): naz = _create_constraint_scannable('naz')
|
||||
|
||||
# Reference constraints
|
||||
alpha = _create_constraint_scannable('alpha')
|
||||
beta = _create_constraint_scannable('beta')
|
||||
psi = _create_constraint_scannable('psi')
|
||||
a_eq_b = 'a_eq_b'
|
||||
|
||||
# Sample constraints
|
||||
if isconstrainable('mu'): mu_con = _create_constraint_scannable('mu', 'mu_con')
|
||||
if isconstrainable('eta'): eta_con = _create_constraint_scannable('eta', 'eta_con')
|
||||
if isconstrainable('chi'): chi_con = _create_constraint_scannable('chi', 'chi_con')
|
||||
if isconstrainable('phi'): phi_con = _create_constraint_scannable('phi', 'phi_con')
|
||||
if isconstrainable('mu') and isconstrainable('gam'): mu_is_gam = 'mu_is_gam'
|
||||
|
||||
|
||||
# Cleanup to allow "from gdasupport.you import *"
|
||||
del DiffractometerScannableGroup, Hkl, SimulatedCrystalCounter
|
||||
del Wavelength, DiffractionCalculatorParameter
|
||||
|
||||
# Cleanup other cruft
|
||||
del format_command_help
|
||||
382
script/__Lib/diffcalc-2.1/diffcalc/hardware.py
Executable file
382
script/__Lib/diffcalc-2.1/diffcalc/hardware.py
Executable file
@@ -0,0 +1,382 @@
|
||||
###
|
||||
# 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()
|
||||
0
script/__Lib/diffcalc-2.1/diffcalc/hkl/__init__.py
Executable file
0
script/__Lib/diffcalc-2.1/diffcalc/hkl/__init__.py
Executable file
155
script/__Lib/diffcalc-2.1/diffcalc/hkl/calcbase.py
Executable file
155
script/__Lib/diffcalc-2.1/diffcalc/hkl/calcbase.py
Executable file
@@ -0,0 +1,155 @@
|
||||
###
|
||||
# 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 math import pi
|
||||
|
||||
from diffcalc.util import DiffcalcException, differ
|
||||
|
||||
TORAD = pi / 180
|
||||
TODEG = 180 / pi
|
||||
|
||||
|
||||
class HklCalculatorBase(object):
|
||||
|
||||
def __init__(self, ubcalc, geometry, hardware,
|
||||
raiseExceptionsIfAnglesDoNotMapBackToHkl=False):
|
||||
|
||||
self._ubcalc = ubcalc # to get the UBMatrix, tau and sigma
|
||||
self._geometry = geometry # to access information about the
|
||||
# diffractometer geometry and mode_selector
|
||||
self._hardware = hardware # Used for tracking parameters only
|
||||
self.raiseExceptionsIfAnglesDoNotMapBackToHkl = \
|
||||
raiseExceptionsIfAnglesDoNotMapBackToHkl
|
||||
|
||||
def anglesToHkl(self, pos, wavelength):
|
||||
"""
|
||||
Return hkl tuple and dictionary of all virtual angles in degrees from
|
||||
Position in degrees and wavelength in Angstroms.
|
||||
"""
|
||||
|
||||
h, k, l = self._anglesToHkl(pos.inRadians(), wavelength)
|
||||
paramDict = self.anglesToVirtualAngles(pos, wavelength)
|
||||
return ((h, k, l), paramDict)
|
||||
|
||||
def anglesToVirtualAngles(self, pos, wavelength):
|
||||
"""
|
||||
Return dictionary of all virtual angles in degrees from Position object
|
||||
in degrees and wavelength in Angstroms.
|
||||
"""
|
||||
anglesDict = self._anglesToVirtualAngles(pos.inRadians(), wavelength)
|
||||
for name in anglesDict:
|
||||
anglesDict[name] = anglesDict[name] * TODEG
|
||||
return anglesDict
|
||||
|
||||
def hklToAngles(self, h, k, l, wavelength):
|
||||
"""
|
||||
Return verified Position and all virtual angles in degrees from
|
||||
h, k & l and wavelength in Angstroms.
|
||||
|
||||
The calculated Position is verified by checking that it maps back using
|
||||
anglesToHkl() to the requested hkl value.
|
||||
|
||||
Those virtual angles fixed or generated while calculating the position
|
||||
are verified by by checking that they map back using
|
||||
anglesToVirtualAngles to the virtual angles for the given position.
|
||||
|
||||
Throws a DiffcalcException if either check fails and
|
||||
raiseExceptionsIfAnglesDoNotMapBackToHkl is True, otherwise displays a
|
||||
warning.
|
||||
"""
|
||||
|
||||
# Update tracked parameters. During this calculation parameter values
|
||||
# will be read directly from self._parameters instead of via
|
||||
# self.getParameter which would trigger another potentially time-costly
|
||||
# position update.
|
||||
self.parameter_manager.update_tracked()
|
||||
|
||||
pos, virtualAngles = self._hklToAngles(h, k, l, wavelength) # in rad
|
||||
|
||||
# to degrees:
|
||||
pos.changeToDegrees()
|
||||
|
||||
for key, val in virtualAngles.items():
|
||||
if val is not None:
|
||||
virtualAngles[key] = val * TODEG
|
||||
|
||||
self._verify_pos_map_to_hkl(h, k, l, wavelength, pos)
|
||||
|
||||
virtualAnglesReadback = self._verify_virtual_angles(h, k, l, wavelength, pos, virtualAngles)
|
||||
|
||||
return pos, virtualAnglesReadback
|
||||
|
||||
def _verify_pos_map_to_hkl(self, h, k, l, wavelength, pos):
|
||||
hkl, _ = self.anglesToHkl(pos, wavelength)
|
||||
e = 0.001
|
||||
if ((abs(hkl[0] - h) > e) or (abs(hkl[1] - k) > e) or
|
||||
(abs(hkl[2] - l) > e)):
|
||||
s = "ERROR: The angles calculated for hkl=(%f,%f,%f) were %s.\n" % (h, k, l, str(pos))
|
||||
s += "Converting these angles back to hkl resulted in hkl="\
|
||||
"(%f,%f,%f)" % (hkl[0], hkl[1], hkl[2])
|
||||
if self.raiseExceptionsIfAnglesDoNotMapBackToHkl:
|
||||
raise DiffcalcException(s)
|
||||
else:
|
||||
print s
|
||||
|
||||
def _verify_virtual_angles(self, h, k, l, wavelength, pos, virtualAngles):
|
||||
# Check that the virtual angles calculated/fixed during the hklToAngles
|
||||
# those read back from pos using anglesToVirtualAngles
|
||||
virtualAnglesReadback = self.anglesToVirtualAngles(pos, wavelength)
|
||||
for key, val in virtualAngles.items():
|
||||
if val != None: # Some values calculated in some mode_selector
|
||||
r = virtualAnglesReadback[key]
|
||||
if ((differ(val, r, .00001) and differ(val, r + 360, .00001) and differ(val, r - 360, .00001))):
|
||||
s = "ERROR: The angles calculated for hkl=(%f,%f,%f) with"\
|
||||
" mode=%s were %s.\n" % (h, k, l, self.repr_mode(), str(pos))
|
||||
s += "During verification the virtual angle %s resulting "\
|
||||
"from (or set for) this calculation of %f" % (key, val)
|
||||
s += "did not match that calculated by "\
|
||||
"anglesToVirtualAngles of %f" % virtualAnglesReadback[key]
|
||||
if self.raiseExceptionsIfAnglesDoNotMapBackToHkl:
|
||||
raise DiffcalcException(s)
|
||||
else:
|
||||
print s
|
||||
|
||||
return virtualAnglesReadback
|
||||
|
||||
def repr_mode(self):
|
||||
pass
|
||||
|
||||
### Collect all math access to context here
|
||||
|
||||
def _getUBMatrix(self):
|
||||
return self._ubcalc.UB
|
||||
|
||||
def _getMode(self):
|
||||
return self.mode_selector.getMode()
|
||||
|
||||
def _getSigma(self):
|
||||
return self._ubcalc.sigma
|
||||
|
||||
def _getTau(self):
|
||||
return self._ubcalc.tau
|
||||
|
||||
def _getParameter(self, name):
|
||||
# Does not use context.getParameter as this will trigger a costly
|
||||
# parameter collection
|
||||
pm = self.parameter_manager
|
||||
return pm.getParameterWithoutUpdatingTrackedParemeters(name)
|
||||
|
||||
def _getGammaParameterName(self):
|
||||
return self._gammaParameterName
|
||||
55
script/__Lib/diffcalc-2.1/diffcalc/hkl/common.py
Executable file
55
script/__Lib/diffcalc-2.1/diffcalc/hkl/common.py
Executable file
@@ -0,0 +1,55 @@
|
||||
###
|
||||
# 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 allnum
|
||||
|
||||
def getNameFromScannableOrString(o):
|
||||
try: # it may be a scannable
|
||||
return o.getName()
|
||||
except AttributeError:
|
||||
return str(o)
|
||||
raise TypeError()
|
||||
|
||||
|
||||
class DummyParameterManager(object):
|
||||
|
||||
def getParameterDict(self):
|
||||
return {}
|
||||
|
||||
def _setParameter(self, name, value):
|
||||
raise KeyError(name)
|
||||
|
||||
def _getParameter(self, name):
|
||||
raise KeyError(name)
|
||||
|
||||
def update_tracked(self):
|
||||
pass
|
||||
|
||||
|
||||
def sim(self, scn, hkl):
|
||||
"""sim hkl scn -- simulates moving scannable (not all)
|
||||
"""
|
||||
if not isinstance(hkl, (tuple, list)):
|
||||
raise TypeError
|
||||
|
||||
if not allnum(hkl):
|
||||
raise TypeError()
|
||||
|
||||
try:
|
||||
print scn.simulateMoveTo(hkl)
|
||||
except AttributeError:
|
||||
raise TypeError("The first argument does not support simulated moves")
|
||||
0
script/__Lib/diffcalc-2.1/diffcalc/hkl/vlieg/__init__.py
Executable file
0
script/__Lib/diffcalc-2.1/diffcalc/hkl/vlieg/__init__.py
Executable file
846
script/__Lib/diffcalc-2.1/diffcalc/hkl/vlieg/calc.py
Executable file
846
script/__Lib/diffcalc-2.1/diffcalc/hkl/vlieg/calc.py
Executable file
@@ -0,0 +1,846 @@
|
||||
###
|
||||
# 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 math import pi, asin, acos, sin, cos, sqrt, atan2, fabs, atan
|
||||
|
||||
try:
|
||||
from numpy import matrix
|
||||
from numpy.linalg import norm
|
||||
except ImportError:
|
||||
from numjy import matrix
|
||||
from numjy.linalg import norm
|
||||
|
||||
from diffcalc.hkl.calcbase import HklCalculatorBase
|
||||
from diffcalc.hkl.vlieg.transform import TransformCInRadians
|
||||
from diffcalc.util import dot3, cross3, bound, differ
|
||||
from diffcalc.hkl.vlieg.geometry import createVliegMatrices, \
|
||||
createVliegsPsiTransformationMatrix, \
|
||||
createVliegsSurfaceTransformationMatrices, calcPHI
|
||||
from diffcalc.hkl.vlieg.geometry import VliegPosition
|
||||
from diffcalc.hkl.vlieg.constraints import VliegParameterManager
|
||||
from diffcalc.hkl.vlieg.constraints import ModeSelector
|
||||
from diffcalc.ub.calc import PaperSpecificUbCalcStrategy
|
||||
|
||||
|
||||
TORAD = pi / 180
|
||||
TODEG = 180 / pi
|
||||
transformC = TransformCInRadians()
|
||||
|
||||
|
||||
PREFER_POSITIVE_CHI_SOLUTIONS = True
|
||||
|
||||
I = matrix('1 0 0; 0 1 0; 0 0 1')
|
||||
y = matrix('0; 1; 0')
|
||||
|
||||
|
||||
def check(condition, ErrorOrStringOrCallable, *args):
|
||||
"""
|
||||
fail = check(condition, ErrorOrString) -- if condition is false raises the
|
||||
Exception passed in, or creates one from a string. If a callable function
|
||||
is passed in this is called with any args specified and the thing returns
|
||||
false.
|
||||
"""
|
||||
# TODO: Remove (really nasty) check function
|
||||
if condition == False:
|
||||
if callable(ErrorOrStringOrCallable):
|
||||
ErrorOrStringOrCallable(*args)
|
||||
return False
|
||||
elif isinstance(ErrorOrStringOrCallable, str):
|
||||
raise Exception(ErrorOrStringOrCallable)
|
||||
else: # assume input is an exception
|
||||
raise ErrorOrStringOrCallable
|
||||
return True
|
||||
|
||||
|
||||
def sign(x):
|
||||
if x < 0:
|
||||
return -1
|
||||
else:
|
||||
return 1
|
||||
|
||||
|
||||
def vliegAnglesToHkl(pos, wavelength, UBMatrix):
|
||||
"""
|
||||
Returns hkl indices from pos object in radians.
|
||||
"""
|
||||
wavevector = 2 * pi / wavelength
|
||||
|
||||
# Create transformation matrices
|
||||
[ALPHA, DELTA, GAMMA, OMEGA, CHI, PHI] = createVliegMatrices(
|
||||
pos.alpha, pos.delta, pos.gamma, pos.omega, pos.chi, pos.phi)
|
||||
|
||||
# Create the plane normal vector in the alpha axis coordinate frame
|
||||
qa = ((DELTA * GAMMA) - ALPHA.I) * matrix([[0], [wavevector], [0]])
|
||||
|
||||
# Transform the plane normal vector from the alpha frame to reciprical
|
||||
# lattice frame.
|
||||
hkl = UBMatrix.I * PHI.I * CHI.I * OMEGA.I * qa
|
||||
|
||||
return hkl[0, 0], hkl[1, 0], hkl[2, 0]
|
||||
|
||||
|
||||
class VliegUbCalcStrategy(PaperSpecificUbCalcStrategy):
|
||||
|
||||
def calculate_q_phi(self, pos):
|
||||
|
||||
[ALPHA, DELTA, GAMMA, OMEGA, CHI, PHI] = createVliegMatrices(
|
||||
pos.alpha, pos.delta, pos.gamma, pos.omega, pos.chi, pos.phi)
|
||||
|
||||
u1a = (DELTA * GAMMA - ALPHA.I) * y
|
||||
u1p = PHI.I * CHI.I * OMEGA.I * u1a
|
||||
return u1p
|
||||
|
||||
|
||||
class VliegHklCalculator(HklCalculatorBase):
|
||||
|
||||
def __init__(self, ubcalc, geometry, hardware,
|
||||
raiseExceptionsIfAnglesDoNotMapBackToHkl=True):
|
||||
r = raiseExceptionsIfAnglesDoNotMapBackToHkl
|
||||
HklCalculatorBase.__init__(self, ubcalc, geometry, hardware,
|
||||
raiseExceptionsIfAnglesDoNotMapBackToHkl=r)
|
||||
self._gammaParameterName = ({'arm': 'gamma', 'base': 'oopgamma'}
|
||||
[self._geometry.gamma_location])
|
||||
self.mode_selector = ModeSelector(self._geometry, None,
|
||||
self._gammaParameterName)
|
||||
self.parameter_manager = VliegParameterManager(
|
||||
self._geometry, self._hardware, self.mode_selector,
|
||||
self._gammaParameterName)
|
||||
self.mode_selector.setParameterManager(self.parameter_manager)
|
||||
|
||||
def __str__(self):
|
||||
# should list paramemeters and indicate which are used in selected mode
|
||||
result = "Available mode_selector:\n"
|
||||
result += self.mode_selector.reportAvailableModes()
|
||||
result += '\nCurrent mode:\n'
|
||||
result += self.mode_selector.reportCurrentMode()
|
||||
result += '\n\nParameters:\n'
|
||||
result += self.parameter_manager.reportAllParameters()
|
||||
return result
|
||||
|
||||
def _anglesToHkl(self, pos, wavelength):
|
||||
"""
|
||||
Return hkl tuple from VliegPosition in radians and wavelength in
|
||||
Angstroms.
|
||||
"""
|
||||
return vliegAnglesToHkl(pos, wavelength, self._getUBMatrix())
|
||||
|
||||
def _anglesToVirtualAngles(self, pos, wavelength):
|
||||
"""
|
||||
Return dictionary of all virtual angles in radians from VliegPosition
|
||||
object win radians and wavelength in Angstroms. The virtual angles are:
|
||||
Bin, Bout, azimuth and 2theta.
|
||||
"""
|
||||
|
||||
# Create transformation matrices
|
||||
[ALPHA, DELTA, GAMMA, OMEGA, CHI, PHI] = createVliegMatrices(
|
||||
pos.alpha, pos.delta, pos.gamma, pos.omega, pos.chi, pos.phi)
|
||||
[SIGMA, TAU] = createVliegsSurfaceTransformationMatrices(
|
||||
self._getSigma() * TORAD, self._getTau() * TORAD)
|
||||
|
||||
S = TAU * SIGMA
|
||||
y_vector = matrix([[0], [1], [0]])
|
||||
|
||||
# Calculate Bin from equation 15:
|
||||
surfacenormal_alpha = OMEGA * CHI * PHI * S * matrix([[0], [0], [1]])
|
||||
incoming_alpha = ALPHA.I * y_vector
|
||||
minusSinBetaIn = dot3(surfacenormal_alpha, incoming_alpha)
|
||||
Bin = asin(bound(-minusSinBetaIn))
|
||||
|
||||
# Calculate Bout from equation 16:
|
||||
# surfacenormal_alpha has just ben calculated
|
||||
outgoing_alpha = DELTA * GAMMA * y_vector
|
||||
sinBetaOut = dot3(surfacenormal_alpha, outgoing_alpha)
|
||||
Bout = asin(bound(sinBetaOut))
|
||||
|
||||
# Calculate 2theta from equation 25:
|
||||
|
||||
cosTwoTheta = dot3(ALPHA * DELTA * GAMMA * y_vector, y_vector)
|
||||
twotheta = acos(bound(cosTwoTheta))
|
||||
psi = self._anglesToPsi(pos, wavelength)
|
||||
|
||||
return {'Bin': Bin, 'Bout': Bout, 'azimuth': psi, '2theta': twotheta}
|
||||
|
||||
def _hklToAngles(self, h, k, l, wavelength):
|
||||
"""
|
||||
Return VliegPosition and virtual angles in radians from h, k & l and
|
||||
wavelength in Angstroms. The virtual angles are those fixed or
|
||||
generated while calculating the position: Bin, Bout and 2theta; and
|
||||
azimuth in four and five circle modes.
|
||||
"""
|
||||
|
||||
if self._getMode().group in ("fourc", "fivecFixedGamma",
|
||||
"fivecFixedAlpha"):
|
||||
return self._hklToAnglesFourAndFiveCirclesModes(h, k, l,
|
||||
wavelength)
|
||||
elif self._getMode().group == "zaxis":
|
||||
return self._hklToAnglesZaxisModes(h, k, l, wavelength)
|
||||
else:
|
||||
raise RuntimeError(
|
||||
'The current mode (%s) has an unrecognised group: %s.'
|
||||
% (self._getMode().name, self._getMode().group))
|
||||
|
||||
def _hklToAnglesFourAndFiveCirclesModes(self, h, k, l, wavelength):
|
||||
"""
|
||||
Return VliegPosition and virtual angles in radians from h, k & l and
|
||||
wavelength in Angstrom for four and five circle modes. The virtual
|
||||
angles are those fixed or generated while calculating the position:
|
||||
Bin, Bout, 2theta and azimuth.
|
||||
"""
|
||||
|
||||
# Results in radians during calculations, returned in degreess
|
||||
pos = VliegPosition(None, None, None, None, None, None)
|
||||
|
||||
# Normalise hkl
|
||||
wavevector = 2 * pi / wavelength
|
||||
hklNorm = matrix([[h], [k], [l]]) / wavevector
|
||||
|
||||
# Compute hkl in phi axis coordinate frame
|
||||
hklPhiNorm = self._getUBMatrix() * hklNorm
|
||||
|
||||
# Determine Bin and Bout
|
||||
if self._getMode().name == '4cPhi':
|
||||
Bin = Bout = None
|
||||
else:
|
||||
Bin, Bout = self._determineBinAndBoutInFourAndFiveCirclesModes(
|
||||
hklNorm)
|
||||
|
||||
# Determine alpha and gamma
|
||||
if self._getMode().group == 'fourc':
|
||||
pos.alpha, pos.gamma = \
|
||||
self._determineAlphaAndGammaForFourCircleModes(hklPhiNorm)
|
||||
else:
|
||||
pos.alpha, pos.gamma = \
|
||||
self._determineAlphaAndGammaForFiveCircleModes(Bin, hklPhiNorm)
|
||||
if pos.alpha < -pi:
|
||||
pos.alpha += 2 * pi
|
||||
if pos.alpha > pi:
|
||||
pos.alpha -= 2 * pi
|
||||
|
||||
# Determine delta
|
||||
(pos.delta, twotheta) = self._determineDelta(hklPhiNorm, pos.alpha,
|
||||
pos.gamma)
|
||||
|
||||
# Determine omega, chi & phi
|
||||
pos.omega, pos.chi, pos.phi, psi = \
|
||||
self._determineSampleAnglesInFourAndFiveCircleModes(
|
||||
hklPhiNorm, pos.alpha, pos.delta, pos.gamma, Bin)
|
||||
# (psi will be None in fixed phi mode)
|
||||
|
||||
# Ensure that by default omega is between -90 and 90, by possibly
|
||||
# transforming the sample angles
|
||||
if self._getMode().name != '4cPhi': # not in fixed-phi mode
|
||||
if pos.omega < -pi / 2 or pos.omega > pi / 2:
|
||||
pos = transformC.transform(pos)
|
||||
|
||||
# Gather up the virtual angles calculated along the way...
|
||||
# -pi<psi<=pi
|
||||
if psi is not None:
|
||||
if psi > pi:
|
||||
psi -= 2 * pi
|
||||
if psi < (-1 * pi):
|
||||
psi += 2 * pi
|
||||
|
||||
v = {'2theta': twotheta, 'Bin': Bin, 'Bout': Bout, 'azimuth': psi}
|
||||
return pos, v
|
||||
|
||||
def _hklToAnglesZaxisModes(self, h, k, l, wavelength):
|
||||
"""
|
||||
Return VliegPosition and virtual angles in radians from h, k & l and
|
||||
wavelength in Angstroms for z-axis modes. The virtual angles are those
|
||||
fixed or generated while calculating the position: Bin, Bout, and
|
||||
2theta.
|
||||
"""
|
||||
# Section 6:
|
||||
|
||||
# Results in radians during calculations, returned in degreess
|
||||
pos = VliegPosition(None, None, None, None, None, None)
|
||||
|
||||
# Normalise hkl
|
||||
wavevector = 2 * pi / wavelength
|
||||
hkl = matrix([[h], [k], [l]])
|
||||
hklNorm = hkl * (1.0 / wavevector)
|
||||
|
||||
# Compute hkl in phi axis coordinate frame
|
||||
hklPhi = self._getUBMatrix() * hkl
|
||||
hklPhiNorm = self._getUBMatrix() * hklNorm
|
||||
|
||||
# Determine Chi and Phi (Equation 29):
|
||||
pos.phi = -self._getTau() * TORAD
|
||||
pos.chi = -self._getSigma() * TORAD
|
||||
|
||||
# Equation 30:
|
||||
[ALPHA, DELTA, GAMMA, OMEGA, CHI, PHI] = createVliegMatrices(
|
||||
None, None, None, None, pos.chi, pos.phi)
|
||||
del ALPHA, DELTA, GAMMA, OMEGA
|
||||
Hw = CHI * PHI * hklPhi
|
||||
|
||||
# Determine Bin and Bout:
|
||||
(Bin, Bout) = self._determineBinAndBoutInZaxisModes(
|
||||
Hw[2, 0] / wavevector)
|
||||
|
||||
# Determine Alpha and Gamma (Equation 32):
|
||||
pos.alpha = Bin
|
||||
pos.gamma = Bout
|
||||
|
||||
# Determine Delta:
|
||||
(pos.delta, twotheta) = self._determineDelta(hklPhiNorm, pos.alpha,
|
||||
pos.gamma)
|
||||
|
||||
# Determine Omega:
|
||||
delta = pos.delta
|
||||
gamma = pos.gamma
|
||||
d1 = (Hw[1, 0] * sin(delta) * cos(gamma) - Hw[0, 0] *
|
||||
(cos(delta) * cos(gamma) - cos(pos.alpha)))
|
||||
d2 = (Hw[0, 0] * sin(delta) * cos(gamma) + Hw[1, 0] *
|
||||
(cos(delta) * cos(gamma) - cos(pos.alpha)))
|
||||
|
||||
if fabs(d2) < 1e-30:
|
||||
pos.omega = sign(d1) * sign(d2) * pi / 2.0
|
||||
else:
|
||||
pos.omega = atan2(d1, d2)
|
||||
|
||||
# Gather up the virtual angles calculated along the way
|
||||
return pos, {'2theta': twotheta, 'Bin': Bin, 'Bout': Bout}
|
||||
|
||||
###
|
||||
|
||||
def _determineBinAndBoutInFourAndFiveCirclesModes(self, hklNorm):
|
||||
"""(Bin, Bout) = _determineBinAndBoutInFourAndFiveCirclesModes()"""
|
||||
BinModes = ('4cBin', '5cgBin', '5caBin')
|
||||
BoutModes = ('4cBout', '5cgBout', '5caBout')
|
||||
BeqModes = ('4cBeq', '5cgBeq', '5caBeq')
|
||||
azimuthModes = ('4cAzimuth')
|
||||
fixedBusingAndLeviWmodes = ('4cFixedw')
|
||||
|
||||
# Calculate RHS of equation 20
|
||||
# RHS (1/K)(S^-1*U*B*H)_3 where H/K = hklNorm
|
||||
UB = self._getUBMatrix()
|
||||
[SIGMA, TAU] = createVliegsSurfaceTransformationMatrices(
|
||||
self._getSigma() * TORAD, self._getTau() * TORAD)
|
||||
#S = SIGMA * TAU
|
||||
S = TAU * SIGMA
|
||||
RHS = (S.I * UB * hklNorm)[2, 0]
|
||||
|
||||
if self._getMode().name in BinModes:
|
||||
Bin = self._getParameter('betain')
|
||||
check(Bin != None, "The parameter betain must be set for mode %s" %
|
||||
self._getMode().name)
|
||||
Bin = Bin * TORAD
|
||||
sinBout = RHS - sin(Bin)
|
||||
check(fabs(sinBout) <= 1, "Could not compute Bout")
|
||||
Bout = asin(sinBout)
|
||||
|
||||
elif self._getMode().name in BoutModes:
|
||||
Bout = self._getParameter('betaout')
|
||||
check(Bout != None, "The parameter Bout must be set for mode %s" %
|
||||
self._getMode().name)
|
||||
Bout = Bout * TORAD
|
||||
sinBin = RHS - sin(Bout)
|
||||
check(fabs(sinBin) <= 1, "Could not compute Bin")
|
||||
Bin = asin(sinBin)
|
||||
|
||||
elif self._getMode().name in BeqModes:
|
||||
sinBeq = RHS / 2
|
||||
check(fabs(sinBeq) <= 1, "Could not compute Bin=Bout")
|
||||
Bin = Bout = asin(sinBeq)
|
||||
|
||||
elif self._getMode().name in azimuthModes:
|
||||
azimuth = self._getParameter('azimuth')
|
||||
check(azimuth != None, "The parameter azimuth must be set for "
|
||||
"mode %s" % self._getMode().name)
|
||||
del azimuth
|
||||
# TODO: codeit
|
||||
raise NotImplementedError()
|
||||
|
||||
elif self._getMode().name in fixedBusingAndLeviWmodes:
|
||||
bandlomega = self._getParameter('blw')
|
||||
check(bandlomega != None, "The parameter abandlomega must be set "
|
||||
"for mode %s" % self._getMode().name)
|
||||
del bandlomega
|
||||
# TODO: codeit
|
||||
raise NotImplementedError()
|
||||
else:
|
||||
raise RuntimeError("AngleCalculator does not know how to handle "
|
||||
"mode %s" % self._getMode().name)
|
||||
|
||||
return (Bin, Bout)
|
||||
|
||||
def _determineBinAndBoutInZaxisModes(self, Hw3OverK):
|
||||
"""(Bin, Bout) = _determineBinAndBoutInZaxisModes(HwOverK)"""
|
||||
BinModes = ('6czBin')
|
||||
BoutModes = ('6czBout')
|
||||
BeqModes = ('6czBeq')
|
||||
|
||||
if self._getMode().name in BinModes:
|
||||
Bin = self._getParameter('betain')
|
||||
check(Bin != None, "The parameter betain must be set for mode %s" %
|
||||
self._getMode().name)
|
||||
Bin = Bin * TORAD
|
||||
# Equation 32a:
|
||||
Bout = asin(Hw3OverK - sin(Bin))
|
||||
|
||||
elif self._getMode().name in BoutModes:
|
||||
Bout = self._getParameter('betaout')
|
||||
check(Bout != None, "The parameter Bout must be set for mode %s" %
|
||||
self._getMode().name)
|
||||
Bout = Bout * TORAD
|
||||
# Equation 32b:
|
||||
Bin = asin(Hw3OverK - sin(Bout))
|
||||
|
||||
elif self._getMode().name in BeqModes:
|
||||
# Equation 32c:
|
||||
Bin = Bout = asin(Hw3OverK / 2)
|
||||
|
||||
return (Bin, Bout)
|
||||
|
||||
###
|
||||
|
||||
def _determineAlphaAndGammaForFourCircleModes(self, hklPhiNorm):
|
||||
|
||||
if self._getMode().group == 'fourc':
|
||||
alpha = self._getParameter('alpha') * TORAD
|
||||
gamma = self._getParameter(self._getGammaParameterName()) * TORAD
|
||||
check(alpha != None, "alpha parameter must be set in fourc modes")
|
||||
check(gamma != None, "gamma parameter must be set in fourc modes")
|
||||
return alpha, gamma
|
||||
else:
|
||||
raise RuntimeError(
|
||||
"determineAlphaAndGammaForFourCirclesModes() "
|
||||
"is not appropriate for %s modes" % self._getMode().group)
|
||||
|
||||
def _determineAlphaAndGammaForFiveCircleModes(self, Bin, hklPhiNorm):
|
||||
|
||||
## Solve equation 34 for one possible Y, Yo
|
||||
# Calculate surface normal in phi frame
|
||||
[SIGMA, TAU] = createVliegsSurfaceTransformationMatrices(
|
||||
self._getSigma() * TORAD, self._getTau() * TORAD)
|
||||
S = TAU * SIGMA
|
||||
surfaceNormalPhi = S * matrix([[0], [0], [1]])
|
||||
# Compute beta in vector
|
||||
BetaVector = matrix([[0], [-sin(Bin)], [cos(Bin)]])
|
||||
# Find Yo
|
||||
Yo = self._findMatrixToTransformAIntoB(surfaceNormalPhi, BetaVector)
|
||||
|
||||
## Calculate Hv from equation 39
|
||||
Z = matrix([[1, 0, 0],
|
||||
[0, cos(Bin), sin(Bin)],
|
||||
[0, -sin(Bin), cos(Bin)]])
|
||||
Hv = Z * Yo * hklPhiNorm
|
||||
# Fixed gamma:
|
||||
if self._getMode().group == 'fivecFixedGamma':
|
||||
gamma = self._getParameter(self._getGammaParameterName())
|
||||
check(gamma != None,
|
||||
"gamma parameter must be set in fivecFixedGamma modes")
|
||||
gamma = gamma * TORAD
|
||||
H2 = (hklPhiNorm[0, 0] ** 2 + hklPhiNorm[1, 0] ** 2 +
|
||||
hklPhiNorm[2, 0] ** 2)
|
||||
a = -(0.5 * H2 * sin(Bin) - Hv[2, 0])
|
||||
b = -(1.0 - 0.5 * H2) * cos(Bin)
|
||||
c = cos(Bin) * sin(gamma)
|
||||
check((b * b + a * a - c * c) >= 0, 'Could not solve for alpha')
|
||||
alpha = 2 * atan2(-(b + sqrt(b * b + a * a - c * c)), -(a + c))
|
||||
|
||||
# Fixed Alpha:
|
||||
elif self._getMode().group == 'fivecFixedAlpha':
|
||||
alpha = self._getParameter('alpha')
|
||||
check(alpha != None,
|
||||
"alpha parameter must be set in fivecFixedAlpha modes")
|
||||
alpha = alpha * TORAD
|
||||
H2 = (hklPhiNorm[0, 0] ** 2 + hklPhiNorm[1, 0] ** 2 +
|
||||
hklPhiNorm[2, 0] ** 2)
|
||||
t0 = ((2 * cos(alpha) * Hv[2, 0] - sin(Bin) * cos(alpha) * H2 +
|
||||
cos(Bin) * sin(alpha) * H2 - 2 * cos(Bin) * sin(alpha)) /
|
||||
(cos(Bin) * 2.0))
|
||||
check(abs(t0) <= 1, "Cannot compute gamma: sin(gamma)>1")
|
||||
gamma = asin(t0)
|
||||
else:
|
||||
raise RuntimeError(
|
||||
"determineAlphaAndGammaInFiveCirclesModes() is not "
|
||||
"appropriate for %s modes" % self._getMode().group)
|
||||
|
||||
return (alpha, gamma)
|
||||
|
||||
###
|
||||
|
||||
def _determineDelta(self, hklPhiNorm, alpha, gamma):
|
||||
"""
|
||||
(delta, twotheta) = _determineDelta(hklPhiNorm, alpha, gamma) --
|
||||
computes delta for all modes. Also returns twotheta for sanity
|
||||
checking. hklPhiNorm is a 3X1 matrix.
|
||||
|
||||
alpha, gamma & delta - in radians.
|
||||
h k & l normalised to wavevector and in phi axis coordinates
|
||||
"""
|
||||
h = hklPhiNorm[0, 0]
|
||||
k = hklPhiNorm[1, 0]
|
||||
l = hklPhiNorm[2, 0]
|
||||
# See Vlieg section 5 (with K=1)
|
||||
cosdelta = ((1 + sin(gamma) * sin(alpha) - (h * h + k * k + l * l) / 2)
|
||||
/ (cos(gamma) * cos(alpha)))
|
||||
costwotheta = (cos(alpha) * cos(gamma) * bound(cosdelta) -
|
||||
sin(alpha) * sin(gamma))
|
||||
return (acos(bound(cosdelta)), acos(bound(costwotheta)))
|
||||
|
||||
def _determineSampleAnglesInFourAndFiveCircleModes(self, hklPhiNorm, alpha,
|
||||
delta, gamma, Bin):
|
||||
"""
|
||||
(omega, chi, phi, psi)=determineNonZAxisSampleAngles(hklPhiNorm, alpha,
|
||||
delta, gamma, sigma, tau) where hkl has been normalised by the
|
||||
wavevector and is in the phi Axis coordinate frame. All angles in
|
||||
radians. hklPhiNorm is a 3X1 matrix
|
||||
"""
|
||||
|
||||
def equation49through59(psi):
|
||||
# equation 49 R = (D^-1)*PI*D*Ro
|
||||
PSI = createVliegsPsiTransformationMatrix(psi)
|
||||
R = D.I * PSI * D * Ro
|
||||
|
||||
# eq 57: extract omega from R
|
||||
if abs(R[0, 2]) < 1e-20:
|
||||
omega = -sign(R[1, 2]) * sign(R[0, 2]) * pi / 2
|
||||
else:
|
||||
omega = -atan2(R[1, 2], R[0, 2])
|
||||
|
||||
# eq 58: extract chi from R
|
||||
sinchi = sqrt(pow(R[0, 2], 2) + pow(R[1, 2], 2))
|
||||
sinchi = bound(sinchi)
|
||||
check(abs(sinchi) <= 1, 'could not compute chi')
|
||||
# (there are two roots to this equation, but only the first is also
|
||||
# a solution to R33=cos(chi))
|
||||
chi = asin(sinchi)
|
||||
|
||||
# eq 59: extract phi from R
|
||||
if abs(R[2, 0]) < 1e-20:
|
||||
phi = sign(R[2, 1]) * sign(R[2, 1]) * pi / 2
|
||||
else:
|
||||
phi = atan2(-R[2, 1], -R[2, 0])
|
||||
return omega, chi, phi
|
||||
|
||||
def checkSolution(omega, chi, phi):
|
||||
_, _, _, OMEGA, CHI, PHI = createVliegMatrices(
|
||||
None, None, None, omega, chi, phi)
|
||||
R = OMEGA * CHI * PHI
|
||||
RtimesH_phi = R * H_phi
|
||||
print ("R*H_phi=%s, Q_alpha=%s" %
|
||||
(R * H_phi.tolist(), Q_alpha.tolist()))
|
||||
return not differ(RtimesH_phi, Q_alpha, .0001)
|
||||
|
||||
# Using Vlieg section 7.2
|
||||
|
||||
# Needed througout:
|
||||
[ALPHA, DELTA, GAMMA, _, _, _] = createVliegMatrices(
|
||||
alpha, delta, gamma, None, None, None)
|
||||
|
||||
## Find Ro, one possible solution to equation 46: R*H_phi=Q_alpha
|
||||
|
||||
# Normalise hklPhiNorm (As it is currently normalised only to the
|
||||
# wavevector)
|
||||
normh = norm(hklPhiNorm)
|
||||
check(normh >= 1e-10, "reciprical lattice vector too close to zero")
|
||||
H_phi = hklPhiNorm * (1 / normh)
|
||||
|
||||
# Create Q_alpha from equation 47, (it comes normalised)
|
||||
Q_alpha = ((DELTA * GAMMA) - ALPHA.I) * matrix([[0], [1], [0]])
|
||||
Q_alpha = Q_alpha * (1 / norm(Q_alpha))
|
||||
|
||||
if self._getMode().name == '4cPhi':
|
||||
### Use the fixed value of phi as the final constraint ###
|
||||
phi = self._getParameter('phi') * TORAD
|
||||
PHI = calcPHI(phi)
|
||||
H_chi = PHI * H_phi
|
||||
omega, chi = _findOmegaAndChiToRotateHchiIntoQalpha(H_chi, Q_alpha)
|
||||
return (omega, chi, phi, None) # psi = None as not calculated
|
||||
else:
|
||||
### Use Bin as the final constraint ###
|
||||
|
||||
# Find a solution Ro to Ro*H_phi=Q_alpha
|
||||
Ro = self._findMatrixToTransformAIntoB(H_phi, Q_alpha)
|
||||
|
||||
## equation 50: Find a solution D to D*Q=norm(Q)*[[1],[0],[0]])
|
||||
D = self._findMatrixToTransformAIntoB(
|
||||
Q_alpha, matrix([[1], [0], [0]]))
|
||||
|
||||
## Find psi and create PSI
|
||||
|
||||
# eq 54: compute u=D*Ro*S*[[0],[0],[1]], the surface normal in
|
||||
# psi frame
|
||||
[SIGMA, TAU] = createVliegsSurfaceTransformationMatrices(
|
||||
self._getSigma() * TORAD, self._getTau() * TORAD)
|
||||
S = TAU * SIGMA
|
||||
[u1], [u2], [u3] = (D * Ro * S * matrix([[0], [0], [1]])).tolist()
|
||||
# TODO: If u points along 100, then any psi is a solution. Choose 0
|
||||
if not differ([u1, u2, u3], [1, 0, 0], 1e-9):
|
||||
psi = 0
|
||||
omega, chi, phi = equation49through59(psi)
|
||||
else:
|
||||
# equation 53: V=A*(D^-1)
|
||||
V = ALPHA * D.I
|
||||
v21 = V[1, 0]
|
||||
v22 = V[1, 1]
|
||||
v23 = V[1, 2]
|
||||
# equation 55
|
||||
a = v22 * u2 + v23 * u3
|
||||
b = v22 * u3 - v23 * u2
|
||||
c = -sin(Bin) - v21 * u1 # TODO: changed sign from paper
|
||||
|
||||
# equation 44
|
||||
# Try first root:
|
||||
def myatan2(y, x):
|
||||
if abs(x) < 1e-20 and abs(y) < 1e-20:
|
||||
return pi / 2
|
||||
else:
|
||||
return atan2(y, x)
|
||||
psi = 2 * myatan2(-(b - sqrt(b * b + a * a - c * c)), -(a + c))
|
||||
#psi = -acos(c/sqrt(a*a+b*b))+atan2(b,a)# -2*pi
|
||||
omega, chi, phi = equation49through59(psi)
|
||||
|
||||
# if u points along z axis, the psi could have been either 0 or 180
|
||||
if (not differ([u1, u2, u3], [0, 0, 1], 1e-9) and
|
||||
abs(psi - pi) < 1e-10):
|
||||
# Choose 0 to match that read up by angles-to-virtual-angles
|
||||
psi = 0.
|
||||
# if u points a long
|
||||
return (omega, chi, phi, psi)
|
||||
|
||||
def _anglesToPsi(self, pos, wavelength):
|
||||
"""
|
||||
pos assumed in radians. -180<= psi <= 180
|
||||
"""
|
||||
# Using Vlieg section 7.2
|
||||
|
||||
# Needed througout:
|
||||
[ALPHA, DELTA, GAMMA, OMEGA, CHI, PHI] = createVliegMatrices(
|
||||
pos.alpha, pos.delta, pos.gamma, pos.omega, pos.chi, pos.phi)
|
||||
|
||||
# Solve equation 49 for psi, the rotation of the a reference solution
|
||||
# about Qalpha or H_phi##
|
||||
|
||||
# Find Ro, the reference solution to equation 46: R*H_phi=Q_alpha
|
||||
|
||||
# Create Q_alpha from equation 47, (it comes normalised)
|
||||
Q_alpha = ((DELTA * GAMMA) - ALPHA.I) * matrix([[0], [1], [0]])
|
||||
Q_alpha = Q_alpha * (1 / norm(Q_alpha))
|
||||
|
||||
# Finh H_phi
|
||||
h, k, l = self._anglesToHkl(pos, wavelength)
|
||||
H_phi = self._getUBMatrix() * matrix([[h], [k], [l]])
|
||||
normh = norm(H_phi)
|
||||
check(normh >= 1e-10, "reciprical lattice vector too close to zero")
|
||||
H_phi = H_phi * (1 / normh)
|
||||
|
||||
# Find a solution Ro to Ro*H_phi=Q_alpha
|
||||
# This the reference solution with zero azimuth (psi)
|
||||
Ro = self._findMatrixToTransformAIntoB(H_phi, Q_alpha)
|
||||
|
||||
# equation 48:
|
||||
R = OMEGA * CHI * PHI
|
||||
|
||||
## equation 50: Find a solution D to D*Q=norm(Q)*[[1],[0],[0]])
|
||||
D = self._findMatrixToTransformAIntoB(Q_alpha, matrix([[1], [0], [0]]))
|
||||
|
||||
# solve equation 49 for psi
|
||||
# D*R = PSI*D*Ro
|
||||
# D*R*(D*Ro)^-1 = PSI
|
||||
PSI = D * R * ((D * Ro).I)
|
||||
|
||||
# Find psi within PSI as defined in equation 51
|
||||
PSI_23 = PSI[1, 2]
|
||||
PSI_33 = PSI[2, 2]
|
||||
psi = atan2(PSI_23, PSI_33)
|
||||
|
||||
#print "PSI: ", PSI.tolist()
|
||||
return psi
|
||||
|
||||
def _findMatrixToTransformAIntoB(self, a, b):
|
||||
"""
|
||||
Finds a particular matrix Mo that transforms the unit vector a into the
|
||||
unit vector b. Thats is it finds Mo Mo*a=b. a and b 3x1 matrixes and Mo
|
||||
is a 3x3 matrix.
|
||||
|
||||
Throws an exception if this is not possible.
|
||||
"""
|
||||
# Maths from the appendix of "Angle caluculations
|
||||
# for a 5-circle diffractometer used for surface X-ray diffraction",
|
||||
# E. Vlieg, J.F. van der Veen, J.E. Macdonald and M. Miller, J. of
|
||||
# Applied Cryst. 20 (1987) 330.
|
||||
# - courtesy of Elias Vlieg again
|
||||
|
||||
# equation A2: compute angle xi between vectors a and b
|
||||
cosxi = dot3(a, b)
|
||||
try:
|
||||
cosxi = bound(cosxi)
|
||||
except ValueError:
|
||||
raise Exception("Could not compute cos(xi), vectors a=%f and b=%f "
|
||||
"must be of unit length" % (norm(a), norm(b)))
|
||||
xi = acos(cosxi)
|
||||
|
||||
# Mo is identity matrix if xi zero (math below would blow up)
|
||||
if abs(xi) < 1e-10:
|
||||
return I
|
||||
|
||||
# equation A3: c=cross(a,b)/sin(xi)
|
||||
c = cross3(a, b) * (1 / sin(xi))
|
||||
|
||||
# equation A4: find D matrix that transforms a into the frame
|
||||
# x = a; y = c x a; z = c. */
|
||||
a1 = a[0, 0]
|
||||
a2 = a[1, 0]
|
||||
a3 = a[2, 0]
|
||||
c1 = c[0, 0]
|
||||
c2 = c[1, 0]
|
||||
c3 = c[2, 0]
|
||||
D = matrix([[a1, a2, a3],
|
||||
[c2 * a3 - c3 * a2, c3 * a1 - c1 * a3, c1 * a2 - c2 * a1],
|
||||
[c1, c2, c3]])
|
||||
|
||||
# equation A5: create Xi to rotate by xi about z-axis
|
||||
XI = matrix([[cos(xi), -sin(xi), 0],
|
||||
[sin(xi), cos(xi), 0],
|
||||
[0, 0, 1]])
|
||||
|
||||
# eq A6: compute Mo
|
||||
return D.I * XI * D
|
||||
|
||||
|
||||
def _findOmegaAndChiToRotateHchiIntoQalpha(h_chi, q_alpha):
|
||||
"""
|
||||
(omega, chi) = _findOmegaAndChiToRotateHchiIntoQalpha(H_chi, Q_alpha)
|
||||
|
||||
Solves for omega and chi in OMEGA*CHI*h_chi = q_alpha where h_chi and
|
||||
q_alpha are 3x1 matrices with unit length. Omega and chi are returned in
|
||||
radians.
|
||||
|
||||
Throws an exception if this is not possible.
|
||||
"""
|
||||
|
||||
def solve(a, b, c):
|
||||
"""
|
||||
x1,x2 = solve(a , b, c)
|
||||
solves for the two solutions to x in equations of the form
|
||||
a*sin(x) + b*cos(x) = c
|
||||
by using the trigonometric identity
|
||||
a*sin(x) + b*cos(x) = a*sin(x)+b*cos(x)=sqrt(a**2+b**2)-sin(x+p)
|
||||
where
|
||||
p = atan(b/a) + {0 if a>=0
|
||||
{pi if a<0
|
||||
"""
|
||||
if a == 0:
|
||||
p = pi / 2 if b >= 0 else - pi / 2
|
||||
else:
|
||||
p = atan(b / a)
|
||||
if a < 0:
|
||||
p = p + pi
|
||||
guts = c / sqrt(a ** 2 + b ** 2)
|
||||
if guts < -1:
|
||||
guts = -1
|
||||
elif guts > 1:
|
||||
guts = 1
|
||||
left1 = asin(guts)
|
||||
left2 = pi - left1
|
||||
return (left1 - p, left2 - p)
|
||||
|
||||
def ne(a, b):
|
||||
"""
|
||||
shifts a and b in between -pi and pi and tests for near equality
|
||||
"""
|
||||
def shift(a):
|
||||
if a > pi:
|
||||
return a - 2 * pi
|
||||
elif a <= -pi:
|
||||
return a + 2 * pi
|
||||
else:
|
||||
return a
|
||||
return abs(shift(a) - shift(b)) < .0000001
|
||||
|
||||
# 1. Compute some solutions
|
||||
h_chi1 = h_chi[0, 0]
|
||||
h_chi2 = h_chi[1, 0]
|
||||
h_chi3 = h_chi[2, 0]
|
||||
q_alpha1 = q_alpha[0, 0]
|
||||
q_alpha2 = q_alpha[1, 0]
|
||||
q_alpha3 = q_alpha[2, 0]
|
||||
|
||||
try:
|
||||
# a) Solve for chi using Equation 3
|
||||
chi1, chi2 = solve(-h_chi1, h_chi3, q_alpha3)
|
||||
|
||||
# b) Solve for omega Equation 1 and each chi
|
||||
B = h_chi1 * cos(chi1) + h_chi3 * sin(chi1)
|
||||
eq1omega11, eq1omega12 = solve(h_chi2, B, q_alpha1)
|
||||
B = h_chi1 * cos(chi2) + h_chi3 * sin(chi2)
|
||||
eq1omega21, eq1omega22 = solve(h_chi2, B, q_alpha1)
|
||||
|
||||
# c) Solve for omega Equation 2 and each chi
|
||||
A = -h_chi1 * cos(chi1) - h_chi3 * sin(chi1)
|
||||
eq2omega11, eq2omega12 = solve(A, h_chi2, q_alpha2)
|
||||
A = -h_chi1 * cos(chi2) - h_chi3 * sin(chi2)
|
||||
eq2omega21, eq2omega22 = solve(A, h_chi2, q_alpha2)
|
||||
|
||||
except ValueError, e:
|
||||
raise ValueError(
|
||||
str(e) + ":\nProblem in fixed-phi calculation for:\nh_chi: " +
|
||||
str(h_chi.tolist()) + " q_alpha: " + str(q_alpha.tolist()))
|
||||
|
||||
# 2. Choose values of chi and omega that are solutions to equations 1 and 2
|
||||
solutions = []
|
||||
# a) Check the chi1 solutions
|
||||
print "_findOmegaAndChiToRotateHchiIntoQalpha:"
|
||||
if ne(eq1omega11, eq2omega11) or ne(eq1omega11, eq2omega12):
|
||||
# print "1: eq1omega11, chi1 = ", eq1omega11, chi1
|
||||
solutions.append((eq1omega11, chi1))
|
||||
if ne(eq1omega12, eq2omega11) or ne(eq1omega12, eq2omega12):
|
||||
# print "2: eq1omega12, chi1 = ", eq1omega12, chi1
|
||||
solutions.append((eq1omega12, chi1))
|
||||
# b) Check the chi2 solutions
|
||||
if ne(eq1omega21, eq2omega21) or ne(eq1omega21, eq2omega22):
|
||||
# print "3: eq1omega21, chi2 = ", eq1omega21, chi2
|
||||
solutions.append((eq1omega21, chi2))
|
||||
if ne(eq1omega22, eq2omega21) or ne(eq1omega22, eq2omega22):
|
||||
# print "4: eq1omega22, chi2 = ", eq1omega22, chi2
|
||||
solutions.append((eq1omega22, chi2))
|
||||
# print solutions
|
||||
# print "*"
|
||||
|
||||
if len(solutions) == 0:
|
||||
e = "h_chi: " + str(h_chi.tolist())
|
||||
e += " q_alpha: " + str(q_alpha.tolist())
|
||||
e += ("\nchi1:%4f eq1omega11:%4f eq1omega12:%4f eq2omega11:%4f "
|
||||
"eq2omega12:%4f" % (chi1 * TODEG, eq1omega11 * TODEG,
|
||||
eq1omega12 * TODEG, eq2omega11 * TODEG, eq2omega12 * TODEG))
|
||||
e += ("\nchi2:%4f eq1omega21:%4f eq1omega22:%4f eq2omega21:%4f "
|
||||
"eq2omega22:%4f" % (chi2 * TODEG, eq1omega21 * TODEG,
|
||||
eq1omega22 * TODEG, eq2omega21 * TODEG, eq2omega22 * TODEG))
|
||||
raise Exception("Could not find simultaneous solution for this fixed "
|
||||
"phi mode problem\n" + e)
|
||||
|
||||
if not PREFER_POSITIVE_CHI_SOLUTIONS:
|
||||
return solutions[0]
|
||||
|
||||
positive_chi_solutions = [sol for sol in solutions if sol[1] > 0]
|
||||
|
||||
if len(positive_chi_solutions) == 0:
|
||||
print "WARNING: A +ve chi solution was requested, but none were found."
|
||||
print " Returning a -ve one. Try the mapper"
|
||||
return solutions[0]
|
||||
|
||||
if len(positive_chi_solutions) > 1:
|
||||
print ("INFO: Multiple +ve chi solutions were found [(omega, chi) ...]"
|
||||
" = " + str(positive_chi_solutions))
|
||||
print " Returning the first"
|
||||
|
||||
return positive_chi_solutions[0]
|
||||
336
script/__Lib/diffcalc-2.1/diffcalc/hkl/vlieg/constraints.py
Executable file
336
script/__Lib/diffcalc-2.1/diffcalc/hkl/vlieg/constraints.py
Executable file
@@ -0,0 +1,336 @@
|
||||
###
|
||||
# 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 copy import copy
|
||||
|
||||
from diffcalc.util import DiffcalcException
|
||||
|
||||
|
||||
class Mode(object):
|
||||
|
||||
def __init__(self, index, name, group, description, parameterNames,
|
||||
implemented=True):
|
||||
self.index = index
|
||||
self.group = group
|
||||
self.name = name
|
||||
self.description = description
|
||||
self.parameterNames = parameterNames
|
||||
self.implemented = implemented
|
||||
|
||||
def __repr__(self):
|
||||
return "%i) %s" % (self.index, self.description)
|
||||
|
||||
def __str__(self):
|
||||
return self.__repr__()
|
||||
|
||||
def usesParameter(self, name):
|
||||
return name in self.parameterNames
|
||||
|
||||
|
||||
class ModeSelector(object):
|
||||
|
||||
def __init__(self, geometry, parameterManager=None,
|
||||
gammaParameterName='gamma'):
|
||||
self.parameter_manager = parameterManager
|
||||
self._geometry = geometry
|
||||
self._gammaParameterName = gammaParameterName
|
||||
self._modelist = {} # indexed by non-contiguous mode number
|
||||
self._configureAvailableModes()
|
||||
self._selectedIndex = 1
|
||||
|
||||
def setParameterManager(self, manager):
|
||||
"""
|
||||
Required as a ParameterManager and ModelSelector are mutually tied
|
||||
together in practice
|
||||
"""
|
||||
self.parameter_manager = manager
|
||||
|
||||
def _configureAvailableModes(self):
|
||||
|
||||
gammaName = self._gammaParameterName
|
||||
|
||||
ml = self._modelist
|
||||
|
||||
ml[0] = Mode(0, '4cFixedw', 'fourc', 'fourc fixed-bandlw',
|
||||
['alpha', gammaName, 'blw'], False)
|
||||
|
||||
ml[1] = Mode(1, '4cBeq', 'fourc', 'fourc bisecting',
|
||||
['alpha', gammaName])
|
||||
|
||||
ml[2] = Mode(2, '4cBin', 'fourc', 'fourc incoming',
|
||||
['alpha', gammaName, 'betain'])
|
||||
|
||||
ml[3] = Mode(3, '4cBout', 'fourc', 'fourc outgoing',
|
||||
['alpha', gammaName, 'betaout'])
|
||||
|
||||
ml[4] = Mode(4, '4cAzimuth', 'fourc', 'fourc azimuth',
|
||||
['alpha', gammaName, 'azimuth'], False)
|
||||
|
||||
ml[5] = Mode(5, '4cPhi', 'fourc', 'fourc fixed-phi',
|
||||
['alpha', gammaName, 'phi'])
|
||||
|
||||
ml[10] = Mode(10, '5cgBeq', 'fivecFixedGamma', 'fivec bisecting',
|
||||
[gammaName])
|
||||
|
||||
ml[11] = Mode(11, '5cgBin', 'fivecFixedGamma', 'fivec incoming',
|
||||
[gammaName, 'betain'])
|
||||
|
||||
ml[12] = Mode(12, '5cgBout', 'fivecFixedGamma', 'fivec outgoing',
|
||||
[gammaName, 'betaout'])
|
||||
|
||||
ml[13] = Mode(13, '5caBeq', 'fivecFixedAlpha', 'fivec bisecting',
|
||||
['alpha'])
|
||||
|
||||
ml[14] = Mode(14, '5caBin', 'fivecFixedAlpha', 'fivec incoming',
|
||||
['alpha', 'betain'])
|
||||
|
||||
ml[15] = Mode(15, '5caBout', 'fivecFixedAlpha', 'fivec outgoing',
|
||||
['alpha', 'betaout'])
|
||||
|
||||
ml[20] = Mode(20, '6czBeq', 'zaxis', 'zaxis bisecting',
|
||||
[])
|
||||
|
||||
ml[21] = Mode(21, '6czBin', 'zaxis', 'zaxis incoming',
|
||||
['betain'])
|
||||
|
||||
ml[22] = Mode(22, '6czBout', 'zaxis', 'zaxiz outgoing',
|
||||
['betaout'])
|
||||
|
||||
def setModeByIndex(self, index):
|
||||
if index in self._modelist:
|
||||
self._selectedIndex = index
|
||||
else:
|
||||
raise DiffcalcException("mode %r is not defined" % index)
|
||||
|
||||
def setModeByName(self, name):
|
||||
def findModeWithName(name):
|
||||
for index, mode in self._modelist.items():
|
||||
if mode.name == name:
|
||||
return index, mode
|
||||
raise ValueError
|
||||
|
||||
try:
|
||||
index, mode = findModeWithName(name)
|
||||
except ValueError:
|
||||
raise DiffcalcException(
|
||||
'Unknown mode. The diffraction calculator supports these '
|
||||
'modeSelector: %s' % self._supportedModes.keys())
|
||||
if self._geometry.supports_mode_group(mode.group):
|
||||
self._selectedIndex = index
|
||||
else:
|
||||
raise DiffcalcException(
|
||||
"Mode %s not supported for this diffractometer (%s)." %
|
||||
(name, self._geometry.name))
|
||||
|
||||
def getMode(self):
|
||||
return self._modelist[self._selectedIndex]
|
||||
|
||||
def reportCurrentMode(self):
|
||||
return self.getMode().__str__()
|
||||
|
||||
def reportAvailableModes(self):
|
||||
result = ''
|
||||
indecis = self._modelist.keys()
|
||||
indecis.sort()
|
||||
for index in indecis:
|
||||
mode = self._modelist[index]
|
||||
if self._geometry.supports_mode_group(mode.group):
|
||||
paramString = ''
|
||||
flags = ''
|
||||
pm = self.parameter_manager
|
||||
for paramName in pm.getUserChangableParametersForMode(mode):
|
||||
paramString += paramName + ", "
|
||||
if paramString:
|
||||
paramString = paramString[:-2] # remove trailing commas
|
||||
if not mode.implemented:
|
||||
flags += "(Not impl.)"
|
||||
result += ('%2i) %-15s (%s) %s\n' % (mode.index,
|
||||
mode.description, paramString, flags))
|
||||
return result
|
||||
|
||||
|
||||
class VliegParameterManager(object):
|
||||
|
||||
def __init__(self, geometry, hardware, modeSelector,
|
||||
gammaParameterName='gamma'):
|
||||
self._geometry = geometry
|
||||
self._hardware = hardware
|
||||
self._modeSelector = modeSelector
|
||||
self._gammaParameterName = gammaParameterName
|
||||
self._parameters = {}
|
||||
self._defineParameters()
|
||||
|
||||
def _defineParameters(self):
|
||||
# Set default fixed values (In degrees if angles)
|
||||
self._parameters = {}
|
||||
self._parameters['alpha'] = 0
|
||||
self._parameters[self._gammaParameterName] = 0
|
||||
self._parameters['blw'] = None # Busing and Levi omega!
|
||||
self._parameters['betain'] = None
|
||||
self._parameters['betaout'] = None
|
||||
self._parameters['azimuth'] = None
|
||||
self._parameters['phi'] = None
|
||||
|
||||
self._parameterDisplayOrder = (
|
||||
'alpha', self._gammaParameterName, 'betain', 'betaout', 'azimuth',
|
||||
'phi', 'blw')
|
||||
self._trackableParameters = ('alpha', self._gammaParameterName, 'phi')
|
||||
self._trackedParameters = []
|
||||
|
||||
# Overide parameters that are unchangable for this diffractometer
|
||||
for (name, value) in self._geometry.fixed_parameters.items():
|
||||
if name not in self._parameters:
|
||||
raise RuntimeError(
|
||||
"The %s diffractometer geometry specifies a fixed "
|
||||
"parameter %s that is not used by the diffractometer "
|
||||
"calculator" % (self._geometry.getName, name))
|
||||
self._parameters[name] = value
|
||||
|
||||
def reportAllParameters(self):
|
||||
self.update_tracked()
|
||||
result = ''
|
||||
for name in self._parameterDisplayOrder:
|
||||
flags = ""
|
||||
if not self._modeSelector.getMode().usesParameter(name):
|
||||
flags += '(not relevant in this mode)'
|
||||
if self._geometry.parameter_fixed(name):
|
||||
flags += ' (fixed by this diffractometer)'
|
||||
if self.isParameterTracked(name):
|
||||
flags += ' (tracking hardware)'
|
||||
value = self._parameters[name]
|
||||
if value is None:
|
||||
value = '---'
|
||||
else:
|
||||
value = float(value)
|
||||
result += '%s: %s %s\n' % (name.rjust(8), value, flags)
|
||||
return result
|
||||
|
||||
def reportParametersUsedInCurrentMode(self):
|
||||
self.update_tracked()
|
||||
result = ''
|
||||
for name in self.getUserChangableParametersForMode(
|
||||
self._modeSelector.getMode()):
|
||||
flags = ""
|
||||
value = self._parameters[name]
|
||||
if value is None:
|
||||
value = '---'
|
||||
else:
|
||||
value = float(value)
|
||||
if self.isParameterTracked(name):
|
||||
flags += ' (tracking hardware)'
|
||||
result += '%s: %s %s\n' % (name.rjust(8), value, flags)
|
||||
return result
|
||||
|
||||
def getUserChangableParametersForMode(self, mode=None):
|
||||
"""
|
||||
(p1,p2...p3) = getUserChangableParametersForMode(mode) returns a list
|
||||
of parameters names used in this mode for this diffractometer geometry.
|
||||
Checks current mode if no mode specified.
|
||||
"""
|
||||
if mode is None:
|
||||
mode = self._mode
|
||||
result = []
|
||||
for name in self._parameterDisplayOrder:
|
||||
if self._isParameterChangeable(name, mode):
|
||||
result += [name]
|
||||
return result
|
||||
|
||||
### Fixed parameters stuff ###
|
||||
|
||||
def set_constraint(self, name, value):
|
||||
if not name in self._parameters:
|
||||
raise DiffcalcException("No fixed parameter %s is used by the "
|
||||
"diffraction calculator" % name)
|
||||
if self._geometry.parameter_fixed(name):
|
||||
raise DiffcalcException(
|
||||
"The parameter %s cannot be changed: It has been fixed by the "
|
||||
"%s diffractometer geometry"
|
||||
% (name, self._geometry.name))
|
||||
if self.isParameterTracked(name):
|
||||
# for safety and to avoid confusion:
|
||||
raise DiffcalcException(
|
||||
"Cannot change parameter %s as it is set to track an axis.\n"
|
||||
"To turn this off use a command like 'trackalpha 0'." % name)
|
||||
|
||||
if not self.isParameterUsedInSelectedMode(name):
|
||||
print ("WARNING: The parameter %s is not used in mode %i" %
|
||||
(name, self._modeSelector.getMode().index))
|
||||
self._parameters[name] = value
|
||||
|
||||
def isParameterUsedInSelectedMode(self, name):
|
||||
return self._modeSelector.getMode().usesParameter(name)
|
||||
|
||||
def getParameterWithoutUpdatingTrackedParemeters(self, name):
|
||||
try:
|
||||
return self._parameters[name]
|
||||
except KeyError:
|
||||
raise DiffcalcException("No fixed parameter %s is used by the "
|
||||
"diffraction calculator" % name)
|
||||
|
||||
def get_constraint(self, name):
|
||||
self.update_tracked()
|
||||
return self.getParameterWithoutUpdatingTrackedParemeters(name)
|
||||
|
||||
def getParameterDict(self):
|
||||
self.update_tracked()
|
||||
return copy(self._parameters)
|
||||
|
||||
@property
|
||||
def settable_constraint_names(self):
|
||||
"""list of all available constraints that have settable values"""
|
||||
return sorted(self.getParameterDict().keys())
|
||||
|
||||
def setTrackParameter(self, name, switch):
|
||||
if not name in self._parameters.keys():
|
||||
raise DiffcalcException("No fixed parameter %s is used by the "
|
||||
"diffraction calculator" % name)
|
||||
if not name in self._trackableParameters:
|
||||
raise DiffcalcException("Parameter %s is not trackable" % name)
|
||||
if not self._isParameterChangeable(name):
|
||||
print ("WARNING: Parameter %s is not used in mode %i" %
|
||||
(name, self._mode.index))
|
||||
if switch:
|
||||
if name not in self._trackedParameters:
|
||||
self._trackedParameters.append(name)
|
||||
else:
|
||||
if name in self._trackedParameters:
|
||||
self._trackedParameters.remove(name)
|
||||
|
||||
def isParameterTracked(self, name):
|
||||
return (name in self._trackedParameters)
|
||||
|
||||
def update_tracked(self):
|
||||
"""Note that the name of a tracked parameter MUST map into the name of
|
||||
an external diffractometer angle
|
||||
"""
|
||||
if self._trackedParameters:
|
||||
externalAnglePositionArray = self._hardware.get_position()
|
||||
externalAngleNames = list(self._hardware.get_axes_names())
|
||||
for name in self._trackedParameters:
|
||||
self._parameters[name] = \
|
||||
externalAnglePositionArray[externalAngleNames.index(name)]
|
||||
|
||||
def _isParameterChangeable(self, name, mode=None):
|
||||
"""
|
||||
Returns true if parameter is used in a mode (current mode if none
|
||||
specified), AND if it is not locked by the diffractometer geometry
|
||||
"""
|
||||
if mode is None:
|
||||
mode = self._modeSelector.getMode()
|
||||
return (mode.usesParameter(name) and
|
||||
not self._geometry.parameter_fixed(name))
|
||||
523
script/__Lib/diffcalc-2.1/diffcalc/hkl/vlieg/geometry.py
Executable file
523
script/__Lib/diffcalc-2.1/diffcalc/hkl/vlieg/geometry.py
Executable file
@@ -0,0 +1,523 @@
|
||||
###
|
||||
# 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 math import tan, cos, sin, asin, atan, pi, fabs
|
||||
|
||||
try:
|
||||
from numpy import matrix
|
||||
except ImportError:
|
||||
from numjy import matrix
|
||||
|
||||
from diffcalc.util import x_rotation, z_rotation, y_rotation
|
||||
from diffcalc.util import AbstractPosition
|
||||
from diffcalc.util import bound, nearlyEqual
|
||||
|
||||
|
||||
TORAD = pi / 180
|
||||
TODEG = 180 / pi
|
||||
|
||||
|
||||
def calcALPHA(alpha):
|
||||
return x_rotation(alpha)
|
||||
|
||||
|
||||
def calcDELTA(delta):
|
||||
return z_rotation(-delta)
|
||||
|
||||
|
||||
def calcGAMMA(gamma):
|
||||
return x_rotation(gamma)
|
||||
|
||||
|
||||
def calcOMEGA(omega):
|
||||
return z_rotation(-omega)
|
||||
|
||||
|
||||
def calcCHI(chi):
|
||||
return y_rotation(chi)
|
||||
|
||||
|
||||
def calcPHI(phi):
|
||||
return z_rotation(-phi)
|
||||
|
||||
|
||||
def createVliegMatrices(alpha=None, delta=None, gamma=None, omega=None,
|
||||
chi=None, phi=None):
|
||||
|
||||
ALPHA = None if alpha is None else calcALPHA(alpha)
|
||||
DELTA = None if delta is None else calcDELTA(delta)
|
||||
GAMMA = None if gamma is None else calcGAMMA(gamma)
|
||||
OMEGA = None if omega is None else calcOMEGA(omega)
|
||||
CHI = None if chi is None else calcCHI(chi)
|
||||
PHI = None if phi is None else calcPHI(phi)
|
||||
return ALPHA, DELTA, GAMMA, OMEGA, CHI, PHI
|
||||
|
||||
|
||||
def createVliegsSurfaceTransformationMatrices(sigma, tau):
|
||||
"""[SIGMA, TAU] = createVliegsSurfaceTransformationMatrices(sigma, tau)
|
||||
angles in radians
|
||||
"""
|
||||
SIGMA = matrix([[cos(sigma), 0, sin(sigma)],
|
||||
[0, 1, 0], \
|
||||
[-sin(sigma), 0, cos(sigma)]])
|
||||
|
||||
TAU = matrix([[cos(tau), sin(tau), 0],
|
||||
[-sin(tau), cos(tau), 0],
|
||||
[0, 0, 1]])
|
||||
return(SIGMA, TAU)
|
||||
|
||||
|
||||
def createVliegsPsiTransformationMatrix(psi):
|
||||
"""PSI = createPsiTransformationMatrices(psi)
|
||||
angles in radians
|
||||
"""
|
||||
return matrix([[1, 0, 0],
|
||||
[0, cos(psi), sin(psi)],
|
||||
[0, -sin(psi), cos(psi)]])
|
||||
|
||||
|
||||
class VliegPosition(AbstractPosition):
|
||||
"""The position of all six diffractometer axis"""
|
||||
def __init__(self, alpha=None, delta=None, gamma=None, omega=None,
|
||||
chi=None, phi=None):
|
||||
self.alpha = alpha
|
||||
self.delta = delta
|
||||
self.gamma = gamma
|
||||
self.omega = omega
|
||||
self.chi = chi
|
||||
self.phi = phi
|
||||
|
||||
def clone(self):
|
||||
return VliegPosition(self.alpha, self.delta, self.gamma, self.omega,
|
||||
self.chi, self.phi)
|
||||
|
||||
def changeToRadians(self):
|
||||
self.alpha *= TORAD
|
||||
self.delta *= TORAD
|
||||
self.gamma *= TORAD
|
||||
self.omega *= TORAD
|
||||
self.chi *= TORAD
|
||||
self.phi *= TORAD
|
||||
|
||||
def changeToDegrees(self):
|
||||
self.alpha *= TODEG
|
||||
self.delta *= TODEG
|
||||
self.gamma *= TODEG
|
||||
self.omega *= TODEG
|
||||
self.chi *= TODEG
|
||||
self.phi *= TODEG
|
||||
|
||||
def inRadians(self):
|
||||
pos = self.clone()
|
||||
pos.changeToRadians()
|
||||
return pos
|
||||
|
||||
def inDegrees(self):
|
||||
pos = self.clone()
|
||||
pos.changeToDegrees()
|
||||
return pos
|
||||
|
||||
def nearlyEquals(self, pos2, maxnorm):
|
||||
for a, b in zip(self.totuple(), pos2.totuple()):
|
||||
if abs(a - b) > maxnorm:
|
||||
return False
|
||||
return True
|
||||
|
||||
def totuple(self):
|
||||
return (self.alpha, self.delta, self.gamma, self.omega,
|
||||
self.chi, self.phi)
|
||||
|
||||
def __str__(self):
|
||||
return ("VliegPosition(alpha %r delta: %r gamma: %r omega: %r chi: %r"
|
||||
" phi: %r)" % self.totuple())
|
||||
|
||||
def __repr__(self):
|
||||
return self.__str__()
|
||||
|
||||
def __eq__(self, b):
|
||||
return self.nearlyEquals(b, .001)
|
||||
|
||||
|
||||
class VliegGeometry(object):
|
||||
|
||||
# Required methods
|
||||
|
||||
def __init__(self, name, supported_mode_groups, fixed_parameters,
|
||||
gamma_location):
|
||||
"""
|
||||
Set geometry name (String), list of supported mode groups (list of
|
||||
strings), list of axis names (list of strings). Define the parameters
|
||||
e.g. alpha and gamma for a four circle (dictionary). Define wether the
|
||||
gamma angle is on the 'arm' or the 'base'; used only by AngleCalculator
|
||||
to interpret the gamma parameter in fixed gamma mode: for instruments
|
||||
with gamma on the base, rather than on the arm as the code assume
|
||||
internally, the two methods physical_angles_to_internal_position and
|
||||
internal_position_to_physical_angles must still be used.
|
||||
"""
|
||||
if gamma_location not in ('arm', 'base', None):
|
||||
raise RuntimeError(
|
||||
"Gamma must be on either 'arm' or 'base' or None")
|
||||
|
||||
self.name = name
|
||||
self.supported_mode_groups = supported_mode_groups
|
||||
self.fixed_parameters = fixed_parameters
|
||||
self.gamma_location = gamma_location
|
||||
|
||||
def physical_angles_to_internal_position(self, physicalAngles):
|
||||
raise NotImplementedError()
|
||||
|
||||
def internal_position_to_physical_angles(self, physicalAngles):
|
||||
raise NotImplementedError()
|
||||
|
||||
### Do not overide these these ###
|
||||
|
||||
def supports_mode_group(self, name):
|
||||
return name in self.supported_mode_groups
|
||||
|
||||
def parameter_fixed(self, name): # parameter_fixed
|
||||
return name in self.fixed_parameters.keys()
|
||||
|
||||
|
||||
class SixCircleGammaOnArmGeometry(VliegGeometry):
|
||||
"""
|
||||
This six-circle diffractometer geometry simply passes through the
|
||||
angles from a six circle diffractometer with the same geometry and
|
||||
angle names as those defined in Vliegs's paper defined internally.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
VliegGeometry.__init__(
|
||||
self,
|
||||
name='sixc_gamma_on_arm',
|
||||
supported_mode_groups=('fourc', 'fivecFixedGamma',
|
||||
'fivecFixedAlpha', 'zaxis'),
|
||||
fixed_parameters={},
|
||||
gamma_location='arm')
|
||||
|
||||
def physical_angles_to_internal_position(self, physicalAngles):
|
||||
""" (a,d,g,o,c,p) = physicalAnglesToInternal(a,d,g,o,c,p)
|
||||
"""
|
||||
assert (len(physicalAngles) == 6), "Wrong length of input list"
|
||||
return VliegPosition(*physicalAngles)
|
||||
|
||||
def internal_position_to_physical_angles(self, internalPosition):
|
||||
""" (a,d,g,o,c,p) = physicalAnglesToInternal(a,d,g,o,c,p)
|
||||
"""
|
||||
return internalPosition.totuple()
|
||||
|
||||
|
||||
class SixCircleGeometry(VliegGeometry):
|
||||
"""
|
||||
This six-circle diffractometer geometry simply passes through the
|
||||
angles from a six circle diffractometer with the same geometry and
|
||||
angle names as those defined in Vliegs's paper defined internally.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
VliegGeometry.__init__(
|
||||
self,
|
||||
name='sixc',
|
||||
supported_mode_groups=('fourc', 'fivecFixedGamma',
|
||||
'fivecFixedAlpha', 'zaxis'),
|
||||
fixed_parameters={},
|
||||
gamma_location='base')
|
||||
self.hardwareMonitor = None
|
||||
#(deltaA, gammaA) = gammaOnBaseToArm(deltaB, gammaB, alpha) (all in radians)
|
||||
#(deltaB, gammaB) = gammaOnArmToBase(deltaA, gammaA, alpha) (all in radians)
|
||||
|
||||
def physical_angles_to_internal_position(self, physicalAngles):
|
||||
""" (a,d,g,o,c,p) = physicalAnglesToInternal(a,d,g,o,c,p)
|
||||
"""
|
||||
assert (len(physicalAngles) == 6), "Wrong length of input list"
|
||||
alpha, deltaB, gammaB, omega, chi, phi = physicalAngles
|
||||
(deltaA, gammaA) = gammaOnBaseToArm(
|
||||
deltaB * TORAD, gammaB * TORAD, alpha * TORAD)
|
||||
return VliegPosition(
|
||||
alpha, deltaA * TODEG, gammaA * TODEG, omega, chi, phi)
|
||||
|
||||
def internal_position_to_physical_angles(self, internalPosition):
|
||||
""" (a,d,g,o,c,p) = physicalAnglesToInternal(a,d,g,o,c,p)
|
||||
"""
|
||||
alpha, deltaA, gammaA, omega, chi, phi = internalPosition.totuple()
|
||||
deltaB, gammaB = gammaOnArmToBase(
|
||||
deltaA * TORAD, gammaA * TORAD, alpha * TORAD)
|
||||
deltaB, gammaB = deltaB * TODEG, gammaB * TODEG
|
||||
|
||||
if self.hardwareMonitor is not None:
|
||||
gammaName = self.hardwareMonitor.get_axes_names()[2]
|
||||
minGamma = self.hardwareMonitor.get_lower_limit(gammaName)
|
||||
maxGamma = self.hardwareMonitor.get_upper_limit(gammaName)
|
||||
|
||||
if maxGamma is not None:
|
||||
if gammaB > maxGamma:
|
||||
gammaB = gammaB - 180
|
||||
deltaB = 180 - deltaB
|
||||
if minGamma is not None:
|
||||
if gammaB < minGamma:
|
||||
gammaB = gammaB + 180
|
||||
deltaB = 180 - deltaB
|
||||
|
||||
return alpha, deltaB, gammaB, omega, chi, phi
|
||||
|
||||
|
||||
class FivecWithGammaOnBase(SixCircleGeometry):
|
||||
|
||||
def __init__(self):
|
||||
VliegGeometry.__init__(
|
||||
self,
|
||||
name='fivec_with_gamma',
|
||||
supported_mode_groups=('fourc', 'fivecFixedGamma'),
|
||||
fixed_parameters={'alpha': 0.0},
|
||||
gamma_location='base')
|
||||
self.hardwareMonitor = None
|
||||
|
||||
def physical_angles_to_internal_position(self, physicalAngles):
|
||||
""" (a,d,g,o,c,p) = physicalAnglesToInternal(d,g,o,c,p)
|
||||
"""
|
||||
assert (len(physicalAngles) == 5), "Wrong length of input list"
|
||||
return SixCircleGeometry.physical_angles_to_internal_position(
|
||||
self, (0,) + tuple(physicalAngles))
|
||||
|
||||
def internal_position_to_physical_angles(self, internalPosition):
|
||||
""" (d,g,o,c,p) = physicalAnglesToInternal(a,d,g,o,c,p)
|
||||
"""
|
||||
return SixCircleGeometry.internal_position_to_physical_angles(
|
||||
self, internalPosition)[1:]
|
||||
|
||||
|
||||
class Fivec(VliegGeometry):
|
||||
"""
|
||||
This five-circle diffractometer geometry is for diffractometers with the
|
||||
same geometry and angle names as those defined in Vliegs's paper defined
|
||||
internally, but with no out plane detector arm gamma."""
|
||||
|
||||
def __init__(self):
|
||||
VliegGeometry.__init__(self,
|
||||
name='fivec',
|
||||
supported_mode_groups=('fourc', 'fivecFixedGamma'),
|
||||
fixed_parameters={'gamma': 0.0},
|
||||
gamma_location='arm'
|
||||
)
|
||||
|
||||
def physical_angles_to_internal_position(self, physicalAngles):
|
||||
""" (a,d,g,o,c,p) = physicalAnglesToInternal(a,d,g,o,c,p)
|
||||
"""
|
||||
assert (len(physicalAngles) == 5), "Wrong length of input list"
|
||||
physicalAngles = tuple(physicalAngles)
|
||||
angles = physicalAngles[0:2] + (0.0,) + physicalAngles[2:]
|
||||
return VliegPosition(*angles)
|
||||
|
||||
def internal_position_to_physical_angles(self, internalPosition):
|
||||
""" (a,d,g,o,c,p) = physicalAnglesToInternal(a,d,g,o,c,p)
|
||||
"""
|
||||
sixAngles = internalPosition.totuple()
|
||||
return sixAngles[0:2] + sixAngles[3:]
|
||||
|
||||
|
||||
class Fourc(VliegGeometry):
|
||||
"""
|
||||
This five-circle diffractometer geometry is for diffractometers with the
|
||||
same geometry and angle names as those defined in Vliegs's paper defined
|
||||
internally, but with no out plane detector arm gamma."""
|
||||
|
||||
def __init__(self):
|
||||
VliegGeometry.__init__(self,
|
||||
name='fourc',
|
||||
supported_mode_groups=('fourc'),
|
||||
fixed_parameters={'gamma': 0.0, 'alpha': 0.0},
|
||||
gamma_location='arm'
|
||||
)
|
||||
|
||||
def physical_angles_to_internal_position(self, physicalAngles):
|
||||
""" (a,d,g,o,c,p) = physicalAnglesToInternal(a,d,g,o,c,p)
|
||||
"""
|
||||
assert (len(physicalAngles) == 4), "Wrong length of input list"
|
||||
physicalAngles = tuple(physicalAngles)
|
||||
angles = (0.0, physicalAngles[0], 0.0) + physicalAngles[1:]
|
||||
return VliegPosition(*angles)
|
||||
|
||||
def internal_position_to_physical_angles(self, internalPosition):
|
||||
""" (a,d,g,o,c,p) = physicalAnglesToInternal(a,d,g,o,c,p)
|
||||
"""
|
||||
sixAngles = internalPosition.totuple()
|
||||
return sixAngles[1:2] + sixAngles[3:]
|
||||
|
||||
|
||||
def sign(x):
|
||||
if x < 0:
|
||||
return -1
|
||||
else:
|
||||
return 1
|
||||
|
||||
"""
|
||||
Based on: Elias Vlieg, "A (2+3)-Type Surface Diffractometer: Mergence of
|
||||
the z-axis and (2+2)-Type Geometries", J. Appl. Cryst. (1998). 31.
|
||||
198-203
|
||||
"""
|
||||
|
||||
|
||||
def solvesEq8(alpha, deltaA, gammaA, deltaB, gammaB):
|
||||
tol = 1e-6
|
||||
return (nearlyEqual(sin(deltaA) * cos(gammaA), sin(deltaB), tol) and
|
||||
nearlyEqual(cos(deltaA) * cos(gammaA),
|
||||
cos(gammaB - alpha) * cos(deltaB), tol) and
|
||||
nearlyEqual(sin(gammaA), sin(gammaB - alpha) * cos(deltaB), tol))
|
||||
|
||||
|
||||
GAMMAONBASETOARM_WARNING = '''
|
||||
WARNING: This diffractometer has the gamma circle attached to the
|
||||
base rather than the end of
|
||||
the delta arm as Vlieg's paper defines. A conversion has
|
||||
been made from the physical angles to their internal
|
||||
representation (gamma-on-base-to-arm). This conversion has
|
||||
forced gamma to be positive by applying the mapping:
|
||||
|
||||
delta --> 180+delta
|
||||
gamma --> 180+gamma.
|
||||
|
||||
This should have no adverse effect.
|
||||
'''
|
||||
|
||||
|
||||
def gammaOnBaseToArm(deltaB, gammaB, alpha):
|
||||
"""
|
||||
(deltaA, gammaA) = gammaOnBaseToArm(deltaB, gammaB, alpha) (all in
|
||||
radians)
|
||||
|
||||
Maps delta and gamma for an instrument where the gamma circle rests on
|
||||
the base to the case where it is on the delta arm.
|
||||
|
||||
There are always two possible solutions. To get the second apply the
|
||||
transform:
|
||||
|
||||
delta --> 180+delta (flip to opposite side of circle)
|
||||
gamma --> 180+gamma (flip to opposite side of circle)
|
||||
|
||||
This code will return the solution where gamma is between 0 and 180.
|
||||
"""
|
||||
|
||||
### Equation11 ###
|
||||
if fabs(cos(gammaB - alpha)) < 1e-20:
|
||||
deltaA1 = sign(tan(deltaB)) * sign(cos(gammaB - alpha)) * pi / 2
|
||||
else:
|
||||
deltaA1 = atan(tan(deltaB) / cos(gammaB - alpha))
|
||||
# ...second root
|
||||
if deltaA1 <= 0:
|
||||
deltaA2 = deltaA1 + pi
|
||||
else:
|
||||
deltaA2 = deltaA1 - pi
|
||||
|
||||
### Equation 12 ###
|
||||
gammaA1 = asin(bound(cos(deltaB) * sin(gammaB - alpha)))
|
||||
# ...second root
|
||||
if gammaA1 >= 0:
|
||||
gammaA2 = pi - gammaA1
|
||||
else:
|
||||
gammaA2 = -pi - gammaA1
|
||||
|
||||
# Choose the delta solution that fits equations 8
|
||||
if solvesEq8(alpha, deltaA1, gammaA1, deltaB, gammaB):
|
||||
deltaA, gammaA = deltaA1, gammaA1
|
||||
elif solvesEq8(alpha, deltaA2, gammaA1, deltaB, gammaB):
|
||||
deltaA, gammaA = deltaA2, gammaA1
|
||||
print "gammaOnBaseToArm choosing 2nd delta root (to internal)"
|
||||
elif solvesEq8(alpha, deltaA1, gammaA2, deltaB, gammaB):
|
||||
print "gammaOnBaseToArm choosing 2nd gamma root (to internal)"
|
||||
deltaA, gammaA = deltaA1, gammaA2
|
||||
elif solvesEq8(alpha, deltaA2, gammaA2, deltaB, gammaB):
|
||||
print "gammaOnBaseToArm choosing 2nd delta root and 2nd gamma root"
|
||||
deltaA, gammaA = deltaA2, gammaA2
|
||||
else:
|
||||
raise RuntimeError(
|
||||
"No valid solutions found mapping from gamma-on-base to gamma-on-arm")
|
||||
|
||||
return deltaA, gammaA
|
||||
|
||||
GAMMAONARMTOBASE_WARNING = '''
|
||||
WARNING: This diffractometer has the gamma circle attached to the base
|
||||
rather than the end of the delta arm as Vlieg's paper defines.
|
||||
A conversion has been made from the internal representation of
|
||||
angles to physical angles (gamma-on-arm-to-base). This
|
||||
conversion has forced gamma to be positive by applying the
|
||||
mapping:
|
||||
|
||||
delta --> 180-delta
|
||||
gamma --> 180+gamma.
|
||||
|
||||
This should have no adverse effect.
|
||||
'''
|
||||
|
||||
|
||||
def gammaOnArmToBase(deltaA, gammaA, alpha):
|
||||
"""
|
||||
(deltaB, gammaB) = gammaOnArmToBase(deltaA, gammaA, alpha) (all in
|
||||
radians)
|
||||
|
||||
Maps delta and gamma for an instrument where the gamma circle is on
|
||||
the delta arm to the case where it rests on the base.
|
||||
|
||||
There are always two possible solutions. To get the second apply the
|
||||
transform:
|
||||
|
||||
delta --> 180-delta (reflect and flip to opposite side)
|
||||
gamma --> 180+gamma (flip to opposite side)
|
||||
|
||||
This code will return the solution where gamma is positive, but will
|
||||
warn if a sign change was made.
|
||||
"""
|
||||
|
||||
### Equation 9 ###
|
||||
deltaB1 = asin(bound(sin(deltaA) * cos(gammaA)))
|
||||
# ...second root:
|
||||
if deltaB1 >= 0:
|
||||
deltaB2 = pi - deltaB1
|
||||
else:
|
||||
deltaB2 = -pi - deltaB1
|
||||
|
||||
### Equation 10 ###:
|
||||
if fabs(cos(deltaA)) < 1e-20:
|
||||
gammaB1 = sign(tan(gammaA)) * sign(cos(deltaA)) * pi / 2 + alpha
|
||||
else:
|
||||
gammaB1 = atan(tan(gammaA) / cos(deltaA)) + alpha
|
||||
#... second root:
|
||||
if gammaB1 <= 0:
|
||||
gammaB2 = gammaB1 + pi
|
||||
else:
|
||||
gammaB2 = gammaB1 - pi
|
||||
|
||||
### Choose the solution that fits equation 8 ###
|
||||
if (solvesEq8(alpha, deltaA, gammaA, deltaB1, gammaB1) and
|
||||
0 <= gammaB1 <= pi):
|
||||
deltaB, gammaB = deltaB1, gammaB1
|
||||
elif (solvesEq8(alpha, deltaA, gammaA, deltaB2, gammaB1) and
|
||||
0 <= gammaB1 <= pi):
|
||||
deltaB, gammaB = deltaB2, gammaB1
|
||||
print "gammaOnArmToBase choosing 2nd delta root (to physical)"
|
||||
elif (solvesEq8(alpha, deltaA, gammaA, deltaB1, gammaB2) and
|
||||
0 <= gammaB2 <= pi):
|
||||
print "gammaOnArmToBase choosing 2nd gamma root (to physical)"
|
||||
deltaB, gammaB = deltaB1, gammaB2
|
||||
elif (solvesEq8(alpha, deltaA, gammaA, deltaB2, gammaB2)
|
||||
and 0 <= gammaB2 <= pi):
|
||||
print "gammaOnArmToBase choosing 2nd delta root and 2nd gamma root"
|
||||
deltaB, gammaB = deltaB2, gammaB2
|
||||
else:
|
||||
raise RuntimeError(
|
||||
"No valid solutions found mapping gamma-on-arm to gamma-on-base")
|
||||
|
||||
return deltaB, gammaB
|
||||
139
script/__Lib/diffcalc-2.1/diffcalc/hkl/vlieg/hkl.py
Executable file
139
script/__Lib/diffcalc-2.1/diffcalc/hkl/vlieg/hkl.py
Executable file
@@ -0,0 +1,139 @@
|
||||
###
|
||||
# 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.hkl.common import getNameFromScannableOrString
|
||||
from diffcalc.util import command
|
||||
from diffcalc import settings
|
||||
|
||||
|
||||
from diffcalc.ub import ub
|
||||
from diffcalc.hkl.vlieg.calc import VliegHklCalculator
|
||||
|
||||
|
||||
__all__ = ['hklmode', 'setpar', 'trackalpha', 'trackgamma', 'trackphi',
|
||||
'parameter_manager', 'hklcalc']
|
||||
|
||||
|
||||
hklcalc = VliegHklCalculator(ub.ubcalc, settings.geometry, settings.hardware)
|
||||
|
||||
parameter_manager = hklcalc.parameter_manager
|
||||
|
||||
def __str__(self):
|
||||
return hklcalc.__str__()
|
||||
|
||||
@command
|
||||
def hklmode(num=None):
|
||||
"""hklmode {num} -- changes mode or shows current and available modes and all settings""" #@IgnorePep8
|
||||
|
||||
if num is None:
|
||||
print hklcalc.__str__()
|
||||
else:
|
||||
hklcalc.mode_selector.setModeByIndex(int(num))
|
||||
pm = hklcalc.parameter_manager
|
||||
print (hklcalc.mode_selector.reportCurrentMode() + "\n" +
|
||||
pm.reportParametersUsedInCurrentMode())
|
||||
|
||||
def _setParameter(name, value):
|
||||
hklcalc.parameter_manager.set_constraint(name, value)
|
||||
|
||||
def _getParameter(name):
|
||||
return hklcalc.parameter_manager.get_constraint(name)
|
||||
|
||||
@command
|
||||
def setpar(scannable_or_string=None, val=None):
|
||||
"""setpar {parameter_scannable {{val}} -- sets or shows a parameter'
|
||||
setpar {parameter_name {val}} -- sets or shows a parameter'
|
||||
"""
|
||||
|
||||
if scannable_or_string is None:
|
||||
#show all
|
||||
parameterDict = hklcalc.parameter_manager.getParameterDict()
|
||||
names = parameterDict.keys()
|
||||
names.sort()
|
||||
for name in names:
|
||||
print _representParameter(name)
|
||||
else:
|
||||
name = getNameFromScannableOrString(scannable_or_string)
|
||||
if val is None:
|
||||
_representParameter(name)
|
||||
else:
|
||||
oldval = _getParameter(name)
|
||||
_setParameter(name, float(val))
|
||||
print _representParameter(name, oldval, float(val))
|
||||
|
||||
def _representParameter(name, oldval=None, newval=None):
|
||||
flags = ''
|
||||
if hklcalc.parameter_manager.isParameterTracked(name):
|
||||
flags += '(tracking hardware) '
|
||||
if settings.geometry.parameter_fixed(name): # @UndefinedVariable
|
||||
flags += '(fixed by geometry) '
|
||||
pm = hklcalc.parameter_manager
|
||||
if not pm.isParameterUsedInSelectedMode(name):
|
||||
flags += '(not relevant in this mode) '
|
||||
if oldval is None:
|
||||
val = _getParameter(name)
|
||||
if val is None:
|
||||
val = "---"
|
||||
else:
|
||||
val = str(val)
|
||||
return "%s: %s %s" % (name, val, flags)
|
||||
else:
|
||||
return "%s: %s --> %f %s" % (name, oldval, newval, flags)
|
||||
|
||||
def _checkInputAndSetOrShowParameterTracking(name, b=None):
|
||||
"""
|
||||
for track-parameter commands: If no args displays parameter settings,
|
||||
otherwise sets the tracking switch for the given parameter and displays
|
||||
settings.
|
||||
"""
|
||||
# set if arg given
|
||||
if b is not None:
|
||||
hklcalc.parameter_manager.setTrackParameter(name, b)
|
||||
# Display:
|
||||
lastValue = _getParameter(name)
|
||||
if lastValue is None:
|
||||
lastValue = "---"
|
||||
else:
|
||||
lastValue = str(lastValue)
|
||||
flags = ''
|
||||
if hklcalc.parameter_manager.isParameterTracked(name):
|
||||
flags += '(tracking hardware)'
|
||||
print "%s: %s %s" % (name, lastValue, flags)
|
||||
|
||||
@command
|
||||
def trackalpha(b=None):
|
||||
"""trackalpha {boolean} -- determines wether alpha parameter will track alpha axis""" #@IgnorePep8
|
||||
_checkInputAndSetOrShowParameterTracking('alpha', b)
|
||||
|
||||
@command
|
||||
def trackgamma(b=None):
|
||||
"""trackgamma {boolean} -- determines wether gamma parameter will track alpha axis""" #@IgnorePep8
|
||||
_checkInputAndSetOrShowParameterTracking('gamma', b)
|
||||
|
||||
@command
|
||||
def trackphi(b=None):
|
||||
"""trackphi {boolean} -- determines wether phi parameter will track phi axis""" #@IgnorePep8
|
||||
_checkInputAndSetOrShowParameterTracking('phi', b)
|
||||
|
||||
|
||||
commands_for_help = ['Mode',
|
||||
hklmode,
|
||||
setpar,
|
||||
trackalpha,
|
||||
trackgamma,
|
||||
trackphi]
|
||||
480
script/__Lib/diffcalc-2.1/diffcalc/hkl/vlieg/transform.py
Executable file
480
script/__Lib/diffcalc-2.1/diffcalc/hkl/vlieg/transform.py
Executable file
@@ -0,0 +1,480 @@
|
||||
###
|
||||
# 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__()
|
||||
|
||||
|
||||
|
||||
0
script/__Lib/diffcalc-2.1/diffcalc/hkl/willmott/__init__.py
Executable file
0
script/__Lib/diffcalc-2.1/diffcalc/hkl/willmott/__init__.py
Executable file
292
script/__Lib/diffcalc-2.1/diffcalc/hkl/willmott/calc.py
Executable file
292
script/__Lib/diffcalc-2.1/diffcalc/hkl/willmott/calc.py
Executable file
@@ -0,0 +1,292 @@
|
||||
###
|
||||
# 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 math import pi, asin, acos, atan2, sin, cos, sqrt
|
||||
|
||||
try:
|
||||
from numpy import matrix
|
||||
except ImportError:
|
||||
from numjy import matrix
|
||||
|
||||
from diffcalc.log import logging
|
||||
from diffcalc.util import bound, AbstractPosition, DiffcalcException,\
|
||||
x_rotation, z_rotation
|
||||
from diffcalc.hkl.vlieg.geometry import VliegGeometry
|
||||
from diffcalc.ub.calc import PaperSpecificUbCalcStrategy
|
||||
from diffcalc.hkl.calcbase import HklCalculatorBase
|
||||
from diffcalc.hkl.common import DummyParameterManager
|
||||
|
||||
logger = logging.getLogger("diffcalc.hkl.willmot.calcwill")
|
||||
|
||||
CHOOSE_POSITIVE_GAMMA = True
|
||||
|
||||
TORAD = pi / 180
|
||||
TODEG = 180 / pi
|
||||
I = matrix('1 0 0; 0 1 0; 0 0 1')
|
||||
SMALL = 1e-10
|
||||
|
||||
TEMPORARY_CONSTRAINTS_DICT_RAD = {'betain': 2 * TORAD}
|
||||
|
||||
|
||||
def create_matrices(delta, gamma, omegah, phi):
|
||||
return (calc_DELTA(delta), calc_GAMMA(gamma), calc_OMEGAH(omegah),
|
||||
calc_PHI(phi))
|
||||
|
||||
|
||||
def calc_DELTA(delta):
|
||||
return x_rotation(delta) # (39)
|
||||
|
||||
|
||||
def calc_GAMMA(gamma):
|
||||
return z_rotation(gamma) # (40)
|
||||
|
||||
|
||||
def calc_OMEGAH(omegah):
|
||||
return x_rotation(omegah) # (41)
|
||||
|
||||
|
||||
def calc_PHI(phi):
|
||||
return z_rotation(phi) # (42)
|
||||
|
||||
|
||||
def angles_to_hkl_phi(delta, gamma, omegah, phi):
|
||||
"""Calculate hkl matrix in phi frame in units of 2*pi/lambda
|
||||
"""
|
||||
DELTA, GAMMA, OMEGAH, PHI = create_matrices(delta, gamma, omegah, phi)
|
||||
H_lab = (GAMMA * DELTA - I) * matrix([[0], [1], [0]]) # (43)
|
||||
H_phi = PHI.I * OMEGAH.I * H_lab # (44)
|
||||
return H_phi
|
||||
|
||||
|
||||
def angles_to_hkl(delta, gamma, omegah, phi, wavelength, UB):
|
||||
"""Calculate hkl matrix in reprical lattice space in units of 1/Angstrom
|
||||
"""
|
||||
H_phi = angles_to_hkl_phi(delta, gamma, omegah, phi) * 2 * pi / wavelength
|
||||
hkl = UB.I * H_phi # (5)
|
||||
return hkl
|
||||
|
||||
|
||||
class WillmottHorizontalPosition(AbstractPosition):
|
||||
|
||||
def __init__(self, delta=None, gamma=None, omegah=None, phi=None):
|
||||
self.delta = delta
|
||||
self.gamma = gamma
|
||||
self.omegah = omegah
|
||||
self.phi = phi
|
||||
|
||||
def clone(self):
|
||||
return WillmottHorizontalPosition(self.delta, self.gamma, self.omegah,
|
||||
self.phi)
|
||||
|
||||
def changeToRadians(self):
|
||||
self.delta *= TORAD
|
||||
self.gamma *= TORAD
|
||||
self.omegah *= TORAD
|
||||
self.phi *= TORAD
|
||||
|
||||
def changeToDegrees(self):
|
||||
self.delta *= TODEG
|
||||
self.gamma *= TODEG
|
||||
self.omegah *= TODEG
|
||||
self.phi *= TODEG
|
||||
|
||||
def totuple(self):
|
||||
return (self.delta, self.gamma, self.omegah, self.phi)
|
||||
|
||||
def __str__(self):
|
||||
return ('WillmottHorizontalPosition('
|
||||
'delta: %.4f gamma: %.4f omegah: %.4f phi: %.4f)' %
|
||||
(self.delta, self.gamma, self.omegah, self.phi))
|
||||
|
||||
|
||||
class WillmottHorizontalGeometry(object):
|
||||
|
||||
def __init__(self):
|
||||
self.name = 'willmott_horizontal'
|
||||
|
||||
def physical_angles_to_internal_position(self, physicalAngles):
|
||||
return WillmottHorizontalPosition(*physicalAngles)
|
||||
|
||||
def internal_position_to_physical_angles(self, internalPosition):
|
||||
return internalPosition.totuple()
|
||||
|
||||
def create_position(self, delta, gamma, omegah, phi):
|
||||
return WillmottHorizontalPosition(delta, gamma, omegah, phi)
|
||||
|
||||
|
||||
class WillmottHorizontalUbCalcStrategy(PaperSpecificUbCalcStrategy):
|
||||
|
||||
def calculate_q_phi(self, pos):
|
||||
H_phi = angles_to_hkl_phi(*pos.totuple())
|
||||
return matrix(H_phi.tolist())
|
||||
|
||||
|
||||
class DummyConstraints(object):
|
||||
|
||||
@property
|
||||
def reference(self):
|
||||
"""dictionary of constrained reference circles"""
|
||||
return TEMPORARY_CONSTRAINTS_DICT_RAD
|
||||
|
||||
|
||||
class ConstraintAdapter(object):
|
||||
|
||||
def __init__(self, constraints):
|
||||
self._constraints = constraints
|
||||
|
||||
def getParameterDict(self):
|
||||
names = self._constraints.available
|
||||
return dict(zip(names, [None] * len(names)))
|
||||
|
||||
def setParameter(self, name, value):
|
||||
self._constraints.set_constraint(name, value)
|
||||
|
||||
def get(self, name):
|
||||
if name in self._constraints.all:
|
||||
val = self._constraints.get_value(name)
|
||||
return 999 if val is None else val
|
||||
else:
|
||||
return 999
|
||||
|
||||
def update_tracked(self):
|
||||
pass
|
||||
|
||||
|
||||
class WillmottHorizontalCalculator(HklCalculatorBase):
|
||||
|
||||
def __init__(self, ubcalc, geometry, hardware, constraints,
|
||||
raiseExceptionsIfAnglesDoNotMapBackToHkl=True):
|
||||
""""
|
||||
Where constraints.reference is a one element dict with the key either
|
||||
('betain', 'betaout' or 'equal') and the value a number or None for
|
||||
'betain_eq_betaout'
|
||||
"""
|
||||
|
||||
HklCalculatorBase.__init__(self, ubcalc, geometry, hardware,
|
||||
raiseExceptionsIfAnglesDoNotMapBackToHkl)
|
||||
|
||||
if constraints is not None:
|
||||
self.constraints = constraints
|
||||
self.parameter_manager = ConstraintAdapter(constraints)
|
||||
else:
|
||||
self.constraints = DummyConstraints()
|
||||
self.parameter_manager = DummyParameterManager()
|
||||
|
||||
@property
|
||||
def _UB(self):
|
||||
return self._ubcalc.UB
|
||||
|
||||
def _anglesToHkl(self, pos, wavelength):
|
||||
"""
|
||||
Calculate miller indices from position in radians.
|
||||
"""
|
||||
hkl_matrix = angles_to_hkl(pos.delta, pos.gamma, pos.omegah, pos.phi,
|
||||
wavelength, self._UB)
|
||||
return hkl_matrix[0, 0], hkl_matrix[1, 0], hkl_matrix[2, 0],
|
||||
|
||||
def _anglesToVirtualAngles(self, pos, wavelength):
|
||||
"""
|
||||
Calculate virtual-angles in radians from position in radians.
|
||||
|
||||
Return theta, alpha, and beta in a dictionary.
|
||||
"""
|
||||
|
||||
betain = pos.omegah # (52)
|
||||
|
||||
hkl = angles_to_hkl(pos.delta, pos.gamma, pos.omegah, pos.phi,
|
||||
wavelength, self._UB)
|
||||
H_phi = self._UB * hkl
|
||||
H_phi = H_phi / (2 * pi / wavelength)
|
||||
l_phi = H_phi[2, 0]
|
||||
sin_betaout = l_phi - sin(betain)
|
||||
betaout = asin(bound(sin_betaout)) # (54)
|
||||
|
||||
cos_2theta = cos(pos.delta) * cos(pos.gamma)
|
||||
theta = acos(bound(cos_2theta)) / 2.
|
||||
|
||||
return {'theta': theta, 'betain': betain, 'betaout': betaout}
|
||||
|
||||
def _hklToAngles(self, h, k, l, wavelength):
|
||||
"""
|
||||
Calculate position and virtual angles in radians for a given hkl.
|
||||
"""
|
||||
|
||||
H_phi = self._UB * matrix([[h], [k], [l]]) # units: 1/Angstrom
|
||||
H_phi = H_phi / (2 * pi / wavelength) # units: 2*pi/wavelength
|
||||
h_phi = H_phi[0, 0]
|
||||
k_phi = H_phi[1, 0]
|
||||
l_phi = H_phi[2, 0] # (5)
|
||||
|
||||
### determine betain (omegah) and betaout ###
|
||||
|
||||
if not self.constraints.reference:
|
||||
raise ValueError("No reference constraint has been constrained.")
|
||||
|
||||
ref_name, ref_value = self.constraints.reference.items()[0]
|
||||
if ref_value is not None:
|
||||
ref_value *= TORAD
|
||||
if ref_name == 'betain':
|
||||
betain = ref_value
|
||||
betaout = asin(bound(l_phi - sin(betain))) # (53)
|
||||
elif ref_name == 'betaout':
|
||||
betaout = ref_value
|
||||
betain = asin(bound(l_phi - sin(betaout))) # (54)
|
||||
elif ref_name == 'bin_eq_bout':
|
||||
betain = betaout = asin(bound(l_phi / 2)) # (55)
|
||||
else:
|
||||
raise ValueError("Unexpected constraint name'%s'." % ref_name)
|
||||
|
||||
if abs(betain) < SMALL:
|
||||
raise DiffcalcException('required betain was 0 degrees (requested '
|
||||
'q is perpendicular to surface normal)')
|
||||
if betain < -SMALL:
|
||||
raise DiffcalcException("betain was -ve (%.4f)" % betain)
|
||||
# logger.info('betain = %.4f, betaout = %.4f',
|
||||
# betain * TODEG, betaout * TODEG)
|
||||
omegah = betain # (52)
|
||||
|
||||
### determine H_lab (X, Y and Z) ###
|
||||
|
||||
Y = -(h_phi ** 2 + k_phi ** 2 + l_phi ** 2) / 2 # (45)
|
||||
|
||||
Z = (sin(betaout) + sin(betain) * (Y + 1)) / cos(omegah) # (47)
|
||||
|
||||
X_squared = (h_phi ** 2 + k_phi ** 2 -
|
||||
((cos(betain) * Y + sin(betain) * Z) ** 2)) # (48)
|
||||
if (X_squared < 0) and (abs(X_squared) < SMALL):
|
||||
X_squared = 0
|
||||
Xpositive = sqrt(X_squared)
|
||||
if CHOOSE_POSITIVE_GAMMA:
|
||||
X = -Xpositive
|
||||
else:
|
||||
X = Xpositive
|
||||
# logger.info('H_lab (X,Y,Z) = [%.4f, %.4f, %.4f]', X, Y, Z)
|
||||
### determine diffractometer angles ###
|
||||
|
||||
gamma = atan2(-X, Y + 1) # (49)
|
||||
if (abs(gamma) < SMALL):
|
||||
# degenerate case, only occurs when q || z
|
||||
delta = 2 * omegah
|
||||
else:
|
||||
delta = atan2(Z * sin(gamma), -X) # (50)
|
||||
M = cos(betain) * Y + sin(betain) * Z
|
||||
phi = atan2(h_phi * M - k_phi * X, h_phi * X + k_phi * M) # (51)
|
||||
|
||||
pos = WillmottHorizontalPosition(delta, gamma, omegah, phi)
|
||||
virtual_angles = {'betain': betain, 'betaout': betaout}
|
||||
return pos, virtual_angles
|
||||
58
script/__Lib/diffcalc-2.1/diffcalc/hkl/willmott/commands.py
Executable file
58
script/__Lib/diffcalc-2.1/diffcalc/hkl/willmott/commands.py
Executable file
@@ -0,0 +1,58 @@
|
||||
###
|
||||
# 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.hkl.common import getNameFromScannableOrString
|
||||
from diffcalc.util import command
|
||||
|
||||
|
||||
class WillmottHklCommands(object):
|
||||
|
||||
def __init__(self, hklcalc):
|
||||
self._hklcalc = hklcalc
|
||||
self.commands = [self.con,
|
||||
self.uncon,
|
||||
self.cons]
|
||||
|
||||
def __str__(self):
|
||||
return self._hklcalc.__str__()
|
||||
|
||||
@command
|
||||
def con(self, scn_or_string):
|
||||
"""con <constraint> -- constrains constraint
|
||||
"""
|
||||
name = getNameFromScannableOrString(scn_or_string)
|
||||
self._hklcalc.constraints.constrain(name)
|
||||
print self._report_constraints()
|
||||
|
||||
@command
|
||||
def uncon(self, scn_or_string):
|
||||
"""uncon <constraint> -- unconstrains constraint
|
||||
"""
|
||||
name = getNameFromScannableOrString(scn_or_string)
|
||||
self._hklcalc.constraints.unconstrain(name)
|
||||
print self._report_constraints()
|
||||
|
||||
@command
|
||||
def cons(self):
|
||||
"""cons -- list available constraints and values
|
||||
"""
|
||||
print self._report_constraints()
|
||||
|
||||
def _report_constraints(self):
|
||||
return (self._hklcalc.constraints.build_display_table_lines() + '\n\n' +
|
||||
self._hklcalc.constraints._report_constraints())
|
||||
156
script/__Lib/diffcalc-2.1/diffcalc/hkl/willmott/constraints.py
Executable file
156
script/__Lib/diffcalc-2.1/diffcalc/hkl/willmott/constraints.py
Executable file
@@ -0,0 +1,156 @@
|
||||
###
|
||||
# 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 DiffcalcException
|
||||
|
||||
|
||||
def filter_dict(d, keys):
|
||||
"""Return a copy of d containing only keys that are in keys"""
|
||||
##return {k: d[k] for k in keys} # requires Python 2.6
|
||||
return dict((k, d[k]) for k in keys if k in d.keys())
|
||||
|
||||
|
||||
ref_constraints = ('betain', 'betaout', 'bin_eq_bout')
|
||||
valueless_constraints = ('bin_eq_bout')
|
||||
all_constraints = ref_constraints
|
||||
|
||||
|
||||
class WillmottConstraintManager(object):
|
||||
"""Constraints in degrees.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self._constrained = {'bin_eq_bout': None}
|
||||
|
||||
@property
|
||||
def available_constraint_names(self):
|
||||
"""list of all available constraints"""
|
||||
return all_constraints
|
||||
|
||||
@property
|
||||
def all(self): # @ReservedAssignment
|
||||
"""dictionary of all constrained values"""
|
||||
return self._constrained.copy()
|
||||
|
||||
@property
|
||||
def reference(self):
|
||||
"""dictionary of constrained reference circles"""
|
||||
return filter_dict(self.all, ref_constraints)
|
||||
|
||||
@property
|
||||
def constrained_names(self):
|
||||
"""ordered tuple of constained circles"""
|
||||
names = self.all.keys()
|
||||
names.sort(key=lambda name: list(all_constraints).index(name))
|
||||
return tuple(names)
|
||||
|
||||
def is_constrained(self, name):
|
||||
return name in self._constrained
|
||||
|
||||
def get_value(self, name):
|
||||
return self._constrained[name]
|
||||
|
||||
def _build_display_table(self):
|
||||
constraint_types = (ref_constraints,)
|
||||
num_rows = max([len(col) for col in constraint_types])
|
||||
max_name_width = max(
|
||||
[len(name) for name in sum(constraint_types[:2], ())])
|
||||
# headings
|
||||
lines = [' ' + 'REF'.ljust(max_name_width)]
|
||||
lines.append(' ' + '=' * max_name_width + ' ')
|
||||
|
||||
# constraint rows
|
||||
for n_row in range(num_rows):
|
||||
cells = []
|
||||
for col in constraint_types:
|
||||
name = col[n_row] if n_row < len(col) else ''
|
||||
cells.append(self._label_constraint(name))
|
||||
cells.append(('%-' + str(max_name_width) + 's ') % name)
|
||||
lines.append(''.join(cells))
|
||||
lines.append
|
||||
return '\n'.join(lines)
|
||||
|
||||
def _report_constraints(self):
|
||||
if not self.reference:
|
||||
return "!!! No reference constraint set"
|
||||
name, val = self.reference.items()[0]
|
||||
if name in valueless_constraints:
|
||||
return " %s" % name
|
||||
else:
|
||||
if val is None:
|
||||
return "!!! %s: ---" % name
|
||||
else:
|
||||
return " %s: %.4f" % (name, val)
|
||||
|
||||
def _label_constraint(self, name):
|
||||
if name == '':
|
||||
label = ' '
|
||||
elif (self.is_constrained(name) and (self.get_value(name) is None) and
|
||||
name not in valueless_constraints):
|
||||
label = 'o-> '
|
||||
elif self.is_constrained(name):
|
||||
label = '--> '
|
||||
else:
|
||||
label = ' '
|
||||
return label
|
||||
|
||||
def constrain(self, name):
|
||||
if name in self.all:
|
||||
return "%s is already constrained." % name.capitalize()
|
||||
elif name in ref_constraints:
|
||||
return self._constrain_reference(name)
|
||||
else:
|
||||
raise DiffcalcException('%s is not a valid constraint name')
|
||||
|
||||
def _constrain_reference(self, name):
|
||||
if self.reference:
|
||||
constrained_name = self.reference.keys()[0]
|
||||
del self._constrained[constrained_name]
|
||||
self._constrained[name] = None
|
||||
return '%s constraint replaced.' % constrained_name.capitalize()
|
||||
else:
|
||||
self._constrained[name] = None
|
||||
|
||||
def unconstrain(self, name):
|
||||
if name in self._constrained:
|
||||
del self._constrained[name]
|
||||
else:
|
||||
return "%s was not already constrained." % name.capitalize()
|
||||
|
||||
###
|
||||
def _check_constraint_settable(self, name, verb):
|
||||
if name not in all_constraints:
|
||||
raise DiffcalcException(
|
||||
'Could not %(verb)s %(name)s as this is not an available '
|
||||
'constraint.' % locals())
|
||||
elif name not in self.all.keys():
|
||||
raise DiffcalcException(
|
||||
'Could not %(verb)s %(name)s as this is not currently '
|
||||
'constrained.' % locals())
|
||||
elif name in valueless_constraints:
|
||||
raise DiffcalcException(
|
||||
'Could not %(verb)s %(name)s as this constraint takes no '
|
||||
'value.' % locals())
|
||||
|
||||
def set_constraint(self, name, value): # @ReservedAssignment
|
||||
self._check_constraint_settable(name, 'set')
|
||||
old_value = self.all[name]
|
||||
old = str(old_value) if old_value is not None else '---'
|
||||
self._constrained[name] = float(value)
|
||||
new = str(value)
|
||||
return "%(name)s : %(old)s --> %(new)s" % locals()
|
||||
0
script/__Lib/diffcalc-2.1/diffcalc/hkl/you/__init__.py
Executable file
0
script/__Lib/diffcalc-2.1/diffcalc/hkl/you/__init__.py
Executable file
1162
script/__Lib/diffcalc-2.1/diffcalc/hkl/you/calc.py
Executable file
1162
script/__Lib/diffcalc-2.1/diffcalc/hkl/you/calc.py
Executable file
File diff suppressed because it is too large
Load Diff
377
script/__Lib/diffcalc-2.1/diffcalc/hkl/you/constraints.py
Executable file
377
script/__Lib/diffcalc-2.1/diffcalc/hkl/you/constraints.py
Executable file
@@ -0,0 +1,377 @@
|
||||
###
|
||||
# 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 math import pi
|
||||
|
||||
try:
|
||||
from numpy import matrix
|
||||
except ImportError:
|
||||
from numjy import matrix
|
||||
|
||||
from diffcalc.util import DiffcalcException, bold
|
||||
|
||||
TODEG = 180 / pi
|
||||
TORAD = pi / 180
|
||||
|
||||
NUNAME = 'gam'
|
||||
|
||||
def filter_dict(d, keys):
|
||||
"""Return a copy of d containing only keys that are in keys"""
|
||||
##return {k: d[k] for k in keys} # requires Python 2.6
|
||||
return dict((k, d[k]) for k in keys if k in d.keys())
|
||||
|
||||
|
||||
det_constraints = ('delta', NUNAME, 'qaz', 'naz')
|
||||
ref_constraints = ('a_eq_b', 'alpha', 'beta', 'psi')
|
||||
samp_constraints = ('mu', 'eta', 'chi', 'phi', 'mu_is_' + NUNAME)
|
||||
|
||||
valueless_constraints = ('a_eq_b', 'mu_is_' + NUNAME)
|
||||
all_constraints = det_constraints + ref_constraints + samp_constraints
|
||||
|
||||
|
||||
number_single_sample = (len(det_constraints) * len(ref_constraints) *
|
||||
len(samp_constraints))
|
||||
|
||||
|
||||
class YouConstraintManager(object):
|
||||
|
||||
def __init__(self, hardware, fixed_constraints = {}):
|
||||
self._hardware = hardware
|
||||
self._constrained = {}
|
||||
# self._tracking = []
|
||||
self.n_phi = matrix([[0], [0], [1]])
|
||||
self._hide_detector_constraint = False # default
|
||||
self._fixed_samp_constraints = ()
|
||||
self._fix_constraints(fixed_constraints)
|
||||
|
||||
def __str__(self):
|
||||
lines = []
|
||||
# TODO: Put somewhere with access to UB matrix!
|
||||
# WIDTH = 13
|
||||
# n_phi = self.n_phi
|
||||
# fmt = "% 9.5f % 9.5f % 9.5f"
|
||||
# lines.append(" n_phi:".ljust(WIDTH) +
|
||||
# fmt % (n_phi[0, 0], n_phi[1, 0], n_phi[2, 0]))
|
||||
# if self._getUBMatrix():
|
||||
# n_cryst = self._getUMatrix().I * self.n_phi
|
||||
# lines.append(" n_cryst:".ljust(WIDTH) +
|
||||
# fmt % (n_cryst[0, 0], n_cryst[1, 0], n_cryst[2, 0]))
|
||||
# n_recip = self._getUBMatrix().I * self.n_phi
|
||||
# lines.append(" n_recip:".ljust(WIDTH) +
|
||||
# fmt % (n_recip[0, 0], n_recip[1, 0], n_recip[2, 0]))
|
||||
# else:
|
||||
# lines.append(
|
||||
# " n_cryst:".ljust(WIDTH) + ' "<<< No U matrix >>>"')
|
||||
# lines.append(
|
||||
# " n_recip:".ljust(WIDTH) + ' "<<< No UB matrix >>>"')
|
||||
|
||||
lines.extend(self.build_display_table_lines())
|
||||
lines.append("")
|
||||
lines.extend(self.report_constraints_lines())
|
||||
lines.append("")
|
||||
if (self.is_fully_constrained() and
|
||||
not self.is_current_mode_implemented()):
|
||||
lines.append(
|
||||
" Sorry, this constraint combination is not implemented")
|
||||
lines.append(" Type 'help con' for available combinations")
|
||||
else:
|
||||
lines.append(" Type 'help con' for instructions") # okay
|
||||
return '\n'.join(lines)
|
||||
|
||||
@property
|
||||
def available_constraint_names(self):
|
||||
"""list of all available constraints"""
|
||||
return all_constraints
|
||||
|
||||
@property
|
||||
def settable_constraint_names(self):
|
||||
"""list of all available constraints that have settable values"""
|
||||
all_copy = list(all_constraints)
|
||||
for valueless in valueless_constraints:
|
||||
all_copy.remove(valueless)
|
||||
return all_copy
|
||||
|
||||
@property
|
||||
def all(self): # @ReservedAssignment
|
||||
"""dictionary of all constrained values"""
|
||||
return self._constrained.copy()
|
||||
|
||||
@property
|
||||
def detector(self):
|
||||
"""dictionary of constrained detector circles"""
|
||||
return filter_dict(self.all, det_constraints[:-1])
|
||||
|
||||
@property
|
||||
def reference(self):
|
||||
"""dictionary of constrained reference circles"""
|
||||
return filter_dict(self.all, ref_constraints)
|
||||
|
||||
@property
|
||||
def sample(self):
|
||||
"""dictionary of constrained sample circles"""
|
||||
return filter_dict(self.all, samp_constraints)
|
||||
|
||||
@property
|
||||
def naz(self):
|
||||
"""dictionary with naz and value if constrained"""
|
||||
return filter_dict(self.all, ('naz',))
|
||||
|
||||
@property
|
||||
def constrained_names(self):
|
||||
"""ordered tuple of constained circles"""
|
||||
names = self.all.keys()
|
||||
names.sort(key=lambda name: list(all_constraints).index(name))
|
||||
return tuple(names)
|
||||
|
||||
def _fix_constraints(self, fixed_constraints):
|
||||
for name in fixed_constraints:
|
||||
self.constrain(name)
|
||||
self.set_constraint(name, fixed_constraints[name])
|
||||
|
||||
if self.detector or self.naz:
|
||||
self._hide_detector_constraint = True
|
||||
|
||||
fixed_samp_constraints = list(self.sample.keys())
|
||||
if 'mu' in self.sample or NUNAME in self.detector:
|
||||
fixed_samp_constraints.append('mu_is_' + NUNAME)
|
||||
self._fixed_samp_constraints = tuple(fixed_samp_constraints)
|
||||
|
||||
|
||||
def is_constrained(self, name):
|
||||
return name in self._constrained
|
||||
|
||||
def get_value(self, name):
|
||||
return self._constrained[name]
|
||||
|
||||
def build_display_table_lines(self):
|
||||
unfixed_samp_constraints = list(samp_constraints)
|
||||
for name in self._fixed_samp_constraints:
|
||||
unfixed_samp_constraints.remove(name)
|
||||
if self._hide_detector_constraint:
|
||||
constraint_types = (ref_constraints, unfixed_samp_constraints)
|
||||
else:
|
||||
constraint_types = (det_constraints, ref_constraints,
|
||||
unfixed_samp_constraints)
|
||||
num_rows = max([len(col) for col in constraint_types])
|
||||
max_name_width = max(
|
||||
[len(name) for name in sum(constraint_types[:-1], ())])
|
||||
|
||||
cells = []
|
||||
|
||||
header_cells = []
|
||||
if not self._hide_detector_constraint:
|
||||
header_cells.append(bold(' ' + 'DET'.ljust(max_name_width)))
|
||||
header_cells.append(bold(' ' + 'REF'.ljust(max_name_width)))
|
||||
header_cells.append(bold(' ' + 'SAMP'))
|
||||
cells.append(header_cells)
|
||||
|
||||
underline_cells = [' ' + '-' * max_name_width] * len(constraint_types)
|
||||
cells.append(underline_cells)
|
||||
|
||||
for n_row in range(num_rows):
|
||||
row_cells = []
|
||||
for col in constraint_types:
|
||||
name = col[n_row] if n_row < len(col) else ''
|
||||
row_cells.append(self._label_constraint(name))
|
||||
row_cells.append(('%-' + str(max_name_width) + 's') % name)
|
||||
cells.append(row_cells)
|
||||
|
||||
lines = [' '.join(row_cells).rstrip() for row_cells in cells]
|
||||
return lines
|
||||
|
||||
def _report_constraint(self, name):
|
||||
val = self.get_constraint(name)
|
||||
if name in valueless_constraints:
|
||||
return " %s" % name
|
||||
else:
|
||||
if val is None:
|
||||
return "! %-5s: ---" % name
|
||||
else:
|
||||
return " %-5s: %.4f" % (name, val)
|
||||
|
||||
def report_constraints_lines(self):
|
||||
lines = []
|
||||
required = 3 - len(self.all)
|
||||
if required == 0:
|
||||
pass
|
||||
elif required == 1:
|
||||
lines.append('! 1 more constraint required')
|
||||
else:
|
||||
lines.append('! %d more constraints required' % required)
|
||||
constraints = []
|
||||
constraints.extend(self.detector.keys())
|
||||
constraints.extend(self.naz.keys())
|
||||
constraints.extend(self.reference.keys())
|
||||
constraints.extend(sorted(self.sample.keys()))
|
||||
for name in constraints:
|
||||
lines.append(self._report_constraint(name))
|
||||
return lines
|
||||
|
||||
def is_fully_constrained(self):
|
||||
return len(self.all) == 3
|
||||
|
||||
def is_current_mode_implemented(self):
|
||||
if not self.is_fully_constrained():
|
||||
raise ValueError("Three constraints required")
|
||||
|
||||
if len(self.sample) == 3:
|
||||
if set(self.sample.keys()) == set(['chi', 'phi', 'eta']):
|
||||
return True
|
||||
return False
|
||||
|
||||
if len(self.sample) == 1:
|
||||
return True
|
||||
|
||||
if len(self.reference) == 1:
|
||||
return (set(self.sample.keys()) == set(['chi', 'phi']) or
|
||||
set(self.sample.keys()) == set(['chi', 'eta']) or
|
||||
set(self.sample.keys()) == set(['chi', 'mu']) or
|
||||
set(self.sample.keys()) == set(['mu', 'eta']))
|
||||
|
||||
if len(self.detector) == 1:
|
||||
return (set(self.sample.keys()) == set(['chi', 'phi']) or
|
||||
set(self.sample.keys()) == set(['mu', 'eta']) or
|
||||
set(self.sample.keys()) == set(['mu', 'phi'])
|
||||
)
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def _label_constraint(self, name):
|
||||
if name == '':
|
||||
label = ' '
|
||||
# elif self.is_tracking(name): # implies constrained
|
||||
# label = '~~> '
|
||||
elif (self.is_constrained(name) and (self.get_value(name) is None) and
|
||||
name not in valueless_constraints):
|
||||
label = 'o->'
|
||||
elif self.is_constrained(name):
|
||||
label = '-->'
|
||||
else:
|
||||
label = ' '
|
||||
return label
|
||||
|
||||
def constrain(self, name):
|
||||
if self.is_constraint_fixed(name):
|
||||
raise DiffcalcException('%s is not a valid constraint name' % name)
|
||||
if name in self.all:
|
||||
return "%s is already constrained." % name.capitalize()
|
||||
elif name in det_constraints:
|
||||
return self._constrain_detector(name)
|
||||
elif name in ref_constraints:
|
||||
return self._constrain_reference(name)
|
||||
elif name in samp_constraints:
|
||||
return self._constrain_sample(name)
|
||||
else:
|
||||
raise DiffcalcException("%s is not a valid constraint name. Type 'con' for a table of constraint name" % name)
|
||||
|
||||
def is_constraint_fixed(self, name):
|
||||
return ((name in det_constraints and self._hide_detector_constraint) or
|
||||
(name in samp_constraints and name in self._fixed_samp_constraints))
|
||||
|
||||
def _constrain_detector(self, name):
|
||||
if self.naz:
|
||||
del self._constrained['naz']
|
||||
self._constrained[name] = None
|
||||
return 'Naz constraint replaced.'
|
||||
elif self.detector:
|
||||
constrained_name = self.detector.keys()[0]
|
||||
del self._constrained[constrained_name]
|
||||
self._constrained[name] = None
|
||||
return'%s constraint replaced.' % constrained_name.capitalize()
|
||||
elif len(self.all) == 3: # and no detector
|
||||
raise self._could_not_constrain_exception(name)
|
||||
else:
|
||||
self._constrained[name] = None
|
||||
|
||||
def _could_not_constrain_exception(self, name):
|
||||
return DiffcalcException(
|
||||
"%s could not be constrained. First un-constrain one of the\n"
|
||||
"angles %s, %s or %s (with 'uncon')" %
|
||||
((name.capitalize(),) + self.constrained_names))
|
||||
|
||||
def _constrain_reference(self, name):
|
||||
if self.reference:
|
||||
constrained_name = self.reference.keys()[0]
|
||||
del self._constrained[constrained_name]
|
||||
self._constrained[name] = None
|
||||
return '%s constraint replaced.' % constrained_name.capitalize()
|
||||
elif len(self.all) == 3: # and no reference
|
||||
raise self._could_not_constrain_exception(name)
|
||||
else:
|
||||
self._constrained[name] = None
|
||||
|
||||
def _constrain_sample(self, name):
|
||||
if len(self._constrained) < 3:
|
||||
# okay, more to add
|
||||
self._constrained[name] = None
|
||||
# else: three constraints are set
|
||||
elif len(self.sample) == 1:
|
||||
# (detector and reference constraints set)
|
||||
# it is clear which sample constraint to remove
|
||||
constrained_name = self.sample.keys()[0]
|
||||
del self._constrained[constrained_name]
|
||||
self._constrained[name] = None
|
||||
return '%s constraint replaced.' % constrained_name.capitalize()
|
||||
else:
|
||||
raise self._could_not_constrain_exception(name)
|
||||
|
||||
def unconstrain(self, name):
|
||||
if self.is_constraint_fixed(name):
|
||||
raise DiffcalcException('%s is not a valid constraint name')
|
||||
if name in self._constrained:
|
||||
del self._constrained[name]
|
||||
else:
|
||||
return "%s was not already constrained." % name.capitalize()
|
||||
|
||||
def _check_constraint_settable(self, name):
|
||||
if name not in all_constraints:
|
||||
raise DiffcalcException(
|
||||
'Could not set %(name)s. This is not an available '
|
||||
'constraint.' % locals())
|
||||
elif name not in self.all.keys():
|
||||
raise DiffcalcException(
|
||||
'Could not set %(name)s. This is not currently '
|
||||
'constrained.' % locals())
|
||||
elif name in valueless_constraints:
|
||||
raise DiffcalcException(
|
||||
'Could not set %(name)s. This constraint takes no '
|
||||
'value.' % locals())
|
||||
|
||||
def clear_constraints(self):
|
||||
self._constrained = {}
|
||||
|
||||
def set_constraint(self, name, value): # @ReservedAssignment
|
||||
if self.is_constraint_fixed(name):
|
||||
raise DiffcalcException('%s is not a valid constraint name')
|
||||
self._check_constraint_settable(name)
|
||||
# if name in self._tracking:
|
||||
# raise DiffcalcException(
|
||||
# "Could not set %s as this constraint is configured to track "
|
||||
# "its associated\nphysical angle. First remove this tracking "
|
||||
# "(use 'untrack %s').""" % (name, name))
|
||||
old_value = self.get_constraint(name)
|
||||
old = str(old_value) if old_value is not None else '---'
|
||||
self._constrained[name] = float(value) * TORAD
|
||||
new = str(value)
|
||||
return "%(name)s : %(old)s --> %(new)s" % locals()
|
||||
|
||||
def get_constraint(self, name):
|
||||
value = self.all[name]
|
||||
return None if value is None else value * TODEG
|
||||
|
||||
219
script/__Lib/diffcalc-2.1/diffcalc/hkl/you/geometry.py
Executable file
219
script/__Lib/diffcalc-2.1/diffcalc/hkl/you/geometry.py
Executable file
@@ -0,0 +1,219 @@
|
||||
###
|
||||
# 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 math import pi
|
||||
|
||||
from diffcalc.util import AbstractPosition, DiffcalcException
|
||||
|
||||
TORAD = pi / 180
|
||||
TODEG = 180 / pi
|
||||
from diffcalc.util import x_rotation, z_rotation, y_rotation
|
||||
|
||||
from diffcalc.hkl.you.constraints import NUNAME
|
||||
|
||||
class YouGeometry(object):
|
||||
|
||||
def __init__(self, name, fixed_constraints, beamline_axes_transform=None):
|
||||
self.name = name
|
||||
self.fixed_constraints = fixed_constraints
|
||||
# beamline_axes_transform matrix is composed of the diffcalc basis vector coordinates
|
||||
# in the beamline coordinate system, i.e. it transforms the beamline coordinate system
|
||||
# into the reference diffcalc one.
|
||||
self.beamline_axes_transform = beamline_axes_transform
|
||||
|
||||
def physical_angles_to_internal_position(self, physical_angle_tuple):
|
||||
raise NotImplementedError()
|
||||
|
||||
def internal_position_to_physical_angles(self, internal_position):
|
||||
raise NotImplementedError()
|
||||
|
||||
def create_position(self, *args):
|
||||
return YouPosition(*args, unit='DEG')
|
||||
|
||||
|
||||
#==============================================================================
|
||||
#==============================================================================
|
||||
# Geometry plugins for use with 'You' hkl calculation engine
|
||||
#==============================================================================
|
||||
#==============================================================================
|
||||
|
||||
|
||||
class SixCircle(YouGeometry):
|
||||
def __init__(self, beamline_axes_transform=None):
|
||||
YouGeometry.__init__(self, 'sixc', {}, beamline_axes_transform)
|
||||
|
||||
def physical_angles_to_internal_position(self, physical_angle_tuple):
|
||||
# mu, delta, nu, eta, chi, phi
|
||||
return YouPosition(*physical_angle_tuple, unit='DEG')
|
||||
|
||||
def internal_position_to_physical_angles(self, internal_position):
|
||||
clone_position = internal_position.clone()
|
||||
clone_position.changeToDegrees()
|
||||
return clone_position.totuple()
|
||||
|
||||
|
||||
class FourCircle(YouGeometry):
|
||||
"""For a diffractometer with angles:
|
||||
delta, eta, chi, phi
|
||||
"""
|
||||
def __init__(self, beamline_axes_transform=None):
|
||||
YouGeometry.__init__(self, 'fourc', {'mu': 0, NUNAME: 0}, beamline_axes_transform)
|
||||
|
||||
def physical_angles_to_internal_position(self, physical_angle_tuple):
|
||||
# mu, delta, nu, eta, chi, phi
|
||||
delta, eta, chi, phi = physical_angle_tuple
|
||||
return YouPosition(0, delta, 0, eta, chi, phi, 'DEG')
|
||||
|
||||
def internal_position_to_physical_angles(self, internal_position):
|
||||
clone_position = internal_position.clone()
|
||||
clone_position.changeToDegrees()
|
||||
_, delta, _, eta, chi, phi = clone_position.totuple()
|
||||
return delta, eta, chi, phi
|
||||
|
||||
|
||||
class FiveCircle(YouGeometry):
|
||||
"""For a diffractometer with angles:
|
||||
delta, nu, eta, chi, phi
|
||||
"""
|
||||
def __init__(self, beamline_axes_transform=None):
|
||||
YouGeometry.__init__(self, 'fivec', {'mu': 0}, beamline_axes_transform)
|
||||
|
||||
def physical_angles_to_internal_position(self, physical_angle_tuple):
|
||||
# mu, delta, nu, eta, chi, phi
|
||||
delta, nu, eta, chi, phi = physical_angle_tuple
|
||||
return YouPosition(0, delta, nu, eta, chi, phi, 'DEG')
|
||||
|
||||
def internal_position_to_physical_angles(self, internal_position):
|
||||
clone_position = internal_position.clone()
|
||||
clone_position.changeToDegrees()
|
||||
_, delta, nu, eta, chi, phi = clone_position.totuple()
|
||||
return delta, nu, eta, chi, phi
|
||||
|
||||
|
||||
#==============================================================================
|
||||
|
||||
|
||||
def create_you_matrices(mu=None, delta=None, nu=None, eta=None, chi=None,
|
||||
phi=None):
|
||||
"""
|
||||
Create transformation matrices from H. You's paper.
|
||||
"""
|
||||
MU = None if mu is None else calcMU(mu)
|
||||
DELTA = None if delta is None else calcDELTA(delta)
|
||||
NU = None if nu is None else calcNU(nu)
|
||||
ETA = None if eta is None else calcETA(eta)
|
||||
CHI = None if chi is None else calcCHI(chi)
|
||||
PHI = None if phi is None else calcPHI(phi)
|
||||
return MU, DELTA, NU, ETA, CHI, PHI
|
||||
|
||||
|
||||
def calcNU(nu):
|
||||
return x_rotation(nu)
|
||||
|
||||
|
||||
def calcDELTA(delta):
|
||||
return z_rotation(-delta)
|
||||
|
||||
|
||||
def calcMU(mu_or_alpha):
|
||||
return x_rotation(mu_or_alpha)
|
||||
|
||||
|
||||
def calcETA(eta):
|
||||
return z_rotation(-eta)
|
||||
|
||||
|
||||
def calcCHI(chi):
|
||||
return y_rotation(chi)
|
||||
|
||||
|
||||
def calcPHI(phi):
|
||||
return z_rotation(-phi)
|
||||
|
||||
|
||||
def you_position_names():
|
||||
return ('mu', 'delta', NUNAME, 'eta', 'chi', 'phi')
|
||||
|
||||
class YouPosition(AbstractPosition):
|
||||
|
||||
def __init__(self, mu, delta, nu, eta, chi, phi, unit):
|
||||
self.mu = mu
|
||||
self.delta = delta
|
||||
self.nu = nu
|
||||
self.eta = eta
|
||||
self.chi = chi
|
||||
self.phi = phi
|
||||
if unit not in ['DEG', 'RAD']:
|
||||
raise DiffcalcException("Invalid angle unit value %s." % str(unit))
|
||||
else:
|
||||
self.unit = unit
|
||||
|
||||
def clone(self):
|
||||
return YouPosition(self.mu, self.delta, self.nu, self.eta, self.chi,
|
||||
self.phi, self.unit)
|
||||
|
||||
def changeToRadians(self):
|
||||
if self.unit == 'DEG':
|
||||
self.mu *= TORAD
|
||||
self.delta *= TORAD
|
||||
self.nu *= TORAD
|
||||
self.eta *= TORAD
|
||||
self.chi *= TORAD
|
||||
self.phi *= TORAD
|
||||
self.unit = 'RAD'
|
||||
elif self.unit == 'RAD':
|
||||
return
|
||||
else:
|
||||
raise DiffcalcException("Invalid angle unit value %s." % str(self.unit))
|
||||
|
||||
def changeToDegrees(self):
|
||||
if self.unit == 'RAD':
|
||||
self.mu *= TODEG
|
||||
self.delta *= TODEG
|
||||
self.nu *= TODEG
|
||||
self.eta *= TODEG
|
||||
self.chi *= TODEG
|
||||
self.phi *= TODEG
|
||||
self.unit = 'DEG'
|
||||
elif self.unit == 'DEG':
|
||||
return
|
||||
else:
|
||||
raise DiffcalcException("Invalid angle unit value %s." % str(self.unit))
|
||||
|
||||
def totuple(self):
|
||||
return (self.mu, self.delta, self.nu, self.eta, self.chi, self.phi)
|
||||
|
||||
def __str__(self):
|
||||
mu, delta, nu, eta, chi, phi = self.totuple()
|
||||
return ("YouPosition(mu %r delta: %r nu: %r eta: %r chi: %r phi: %r) in %s"
|
||||
% (mu, delta, nu, eta, chi, phi, self.unit))
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.totuple() == other.totuple()
|
||||
|
||||
|
||||
class WillmottHorizontalPosition(YouPosition):
|
||||
|
||||
def __init__(self, delta=None, gamma=None, omegah=None, phi=None):
|
||||
self.mu = 0
|
||||
self.delta = delta
|
||||
self.nu = gamma
|
||||
self.eta = omegah
|
||||
self.chi = -90
|
||||
self.phi = phi
|
||||
self.unit= 'DEG'
|
||||
187
script/__Lib/diffcalc-2.1/diffcalc/hkl/you/hkl.py
Executable file
187
script/__Lib/diffcalc-2.1/diffcalc/hkl/you/hkl.py
Executable file
@@ -0,0 +1,187 @@
|
||||
###
|
||||
# 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.hkl.common import getNameFromScannableOrString
|
||||
from diffcalc.util import command
|
||||
from diffcalc.hkl.you.calc import YouHklCalculator
|
||||
from diffcalc import settings
|
||||
|
||||
|
||||
|
||||
import diffcalc.ub.ub
|
||||
from diffcalc.hkl.you.constraints import YouConstraintManager
|
||||
|
||||
__all__ = ['allhkl', 'con', 'uncon', 'hklcalc', 'constraint_manager']
|
||||
|
||||
|
||||
_fixed_constraints = settings.geometry.fixed_constraints # @UndefinedVariable
|
||||
|
||||
constraint_manager = YouConstraintManager(settings.hardware, _fixed_constraints)
|
||||
|
||||
hklcalc = YouHklCalculator(
|
||||
diffcalc.ub.ub.ubcalc, settings.geometry, settings.hardware, constraint_manager)
|
||||
|
||||
|
||||
def __str__(self):
|
||||
return hklcalc.__str__()
|
||||
|
||||
@command
|
||||
def con(*args):
|
||||
"""
|
||||
con -- list available constraints and values
|
||||
con <name> {val} -- constrains and optionally sets one constraint
|
||||
con <name> {val} <name> {val} <name> {val} -- clears and then fully constrains
|
||||
|
||||
Select three constraints using 'con' and 'uncon'. Choose up to one
|
||||
from each of the sample and detector columns and up to three from
|
||||
the sample column.
|
||||
|
||||
Not all constraint combinations are currently available:
|
||||
|
||||
1 x samp: all 80 of 80
|
||||
|
||||
2 x samp and 1 x ref: chi & phi
|
||||
chi & eta
|
||||
chi & mu
|
||||
mu & eta (4 of 6)
|
||||
|
||||
2 x samp and 1 x det: chi & phi
|
||||
mu & eta
|
||||
mu & phi (3 of 6)
|
||||
|
||||
3 x samp: eta, chi & phi (1 of 4)
|
||||
|
||||
See also 'uncon'
|
||||
"""
|
||||
args = list(args)
|
||||
msg = _handle_con(args)
|
||||
if (hklcalc.constraints.is_fully_constrained() and
|
||||
not hklcalc.constraints.is_current_mode_implemented()):
|
||||
msg += ("\n\nWARNING:. The selected constraint combination is valid but "
|
||||
"is not implemented.\n\nType 'help con' to see implemented combinations")
|
||||
|
||||
if msg:
|
||||
print msg
|
||||
|
||||
def _handle_con(args):
|
||||
if not args:
|
||||
return hklcalc.constraints.__str__()
|
||||
|
||||
if len(args) > 6:
|
||||
raise TypeError("Unexpected args: " + str(args))
|
||||
|
||||
cons_value_pairs = []
|
||||
while args:
|
||||
scn_or_str = args.pop(0)
|
||||
name = getNameFromScannableOrString(scn_or_str)
|
||||
if args and isinstance(args[0], (int, long, float)):
|
||||
value = args.pop(0)
|
||||
else:
|
||||
try:
|
||||
value = settings.hardware.get_position_by_name(name)
|
||||
except ValueError:
|
||||
value = None
|
||||
cons_value_pairs.append((name, value))
|
||||
|
||||
if len(cons_value_pairs) == 1:
|
||||
pass
|
||||
elif len(cons_value_pairs) == 3:
|
||||
hklcalc.constraints.clear_constraints()
|
||||
else:
|
||||
raise TypeError("Either one or three constraints must be specified")
|
||||
for name, value in cons_value_pairs:
|
||||
hklcalc.constraints.constrain(name)
|
||||
if value is not None:
|
||||
hklcalc.constraints.set_constraint(name, value)
|
||||
return '\n'.join(hklcalc.constraints.report_constraints_lines())
|
||||
|
||||
|
||||
@command
|
||||
def uncon(scn_or_string):
|
||||
"""uncon <name> -- remove constraint
|
||||
|
||||
See also 'con'
|
||||
"""
|
||||
name = getNameFromScannableOrString(scn_or_string)
|
||||
hklcalc.constraints.unconstrain(name)
|
||||
print '\n'.join(hklcalc.constraints.report_constraints_lines())
|
||||
|
||||
@command
|
||||
def allhkl(hkl, wavelength=None):
|
||||
"""allhkl [h k l] -- print all hkl solutions ignoring limits
|
||||
|
||||
"""
|
||||
hardware = hklcalc._hardware
|
||||
geometry = hklcalc._geometry
|
||||
if wavelength is None:
|
||||
wavelength = hardware.get_wavelength()
|
||||
h, k, l = hkl
|
||||
pos_virtual_angles_pairs = hklcalc.hkl_to_all_angles(
|
||||
h, k, l, wavelength)
|
||||
cells = []
|
||||
# virtual_angle_names = list(pos_virtual_angles_pairs[0][1].keys())
|
||||
# virtual_angle_names.sort()
|
||||
virtual_angle_names = ['qaz', 'psi', 'naz', 'tau', 'theta', 'alpha', 'beta']
|
||||
header_cells = list(hardware.get_axes_names()) + [' '] + virtual_angle_names
|
||||
cells.append(['%9s' % s for s in header_cells])
|
||||
cells.append([''] * len(header_cells))
|
||||
|
||||
|
||||
for pos, virtual_angles in pos_virtual_angles_pairs:
|
||||
row_cells = []
|
||||
|
||||
|
||||
angle_tuple = geometry.internal_position_to_physical_angles(pos)
|
||||
angle_tuple = hardware.cut_angles(angle_tuple)
|
||||
for val in angle_tuple:
|
||||
row_cells.append('%9.4f' % val)
|
||||
|
||||
row_cells.append('|')
|
||||
|
||||
for name in virtual_angle_names:
|
||||
row_cells.append('%9.4f' % virtual_angles[name])
|
||||
cells.append(row_cells)
|
||||
|
||||
|
||||
column_widths = []
|
||||
for col in range(len(cells[0])):
|
||||
widths = []
|
||||
for row in range(len(cells)):
|
||||
cell = cells[row][col]
|
||||
width = len(cell.strip())
|
||||
widths.append(width)
|
||||
column_widths.append(max(widths))
|
||||
|
||||
lines = []
|
||||
for row_cells in cells:
|
||||
trimmed_row_cells = []
|
||||
for cell, width in zip(row_cells, column_widths):
|
||||
trimmed_cell = cell.strip().rjust(width)
|
||||
trimmed_row_cells.append(trimmed_cell)
|
||||
lines.append(' '.join(trimmed_row_cells))
|
||||
print '\n'.join(lines)
|
||||
|
||||
|
||||
commands_for_help = ['Constraints',
|
||||
con,
|
||||
uncon,
|
||||
'Hkl',
|
||||
allhkl
|
||||
]
|
||||
27
script/__Lib/diffcalc-2.1/diffcalc/log.py
Executable file
27
script/__Lib/diffcalc-2.1/diffcalc/log.py
Executable file
@@ -0,0 +1,27 @@
|
||||
###
|
||||
# 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
|
||||
|
||||
import logging
|
||||
import getpass
|
||||
|
||||
logging.basicConfig(format="%(asctime)s %(levelname)s:%(name)s:%(message)s",
|
||||
datefmt='%m/%d/%Y %I:%M:%S',
|
||||
filename='/tmp/diffcalc_%s.log' % getpass.getuser(),
|
||||
level=logging.DEBUG)
|
||||
24
script/__Lib/diffcalc-2.1/diffcalc/settings.py
Executable file
24
script/__Lib/diffcalc-2.1/diffcalc/settings.py
Executable file
@@ -0,0 +1,24 @@
|
||||
'''
|
||||
Created on Aug 5, 2013
|
||||
|
||||
@author: walton
|
||||
'''
|
||||
import os
|
||||
|
||||
from diffcalc.ub.persistence import UbCalculationNonPersister
|
||||
|
||||
# These should be by the user *before* importing other modules
|
||||
geometry = None
|
||||
hardware = None
|
||||
ubcalc_persister = UbCalculationNonPersister()
|
||||
|
||||
axes_scannable_group = None
|
||||
energy_scannable = None
|
||||
energy_scannable_multiplier_to_get_KeV=1
|
||||
|
||||
|
||||
# These will be set by dcyou, dcvlieg or dcwillmot
|
||||
ubcalc_strategy = None
|
||||
angles_to_hkl_function = None # Used by checkub to avoid coupling it to an hkl module
|
||||
include_sigtau=False
|
||||
include_reference=False
|
||||
0
script/__Lib/diffcalc-2.1/diffcalc/ub/__init__.py
Executable file
0
script/__Lib/diffcalc-2.1/diffcalc/ub/__init__.py
Executable file
854
script/__Lib/diffcalc-2.1/diffcalc/ub/calc.py
Executable file
854
script/__Lib/diffcalc-2.1/diffcalc/ub/calc.py
Executable file
@@ -0,0 +1,854 @@
|
||||
###
|
||||
# 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.ub.calcstate import decode_ubcalcstate
|
||||
from diffcalc.ub.calcstate import UBCalcState
|
||||
from diffcalc.ub.crystal import CrystalUnderTest
|
||||
from diffcalc.ub.reflections import ReflectionList
|
||||
from diffcalc.ub.persistence import UBCalculationJSONPersister, UBCalculationPersister
|
||||
from diffcalc.util import DiffcalcException, cross3, dot3, bold, xyz_rotation,\
|
||||
bound
|
||||
from math import acos, cos, sin, pi
|
||||
from diffcalc.ub.reference import YouReference
|
||||
from diffcalc.ub.orientations import OrientationList
|
||||
|
||||
try:
|
||||
from numpy import matrix, hstack
|
||||
from numpy.linalg import norm
|
||||
except ImportError:
|
||||
from numjy import matrix, hstack
|
||||
from numjy.linalg import norm
|
||||
|
||||
SMALL = 1e-7
|
||||
TODEG = 180 / pi
|
||||
|
||||
WIDTH = 13
|
||||
|
||||
def z(num):
|
||||
"""Round to zero if small. This is useful to get rid of erroneous
|
||||
minus signs resulting from float representation close to zero.
|
||||
"""
|
||||
if abs(num) < SMALL:
|
||||
num = 0
|
||||
return num
|
||||
|
||||
#The UB matrix is used to find or set the orientation of a set of
|
||||
#planes described by an hkl vector. The U matrix can be used to find
|
||||
#or set the orientation of the crystal lattices' y axis. If there is
|
||||
#crystal miscut the crystal lattices y axis is not parallel to the
|
||||
#crystals optical surface normal. For surface diffraction experiments,
|
||||
#where not only the crystal lattice must be oriented appropriately but
|
||||
#so must the crystal's optical surface, two angles tau and sigma are
|
||||
#used to describe the difference between the two. Sigma is (minus) the
|
||||
#ammount of chi axis rotation and tau (minus) the ammount of phi axis
|
||||
#rotation needed to move the surface normal into the direction of the
|
||||
|
||||
|
||||
class PaperSpecificUbCalcStrategy(object):
|
||||
|
||||
def calculate_q_phi(self, pos):
|
||||
"""Calculate hkl in the phi frame in units of 2 * pi / lambda from
|
||||
pos object in radians"""
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class UBCalculation:
|
||||
"""A UB matrix calculation for an experiment.
|
||||
|
||||
Contains the parameters for the _crystal under test, a list of measured
|
||||
reflections and, if its been calculated, a UB matrix to be used by the rest
|
||||
of the code.
|
||||
"""
|
||||
|
||||
def __init__(self, hardware, diffractometerPluginObject,
|
||||
persister, strategy, include_sigtau=True, include_reference=True):
|
||||
|
||||
# The diffractometer geometry is required to map the internal angles
|
||||
# into those used by this diffractometer (for display only)
|
||||
|
||||
self._hardware = hardware
|
||||
self._geometry = diffractometerPluginObject
|
||||
self._persister = persister
|
||||
self._strategy = strategy
|
||||
self.include_sigtau = include_sigtau
|
||||
self.include_reference = include_reference
|
||||
try:
|
||||
self._ROT = diffractometerPluginObject.beamline_axes_transform
|
||||
except AttributeError:
|
||||
self._ROT = None
|
||||
self._clear()
|
||||
|
||||
def _get_diffractometer_axes_names(self):
|
||||
return self._hardware.get_axes_names()
|
||||
|
||||
def _clear(self, name=None):
|
||||
# NOTE the Diffraction calculator is expecting this object to exist in
|
||||
# the long run. We can't remove this entire object, and recreate it.
|
||||
# It also contains a required link to the angle calculator.
|
||||
reflist = ReflectionList(self._geometry, self._get_diffractometer_axes_names())
|
||||
orientlist = OrientationList()
|
||||
reference = YouReference(self._get_UB)
|
||||
self._state = UBCalcState(name=name, reflist=reflist, orientlist=orientlist, reference=reference)
|
||||
self._U = None
|
||||
self._UB = None
|
||||
self._state.configure_calc_type()
|
||||
|
||||
### State ###
|
||||
def start_new(self, name):
|
||||
"""start_new(name) --- creates a new blank ub calculation"""
|
||||
# Create storage object if name does not exist (TODO)
|
||||
if name in self._persister.list():
|
||||
print ("No UBCalculation started: There is already a calculation "
|
||||
"called: " + name)
|
||||
print "Saved calculations: " + repr(self._persister.list())
|
||||
return
|
||||
self._clear(name)
|
||||
self.save()
|
||||
|
||||
def load(self, name):
|
||||
state = self._persister.load(name)
|
||||
if isinstance(self._persister, UBCalculationJSONPersister):
|
||||
self._state = decode_ubcalcstate(state, self._geometry, self._get_diffractometer_axes_names())
|
||||
self._state.reference.get_UB = self._get_UB
|
||||
elif isinstance(self._persister, UBCalculationPersister):
|
||||
self._state = state
|
||||
else:
|
||||
raise Exception('Unexpected persister type: ' + str(self._persister))
|
||||
if self._state.manual_U is not None:
|
||||
self._U = self._state.manual_U
|
||||
self._UB = self._U * self._state.crystal.B
|
||||
self.save()
|
||||
elif self._state.manual_UB is not None:
|
||||
self._UB = self._state.manual_UB
|
||||
self.save()
|
||||
elif self._state.or0 is not None:
|
||||
if self._state.or1 is None:
|
||||
self.calculate_UB_from_primary_only()
|
||||
else:
|
||||
if self._state.reflist:
|
||||
self.calculate_UB()
|
||||
elif self._state.orientlist:
|
||||
self.calculate_UB_from_orientation()
|
||||
else:
|
||||
pass
|
||||
else:
|
||||
pass
|
||||
|
||||
def save(self):
|
||||
self.saveas(self._state.name)
|
||||
|
||||
def saveas(self, name):
|
||||
self._state.name = name
|
||||
self._persister.save(self._state, name)
|
||||
|
||||
def listub(self):
|
||||
return self._persister.list()
|
||||
|
||||
def listub_metadata(self):
|
||||
return self._persister.list_metadata()
|
||||
|
||||
def remove(self, name):
|
||||
self._persister.remove(name)
|
||||
if self._state == name:
|
||||
self._clear(name)
|
||||
|
||||
def getState(self):
|
||||
return self._state.getState()
|
||||
|
||||
def __str__(self):
|
||||
|
||||
if self._state.name is None:
|
||||
return "<<< No UB calculation started >>>"
|
||||
lines = []
|
||||
lines.append(bold("UBCALC"))
|
||||
lines.append("")
|
||||
lines.append(
|
||||
" name:".ljust(WIDTH) + self._state.name.rjust(9))
|
||||
|
||||
if self.include_sigtau:
|
||||
lines.append("")
|
||||
lines.append(
|
||||
" sigma:".ljust(WIDTH) + ("% 9.5f" % self._state.sigma).rjust(9))
|
||||
lines.append(
|
||||
" tau:".ljust(WIDTH) + ("% 9.5f" % self._state.tau).rjust(9))
|
||||
|
||||
if self.include_reference:
|
||||
lines.append("")
|
||||
ub_calculated = self._UB is not None
|
||||
lines.extend(self._state.reference.repr_lines(ub_calculated, WIDTH, self._ROT))
|
||||
|
||||
lines.append("")
|
||||
lines.append(bold("CRYSTAL"))
|
||||
lines.append("")
|
||||
|
||||
if self._state.crystal is None:
|
||||
lines.append(" <<< none specified >>>")
|
||||
else:
|
||||
lines.extend(self._state.crystal.str_lines())
|
||||
|
||||
lines.append("")
|
||||
lines.append(bold("UB MATRIX"))
|
||||
lines.append("")
|
||||
|
||||
if self._UB is None:
|
||||
lines.append(" <<< none calculated >>>")
|
||||
else:
|
||||
lines.extend(self.str_lines_u())
|
||||
lines.append("")
|
||||
lines.extend(self.str_lines_u_angle_and_axis())
|
||||
lines.append("")
|
||||
lines.extend(self.str_lines_ub())
|
||||
|
||||
lines.append("")
|
||||
lines.append(bold("REFLECTIONS"))
|
||||
lines.append("")
|
||||
|
||||
lines.extend(self._state.reflist.str_lines())
|
||||
|
||||
lines.append("")
|
||||
lines.append(bold("CRYSTAL ORIENTATIONS"))
|
||||
lines.append("")
|
||||
|
||||
lines.extend(self._state.orientlist.str_lines(R=self._ROT))
|
||||
|
||||
return '\n'.join(lines)
|
||||
|
||||
def str_lines_u(self):
|
||||
lines = []
|
||||
fmt = "% 9.5f % 9.5f % 9.5f"
|
||||
try:
|
||||
U = self._ROT.I * self.U * self._ROT
|
||||
except AttributeError:
|
||||
U = self.U
|
||||
lines.append(" U matrix:".ljust(WIDTH) +
|
||||
fmt % (z(U[0, 0]), z(U[0, 1]), z(U[0, 2])))
|
||||
lines.append(' ' * WIDTH + fmt % (z(U[1, 0]), z(U[1, 1]), z(U[1, 2])))
|
||||
lines.append(' ' * WIDTH + fmt % (z(U[2, 0]), z(U[2, 1]), z(U[2, 2])))
|
||||
return lines
|
||||
|
||||
def str_lines_u_angle_and_axis(self):
|
||||
lines = []
|
||||
fmt = "% 9.5f % 9.5f % 9.5f"
|
||||
y = matrix('0; 0; 1')
|
||||
try:
|
||||
rotation_axis = cross3(self._ROT * y, self._ROT * self.U * y)
|
||||
except TypeError:
|
||||
rotation_axis = cross3(y, self.U * y)
|
||||
if abs(norm(rotation_axis)) < SMALL:
|
||||
lines.append(" miscut angle:".ljust(WIDTH) + " 0")
|
||||
else:
|
||||
rotation_axis = rotation_axis * (1 / norm(rotation_axis))
|
||||
cos_rotation_angle = dot3(y, self.U * y)
|
||||
rotation_angle = acos(cos_rotation_angle)
|
||||
|
||||
lines.append(" miscut:")
|
||||
lines.append(" angle:".ljust(WIDTH) + "% 9.5f" % (rotation_angle * TODEG))
|
||||
lines.append(" axis:".ljust(WIDTH) + fmt % tuple((rotation_axis.T).tolist()[0]))
|
||||
|
||||
return lines
|
||||
|
||||
def str_lines_ub(self):
|
||||
lines = []
|
||||
fmt = "% 9.5f % 9.5f % 9.5f"
|
||||
try:
|
||||
RI = self._ROT.I
|
||||
B = self._state.crystal.B
|
||||
UB = RI * self.UB * B.I * self._ROT * B
|
||||
except AttributeError:
|
||||
UB = self.UB
|
||||
lines.append(" UB matrix:".ljust(WIDTH) +
|
||||
fmt % (z(UB[0, 0]), z(UB[0, 1]), z(UB[0, 2])))
|
||||
lines.append(' ' * WIDTH + fmt % (z(UB[1, 0]), z(UB[1, 1]), z(UB[1, 2])))
|
||||
lines.append(' ' * WIDTH + fmt % (z(UB[2, 0]), z(UB[2, 1]), z(UB[2, 2])))
|
||||
return lines
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self._state.name
|
||||
### Lattice ###
|
||||
|
||||
def set_lattice(self, name, *shortform):
|
||||
"""
|
||||
Converts a list shortform crystal parameter specification to a six-long
|
||||
tuple returned as . Returns None if wrong number of input args. See
|
||||
set_lattice() for a description of the shortforms supported.
|
||||
|
||||
shortformLattice -- a tuple as follows:
|
||||
[a] - assumes cubic
|
||||
[a,b]) - assumes tetragonal
|
||||
[a,b,c]) - assumes ortho
|
||||
[a,b,c,gam]) - assumes mon/hex gam different from 90.
|
||||
[a,b,c,alp,bet,gam]) - for arbitrary
|
||||
where all measurements in angstroms and angles in degrees
|
||||
"""
|
||||
self._set_lattice_without_saving(name, *shortform)
|
||||
self.save()
|
||||
|
||||
def _set_lattice_without_saving(self, name, *shortform):
|
||||
sf = shortform
|
||||
if len(sf) == 1:
|
||||
fullform = (sf[0], sf[0], sf[0], 90., 90., 90.) # cubic
|
||||
elif len(sf) == 2:
|
||||
fullform = (sf[0], sf[0], sf[1], 90., 90., 90.) # tetragonal
|
||||
elif len(sf) == 3:
|
||||
fullform = (sf[0], sf[1], sf[2], 90., 90., 90.) # ortho
|
||||
elif len(sf) == 4:
|
||||
fullform = (sf[0], sf[1], sf[2], 90., 90., sf[3]) # mon/hex gam
|
||||
# not 90
|
||||
elif len(sf) == 5:
|
||||
raise ValueError("wrong length input to set_lattice")
|
||||
elif len(sf) == 6:
|
||||
fullform = sf # triclinic/arbitrary
|
||||
else:
|
||||
raise ValueError("wrong length input to set_lattice")
|
||||
self._set_lattice(name, *fullform)
|
||||
|
||||
def _set_lattice(self, name, a, b, c, alpha, beta, gamma):
|
||||
"""set lattice parameters in degrees"""
|
||||
if self._state.name is None:
|
||||
raise DiffcalcException(
|
||||
"Cannot set lattice until a UBCalcaluation has been started "
|
||||
"with newubcalc")
|
||||
self._state.crystal = CrystalUnderTest(name, a, b, c, alpha, beta, gamma)
|
||||
# Clear U and UB if these exist
|
||||
if self._U is not None: # (UB will also exist)
|
||||
print "Warning: the old UB calculation has been cleared."
|
||||
print " Use 'calcub' to recalculate with old reflections or"
|
||||
print " 'orientub' to recalculate with old orientations."
|
||||
|
||||
### Surface normal stuff ###
|
||||
|
||||
def _gettau(self):
|
||||
"""
|
||||
Returns tau (in degrees): the (minus) ammount of phi axis rotation ,
|
||||
that together with some chi axis rotation (minus sigma) brings the
|
||||
optical surface normal parallel to the omega axis.
|
||||
"""
|
||||
return self._state.tau
|
||||
|
||||
def _settau(self, tau):
|
||||
self._state.tau = tau
|
||||
self.save()
|
||||
|
||||
tau = property(_gettau, _settau)
|
||||
|
||||
def _getsigma(self):
|
||||
"""
|
||||
Returns sigma (in degrees): the (minus) ammount of phi axis rotation ,
|
||||
that together with some phi axis rotation (minus tau) brings the
|
||||
optical surface normal parallel to the omega axis.
|
||||
"""
|
||||
return self._state.sigma
|
||||
|
||||
def _setsigma(self, sigma):
|
||||
self.state._sigma = sigma
|
||||
self.save()
|
||||
|
||||
sigma = property(_getsigma, _setsigma)
|
||||
|
||||
|
||||
### Reference vector ###
|
||||
|
||||
def _get_n_phi(self):
|
||||
return self._state.reference.n_phi
|
||||
|
||||
n_phi = property(_get_n_phi)
|
||||
|
||||
def set_n_phi_configured(self, n_phi):
|
||||
try:
|
||||
self._state.reference.n_phi_configured = self._ROT.I * n_phi
|
||||
except AttributeError:
|
||||
self._state.reference.n_phi_configured = n_phi
|
||||
self.save()
|
||||
|
||||
def set_n_hkl_configured(self, n_hkl):
|
||||
self._state.reference.n_hkl_configured = n_hkl
|
||||
self.save()
|
||||
|
||||
def print_reference(self):
|
||||
print '\n'.join(self._state.reference.repr_lines(self.is_ub_calculated(), R=self._ROT))
|
||||
|
||||
### Reflections ###
|
||||
|
||||
def add_reflection(self, h, k, l, position, energy, tag, time):
|
||||
"""add_reflection(h, k, l, position, tag=None) -- adds a reflection
|
||||
|
||||
position is in degrees and in the systems internal representation.
|
||||
"""
|
||||
if self._state.reflist is None:
|
||||
raise DiffcalcException("No UBCalculation loaded")
|
||||
self._state.reflist.add_reflection(h, k, l, position, energy, tag, time)
|
||||
self.save() # incase autocalculateUbAndReport fails
|
||||
|
||||
# If second reflection has just been added then calculateUB
|
||||
if len(self._state.reflist) == 2:
|
||||
self._autocalculateUbAndReport()
|
||||
self.save()
|
||||
|
||||
def edit_reflection(self, num, h, k, l, position, energy, tag, time):
|
||||
"""
|
||||
edit_reflection(num, h, k, l, position, tag=None) -- adds a reflection
|
||||
|
||||
position is in degrees and in the systems internal representation.
|
||||
"""
|
||||
if self._state.reflist is None:
|
||||
raise DiffcalcException("No UBCalculation loaded")
|
||||
self._state.reflist.edit_reflection(num, h, k, l, position, energy, tag, time)
|
||||
|
||||
# If first or second reflection has been changed and there are at least
|
||||
# two reflections then recalculate UB
|
||||
if (num == 1 or num == 2) and len(self._state.reflist) >= 2:
|
||||
self._autocalculateUbAndReport()
|
||||
self.save()
|
||||
|
||||
def get_reflection(self, num):
|
||||
"""--> ( [h, k, l], position, energy, tag, time
|
||||
num starts at 1, position in degrees"""
|
||||
return self._state.reflist.getReflection(num)
|
||||
|
||||
def get_reflection_in_external_angles(self, num):
|
||||
"""--> ( [h, k, l], position, energy, tag, time
|
||||
num starts at 1, position in degrees"""
|
||||
return self._state.reflist.get_reflection_in_external_angles(num)
|
||||
|
||||
def get_number_reflections(self):
|
||||
return 0 if self._state.reflist is None else len(self._state.reflist)
|
||||
|
||||
def del_reflection(self, reflectionNumber):
|
||||
self._state.reflist.removeReflection(reflectionNumber)
|
||||
if ((reflectionNumber == 1 or reflectionNumber == 2) and
|
||||
(self._U is not None)):
|
||||
self._autocalculateUbAndReport()
|
||||
self.save()
|
||||
|
||||
def swap_reflections(self, num1, num2):
|
||||
self._state.reflist.swap_reflections(num1, num2)
|
||||
if ((num1 == 1 or num1 == 2 or num2 == 1 or num2 == 2) and
|
||||
(self._U is not None)):
|
||||
self._autocalculateUbAndReport()
|
||||
self.save()
|
||||
|
||||
def _autocalculateUbAndReport(self):
|
||||
if len(self._state.reflist) < 2:
|
||||
pass
|
||||
elif self._state.crystal is None:
|
||||
print ("Not calculating UB matrix as no lattice parameters have "
|
||||
"been specified.")
|
||||
elif not self._state.is_okay_to_autocalculate_ub:
|
||||
print ("Not calculating UB matrix as it has been manually set. "
|
||||
"Use 'calcub' to explicitly recalculate it.")
|
||||
else: # okay to autocalculate
|
||||
if self._UB is None:
|
||||
print "Calculating UB matrix."
|
||||
else:
|
||||
print "Recalculating UB matrix."
|
||||
self.calculate_UB()
|
||||
|
||||
### Orientations ###
|
||||
|
||||
def add_orientation(self, h, k, l, x, y, z, tag, time):
|
||||
"""add_reflection(h, k, l, x, y, z, tag=None) -- adds a crystal orientation
|
||||
"""
|
||||
if self._state.orientlist is None:
|
||||
raise DiffcalcException("No UBCalculation loaded")
|
||||
try:
|
||||
xyz_rot = self._ROT * matrix([[x],[y],[z]])
|
||||
xr, yr, zr = xyz_rot.T.tolist()[0]
|
||||
self._state.orientlist.add_orientation(h, k, l, xr, yr, zr, tag, time)
|
||||
except TypeError:
|
||||
self._state.orientlist.add_orientation(h, k, l, x, y, z, tag, time)
|
||||
self.save() # incase autocalculateUbAndReport fails
|
||||
|
||||
# If second reflection has just been added then calculateUB
|
||||
if len(self._state.orientlist) == 2:
|
||||
self._autocalculateOrientationUbAndReport()
|
||||
self.save()
|
||||
|
||||
def edit_orientation(self, num, h, k, l, x, y, z, tag, time):
|
||||
"""
|
||||
edit_orientation(num, h, k, l, x, y, z, tag=None) -- edit a crystal reflection """
|
||||
if self._state.orientlist is None:
|
||||
raise DiffcalcException("No UBCalculation loaded")
|
||||
try:
|
||||
xyz_rot = self._ROT * matrix([[x],[y],[z]])
|
||||
xr, yr, zr = xyz_rot.T.tolist()[0]
|
||||
self._state.orientlist.edit_orientation(num, h, k, l, xr, yr, zr, tag, time)
|
||||
except TypeError:
|
||||
self._state.orientlist.edit_orientation(num, h, k, l, x, y, z, tag, time)
|
||||
|
||||
# If first or second orientation has been changed and there are
|
||||
# two orientations then recalculate UB
|
||||
if (num == 1 or num == 2) and len(self._state.orientlist) == 2:
|
||||
self._autocalculateOrientationUbAndReport()
|
||||
self.save()
|
||||
|
||||
def get_orientation(self, num):
|
||||
"""--> ( [h, k, l], [x, y, z], tag, time )
|
||||
num starts at 1"""
|
||||
try:
|
||||
hkl, xyz, tg, tm = self._state.orientlist.getOrientation(num)
|
||||
xyz_rot = self._ROT.I * matrix([[xyz[0]],[xyz[1]],[xyz[2]]])
|
||||
xyz_lst = xyz_rot.T.tolist()[0]
|
||||
return hkl, xyz_lst, tg, tm
|
||||
except AttributeError:
|
||||
return self._state.orientlist.getOrientation(num)
|
||||
|
||||
|
||||
def get_number_orientations(self):
|
||||
return 0 if self._state.orientlist is None else len(self._state.reflist)
|
||||
|
||||
def del_orientation(self, orientationNumber):
|
||||
self._state.orientlist.removeOrientation(orientationNumber)
|
||||
if ((orientationNumber == 2) and (self._U is not None)):
|
||||
self._autocalculateOrientationUbAndReport()
|
||||
self.save()
|
||||
|
||||
def swap_orientations(self, num1, num2):
|
||||
self._state.orientlist.swap_orientations(num1, num2)
|
||||
if ((num1 == 2 or num2 == 2) and
|
||||
(self._U is not None)):
|
||||
self._autocalculateOrientationUbAndReport()
|
||||
self.save()
|
||||
|
||||
def _autocalculateOrientationUbAndReport(self):
|
||||
if len(self._state.orientlist) < 2:
|
||||
pass
|
||||
elif self._state.crystal is None:
|
||||
print ("Not calculating UB matrix as no lattice parameters have "
|
||||
"been specified.")
|
||||
elif not self._state.is_okay_to_autocalculate_ub:
|
||||
print ("Not calculating UB matrix as it has been manually set. "
|
||||
"Use 'orientub' to explicitly recalculate it.")
|
||||
else: # okay to autocalculate
|
||||
if self._UB is None:
|
||||
print "Calculating UB matrix."
|
||||
else:
|
||||
print "Recalculating UB matrix."
|
||||
self.calculate_UB_from_orientation()
|
||||
|
||||
# @property
|
||||
# def reflist(self):
|
||||
# return self._state.reflist
|
||||
### Calculations ###
|
||||
|
||||
def set_U_manually(self, m):
|
||||
"""Manually sets U. matrix must be 3*3 Jama or python matrix.
|
||||
Turns off aution UB calcualtion."""
|
||||
|
||||
# Check matrix is a 3*3 Jama matrix
|
||||
if m.__class__ != matrix:
|
||||
m = matrix(m) # assume its a python matrix
|
||||
if m.shape[0] != 3 or m.shape[1] != 3:
|
||||
raise ValueError("Expects 3*3 matrix")
|
||||
|
||||
if self._UB is None:
|
||||
print "Calculating UB matrix."
|
||||
else:
|
||||
print "Recalculating UB matrix."
|
||||
|
||||
if self._ROT is not None:
|
||||
self._U = self._ROT * m * self._ROT.I
|
||||
else:
|
||||
self._U = m
|
||||
self._state.configure_calc_type(manual_U=self._U)
|
||||
if self._state.crystal is None:
|
||||
raise DiffcalcException(
|
||||
"A crystal must be specified before manually setting U")
|
||||
self._UB = self._U * self._state.crystal.B
|
||||
print ("NOTE: A new UB matrix will not be automatically calculated "
|
||||
"when the orientation reflections are modified.")
|
||||
self.save()
|
||||
|
||||
def set_UB_manually(self, m):
|
||||
"""Manually sets UB. matrix must be 3*3 Jama or python matrix.
|
||||
Turns off aution UB calcualtion."""
|
||||
|
||||
# Check matrix is a 3*3 Jama matrix
|
||||
if m.__class__ != matrix:
|
||||
m = matrix(m) # assume its a python matrix
|
||||
if m.shape[0] != 3 or m.shape[1] != 3:
|
||||
raise ValueError("Expects 3*3 matrix")
|
||||
|
||||
if self._ROT is not None:
|
||||
self._UB = self._ROT * m
|
||||
else:
|
||||
self._UB = m
|
||||
self._state.configure_calc_type(manual_UB=self._UB)
|
||||
self.save()
|
||||
|
||||
@property
|
||||
def U(self):
|
||||
if self._U is None:
|
||||
raise DiffcalcException(
|
||||
"No U matrix has been calculated during this ub calculation")
|
||||
return self._U
|
||||
|
||||
@property
|
||||
def UB(self):
|
||||
return self._get_UB()
|
||||
|
||||
def is_ub_calculated(self):
|
||||
return self._UB is not None
|
||||
|
||||
def _get_UB(self):
|
||||
if not self.is_ub_calculated():
|
||||
raise DiffcalcException(
|
||||
"No UB matrix has been calculated during this ub calculation")
|
||||
else:
|
||||
return self._UB
|
||||
|
||||
def _calc_UB(self, h1, h2, u1p, u2p):
|
||||
B = self._state.crystal.B
|
||||
h1c = B * h1
|
||||
h2c = B * h2
|
||||
|
||||
# Create modified unit vectors t1, t2 and t3 in crystal and phi systems
|
||||
t1c = h1c
|
||||
t3c = cross3(h1c, h2c)
|
||||
t2c = cross3(t3c, t1c)
|
||||
|
||||
t1p = u1p # FIXED from h1c 9July08
|
||||
t3p = cross3(u1p, u2p)
|
||||
t2p = cross3(t3p, t1p)
|
||||
|
||||
# ...and nornmalise and check that the reflections used are appropriate
|
||||
SMALL = 1e-4 # Taken from Vlieg's code
|
||||
e = DiffcalcException("Invalid orientation reflection(s)")
|
||||
|
||||
def normalise(m):
|
||||
d = norm(m)
|
||||
if d < SMALL:
|
||||
raise e
|
||||
return m / d
|
||||
|
||||
t1c = normalise(t1c)
|
||||
t2c = normalise(t2c)
|
||||
t3c = normalise(t3c)
|
||||
|
||||
t1p = normalise(t1p)
|
||||
t2p = normalise(t2p)
|
||||
t3p = normalise(t3p)
|
||||
|
||||
Tc = hstack([t1c, t2c, t3c])
|
||||
Tp = hstack([t1p, t2p, t3p])
|
||||
self._state.configure_calc_type(or0=1, or1=2)
|
||||
self._U = Tp * Tc.I
|
||||
self._UB = self._U * B
|
||||
self.save()
|
||||
|
||||
def calculate_UB(self):
|
||||
"""
|
||||
Calculate orientation matrix. Uses first two orientation reflections
|
||||
as in Busang and Levy, but for the diffractometer in Lohmeier and
|
||||
Vlieg.
|
||||
"""
|
||||
|
||||
# Major variables:
|
||||
# h1, h2: user input reciprical lattice vectors of the two reflections
|
||||
# h1c, h2c: user input vectors in cartesian crystal plane
|
||||
# pos1, pos2: measured diffractometer positions of the two reflections
|
||||
# u1a, u2a: measured reflection vectors in alpha frame
|
||||
# u1p, u2p: measured reflection vectors in phi frame
|
||||
|
||||
|
||||
# Get hkl and angle values for the first two refelctions
|
||||
if self._state.reflist is None:
|
||||
raise DiffcalcException("Cannot calculate a U matrix until a "
|
||||
"UBCalculation has been started with "
|
||||
"'newub'")
|
||||
try:
|
||||
(h1, pos1, _, _, _) = self._state.reflist.getReflection(1)
|
||||
(h2, pos2, _, _, _) = self._state.reflist.getReflection(2)
|
||||
except IndexError:
|
||||
raise DiffcalcException(
|
||||
"Two reflections are required to calculate a U matrix")
|
||||
h1 = matrix([h1]).T # row->column
|
||||
h2 = matrix([h2]).T
|
||||
pos1.changeToRadians()
|
||||
pos2.changeToRadians()
|
||||
|
||||
# Compute the two reflections' reciprical lattice vectors in the
|
||||
# cartesian crystal frame
|
||||
u1p = self._strategy.calculate_q_phi(pos1)
|
||||
u2p = self._strategy.calculate_q_phi(pos2)
|
||||
|
||||
self._calc_UB(h1, h2, u1p, u2p)
|
||||
|
||||
def calculate_UB_from_orientation(self):
|
||||
"""
|
||||
Calculate orientation matrix. Uses first two crystal orientations.
|
||||
"""
|
||||
|
||||
# Major variables:
|
||||
# h1, h2: user input reciprical lattice vectors of the two reflections
|
||||
# h1c, h2c: user input vectors in cartesian crystal plane
|
||||
# pos1, pos2: measured diffractometer positions of the two reflections
|
||||
# u1a, u2a: measured reflection vectors in alpha frame
|
||||
# u1p, u2p: measured reflection vectors in phi frame
|
||||
|
||||
|
||||
# Get hkl and angle values for the first two crystal orientations
|
||||
if self._state.orientlist is None:
|
||||
raise DiffcalcException("Cannot calculate a U matrix until a "
|
||||
"UBCalculation has been started with "
|
||||
"'newub'")
|
||||
try:
|
||||
(h1, x1, _, _) = self._state.orientlist.getOrientation(1)
|
||||
(h2, x2, _, _) = self._state.orientlist.getOrientation(2)
|
||||
except IndexError:
|
||||
raise DiffcalcException(
|
||||
"Two crystal orientations are required to calculate a U matrix")
|
||||
h1 = matrix([h1]).T # row->column
|
||||
h2 = matrix([h2]).T
|
||||
u1p = matrix([x1]).T
|
||||
u2p = matrix([x2]).T
|
||||
|
||||
self._calc_UB(h1, h2, u1p, u2p)
|
||||
|
||||
def calculate_UB_from_primary_only(self):
|
||||
"""
|
||||
Calculate orientation matrix with the shortest absolute angle change.
|
||||
Uses first orientation reflection
|
||||
"""
|
||||
|
||||
# Algorithm from http://www.j3d.org/matrix_faq/matrfaq_latest.html
|
||||
|
||||
# Get hkl and angle values for the first two refelctions
|
||||
if self._state.reflist is None:
|
||||
raise DiffcalcException(
|
||||
"Cannot calculate a u matrix until a UBCalcaluation has been "
|
||||
"started with newub")
|
||||
try:
|
||||
(h, pos, _, _, _) = self._state.reflist.getReflection(1)
|
||||
except IndexError:
|
||||
raise DiffcalcException(
|
||||
"One reflection is required to calculate a u matrix")
|
||||
|
||||
h = matrix([h]).T # row->column
|
||||
pos.changeToRadians()
|
||||
B = self._state.crystal.B
|
||||
h_crystal = B * h
|
||||
h_crystal = h_crystal * (1 / norm(h_crystal))
|
||||
|
||||
q_measured_phi = self._strategy.calculate_q_phi(pos)
|
||||
q_measured_phi = q_measured_phi * (1 / norm(q_measured_phi))
|
||||
|
||||
rotation_axis = cross3(h_crystal, q_measured_phi)
|
||||
rotation_axis = rotation_axis * (1 / norm(rotation_axis))
|
||||
|
||||
cos_rotation_angle = dot3(h_crystal, q_measured_phi)
|
||||
rotation_angle = acos(cos_rotation_angle)
|
||||
|
||||
uvw = rotation_axis.T.tolist()[0] # TODO: cleanup
|
||||
print "resulting U angle: %.5f deg" % (rotation_angle * TODEG)
|
||||
u_repr = (', '.join(['% .5f' % el for el in uvw]))
|
||||
print "resulting U axis direction: [%s]" % u_repr
|
||||
|
||||
u, v, w = uvw
|
||||
rcos = cos(rotation_angle)
|
||||
rsin = sin(rotation_angle)
|
||||
m = [[0, 0, 0], [0, 0, 0], [0, 0, 0]] # TODO: tidy
|
||||
m[0][0] = rcos + u * u * (1 - rcos)
|
||||
m[1][0] = w * rsin + v * u * (1 - rcos)
|
||||
m[2][0] = -v * rsin + w * u * (1 - rcos)
|
||||
m[0][1] = -w * rsin + u * v * (1 - rcos)
|
||||
m[1][1] = rcos + v * v * (1 - rcos)
|
||||
m[2][1] = u * rsin + w * v * (1 - rcos)
|
||||
m[0][2] = v * rsin + u * w * (1 - rcos)
|
||||
m[1][2] = -u * rsin + v * w * (1 - rcos)
|
||||
m[2][2] = rcos + w * w * (1 - rcos)
|
||||
|
||||
if self._UB is None:
|
||||
print "Calculating UB matrix from the first reflection only."
|
||||
else:
|
||||
print "Recalculating UB matrix from the first reflection only."
|
||||
print ("NOTE: A new UB matrix will not be automatically calculated "
|
||||
"when the orientation reflections are modified.")
|
||||
|
||||
self._state.configure_calc_type(or0=1)
|
||||
|
||||
self._U = matrix(m)
|
||||
self._UB = self._U * B
|
||||
|
||||
self.save()
|
||||
|
||||
def set_miscut(self, xyz, angle, add_miscut=False):
|
||||
"""Calculate U matrix using a miscut axis and an angle"""
|
||||
if xyz is None:
|
||||
rot_matrix = xyz_rotation([0, 1, 0], angle)
|
||||
if self.is_ub_calculated() and add_miscut:
|
||||
self._U = rot_matrix * self._U
|
||||
else:
|
||||
self._U = rot_matrix
|
||||
else:
|
||||
rot_matrix = xyz_rotation(xyz, angle)
|
||||
try:
|
||||
rot_matrix = self._ROT * rot_matrix * self._ROT.I
|
||||
except TypeError:
|
||||
pass
|
||||
if self.is_ub_calculated() and add_miscut:
|
||||
self._U = rot_matrix * self._U
|
||||
else:
|
||||
self._U = rot_matrix
|
||||
self._state.configure_calc_type(manual_U=self._U)
|
||||
self._UB = self._U * self._state.crystal.B
|
||||
self.print_reference()
|
||||
self.save()
|
||||
|
||||
def get_hkl_plane_distance(self, hkl):
|
||||
"""Calculates and returns the distance between planes"""
|
||||
return self._state.crystal.get_hkl_plane_distance(hkl)
|
||||
|
||||
def get_hkl_plane_angle(self, hkl1, hkl2):
|
||||
"""Calculates and returns the angle between planes"""
|
||||
return self._state.crystal.get_hkl_plane_angle(hkl1, hkl2)
|
||||
|
||||
def rescale_unit_cell(self, h, k, l, pos):
|
||||
"""
|
||||
Calculate unit cell scaling parameter that matches
|
||||
given hkl position and diffractometer angles
|
||||
"""
|
||||
q_vec = self._strategy.calculate_q_phi(pos)
|
||||
q_hkl = norm(q_vec) / self._hardware.get_wavelength()
|
||||
d_hkl = self._state.crystal.get_hkl_plane_distance([h, k, l])
|
||||
sc = 1/ (q_hkl * d_hkl)
|
||||
name, a1, a2, a3, alpha1, alpha2, alpha3 = self._state.crystal.getLattice()
|
||||
if abs(sc - 1.) < SMALL:
|
||||
return None, None
|
||||
return sc, (name, sc * a1, sc* a2, sc * a3, alpha1, alpha2, alpha3)
|
||||
|
||||
def calc_miscut(self, h, k, l, pos):
|
||||
"""
|
||||
Calculate miscut angle and axis that matches
|
||||
given hkl position and diffractometer angles
|
||||
"""
|
||||
q_vec = self._strategy.calculate_q_phi(pos)
|
||||
hkl_nphi = self._UB * matrix([[h], [k], [l]])
|
||||
try:
|
||||
axis = cross3(self._ROT.I * q_vec, self._ROT.I * hkl_nphi)
|
||||
except AttributeError:
|
||||
axis = cross3(q_vec, hkl_nphi)
|
||||
norm_axis = norm(axis)
|
||||
if norm_axis < SMALL:
|
||||
return None, None
|
||||
axis = axis / norm(axis)
|
||||
try:
|
||||
miscut = acos(bound(dot3(q_vec, hkl_nphi) / (norm(q_vec) * norm(hkl_nphi)))) * TODEG
|
||||
except AssertionError:
|
||||
return None, None
|
||||
return miscut, axis.T.tolist()[0]
|
||||
220
script/__Lib/diffcalc-2.1/diffcalc/ub/calcstate.py
Executable file
220
script/__Lib/diffcalc-2.1/diffcalc/ub/calcstate.py
Executable file
@@ -0,0 +1,220 @@
|
||||
from diffcalc.hkl.vlieg.geometry import VliegPosition
|
||||
from diffcalc.ub.crystal import CrystalUnderTest
|
||||
from diffcalc.ub.reflections import ReflectionList, _Reflection
|
||||
from math import pi
|
||||
import datetime # @UnusedImport For crazy time eval code!
|
||||
from diffcalc.ub.reference import YouReference
|
||||
from diffcalc.ub.orientations import _Orientation, OrientationList
|
||||
|
||||
try:
|
||||
from collection import OrderedDict
|
||||
except ImportError:
|
||||
from simplejson import OrderedDict
|
||||
|
||||
try:
|
||||
import json
|
||||
except ImportError:
|
||||
import simplejson as json
|
||||
|
||||
try:
|
||||
from numpy import matrix
|
||||
except ImportError:
|
||||
from numjy import matrix
|
||||
|
||||
|
||||
TODEG = 180 / pi
|
||||
|
||||
|
||||
class UBCalcState():
|
||||
|
||||
def __init__(self, name=None, crystal=None, reflist=None, orientlist=None, tau=0, sigma=0,
|
||||
manual_U=None, manual_UB=None, or0=None, or1=None, reference=None):
|
||||
|
||||
assert reflist is not None
|
||||
self.name = name
|
||||
self.crystal = crystal
|
||||
self.reflist = reflist
|
||||
self.orientlist = orientlist
|
||||
self.tau = tau # degrees
|
||||
self.sigma = sigma # degrees
|
||||
self.manual_U = manual_U
|
||||
self.manual_UB = manual_UB
|
||||
self.or0 = or0
|
||||
self.or1 = or1
|
||||
self.reference = reference
|
||||
|
||||
@property
|
||||
def is_okay_to_autocalculate_ub(self):
|
||||
nothing_set = ((self.manual_U is None) and
|
||||
(self.manual_UB is None) and
|
||||
(self.or0 is None) and
|
||||
(self.or1 is None))
|
||||
or0_and_or1_used = (self.or0 is not None) and (self.or1 is not None)
|
||||
return nothing_set or or0_and_or1_used
|
||||
|
||||
|
||||
def configure_calc_type(self,
|
||||
manual_U=None,
|
||||
manual_UB=None,
|
||||
or0=None,
|
||||
or1=None):
|
||||
self.manual_U = manual_U
|
||||
self.manual_UB = manual_UB
|
||||
self.or0 = or0
|
||||
self.or1 = or1
|
||||
|
||||
|
||||
class UBCalcStateEncoder(json.JSONEncoder):
|
||||
|
||||
def default(self, obj):
|
||||
|
||||
if isinstance(obj, UBCalcState):
|
||||
d = OrderedDict()
|
||||
d['name'] = obj.name
|
||||
d['crystal'] = obj.crystal
|
||||
d['reflist'] = obj.reflist
|
||||
d['orientlist'] = obj.orientlist
|
||||
d['tau'] = obj.tau
|
||||
d['sigma'] = obj.sigma
|
||||
d['reference'] = obj.reference
|
||||
d['u'] = obj.manual_U
|
||||
d['ub'] = obj.manual_UB
|
||||
d['or0'] = obj.or0
|
||||
d['or1'] = obj.or1
|
||||
|
||||
return d
|
||||
|
||||
if isinstance(obj, CrystalUnderTest):
|
||||
return repr([obj._name, obj._a1, obj._a2, obj._a3, obj._alpha1 * TODEG,
|
||||
obj._alpha2 * TODEG, obj._alpha3 * TODEG])
|
||||
|
||||
if isinstance(obj, matrix):
|
||||
l = [', '.join((repr(e) for e in row)) for row in obj.tolist()]
|
||||
return l
|
||||
|
||||
if isinstance(obj, ReflectionList):
|
||||
d = OrderedDict()
|
||||
for n, ref in enumerate(obj._reflist):
|
||||
d[str(n+1)] = ref
|
||||
return d
|
||||
|
||||
if isinstance(obj, _Reflection):
|
||||
d = OrderedDict()
|
||||
d['tag'] = obj.tag
|
||||
d['hkl'] = repr([obj.h, obj.k, obj.l])
|
||||
d['pos'] = repr(list(obj.pos.totuple()))
|
||||
d['energy'] = obj.energy
|
||||
dt = eval(obj.time) # e.g. --> datetime.datetime(2013, 8, 5, 15, 47, 7, 962432)
|
||||
d['time'] = None if dt is None else dt.isoformat()
|
||||
return d
|
||||
|
||||
if isinstance(obj, OrientationList):
|
||||
d = OrderedDict()
|
||||
for n, orient in enumerate(obj._orientlist):
|
||||
d[str(n+1)] = orient
|
||||
return d
|
||||
|
||||
if isinstance(obj, _Orientation):
|
||||
d = OrderedDict()
|
||||
d['tag'] = obj.tag
|
||||
d['hkl'] = repr([obj.h, obj.k, obj.l])
|
||||
d['xyz'] = repr([obj.x, obj.y, obj.z])
|
||||
dt = eval(obj.time) # e.g. --> datetime.datetime(2013, 8, 5, 15, 47, 7, 962432)
|
||||
d['time'] = None if dt is None else dt.isoformat()
|
||||
return d
|
||||
|
||||
if isinstance(obj, YouReference):
|
||||
d = OrderedDict()
|
||||
if obj.n_hkl_configured is not None:
|
||||
d['n_hkl_configured'] = repr(obj.n_hkl_configured.T.tolist()[0])
|
||||
else:
|
||||
d['n_hkl_configured'] = None
|
||||
if obj.n_phi_configured is not None:
|
||||
d['n_phi_configured'] = repr(obj.n_phi_configured.T.tolist()[0])
|
||||
else:
|
||||
d['n_phi_configured'] = None
|
||||
return d
|
||||
|
||||
|
||||
return json.JSONEncoder.default(self, obj)
|
||||
|
||||
|
||||
def decode_ubcalcstate(state, geometry, diffractometer_axes_names):
|
||||
|
||||
# Backwards compatibility code
|
||||
orientlist_=OrientationList([])
|
||||
try:
|
||||
orientlist_=decode_orientlist(state['orientlist'])
|
||||
except KeyError:
|
||||
pass
|
||||
return UBCalcState(
|
||||
name=state['name'],
|
||||
crystal=state['crystal'] and CrystalUnderTest(*eval(state['crystal'])),
|
||||
reflist=decode_reflist(state['reflist'], geometry, diffractometer_axes_names),
|
||||
orientlist=orientlist_,
|
||||
tau=state['tau'],
|
||||
sigma=state['sigma'],
|
||||
manual_U=state['u'] and decode_matrix(state['u']),
|
||||
manual_UB=state['ub'] and decode_matrix(state['ub']),
|
||||
or0=state['or0'],
|
||||
or1=state['or1'],
|
||||
reference=decode_reference(state.get('reference', None))
|
||||
)
|
||||
|
||||
|
||||
def decode_matrix(rows):
|
||||
return matrix([[eval(e) for e in row.split(', ')] for row in rows])
|
||||
|
||||
|
||||
def decode_reflist(reflist_dict, geometry, diffractometer_axes_names):
|
||||
reflections = []
|
||||
for key in sorted(reflist_dict.keys()):
|
||||
reflections.append(decode_reflection(reflist_dict[key], geometry))
|
||||
|
||||
return ReflectionList(geometry, diffractometer_axes_names, reflections)
|
||||
|
||||
|
||||
def decode_orientlist(orientlist_dict):
|
||||
orientations = []
|
||||
for key in sorted(orientlist_dict.keys()):
|
||||
orientations.append(decode_orientation(orientlist_dict[key]))
|
||||
|
||||
return OrientationList(orientations)
|
||||
|
||||
|
||||
def decode_reflection(ref_dict, geometry):
|
||||
h, k, l = eval(ref_dict['hkl'])
|
||||
time = ref_dict['time'] and gt(ref_dict['time'])
|
||||
pos_tuple = eval(ref_dict['pos'])
|
||||
try:
|
||||
position = geometry.create_position(*pos_tuple)
|
||||
except AttributeError:
|
||||
position = VliegPosition(*pos_tuple)
|
||||
return _Reflection(h, k, l, position, ref_dict['energy'], str(ref_dict['tag']), repr(time))
|
||||
|
||||
|
||||
def decode_reference(ref_dict):
|
||||
reference = YouReference(None) # TODO: We can't set get_ub method yet (tangles!)
|
||||
if ref_dict:
|
||||
nhkl = ref_dict.get('n_hkl_configured', None)
|
||||
nphi = ref_dict.get('n_phi_configured', None)
|
||||
if nhkl:
|
||||
reference.n_hkl_configured = matrix([eval(nhkl)]).T
|
||||
if nphi:
|
||||
reference.n_phi_configured = matrix([eval(nphi)]).T
|
||||
return reference
|
||||
|
||||
|
||||
def decode_orientation(orient_dict):
|
||||
h, k, l = eval(orient_dict['hkl'])
|
||||
x, y, z = eval(orient_dict['xyz'])
|
||||
time = orient_dict['time'] and gt(orient_dict['time'])
|
||||
return _Orientation(h, k, l, x, y, z, str(orient_dict['tag']), repr(time))
|
||||
|
||||
|
||||
# From: http://stackoverflow.com/questions/127803/how-to-parse-iso-formatted-date-in-python
|
||||
def gt(dt_str):
|
||||
dt, _, us= dt_str.partition(".")
|
||||
dt= datetime.datetime.strptime(dt, "%Y-%m-%dT%H:%M:%S")
|
||||
us= int(us.rstrip("Z"), 10)
|
||||
return dt + datetime.timedelta(microseconds=us)
|
||||
147
script/__Lib/diffcalc-2.1/diffcalc/ub/crystal.py
Executable file
147
script/__Lib/diffcalc-2.1/diffcalc/ub/crystal.py
Executable file
@@ -0,0 +1,147 @@
|
||||
###
|
||||
# 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 math import pi, cos, sin, acos, sqrt
|
||||
from diffcalc.util import angle_between_vectors
|
||||
|
||||
|
||||
try:
|
||||
from numpy import matrix
|
||||
except ImportError:
|
||||
from numjy import matrix
|
||||
|
||||
TORAD = pi / 180
|
||||
TODEG = 180 / pi
|
||||
SMALL = 1e-7
|
||||
|
||||
def z(num):
|
||||
"""Round to zero if small. This is useful to get rid of erroneous
|
||||
minus signs resulting from float representation close to zero.
|
||||
"""
|
||||
if abs(num) < SMALL:
|
||||
num = 0
|
||||
return num
|
||||
|
||||
|
||||
class CrystalUnderTest(object):
|
||||
"""
|
||||
Contains the lattice parameters and calculated B matrix for the crytsal
|
||||
under test. Also Calculates the distance between planes at a given hkl
|
||||
value.
|
||||
|
||||
The lattice paraemters can be specified and then if desired saved to a
|
||||
__library to be loaded later. The parameters are persisted across restarts.
|
||||
Lattices stored in config/var/crystals.xml .
|
||||
"""
|
||||
|
||||
def __init__(self, name, a, b, c, alpha, beta, gamma):
|
||||
'''Creates a new lattice and calculates related values.
|
||||
|
||||
Keyword arguments:
|
||||
name -- a string
|
||||
a,b,c,alpha,beta,gamma -- lengths and angles (in degrees)
|
||||
'''
|
||||
|
||||
self._name = name
|
||||
|
||||
# Set the direct lattice parameters
|
||||
self._a1 = a1 = a
|
||||
self._a2 = a2 = b
|
||||
self._a3 = a3 = c
|
||||
self._alpha1 = alpha1 = alpha * TORAD
|
||||
self._alpha2 = alpha2 = beta * TORAD
|
||||
self._alpha3 = alpha3 = gamma * TORAD
|
||||
|
||||
# Calculate the reciprocal lattice parameters
|
||||
self._beta1 = acos((cos(alpha2) * cos(alpha3) - cos(alpha1)) /
|
||||
(sin(alpha2) * sin(alpha3)))
|
||||
|
||||
self._beta2 = beta2 = acos((cos(alpha1) * cos(alpha3) - cos(alpha2)) /
|
||||
(sin(alpha1) * sin(alpha3)))
|
||||
|
||||
self._beta3 = beta3 = acos((cos(alpha1) * cos(alpha2) - cos(alpha3)) /
|
||||
(sin(alpha1) * sin(alpha2)))
|
||||
|
||||
volume = (a1 * a2 * a3 *
|
||||
sqrt(1 + 2 * cos(alpha1) * cos(alpha2) * cos(alpha3) -
|
||||
cos(alpha1) ** 2 - cos(alpha2) ** 2 - cos(alpha3) ** 2))
|
||||
|
||||
self._b1 = b1 = 2 * pi * a2 * a3 * sin(alpha1) / volume
|
||||
self._b2 = b2 = 2 * pi * a1 * a3 * sin(alpha2) / volume
|
||||
self._b3 = b3 = 2 * pi * a1 * a2 * sin(alpha3) / volume
|
||||
|
||||
# Calculate the BMatrix from the direct and reciprical parameters.
|
||||
# Reference: Busang and Levy (1967)
|
||||
self._bMatrix = matrix([
|
||||
[b1, b2 * cos(beta3), b3 * cos(beta2)],
|
||||
[0.0, b2 * sin(beta3), -b3 * sin(beta2) * cos(alpha1)],
|
||||
[0.0, 0.0, 2 * pi / a3]])
|
||||
|
||||
@property
|
||||
def B(self):
|
||||
'''
|
||||
Returns the B matrix, may be null if crystal is not set, or if there
|
||||
was a problem calculating this'''
|
||||
return self._bMatrix
|
||||
|
||||
def get_hkl_plane_distance(self, hkl):
|
||||
'''Calculates and returns the distance between planes'''
|
||||
hkl = matrix([hkl])
|
||||
bReduced = self._bMatrix / (2 * pi)
|
||||
bMT = bReduced.I * bReduced.T.I
|
||||
return 1.0 / sqrt((hkl * bMT.I * hkl.T)[0,0])
|
||||
|
||||
def get_hkl_plane_angle(self, hkl1, hkl2):
|
||||
'''Calculates and returns the angle between [hkl1] and [hkl2] planes'''
|
||||
hkl1 = matrix([hkl1]).T
|
||||
hkl2 = matrix([hkl2]).T
|
||||
nphi1 = self._bMatrix * hkl1
|
||||
nphi2 = self._bMatrix * hkl2
|
||||
angle = angle_between_vectors(nphi1, nphi2)
|
||||
return angle
|
||||
|
||||
def __str__(self):
|
||||
''' Returns lattice name and all set and calculated parameters'''
|
||||
return '\n'.join(self.str_lines())
|
||||
|
||||
def str_lines(self):
|
||||
WIDTH = 13
|
||||
if self._name is None:
|
||||
return [" none specified"]
|
||||
|
||||
b = self._bMatrix
|
||||
lines = []
|
||||
lines.append(" name:".ljust(WIDTH) + self._name.rjust(9))
|
||||
lines.append("")
|
||||
lines.append(" a, b, c:".ljust(WIDTH) +
|
||||
"% 9.5f % 9.5f % 9.5f" % (self.getLattice()[1:4]))
|
||||
lines.append(" " * WIDTH +
|
||||
"% 9.5f % 9.5f % 9.5f" % (self.getLattice()[4:]))
|
||||
lines.append("")
|
||||
|
||||
fmt = "% 9.5f % 9.5f % 9.5f"
|
||||
lines.append(" B matrix:".ljust(WIDTH) +
|
||||
fmt % (z(b[0, 0]), z(b[0, 1]), z(b[0, 2])))
|
||||
lines.append(' ' * WIDTH + fmt % (z(b[1, 0]), z(b[1, 1]), z(b[1, 2])))
|
||||
lines.append(' ' * WIDTH + fmt % (z(b[2, 0]), z(b[2, 1]), z(b[2, 2])))
|
||||
return lines
|
||||
|
||||
def getLattice(self):
|
||||
return(self._name, self._a1, self._a2, self._a3, self._alpha1 * TODEG,
|
||||
self._alpha2 * TODEG, self._alpha3 * TODEG)
|
||||
|
||||
118
script/__Lib/diffcalc-2.1/diffcalc/ub/orientations.py
Executable file
118
script/__Lib/diffcalc-2.1/diffcalc/ub/orientations.py
Executable file
@@ -0,0 +1,118 @@
|
||||
###
|
||||
# Copyright 2008-2017 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 copy import deepcopy
|
||||
import datetime # @UnusedImport for the eval below
|
||||
|
||||
try:
|
||||
from numpy import matrix
|
||||
except ImportError:
|
||||
from numjy import matrix
|
||||
|
||||
from diffcalc.util import DiffcalcException, bold
|
||||
|
||||
|
||||
class _Orientation:
|
||||
"""A orientation"""
|
||||
def __init__(self, h, k, l, x, y, z, tag, time):
|
||||
|
||||
self.h = float(h)
|
||||
self.k = float(k)
|
||||
self.l = float(l)
|
||||
self.x = float(x)
|
||||
self.y = float(y)
|
||||
self.z = float(z)
|
||||
self.tag = tag
|
||||
self.time = time # Saved as e.g. repr(datetime.now())
|
||||
|
||||
def __str__(self):
|
||||
return ("h=%-4.2f k=%-4.2f l=%-4.2f x=%-4.2f "
|
||||
"y=%-4.2f z=%-4.2 "
|
||||
" %-s %s" % (self.h, self.k, self.l,
|
||||
self.x, self.y, self.z,
|
||||
self.tag, self.time))
|
||||
|
||||
|
||||
class OrientationList:
|
||||
|
||||
def __init__(self, orientations=None):
|
||||
self._orientlist = orientations if orientations else []
|
||||
|
||||
|
||||
def add_orientation(self, h, k, l, x, y, z, tag, time):
|
||||
"""adds a crystal orientation
|
||||
"""
|
||||
self._orientlist += [_Orientation(h, k, l, x, y, z, tag,
|
||||
time.__repr__())]
|
||||
|
||||
def edit_orientation(self, num, h, k, l, x, y, z, tag, time):
|
||||
"""num starts at 1"""
|
||||
try:
|
||||
self._orientlist[num - 1] = _Orientation(h, k, l, x, y, z, tag,
|
||||
time.__repr__())
|
||||
except IndexError:
|
||||
raise DiffcalcException("There is no orientation " + repr(num)
|
||||
+ " to edit.")
|
||||
|
||||
def getOrientation(self, num):
|
||||
"""
|
||||
getOrientation(num) --> ( [h, k, l], [x, y, z], tag, time ) --
|
||||
num starts at 1
|
||||
"""
|
||||
r = deepcopy(self._orientlist[num - 1]) # for convenience
|
||||
return [r.h, r.k, r.l], [r.x, r.y, r.z], r.tag, eval(r.time)
|
||||
|
||||
def removeOrientation(self, num):
|
||||
del self._orientlist[num - 1]
|
||||
|
||||
def swap_orientations(self, num1, num2):
|
||||
orig1 = self._orientlist[num1 - 1]
|
||||
self._orientlist[num1 - 1] = self._orientlist[num2 - 1]
|
||||
self._orientlist[num2 - 1] = orig1
|
||||
|
||||
def __len__(self):
|
||||
return len(self._orientlist)
|
||||
|
||||
def __str__(self):
|
||||
return '\n'.join(self.str_lines())
|
||||
|
||||
def str_lines(self, R=None):
|
||||
if not self._orientlist:
|
||||
return [" <<< none specified >>>"]
|
||||
|
||||
lines = []
|
||||
|
||||
str_format = (" %5s %5s %5s %5s %5s %5s TAG")
|
||||
values = ('H', 'K', 'L', 'X', 'Y', 'Z')
|
||||
lines.append(bold(str_format % values))
|
||||
|
||||
for n in range(len(self._orientlist)):
|
||||
orient_tuple = self.getOrientation(n + 1)
|
||||
[h, k, l], [x, y, z], tag, _ = orient_tuple
|
||||
try:
|
||||
xyz_rot = R.I * matrix([[x],[y],[z]])
|
||||
xr, yr, zr = xyz_rot.T.tolist()[0]
|
||||
except AttributeError:
|
||||
xr, yr, zr = x ,y ,z
|
||||
if tag is None:
|
||||
tag = ""
|
||||
str_format = (" %2d % 4.2f % 4.2f % 4.2f " +
|
||||
"% 4.2f % 4.2f % 4.2f %s")
|
||||
values = (n + 1, h, k, l, xr, yr, zr, tag)
|
||||
lines.append(str_format % values)
|
||||
return lines
|
||||
151
script/__Lib/diffcalc-2.1/diffcalc/ub/persistence.py
Executable file
151
script/__Lib/diffcalc-2.1/diffcalc/ub/persistence.py
Executable file
@@ -0,0 +1,151 @@
|
||||
###
|
||||
# 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 with_statement
|
||||
|
||||
import os, glob
|
||||
from diffcalc.ub.calcstate import UBCalcStateEncoder
|
||||
import datetime
|
||||
|
||||
try:
|
||||
import json
|
||||
except ImportError:
|
||||
import simplejson as json
|
||||
|
||||
|
||||
def is_writable(directory):
|
||||
"""Return true if the file is writable from the current user
|
||||
"""
|
||||
probe = os.path.join(directory, "probe")
|
||||
try:
|
||||
open(probe, 'w')
|
||||
except IOError:
|
||||
return False
|
||||
else:
|
||||
os.remove(probe)
|
||||
return True
|
||||
|
||||
def check_directory_appropriate(directory):
|
||||
|
||||
if not os.path.exists(directory):
|
||||
raise IOError("'%s' does not exist")
|
||||
|
||||
if not os.path.isdir(directory):
|
||||
raise IOError("'%s' is not a directory")
|
||||
|
||||
if not is_writable(directory):
|
||||
raise IOError("'%s' is not writable")
|
||||
|
||||
|
||||
class UBCalculationJSONPersister(object):
|
||||
|
||||
def __init__(self, directory):
|
||||
check_directory_appropriate(directory)
|
||||
self.directory = directory
|
||||
self.description = directory
|
||||
|
||||
def filepath(self, name):
|
||||
return os.path.join(self.directory, name + '.json')
|
||||
|
||||
def save(self, state, name):
|
||||
# FORMAT = '%Y-%m-%d %H:%M:%S'
|
||||
# time_string = datetime.datetime.strftime(datetime.datetime.now(), FORMAT)
|
||||
with open(self.filepath(name), 'w') as f:
|
||||
json.dump(state, f, indent=4, cls=UBCalcStateEncoder)
|
||||
|
||||
def load(self, name):
|
||||
with open(self.filepath(name), 'r') as f:
|
||||
return json.load(f)
|
||||
|
||||
def list(self): # @ReservedAssignment
|
||||
files = self._get_save_files()
|
||||
return [os.path.basename(f + '.json').split('.json')[0] for f in files]
|
||||
|
||||
def list_metadata(self):
|
||||
metadata = []
|
||||
for f in self._get_save_files():
|
||||
dt = datetime.datetime.fromtimestamp(os.path.getmtime(f))
|
||||
metadata.append(dt.strftime('%d %b %Y (%H:%M)'))
|
||||
return metadata
|
||||
|
||||
def _get_save_files(self):
|
||||
files = filter(os.path.isfile, glob.glob(os.path.join(self.directory, '*.json')))
|
||||
files.sort(key=lambda x: os.path.getmtime(x))
|
||||
files.reverse()
|
||||
return files
|
||||
|
||||
def remove(self, name):
|
||||
os.remove(self.filepath(name))
|
||||
|
||||
|
||||
|
||||
class UBCalculationPersister(object):
|
||||
"""Attempts to the use the gda's database to store ub calculation state
|
||||
"""
|
||||
def __init__(self):
|
||||
try:
|
||||
from uk.ac.diamond.daq.persistence.jythonshelf import LocalJythonShelfManager
|
||||
from uk.ac.diamond.daq.persistence.jythonshelf.LocalDatabase import \
|
||||
LocalDatabaseException
|
||||
self.shelf = LocalJythonShelfManager.getLocalObjectShelf(
|
||||
'diffcalc.ub')
|
||||
except ImportError, e:
|
||||
print ("!!! UBCalculationPersister could not import the gda database "
|
||||
"code: " + repr(e))
|
||||
self.shelf = None
|
||||
except LocalDatabaseException, e:
|
||||
print ("UBCalculationPersister could not connect to the gda "
|
||||
"database: " + repr(e))
|
||||
self.shelf = None
|
||||
self.description = 'GDA sql database'
|
||||
|
||||
def save(self, state, key):
|
||||
if self.shelf is not None:
|
||||
self.shelf[key] = state
|
||||
else:
|
||||
print "<<<no database available to save UB calculation>>>"
|
||||
|
||||
def load(self, name):
|
||||
if self.shelf is not None:
|
||||
return self.shelf[name]
|
||||
else:
|
||||
raise IOError("Could not load UB calculation: no database available")
|
||||
|
||||
def list(self): # @ReservedAssignment
|
||||
if self.shelf is not None:
|
||||
names = list(self.shelf.keys())
|
||||
names.sort()
|
||||
return names
|
||||
else:
|
||||
return []
|
||||
|
||||
def remove(self, name):
|
||||
if self.shelf is not None:
|
||||
del self.shelf[name]
|
||||
else:
|
||||
raise IOError("Could not remove UB calculation: no database available")
|
||||
|
||||
|
||||
class UbCalculationNonPersister(UBCalculationPersister):
|
||||
"""
|
||||
A version of UBCalculationPersister that simply stores to a local dict
|
||||
rather than a database. Useful for testing.
|
||||
"""
|
||||
def __init__(self):
|
||||
self.shelf = dict()
|
||||
self.description = 'memory only'
|
||||
99
script/__Lib/diffcalc-2.1/diffcalc/ub/reference.py
Executable file
99
script/__Lib/diffcalc-2.1/diffcalc/ub/reference.py
Executable file
@@ -0,0 +1,99 @@
|
||||
from math import pi, acos
|
||||
|
||||
try:
|
||||
from numpy import matrix
|
||||
from numpy.linalg import norm
|
||||
except ImportError:
|
||||
from numjy import matrix
|
||||
from numjy.linalg import norm
|
||||
|
||||
from diffcalc.util import cross3, dot3
|
||||
|
||||
|
||||
SMALL = 1e-7
|
||||
TODEG = 180 / pi
|
||||
|
||||
class YouReference(object):
|
||||
|
||||
def __init__(self, get_UB):
|
||||
self.get_UB = get_UB # callable
|
||||
self._n_phi_configured = None
|
||||
self._n_hkl_configured = None
|
||||
self._set_n_phi_configured(matrix('0; 0; 1'))
|
||||
|
||||
def _set_n_phi_configured(self, n_phi):
|
||||
self._n_phi_configured = n_phi
|
||||
self._n_hkl_configured = None
|
||||
|
||||
def _get_n_phi_configured(self):
|
||||
return self._n_phi_configured
|
||||
|
||||
n_phi_configured = property(_get_n_phi_configured, _set_n_phi_configured)
|
||||
|
||||
def _set_n_hkl_configured(self, n_hkl):
|
||||
self._n_phi_configured = None
|
||||
self._n_hkl_configured = n_hkl
|
||||
|
||||
def _get_n_hkl_configured(self):
|
||||
return self._n_hkl_configured
|
||||
|
||||
n_hkl_configured = property(_get_n_hkl_configured, _set_n_hkl_configured)
|
||||
|
||||
@property
|
||||
def n_phi(self):
|
||||
n_phi = (self.get_UB() * self._n_hkl_configured if self._n_phi_configured is None
|
||||
else self._n_phi_configured)
|
||||
return n_phi / norm(n_phi)
|
||||
|
||||
@property
|
||||
def n_hkl(self):
|
||||
n_hkl = (self.get_UB().I * self._n_phi_configured if self._n_hkl_configured is None
|
||||
else self._n_hkl_configured)
|
||||
return n_hkl / norm(n_hkl)
|
||||
|
||||
def _pretty_vector(self, m):
|
||||
return ' '.join([('% 9.5f' % e).rjust(9) for e in m.T.tolist()[0]])
|
||||
|
||||
def repr_lines(self, ub_calculated, WIDTH=9, R=None):
|
||||
SET_LABEL = ' <- set'
|
||||
lines = []
|
||||
if self._n_phi_configured is not None:
|
||||
nphi_label = SET_LABEL
|
||||
nhkl_label = ''
|
||||
elif self._n_hkl_configured is not None:
|
||||
nphi_label = ''
|
||||
nhkl_label = SET_LABEL
|
||||
else:
|
||||
raise AssertionError("Neither a manual n_phi nor n_hkl is configured")
|
||||
|
||||
if ub_calculated:
|
||||
try:
|
||||
lines.append(" n_phi:".ljust(WIDTH) + self._pretty_vector(R.I * self.n_phi) + nphi_label)
|
||||
except AttributeError:
|
||||
lines.append(" n_phi:".ljust(WIDTH) + self._pretty_vector(self.n_phi) + nphi_label)
|
||||
lines.append(" n_hkl:".ljust(WIDTH) + self._pretty_vector(self.n_hkl) + nhkl_label)
|
||||
try:
|
||||
rotation_axis = R.I * cross3(matrix('0; 0; 1'), self.n_phi)
|
||||
except AttributeError:
|
||||
rotation_axis = cross3(matrix('0; 0; 1'), self.n_phi)
|
||||
if abs(norm(rotation_axis)) < SMALL:
|
||||
lines.append(" normal:".ljust(WIDTH) + " None")
|
||||
else:
|
||||
rotation_axis = rotation_axis * (1 / norm(rotation_axis))
|
||||
cos_rotation_angle = dot3(matrix('0; 0; 1'), self.n_phi)
|
||||
rotation_angle = acos(cos_rotation_angle)
|
||||
lines.append(" normal:")
|
||||
lines.append(" angle:".ljust(WIDTH) + "% 9.5f" % (rotation_angle * TODEG))
|
||||
lines.append(" axis:".ljust(WIDTH) + self._pretty_vector(rotation_axis))
|
||||
|
||||
else: # no ub calculated
|
||||
if self._n_phi_configured is not None:
|
||||
try:
|
||||
lines.append(" n_phi:".ljust(WIDTH) + self._pretty_vector(R.I * self._n_phi_configured) + SET_LABEL)
|
||||
except AttributeError:
|
||||
lines.append(" n_phi:".ljust(WIDTH) + self._pretty_vector(self._n_phi_configured) + SET_LABEL)
|
||||
elif self._n_hkl_configured is not None:
|
||||
lines.append(" n_hkl:".ljust(WIDTH) + self._pretty_vector(self._n_hkl_configured) + SET_LABEL)
|
||||
|
||||
return lines
|
||||
|
||||
126
script/__Lib/diffcalc-2.1/diffcalc/ub/reflections.py
Executable file
126
script/__Lib/diffcalc-2.1/diffcalc/ub/reflections.py
Executable file
@@ -0,0 +1,126 @@
|
||||
###
|
||||
# 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 copy import deepcopy
|
||||
import datetime # @UnusedImport for the eval below
|
||||
from diffcalc.util import DiffcalcException, bold
|
||||
from diffcalc.hkl.vlieg.geometry import VliegPosition
|
||||
|
||||
|
||||
class _Reflection:
|
||||
"""A reflection"""
|
||||
def __init__(self, h, k, l, position, energy, tag, time):
|
||||
|
||||
self.h = float(h)
|
||||
self.k = float(k)
|
||||
self.l = float(l)
|
||||
self.pos = position
|
||||
self.tag = tag
|
||||
self.energy = float(energy) # energy=12.39842/lambda
|
||||
self.wavelength = 12.3984 / self.energy
|
||||
self.time = time # Saved as e.g. repr(datetime.now())
|
||||
|
||||
def __str__(self):
|
||||
return ("energy=%-6.3f h=%-4.2f k=%-4.2f l=%-4.2f alpha=%-8.4f "
|
||||
"delta=%-8.4f gamma=%-8.4f omega=%-8.4f chi=%-8.4f "
|
||||
"phi=%-8.4f %-s %s" % (self.energy, self.h, self.k, self.l,
|
||||
self.pos.alpha, self.pos.delta, self.pos.gamma, self.pos.omega,
|
||||
self.pos.chi, self.pos.phi, self.tag, self.time))
|
||||
|
||||
|
||||
class ReflectionList:
|
||||
|
||||
def __init__(self, diffractometerPluginObject, externalAngleNames, reflections=None):
|
||||
self._geometry = diffractometerPluginObject
|
||||
self._externalAngleNames = externalAngleNames
|
||||
self._reflist = reflections if reflections else []
|
||||
|
||||
|
||||
def add_reflection(self, h, k, l, position, energy, tag, time):
|
||||
"""adds a reflection, position in degrees
|
||||
"""
|
||||
if type(position) in (list, tuple):
|
||||
try:
|
||||
position = self._geometry.create_position(*position)
|
||||
except AttributeError:
|
||||
position = VliegPosition(*position)
|
||||
self._reflist += [_Reflection(h, k, l, position, energy, tag,
|
||||
time.__repr__())]
|
||||
|
||||
def edit_reflection(self, num, h, k, l, position, energy, tag, time):
|
||||
"""num starts at 1"""
|
||||
if type(position) in (list, tuple):
|
||||
position = VliegPosition(*position)
|
||||
try:
|
||||
self._reflist[num - 1] = _Reflection(h, k, l, position, energy, tag,
|
||||
time.__repr__())
|
||||
except IndexError:
|
||||
raise DiffcalcException("There is no reflection " + repr(num)
|
||||
+ " to edit.")
|
||||
|
||||
def getReflection(self, num):
|
||||
"""
|
||||
getReflection(num) --> ( [h, k, l], position, energy, tag, time ) --
|
||||
num starts at 1 position in degrees
|
||||
"""
|
||||
r = deepcopy(self._reflist[num - 1]) # for convenience
|
||||
return [r.h, r.k, r.l], deepcopy(r.pos), r.energy, r.tag, eval(r.time)
|
||||
|
||||
def get_reflection_in_external_angles(self, num):
|
||||
"""getReflection(num) --> ( [h, k, l], (angle1...angleN), energy, tag )
|
||||
-- num starts at 1 position in degrees"""
|
||||
r = deepcopy(self._reflist[num - 1]) # for convenience
|
||||
externalAngles = self._geometry.internal_position_to_physical_angles(r.pos)
|
||||
result = [r.h, r.k, r.l], externalAngles, r.energy, r.tag, eval(r.time)
|
||||
return result
|
||||
|
||||
def removeReflection(self, num):
|
||||
del self._reflist[num - 1]
|
||||
|
||||
def swap_reflections(self, num1, num2):
|
||||
orig1 = self._reflist[num1 - 1]
|
||||
self._reflist[num1 - 1] = self._reflist[num2 - 1]
|
||||
self._reflist[num2 - 1] = orig1
|
||||
|
||||
def __len__(self):
|
||||
return len(self._reflist)
|
||||
|
||||
def __str__(self):
|
||||
return '\n'.join(self.str_lines())
|
||||
|
||||
def str_lines(self):
|
||||
axes = tuple(s.upper() for s in self._externalAngleNames)
|
||||
if not self._reflist:
|
||||
return [" <<< none specified >>>"]
|
||||
|
||||
lines = []
|
||||
|
||||
format = (" %6s %5s %5s %5s " + "%8s " * len(axes) + " TAG")
|
||||
values = ('ENERGY', 'H', 'K', 'L') + axes
|
||||
lines.append(bold(format % values))
|
||||
|
||||
for n in range(len(self._reflist)):
|
||||
ref_tuple = self.get_reflection_in_external_angles(n + 1)
|
||||
[h, k, l], externalAngles, energy, tag, _ = ref_tuple
|
||||
if tag is None:
|
||||
tag = ""
|
||||
format = (" %2d %6.3f % 4.2f % 4.2f % 4.2f " +
|
||||
"% 8.4f " * len(axes) + " %s")
|
||||
values = (n + 1, energy, h, k, l) + externalAngles + (tag,)
|
||||
lines.append(format % values)
|
||||
return lines
|
||||
768
script/__Lib/diffcalc-2.1/diffcalc/ub/ub.py
Executable file
768
script/__Lib/diffcalc-2.1/diffcalc/ub/ub.py
Executable file
@@ -0,0 +1,768 @@
|
||||
###
|
||||
# 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 import settings
|
||||
from diffcalc.ub.calc import UBCalculation
|
||||
|
||||
from math import asin, pi
|
||||
from datetime import datetime
|
||||
|
||||
try:
|
||||
from numpy import matrix
|
||||
except ImportError:
|
||||
from numjy import matrix
|
||||
|
||||
|
||||
from diffcalc.util import getInputWithDefault as promptForInput, \
|
||||
promptForNumber, promptForList, allnum, isnum, bold, xyz_rotation
|
||||
from diffcalc.util import command
|
||||
|
||||
TORAD = pi / 180
|
||||
TODEG = 180 / pi
|
||||
|
||||
|
||||
# When using ipython magic, these functions must not be imported to the top
|
||||
# level namespace. Doing so will stop them from being called with magic.
|
||||
|
||||
__all__ = ['addorient', 'addref', 'c2th', 'hklangle', 'calcub', 'delorient', 'delref', 'editorient',
|
||||
'editref', 'listub', 'loadub', 'newub', 'orientub', 'saveubas', 'setlat',
|
||||
'addmiscut', 'setmiscut', 'setu', 'setub', 'showorient', 'showref', 'swaporient',
|
||||
'swapref', 'trialub', 'checkub', 'ub', 'ubcalc', 'rmub', 'clearorient',
|
||||
'clearref', 'lastub', 'refineub']
|
||||
|
||||
if settings.include_sigtau:
|
||||
__all__.append('sigtau')
|
||||
|
||||
if settings.include_reference:
|
||||
__all__.append('setnphi')
|
||||
__all__.append('setnhkl')
|
||||
|
||||
|
||||
ubcalc = UBCalculation(settings.hardware,
|
||||
settings.geometry,
|
||||
settings.ubcalc_persister,
|
||||
settings.ubcalc_strategy,
|
||||
settings.include_sigtau,
|
||||
settings.include_reference)
|
||||
|
||||
|
||||
|
||||
### UB state ###
|
||||
|
||||
@command
|
||||
def newub(name=None):
|
||||
"""newub {'name'} -- start a new ub calculation name
|
||||
"""
|
||||
if name is None:
|
||||
# interactive
|
||||
name = promptForInput('calculation name')
|
||||
ubcalc.start_new(name)
|
||||
setlat()
|
||||
elif isinstance(name, str):
|
||||
# just trying might cause confusion here
|
||||
ubcalc.start_new(name)
|
||||
else:
|
||||
raise TypeError()
|
||||
|
||||
@command
|
||||
def loadub(name_or_num):
|
||||
"""loadub 'name' | num -- load an existing ub calculation
|
||||
"""
|
||||
if isinstance(name_or_num, str):
|
||||
ubcalc.load(name_or_num)
|
||||
else:
|
||||
ubcalc.load(ubcalc.listub()[int(name_or_num)])
|
||||
|
||||
@command
|
||||
def lastub():
|
||||
"""lastub -- load the last used ub calculation
|
||||
"""
|
||||
try:
|
||||
lastub_name = ubcalc.listub()[0]
|
||||
print "Loading ub calculation: '%s'" % lastub_name
|
||||
loadub(0)
|
||||
except IndexError:
|
||||
print "WARNING: There is no record of the last ub calculation used"
|
||||
|
||||
@command
|
||||
def rmub(name_or_num):
|
||||
"""rmub 'name'|num -- remove existing ub calculation
|
||||
"""
|
||||
if isinstance(name_or_num, str):
|
||||
ubcalc.remove(name_or_num)
|
||||
else:
|
||||
ubcalc.remove(ubcalc.listub()[int(name_or_num)])
|
||||
|
||||
@command
|
||||
def listub():
|
||||
"""listub -- list the ub calculations available to load.
|
||||
"""
|
||||
if hasattr(ubcalc._persister, 'description'):
|
||||
print "UB calculations in: " + ubcalc._persister.description
|
||||
else:
|
||||
print "UB calculations:"
|
||||
print
|
||||
ubnames = ubcalc.listub()
|
||||
# TODO: whole mechanism of making two calls is messy
|
||||
try:
|
||||
ub_metadata = ubcalc.listub_metadata()
|
||||
except AttributeError:
|
||||
ub_metadata = [''] * len(ubnames)
|
||||
|
||||
for n, name, data in zip(range(len(ubnames)), ubnames, ub_metadata):
|
||||
print "%2i) %-15s %s" % (n, name, data)
|
||||
|
||||
@command
|
||||
def saveubas(name):
|
||||
"""saveubas 'name' -- save the ub calculation with a new name
|
||||
"""
|
||||
if isinstance(name, str):
|
||||
# just trying might cause confusion here
|
||||
ubcalc.saveas(name)
|
||||
else:
|
||||
raise TypeError()
|
||||
|
||||
@command
|
||||
def ub():
|
||||
"""ub -- show the complete state of the ub calculation
|
||||
"""
|
||||
#wavelength = float(hardware.get_wavelength())
|
||||
#energy = float(hardware.get_energy())
|
||||
print ubcalc.__str__()
|
||||
|
||||
@command
|
||||
def refineub(*args):
|
||||
"""
|
||||
refineub {[h k l]} {pos} -- refine unit cell dimensions and U matrix to match diffractometer angles for a given hkl value
|
||||
"""
|
||||
if len(args) > 0:
|
||||
args = list(args)
|
||||
h, k, l = args.pop(0)
|
||||
if not (isnum(h) and isnum(k) and isnum(l)):
|
||||
raise TypeError()
|
||||
else:
|
||||
h = promptForNumber('h', 0.)
|
||||
k = promptForNumber('k', 0.)
|
||||
l = promptForNumber('l', 0.)
|
||||
if None in (h, k, l):
|
||||
_handleInputError("h,k and l must all be numbers")
|
||||
if len(args) == 1:
|
||||
pos = settings.geometry.physical_angles_to_internal_position( # @UndefinedVariable
|
||||
args.pop(0))
|
||||
elif len(args) == 0:
|
||||
reply = promptForInput('current pos', 'y')
|
||||
if reply in ('y', 'Y', 'yes'):
|
||||
positionList = settings.hardware.get_position() # @UndefinedVariable
|
||||
else:
|
||||
currentPos = settings.hardware.get_position() # @UndefinedVariable
|
||||
positionList = []
|
||||
names = settings.hardware.get_axes_names() # @UndefinedVariable
|
||||
for i, angleName in enumerate(names):
|
||||
val = promptForNumber(angleName.rjust(7), currentPos[i])
|
||||
if val is None:
|
||||
_handleInputError("Please enter a number, or press"
|
||||
" Return to accept default!")
|
||||
return
|
||||
positionList.append(val)
|
||||
pos = settings.geometry.physical_angles_to_internal_position(positionList) # @UndefinedVariable
|
||||
else:
|
||||
raise TypeError()
|
||||
|
||||
pos.changeToRadians()
|
||||
scale, lat = ubcalc.rescale_unit_cell(h, k, l, pos)
|
||||
if scale:
|
||||
lines = ["Unit cell scaling factor:".ljust(9) +
|
||||
"% 9.5f" % scale]
|
||||
lines.append("Refined crystal lattice:")
|
||||
lines.append(" a, b, c:".ljust(9) +
|
||||
"% 9.5f % 9.5f % 9.5f" % (lat[1:4]))
|
||||
lines.append(" " * 12 +
|
||||
"% 9.5f % 9.5f % 9.5f" % (lat[4:]))
|
||||
lines.append("")
|
||||
print '\n'.join(lines)
|
||||
reply = promptForInput('Update crystal settings?', 'y')
|
||||
if reply in ('y', 'Y', 'yes'):
|
||||
ubcalc.set_lattice(*lat)
|
||||
else:
|
||||
print "No unit cell mismatch detected"
|
||||
mc_angle, mc_axis = ubcalc.calc_miscut(h, k, l, pos)
|
||||
if mc_angle:
|
||||
lines = ["Miscut parameters:",]
|
||||
lines.append(" angle:".ljust(9) + "% 9.5f" % mc_angle)
|
||||
lines.append(" axis:".ljust(9) + "% 9.5f % 9.5f % 9.5f" % tuple(mc_axis))
|
||||
print '\n'.join(lines)
|
||||
reply = promptForInput('Apply miscut parameters?', 'y')
|
||||
if reply in ('y', 'Y', 'yes'):
|
||||
ubcalc.set_miscut(mc_axis, -mc_angle * TORAD, True)
|
||||
else:
|
||||
print "No miscut detected for the given settings"
|
||||
|
||||
### UB lattice ###
|
||||
|
||||
@command
|
||||
def setlat(name=None, *args):
|
||||
"""
|
||||
setlat -- interactively enter lattice parameters (Angstroms and Deg)
|
||||
setlat name a -- assumes cubic
|
||||
setlat name a b -- assumes tetragonal
|
||||
setlat name a b c -- assumes ortho
|
||||
setlat name a b c gamma -- assumes mon/hex with gam not equal to 90
|
||||
setlat name a b c alpha beta gamma -- arbitrary
|
||||
"""
|
||||
|
||||
if name is None: # Interactive
|
||||
name = promptForInput("crystal name")
|
||||
a = promptForNumber(' a', 1)
|
||||
b = promptForNumber(' b', a)
|
||||
c = promptForNumber(' c', a)
|
||||
alpha = promptForNumber('alpha', 90)
|
||||
beta = promptForNumber('beta', 90)
|
||||
gamma = promptForNumber('gamma', 90)
|
||||
ubcalc.set_lattice(name, a, b, c, alpha, beta, gamma)
|
||||
|
||||
elif (isinstance(name, str) and
|
||||
len(args) in (1, 2, 3, 4, 6) and
|
||||
allnum(args)):
|
||||
# first arg is string and rest are numbers
|
||||
ubcalc.set_lattice(name, *args)
|
||||
else:
|
||||
raise TypeError()
|
||||
|
||||
@command
|
||||
def c2th(hkl, en=None):
|
||||
"""
|
||||
c2th [h k l] -- calculate two-theta angle for reflection
|
||||
"""
|
||||
if en is None:
|
||||
wl = settings.hardware.get_wavelength() # @UndefinedVariable
|
||||
else:
|
||||
wl = 12.39842 / en
|
||||
d = ubcalc.get_hkl_plane_distance(hkl)
|
||||
if wl > (2 * d):
|
||||
raise ValueError(
|
||||
'Reflection un-reachable as wavelength (%f) is more than twice\n'
|
||||
'the plane distance (%f)' % (wl, d))
|
||||
try:
|
||||
return 2.0 * asin(wl / (d * 2)) * TODEG
|
||||
except ValueError as e:
|
||||
raise ValueError('asin(wl / (d * 2) with wl=%f and d=%f: ' %(wl, d) + e.args[0])
|
||||
|
||||
|
||||
@command
|
||||
def hklangle(hkl1, hkl2):
|
||||
"""
|
||||
hklangle [h1 k1 l1] [h2 k2 l2] -- calculate angle between [h1 k1 l1] and [h2 k2 l2] planes
|
||||
"""
|
||||
return ubcalc.get_hkl_plane_angle(hkl1, hkl2) * TODEG
|
||||
|
||||
### Surface and reference vector stuff ###
|
||||
|
||||
@command
|
||||
def sigtau(sigma=None, tau=None):
|
||||
"""sigtau {sigma tau} -- sets or displays sigma and tau"""
|
||||
if sigma is None and tau is None:
|
||||
chi = settings.hardware.get_position_by_name('chi') # @UndefinedVariable
|
||||
phi = settings.hardware.get_position_by_name('phi') # @UndefinedVariable
|
||||
_sigma, _tau = ubcalc.sigma, ubcalc.tau
|
||||
print "sigma, tau = %f, %f" % (_sigma, _tau)
|
||||
print " chi, phi = %f, %f" % (chi, phi)
|
||||
sigma = promptForInput("sigma", -chi)
|
||||
tau = promptForInput(" tau", -phi)
|
||||
ubcalc.sigma = sigma
|
||||
ubcalc.tau = tau
|
||||
else:
|
||||
ubcalc.sigma = float(sigma)
|
||||
ubcalc.tau = float(tau)
|
||||
|
||||
|
||||
@command
|
||||
def setnphi(xyz=None):
|
||||
"""setnphi {[x y z]} -- sets or displays n_phi reference"""
|
||||
if xyz is None:
|
||||
ubcalc.print_reference()
|
||||
else:
|
||||
ubcalc.set_n_phi_configured(_to_column_vector_triple(xyz))
|
||||
ubcalc.print_reference()
|
||||
|
||||
@command
|
||||
def setnhkl(hkl=None):
|
||||
"""setnhkl {[h k l]} -- sets or displays n_hkl reference"""
|
||||
if hkl is None:
|
||||
ubcalc.print_reference()
|
||||
else:
|
||||
ubcalc.set_n_hkl_configured(_to_column_vector_triple(hkl))
|
||||
ubcalc.print_reference()
|
||||
|
||||
|
||||
def _to_column_vector_triple(o):
|
||||
m = matrix(o)
|
||||
if m.shape == (1, 3):
|
||||
return m.T
|
||||
elif m.shape == (3, 1):
|
||||
return m
|
||||
else:
|
||||
raise ValueError("Unexpected shape matrix: " + m)
|
||||
|
||||
### UB refelections ###
|
||||
|
||||
@command
|
||||
def showref():
|
||||
"""showref -- shows full reflection list"""
|
||||
if ubcalc._state.reflist:
|
||||
print '\n'.join(ubcalc._state.reflist.str_lines())
|
||||
else:
|
||||
print "<<< No reflections stored >>>"
|
||||
|
||||
@command
|
||||
def addref(*args):
|
||||
"""
|
||||
addref -- add reflection interactively
|
||||
addref [h k l] {'tag'} -- add reflection with current position and energy
|
||||
addref [h k l] (p1, .., pN) energy {'tag'} -- add arbitrary reflection
|
||||
"""
|
||||
|
||||
if len(args) == 0:
|
||||
h = promptForNumber('h', 0.)
|
||||
k = promptForNumber('k', 0.)
|
||||
l = promptForNumber('l', 0.)
|
||||
if None in (h, k, l):
|
||||
_handleInputError("h,k and l must all be numbers")
|
||||
reply = promptForInput('current pos', 'y')
|
||||
if reply in ('y', 'Y', 'yes'):
|
||||
positionList = settings.hardware.get_position() # @UndefinedVariable
|
||||
energy = settings.hardware.get_energy() # @UndefinedVariable
|
||||
else:
|
||||
currentPos = settings.hardware.get_position() # @UndefinedVariable
|
||||
positionList = []
|
||||
names = settings.hardware.get_axes_names() # @UndefinedVariable
|
||||
for i, angleName in enumerate(names):
|
||||
val = promptForNumber(angleName.rjust(7), currentPos[i])
|
||||
if val is None:
|
||||
_handleInputError("Please enter a number, or press"
|
||||
" Return to accept default!")
|
||||
return
|
||||
positionList.append(val)
|
||||
muliplier = settings.hardware.energyScannableMultiplierToGetKeV # @UndefinedVariable
|
||||
energy = promptForNumber('energy', settings.hardware.get_energy() / muliplier) # @UndefinedVariable
|
||||
if val is None:
|
||||
_handleInputError("Please enter a number, or press "
|
||||
"Return to accept default!")
|
||||
return
|
||||
energy = energy * muliplier
|
||||
tag = promptForInput("tag")
|
||||
if tag == '':
|
||||
tag = None
|
||||
pos = settings.geometry.physical_angles_to_internal_position(positionList) # @UndefinedVariable
|
||||
ubcalc.add_reflection(h, k, l, pos, energy, tag,
|
||||
datetime.now())
|
||||
elif len(args) in (1, 2, 3, 4):
|
||||
args = list(args)
|
||||
h, k, l = args.pop(0)
|
||||
if not (isnum(h) and isnum(k) and isnum(l)):
|
||||
raise TypeError()
|
||||
if len(args) >= 2:
|
||||
pos = settings.geometry.physical_angles_to_internal_position( # @UndefinedVariable
|
||||
args.pop(0))
|
||||
energy = args.pop(0)
|
||||
if not isnum(energy):
|
||||
raise TypeError()
|
||||
else:
|
||||
pos = settings.geometry.physical_angles_to_internal_position( # @UndefinedVariable
|
||||
settings.hardware.get_position()) # @UndefinedVariable
|
||||
energy = settings.hardware.get_energy() # @UndefinedVariable
|
||||
if len(args) == 1:
|
||||
tag = args.pop(0)
|
||||
if not isinstance(tag, str):
|
||||
raise TypeError()
|
||||
else:
|
||||
tag = None
|
||||
ubcalc.add_reflection(h, k, l, pos, energy, tag,
|
||||
datetime.now())
|
||||
else:
|
||||
raise TypeError()
|
||||
|
||||
@command
|
||||
def editref(num):
|
||||
"""editref num -- interactively edit a reflection.
|
||||
"""
|
||||
num = int(num)
|
||||
|
||||
# Get old reflection values
|
||||
[oldh, oldk, oldl], oldExternalAngles, oldEnergy, oldTag, oldT = \
|
||||
ubcalc.get_reflection_in_external_angles(num)
|
||||
del oldT # current time will be used.
|
||||
|
||||
h = promptForNumber('h', oldh)
|
||||
k = promptForNumber('k', oldk)
|
||||
l = promptForNumber('l', oldl)
|
||||
if None in (h, k, l):
|
||||
_handleInputError("h,k and l must all be numbers")
|
||||
reply = promptForInput('update position with current hardware setting',
|
||||
'n')
|
||||
if reply in ('y', 'Y', 'yes'):
|
||||
positionList = settings.hardware.get_position() # @UndefinedVariable
|
||||
energy = settings.hardware.get_energy() # @UndefinedVariable
|
||||
else:
|
||||
positionList = []
|
||||
names = settings.hardware.get_axes_names() # @UndefinedVariable
|
||||
for i, angleName in enumerate(names):
|
||||
val = promptForNumber(angleName.rjust(7), oldExternalAngles[i])
|
||||
if val is None:
|
||||
_handleInputError("Please enter a number, or press "
|
||||
"Return to accept default!")
|
||||
return
|
||||
positionList.append(val)
|
||||
muliplier = settings.hardware.energyScannableMultiplierToGetKeV # @UndefinedVariable
|
||||
energy = promptForNumber('energy', oldEnergy / muliplier)
|
||||
if val is None:
|
||||
_handleInputError("Please enter a number, or press Return "
|
||||
"to accept default!")
|
||||
return
|
||||
energy = energy * muliplier
|
||||
tag = promptForInput("tag", oldTag)
|
||||
if tag == '':
|
||||
tag = None
|
||||
pos = settings.geometry.physical_angles_to_internal_position(positionList) # @UndefinedVariable
|
||||
ubcalc.edit_reflection(num, h, k, l, pos, energy, tag,
|
||||
datetime.now())
|
||||
|
||||
@command
|
||||
def delref(num):
|
||||
"""delref num -- deletes a reflection (numbered from 1)
|
||||
"""
|
||||
ubcalc.del_reflection(int(num))
|
||||
|
||||
@command
|
||||
def clearref():
|
||||
"""clearref -- deletes all the reflections
|
||||
"""
|
||||
while ubcalc.get_number_reflections():
|
||||
ubcalc.del_reflection(1)
|
||||
|
||||
@command
|
||||
def swapref(num1=None, num2=None):
|
||||
"""
|
||||
swapref -- swaps first two reflections used for calulating U matrix
|
||||
swapref num1 num2 -- swaps two reflections (numbered from 1)
|
||||
"""
|
||||
if num1 is None and num2 is None:
|
||||
ubcalc.swap_reflections(1, 2)
|
||||
elif isinstance(num1, int) and isinstance(num2, int):
|
||||
ubcalc.swap_reflections(num1, num2)
|
||||
else:
|
||||
raise TypeError()
|
||||
|
||||
### U calculation from crystal orientation
|
||||
@command
|
||||
def showorient():
|
||||
"""showorient -- shows full list of crystal orientations"""
|
||||
if ubcalc._state.orientlist:
|
||||
print '\n'.join(ubcalc._state.orientlist.str_lines())
|
||||
else:
|
||||
print "<<< No crystal orientations stored >>>"
|
||||
|
||||
@command
|
||||
def addorient(*args):
|
||||
"""
|
||||
addorient -- add crystal orientation interactively
|
||||
addorient [h k l] [x y z] {'tag'} -- add crystal orientation in laboratory frame
|
||||
"""
|
||||
|
||||
if len(args) == 0:
|
||||
h = promptForNumber('h', 0.)
|
||||
k = promptForNumber('k', 0.)
|
||||
l = promptForNumber('l', 0.)
|
||||
if None in (h, k, l):
|
||||
_handleInputError("h,k and l must all be numbers")
|
||||
|
||||
x = promptForNumber('x', 0.)
|
||||
y = promptForNumber('y', 0.)
|
||||
z = promptForNumber('z', 0.)
|
||||
if None in (x, y, z):
|
||||
_handleInputError("x,y and z must all be numbers")
|
||||
|
||||
tag = promptForInput("tag")
|
||||
if tag == '':
|
||||
tag = None
|
||||
ubcalc.add_orientation(h, k, l, x , y, z, tag,
|
||||
datetime.now())
|
||||
elif len(args) in (1, 2, 3):
|
||||
args = list(args)
|
||||
h, k, l = args.pop(0)
|
||||
if not (isnum(h) and isnum(k) and isnum(l)):
|
||||
raise TypeError()
|
||||
x, y, z = args.pop(0)
|
||||
if not (isnum(x) and isnum(y) and isnum(z)):
|
||||
raise TypeError()
|
||||
if len(args) == 1:
|
||||
tag = args.pop(0)
|
||||
if not isinstance(tag, str):
|
||||
raise TypeError()
|
||||
else:
|
||||
tag = None
|
||||
ubcalc.add_orientation(h, k, l, x, y ,z, tag,
|
||||
datetime.now())
|
||||
else:
|
||||
raise TypeError()
|
||||
|
||||
@command
|
||||
def editorient(num):
|
||||
"""editorient num -- interactively edit a crystal orientation.
|
||||
"""
|
||||
num = int(num)
|
||||
|
||||
# Get old reflection values
|
||||
[oldh, oldk, oldl], [oldx, oldy, oldz], oldTag, oldT = \
|
||||
ubcalc.get_orientation(num)
|
||||
del oldT # current time will be used.
|
||||
|
||||
h = promptForNumber('h', oldh)
|
||||
k = promptForNumber('k', oldk)
|
||||
l = promptForNumber('l', oldl)
|
||||
if None in (h, k, l):
|
||||
_handleInputError("h,k and l must all be numbers")
|
||||
x = promptForNumber('x', oldx)
|
||||
y = promptForNumber('y', oldy)
|
||||
z = promptForNumber('z', oldz)
|
||||
if None in (x, y, z):
|
||||
_handleInputError("x,y and z must all be numbers")
|
||||
tag = promptForInput("tag", oldTag)
|
||||
if tag == '':
|
||||
tag = None
|
||||
ubcalc.edit_orientation(num, h, k, l, x, y, z, tag,
|
||||
datetime.now())
|
||||
|
||||
@command
|
||||
def delorient(num):
|
||||
"""delorient num -- deletes a crystal orientation (numbered from 1)
|
||||
"""
|
||||
ubcalc.del_orientation(int(num))
|
||||
|
||||
@command
|
||||
def clearorient():
|
||||
"""clearorient -- deletes all the crystal orientations
|
||||
"""
|
||||
while ubcalc.get_number_orientations():
|
||||
ubcalc.del_orientation(1)
|
||||
|
||||
@command
|
||||
def swaporient(num1=None, num2=None):
|
||||
"""
|
||||
swaporient -- swaps first two crystal orientations used for calulating U matrix
|
||||
swaporient num1 num2 -- swaps two crystal orientations (numbered from 1)
|
||||
"""
|
||||
if num1 is None and num2 is None:
|
||||
ubcalc.swap_orientations(1, 2)
|
||||
elif isinstance(num1, int) and isinstance(num2, int):
|
||||
ubcalc.swap_orientations(num1, num2)
|
||||
else:
|
||||
raise TypeError()
|
||||
|
||||
|
||||
### UB calculations ###
|
||||
|
||||
@command
|
||||
def setu(U=None):
|
||||
"""setu {[[..][..][..]]} -- manually set U matrix
|
||||
"""
|
||||
if U is None:
|
||||
U = _promptFor3x3MatrixDefaultingToIdentity()
|
||||
if U is None:
|
||||
return # an error will have been printed or thrown
|
||||
if _is3x3TupleOrList(U) or _is3x3Matrix(U):
|
||||
ubcalc.set_U_manually(U)
|
||||
else:
|
||||
raise TypeError("U must be given as 3x3 list or tuple")
|
||||
|
||||
@command
|
||||
def setub(UB=None):
|
||||
"""setub {[[..][..][..]]} -- manually set UB matrix"""
|
||||
if UB is None:
|
||||
UB = _promptFor3x3MatrixDefaultingToIdentity()
|
||||
if UB is None:
|
||||
return # an error will have been printed or thrown
|
||||
if _is3x3TupleOrList(UB):
|
||||
ubcalc.set_UB_manually(UB)
|
||||
else:
|
||||
raise TypeError("UB must be given as 3x3 list or tuple")
|
||||
|
||||
def _promptFor3x3MatrixDefaultingToIdentity():
|
||||
estring = "Please enter a number, or press Return to accept default!"
|
||||
row1 = promptForList("row1", (1, 0, 0))
|
||||
if row1 is None:
|
||||
_handleInputError(estring)
|
||||
return None
|
||||
row2 = promptForList("row2", (0, 1, 0))
|
||||
if row2 is None:
|
||||
_handleInputError(estring)
|
||||
return None
|
||||
row3 = promptForList("row3", (0, 0, 1))
|
||||
if row3 is None:
|
||||
_handleInputError(estring)
|
||||
return None
|
||||
return [row1, row2, row3]
|
||||
|
||||
@command
|
||||
def calcub():
|
||||
"""calcub -- (re)calculate U matrix from ref1 and ref2.
|
||||
"""
|
||||
ubcalc.calculate_UB()
|
||||
|
||||
@command
|
||||
def trialub():
|
||||
"""trialub -- (re)calculate U matrix from ref1 only (check carefully).
|
||||
"""
|
||||
ubcalc.calculate_UB_from_primary_only()
|
||||
|
||||
@command
|
||||
def orientub():
|
||||
"""orientub -- (re)calculate U matrix from orient1 and orient2.
|
||||
"""
|
||||
ubcalc.calculate_UB_from_orientation()
|
||||
|
||||
|
||||
# This command requires the ubcalc
|
||||
|
||||
def checkub():
|
||||
"""checkub -- show calculated and entered hkl values for reflections.
|
||||
"""
|
||||
|
||||
s = "\n %7s %4s %4s %4s %6s %6s %6s TAG\n" % \
|
||||
('ENERGY', 'H', 'K', 'L', 'H_COMP', 'K_COMP', 'L_COMP')
|
||||
s = bold(s)
|
||||
nref = ubcalc.get_number_reflections()
|
||||
if not nref:
|
||||
s += "<<empty>>"
|
||||
for n in range(nref):
|
||||
hklguess, pos, energy, tag, _ = ubcalc.get_reflection(n + 1)
|
||||
wavelength = 12.39842 / energy
|
||||
hkl = settings.angles_to_hkl_function(pos.inRadians(), wavelength, ubcalc.UB)
|
||||
h, k, l = hkl
|
||||
if tag is None:
|
||||
tag = ""
|
||||
s += ("% 2d % 6.4f % 4.2f % 4.2f % 4.2f % 6.4f % 6.4f "
|
||||
"% 6.4f %6s\n" % (n + 1, energy, hklguess[0],
|
||||
hklguess[1], hklguess[2], h, k, l, tag))
|
||||
print s
|
||||
|
||||
|
||||
@command
|
||||
def addmiscut(*args):
|
||||
"""addmiscut angle {[x y z]} -- apply miscut to U matrix using a specified miscut angle in degrees and a rotation axis"""
|
||||
|
||||
if len(args) == 0:
|
||||
_handleInputError("Please specify a miscut angle in degrees "
|
||||
"and, optionally, a rotation axis (default: [0 1 0])")
|
||||
else:
|
||||
args=list(args)
|
||||
angle = args.pop(0)
|
||||
rad_angle = float(angle) * TORAD
|
||||
if len(args) == 0:
|
||||
xyz = None
|
||||
else:
|
||||
xyz = args.pop(0)
|
||||
ubcalc.set_miscut(xyz, rad_angle, True)
|
||||
|
||||
@command
|
||||
def setmiscut(*args):
|
||||
"""setmiscut angle {[x y z]} -- manually set U matrix using a specified miscut angle in degrees and a rotation axis (default: [0 1 0])"""
|
||||
|
||||
if len(args) == 0:
|
||||
_handleInputError("Please specify a miscut angle in degrees "
|
||||
"and, optionally, a rotation axis (default: [0 1 0])")
|
||||
else:
|
||||
args=list(args)
|
||||
angle = args.pop(0)
|
||||
rad_angle = float(angle) * TORAD
|
||||
if len(args) == 0:
|
||||
xyz = None
|
||||
else:
|
||||
xyz = args.pop(0)
|
||||
ubcalc.set_miscut(xyz, rad_angle, False)
|
||||
|
||||
commands_for_help = ['State',
|
||||
newub,
|
||||
loadub,
|
||||
lastub,
|
||||
listub,
|
||||
rmub,
|
||||
saveubas,
|
||||
ub,
|
||||
'Lattice',
|
||||
setlat,
|
||||
c2th,
|
||||
hklangle]
|
||||
|
||||
if ubcalc.include_reference:
|
||||
commands_for_help.extend([
|
||||
'Reference (surface)',
|
||||
setnphi,
|
||||
setnhkl])
|
||||
|
||||
if ubcalc.include_sigtau:
|
||||
commands_for_help.extend([
|
||||
'Surface',
|
||||
sigtau])
|
||||
|
||||
commands_for_help.extend([
|
||||
'Reflections',
|
||||
showref,
|
||||
addref,
|
||||
editref,
|
||||
delref,
|
||||
clearref,
|
||||
swapref,
|
||||
'Orientations',
|
||||
showorient,
|
||||
addorient,
|
||||
editorient,
|
||||
delorient,
|
||||
clearorient,
|
||||
swaporient,
|
||||
'UB matrix',
|
||||
checkub,
|
||||
setu,
|
||||
setub,
|
||||
calcub,
|
||||
orientub,
|
||||
trialub,
|
||||
refineub,
|
||||
addmiscut,
|
||||
setmiscut])
|
||||
|
||||
|
||||
|
||||
def _is3x3TupleOrList(m):
|
||||
if type(m) not in (list, tuple):
|
||||
return False
|
||||
if len(m) != 3:
|
||||
return False
|
||||
for mrow in m:
|
||||
if type(mrow) not in (list, tuple):
|
||||
return False
|
||||
if len(mrow) != 3:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def _is3x3Matrix(m):
|
||||
return isinstance(m, matrix) and tuple(m.shape) == (3, 3)
|
||||
|
||||
|
||||
def _handleInputError(msg):
|
||||
raise TypeError(msg)
|
||||
347
script/__Lib/diffcalc-2.1/diffcalc/util.py
Executable file
347
script/__Lib/diffcalc-2.1/diffcalc/util.py
Executable file
@@ -0,0 +1,347 @@
|
||||
###
|
||||
# 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 math import pi, acos, cos, sin
|
||||
from functools import wraps
|
||||
import textwrap
|
||||
|
||||
try:
|
||||
from gda.jython.commands.InputCommands import requestInput as raw_input
|
||||
GDA = True
|
||||
except ImportError:
|
||||
GDA = False
|
||||
pass # raw_input unavailable in gda
|
||||
try:
|
||||
from numpy import matrix
|
||||
from numpy.linalg import norm
|
||||
except ImportError:
|
||||
from numjy import matrix
|
||||
from numjy.linalg import norm
|
||||
|
||||
|
||||
# from http://physics.nist.gov/
|
||||
h_in_eV_per_s = 4.135667516E-15
|
||||
c = 299792458
|
||||
TWELVEISH = c * h_in_eV_per_s # 12.39842
|
||||
|
||||
|
||||
SMALL = 1e-10
|
||||
TORAD = pi / 180
|
||||
TODEG = 180 / pi
|
||||
|
||||
|
||||
COLOURISE_TERMINAL_OUTPUT = not GDA
|
||||
|
||||
def bold(s):
|
||||
if not COLOURISE_TERMINAL_OUTPUT:
|
||||
return s
|
||||
else:
|
||||
BOLD = '\033[1m'
|
||||
END = '\033[0m'
|
||||
return BOLD + s + END
|
||||
|
||||
|
||||
def x_rotation(th):
|
||||
return matrix(((1, 0, 0), (0, cos(th), -sin(th)), (0, sin(th), cos(th))))
|
||||
|
||||
|
||||
def y_rotation(th):
|
||||
return matrix(((cos(th), 0, sin(th)), (0, 1, 0), (-sin(th), 0, cos(th))))
|
||||
|
||||
|
||||
def z_rotation(th):
|
||||
return matrix(((cos(th), -sin(th), 0), (sin(th), cos(th), 0), (0, 0, 1)))
|
||||
|
||||
|
||||
def xyz_rotation(u, angle):
|
||||
u = [list(u), [0, 0, 0], [0, 0, 0]]
|
||||
u = matrix(u) / norm(matrix(u))
|
||||
e11=u[0,0]**2+(1-u[0,0]**2)*cos(angle)
|
||||
e12=u[0,0]*u[0,1]*(1-cos(angle))-u[0,2]*sin(angle)
|
||||
e13=u[0,0]*u[0,2]*(1-cos(angle))+u[0,1]*sin(angle)
|
||||
e21=u[0,0]*u[0,1]*(1-cos(angle))+u[0,2]*sin(angle)
|
||||
e22=u[0,1]**2+(1-u[0,1]**2)*cos(angle)
|
||||
e23=u[0,1]*u[0,2]*(1-cos(angle))-u[0,0]*sin(angle)
|
||||
e31=u[0,0]*u[0,2]*(1-cos(angle))-u[0,1]*sin(angle)
|
||||
e32=u[0,1]*u[0,2]*(1-cos(angle))+u[0,0]*sin(angle)
|
||||
e33=u[0,2]**2+(1-u[0,2]**2)*cos(angle)
|
||||
return matrix([[e11,e12,e13],[e21,e22,e23],[e31,e32,e33]])
|
||||
|
||||
|
||||
class DiffcalcException(Exception):
|
||||
"""Error caused by user misuse of diffraction calculator.
|
||||
"""
|
||||
def __str__(self):
|
||||
lines = []
|
||||
for msg_line in self.message.split('\n'):
|
||||
lines.append('* ' + msg_line)
|
||||
width = max(len(l) for l in lines)
|
||||
lines.insert(0, '\n\n' + '*' * width)
|
||||
lines.append('*' * width)
|
||||
return '\n'.join(lines)
|
||||
|
||||
|
||||
class AbstractPosition(object):
|
||||
|
||||
def inRadians(self):
|
||||
pos = self.clone()
|
||||
pos.changeToRadians()
|
||||
return pos
|
||||
|
||||
def inDegrees(self):
|
||||
pos = self.clone()
|
||||
pos.changeToDegrees()
|
||||
return pos
|
||||
|
||||
def changeToRadians(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
def changeToDegrees(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
def totuple(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
### Matrices
|
||||
|
||||
def cross3(x, y):
|
||||
"""z = cross3(x ,y) -- where x, y & z are 3*1 Jama matrices"""
|
||||
[[x1], [x2], [x3]] = x.tolist()
|
||||
[[y1], [y2], [y3]] = y.tolist()
|
||||
return matrix([[x2 * y3 - x3 * y2],
|
||||
[x3 * y1 - x1 * y3],
|
||||
[x1 * y2 - x2 * y1]])
|
||||
|
||||
|
||||
def dot3(x, y):
|
||||
"""z = dot3(x ,y) -- where x, y are 3*1 Jama matrices"""
|
||||
return x[0, 0] * y[0, 0] + x[1, 0] * y[1, 0] + x[2, 0] * y[2, 0]
|
||||
|
||||
|
||||
def angle_between_vectors(a, b):
|
||||
costheta = dot3(a * (1 / norm(a)), b * (1 / norm(b)))
|
||||
return acos(bound(costheta))
|
||||
|
||||
|
||||
## Math
|
||||
|
||||
def bound(x):
|
||||
"""
|
||||
moves x between -1 and 1. Used to correct for rounding errors which may
|
||||
have moved the sin or cosine of a value outside this range.
|
||||
"""
|
||||
if abs(x) > (1 + SMALL):
|
||||
raise AssertionError(
|
||||
"The value (%f) was unexpectedly too far outside -1 or 1 to "
|
||||
"safely bound. Please report this." % x)
|
||||
if x > 1:
|
||||
return 1
|
||||
if x < -1:
|
||||
return -1
|
||||
return x
|
||||
|
||||
|
||||
def matrixToString(m):
|
||||
''' str = matrixToString(m) --- displays a Jama matrix m as a string
|
||||
'''
|
||||
toReturn = ''
|
||||
for row in m.array:
|
||||
for el in row:
|
||||
toReturn += str(el) + '\t'
|
||||
toReturn += '\n'
|
||||
return toReturn
|
||||
|
||||
|
||||
def nearlyEqual(first, second, tolerance):
|
||||
if type(first) in (int, float):
|
||||
return abs(first - second) <= tolerance
|
||||
|
||||
if type(first) != type(matrix([[1]])):
|
||||
# lists
|
||||
first = matrix([list(first)])
|
||||
second = matrix([list(second)])
|
||||
diff = first - (second)
|
||||
return norm(diff) <= tolerance
|
||||
|
||||
|
||||
def radiansEquivilant(first, second, tolerance):
|
||||
if abs(first - second) <= tolerance:
|
||||
return True
|
||||
if abs((first - 2 * pi) - second) <= tolerance:
|
||||
return True
|
||||
if abs((first + 2 * pi) - second) <= tolerance:
|
||||
return True
|
||||
if abs(first - (second - 2 * pi)) <= tolerance:
|
||||
return True
|
||||
if abs(first - (second + 2 * pi)) <= tolerance:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def degreesEquivilant(first, second, tolerance):
|
||||
return radiansEquivilant(first * TORAD, second * TORAD, tolerance)
|
||||
|
||||
|
||||
def differ(first, second, tolerance):
|
||||
"""Returns error message if the norm of the difference between two arrays
|
||||
or numbers is greater than the given tolerance. Else returns False.
|
||||
"""
|
||||
# TODO: Fix spaghetti
|
||||
nonArray = False
|
||||
if type(first) in (int, float):
|
||||
if type(second) not in (int, float):
|
||||
raise TypeError(
|
||||
"If first is an int or float, so must second. "
|
||||
"first=%s, second=%s" & (repr(first), repr(second)))
|
||||
first = [first]
|
||||
second = [second]
|
||||
nonArray = True
|
||||
if not isinstance(first, matrix):
|
||||
first = matrix([list(first)])
|
||||
if not isinstance(second, matrix):
|
||||
second = matrix([list(second)])
|
||||
diff = first - second
|
||||
if norm(diff) >= tolerance:
|
||||
if nonArray:
|
||||
return ('%s!=%s' %
|
||||
(repr(first.tolist()[0][0]), repr(second.tolist()[0][0])))
|
||||
return ('%s!=%s' %
|
||||
(repr(tuple(first.tolist()[0])),
|
||||
repr(tuple(second.tolist()[0]))))
|
||||
return False
|
||||
|
||||
|
||||
### user input
|
||||
|
||||
def getInputWithDefault(prompt, default=""):
|
||||
"""
|
||||
Prompts user for input and returns if possible a float or a list of floats,
|
||||
or if failing this a string. default may be a number, array of numbers,
|
||||
or string.
|
||||
"""
|
||||
if default is not "":
|
||||
# Generate default string
|
||||
if type(default) in (list, tuple):
|
||||
defaultString = ""
|
||||
for val in default:
|
||||
defaultString += str(val) + ' '
|
||||
defaultString = defaultString.strip()
|
||||
else:
|
||||
defaultString = str(default)
|
||||
prompt = str(prompt) + '[' + defaultString + ']: '
|
||||
else:
|
||||
prompt = str(prompt) + ': '
|
||||
|
||||
rawresult = raw_input(prompt)
|
||||
|
||||
# Return default if no input provided
|
||||
if rawresult == "":
|
||||
return default
|
||||
|
||||
# Try to process result into list of numbers
|
||||
try:
|
||||
result = []
|
||||
for val in rawresult.split():
|
||||
result.append(float(val))
|
||||
except ValueError:
|
||||
# return a string
|
||||
return rawresult
|
||||
if len(result) == 1:
|
||||
result = result[0]
|
||||
return result
|
||||
|
||||
|
||||
class MockRawInput(object):
|
||||
def __init__(self, toReturnList):
|
||||
if type(toReturnList) != list:
|
||||
toReturnList = [toReturnList]
|
||||
self.toReturnList = toReturnList
|
||||
|
||||
def __call__(self, prompt):
|
||||
toReturn = self.toReturnList.pop(0)
|
||||
if type(toReturn) != str:
|
||||
raise TypeError
|
||||
print prompt + toReturn
|
||||
return toReturn
|
||||
|
||||
|
||||
def getMessageFromException(e):
|
||||
try: # Jython
|
||||
return e.args[0]
|
||||
except:
|
||||
try: # Python
|
||||
return e.message
|
||||
except:
|
||||
# Java
|
||||
return e.args[0]
|
||||
|
||||
|
||||
def promptForNumber(prompt, default=""):
|
||||
val = getInputWithDefault(prompt, default)
|
||||
if type(val) not in (float, int):
|
||||
return None
|
||||
return val
|
||||
|
||||
|
||||
def promptForList(prompt, default=""):
|
||||
val = getInputWithDefault(prompt, default)
|
||||
if type(val) not in (list, tuple):
|
||||
return None
|
||||
return val
|
||||
|
||||
|
||||
def isnum(o):
|
||||
return isinstance(o, (int, float))
|
||||
|
||||
|
||||
def allnum(l):
|
||||
return not [o for o in l if not isnum(o)]
|
||||
|
||||
|
||||
DEBUG = False
|
||||
|
||||
|
||||
|
||||
def command(f):
|
||||
"""A decorator to wrap a command method or function.
|
||||
|
||||
Calls to the decorated method or function are wrapped by call_command.
|
||||
"""
|
||||
# TODO: remove one level of stack trace by not using wraps
|
||||
@wraps(f)
|
||||
def wrapper(*args, **kwds):
|
||||
return call_command(f, args)
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
def call_command(f, args):
|
||||
|
||||
if DEBUG:
|
||||
return f(*args)
|
||||
try:
|
||||
return f(*args)
|
||||
except TypeError, e:
|
||||
# NOTE: TypeErrors resulting from bugs in the core code will be
|
||||
# erroneously caught here! TODO: check depth of TypeError stack
|
||||
raise TypeError(e.message + '\n\nUSAGE:\n' + f.__doc__)
|
||||
except DiffcalcException, e:
|
||||
# TODO: log and create a new one to shorten stack trace for user
|
||||
raise DiffcalcException(e.message)
|
||||
Reference in New Issue
Block a user