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

293 lines
9.9 KiB
Python
Executable File

###
# Copyright 2008-2011 Diamond Light Source Ltd.
# This file is part of Diffcalc.
#
# Diffcalc is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Diffcalc is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Diffcalc. If not, see <http://www.gnu.org/licenses/>.
###
from 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