This commit is contained in:
2019-03-20 13:52:00 +01:00
parent 3084fe0510
commit 5db0f78aee
910 changed files with 191152 additions and 322 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,370 @@
###
# 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(['mu', 'eta']) or
self.sample == {'mu': 0, 'chi': pi / 2})
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

View File

@@ -0,0 +1,178 @@
###
# 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
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 ):
self.name = name
self.fixed_constraints = fixed_constraints
def physical_angles_to_internal_position(self, physicalAngles):
raise NotImplementedError()
def internal_position_to_physical_angles(self, physicalAngles):
raise NotImplementedError()
def create_position(self, *args):
return YouPosition(*args)
#==============================================================================
#==============================================================================
# Geometry plugins for use with 'You' hkl calculation engine
#==============================================================================
#==============================================================================
class SixCircle(YouGeometry):
def __init__(self):
YouGeometry.__init__(self, 'sixc', {})
def physical_angles_to_internal_position(self, physical_angle_tuple):
# mu, delta, nu, eta, chi, phi
return YouPosition(*physical_angle_tuple)
def internal_position_to_physical_angles(self, internal_position):
return internal_position.totuple()
class FourCircle(YouGeometry):
"""For a diffractometer with angles:
delta, eta, chi, phi
"""
def __init__(self):
YouGeometry.__init__(self, 'fourc', {'mu': 0, NUNAME: 0})
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)
def internal_position_to_physical_angles(self, internal_position):
_, delta, _, eta, chi, phi = internal_position.totuple()
return delta, eta, chi, phi
class FiveCircle(YouGeometry):
"""For a diffractometer with angles:
delta, nu, eta, chi, phi
"""
def __init__(self):
YouGeometry.__init__(self, 'fivec', {'mu': 0})
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)
def internal_position_to_physical_angles(self, internal_position):
_, delta, nu, eta, chi, phi = internal_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)
class YouPosition(AbstractPosition):
def __init__(self, mu=None, delta=None, nu=None, eta=None, chi=None,
phi=None):
self.mu = mu
self.delta = delta
self.nu = nu
self.eta = eta
self.chi = chi
self.phi = phi
def clone(self):
return YouPosition(self.mu, self.delta, self.nu, self.eta, self.chi,
self.phi)
def changeToRadians(self):
self.mu *= TORAD
self.delta *= TORAD
self.nu *= TORAD
self.eta *= TORAD
self.chi *= TORAD
self.phi *= TORAD
def changeToDegrees(self):
self.mu *= TODEG
self.delta *= TODEG
self.nu *= TODEG
self.eta *= TODEG
self.chi *= TODEG
self.phi *= TODEG
def totuple(self):
return (self.mu, self.delta, self.nu, self.eta, self.chi, self.phi)
def __str__(self):
return ("YouPosition(mu %r delta: %r nu: %r eta: %r chi: %r phi: %r)"
% self.totuple())
def __eq__(self, other):
return self.totuple() == other.totuple()

View File

@@ -0,0 +1,178 @@
###
# 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
mu & eta
chi=90 & mu=0 (2.5 of 6)
2 x samp and 1 x det: 0 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:
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
]