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 _calc_remaining_reference_angles(self, name, value, theta, tau): """Return psi, alpha and beta given one of a_eq_b, alpha, beta or psi """ if sin(tau) == 0: raise DiffcalcException( 'The scattering vector (Q) and the reference vector (n) are\n' 'parallel. The constraint %s could not be used to determine a\n' 'unique azimuthal rotation about the Q vector.' % name) if cos(theta) == 0: raise DiffcalcException( 'The constraint %s could not be used to determine a unique ' 'azimuth (psi) as the scattering vector (Q) and xray beam are ' "are parallel and don't form a unique'reference plane" % name) if name == 'psi': psi = value # Equation 26 for alpha sin_alpha = (cos(tau) * sin(theta) - cos(theta) * sin(tau) * cos(psi)) if abs(sin_alpha) > 1 + SMALL: raise DiffcalcException(UNREACHABLE_MSG % (name, value * TODEG)) alpha = asin(bound(sin_alpha)) # Equation 27 for beta sin_beta = cos(tau) * sin(theta) + cos(theta) * sin(tau) * cos(psi) if abs(sin_beta) > 1 + SMALL: raise DiffcalcException(UNREACHABLE_MSG % (name, value * TODEG)) beta = asin(bound(sin_beta)) elif name == 'a_eq_b': alpha = beta = asin(bound(cos(tau) * sin(theta))) # (24) elif name == 'alpha': alpha = value # (24) sin_beta = 2 * sin(theta) * cos(tau) - sin(alpha) if abs(sin_beta) > 1 + SMALL: raise DiffcalcException(UNREACHABLE_MSG % (name, value * TODEG)) beta = asin(sin_beta) elif name == 'beta': beta = value sin_alpha = 2 * sin(theta) * cos(tau) - sin(beta) # (24) if abs(sin_alpha) > 1 + SMALL: raise DiffcalcException(UNREACHABLE_MSG % (name, value * TODEG)) alpha = asin(sin_alpha) if name != 'psi': cos_psi = ((cos(tau) * sin(theta) - sin(alpha)) / # (28) (sin(tau) * cos(theta))) if abs(cos_psi) > (1 + SMALL): hint = "\nAlpha may be too low?" if name == 'alpha' else ""; raise DiffcalcException(UNREACHABLE_MSG % (name, value * TODEG) + hint) # TODO: If set, alpha is probably too low psi = acos(bound(cos_psi)) return psi, alpha, beta
def _calc_remaining_detector_angles(self, constraint_name, constraint_value, theta): """Return delta, nu and qaz given one detector angle """ # (section 5.1) # Find qaz using various derivations of 17 and 18 if constraint_name == 'delta': delta = constraint_value sin_2theta = sin(2 * theta) if is_small(sin_2theta): raise DiffcalcException( 'No meaningful scattering vector (Q) can be found when ' 'theta is so small (%.4f).' % theta * TODEG) qaz = asin(bound(sin(delta) / sin_2theta)) # (17 & 18) elif constraint_name == NUNAME: nu = constraint_value # cos_delta = cos(2*theta) / cos(nu)#<--fails when nu = 90 # delta = acos(bound(cos_delta)) # tan sin_2theta = sin(2 * theta) if is_small(sin_2theta): raise DiffcalcException( 'No meaningful scattering vector (Q) can be found when ' 'theta is so small (%.4f).' % theta * TODEG) cos_qaz = tan(nu) / tan(2 * theta) if abs(cos_qaz) > 1 + SMALL: raise DiffcalcException( 'The specified %s=%.4f is greater than the 2theta (%.4f)' % (NUNAME, nu, theta)) qaz = acos(bound(cos_qaz)) elif constraint_name == 'qaz': qaz = constraint_value else: raise ValueError( constraint_name + ' is not an explicit detector angle ' '(naz cannot be handled here)') if constraint_name != NUNAME: nu = atan2(sin(2 * theta) * cos(qaz), cos(2 * theta)) if constraint_name != 'delta': cos_qaz = cos(qaz) if not is_small(cos_qaz): # TODO: switch methods at 45 deg? delta = atan2(sin(qaz) * sin(nu), cos_qaz) else: # qaz is close to 90 (a common place for it) delta = sign(qaz) * acos(bound(cos(2 * theta) / cos(nu))) return delta, nu, qaz
def _anglesToVirtualAngles(self, pos, _wavelength): """Calculate pseudo-angles in radians from position in radians. Return theta, qaz, alpha, naz, tau, psi and beta in a dictionary. """ # depends on surface normal n_lab. mu, delta, nu, eta, chi, phi = pos.totuple() theta, qaz = _theta_and_qaz_from_detector_angles(delta, nu) # (19) [MU, _, _, ETA, CHI, PHI] = create_you_matrices(mu, delta, nu, eta, chi, phi) Z = MU * ETA * CHI * PHI n_lab = Z * self._get_n_phi() alpha = asin(bound((-n_lab[1, 0]))) naz = atan2(n_lab[0, 0], n_lab[2, 0]) # (20) cos_tau = cos(alpha) * cos(theta) * cos(naz - qaz) + \ sin(alpha) * sin(theta) tau = acos(bound(cos_tau)) # (23) # Compute Tau using the dot product directly (THIS ALSO WORKS) # q_lab = ( (NU * DELTA - I ) * matrix([[0],[1],[0]]) # norm = norm(q_lab) # q_lab = matrix([[1],[0],[0]]) if norm == 0 else q_lab * (1/norm) # tau_from_dot_product = acos(bound(dot3(q_lab, n_lab))) sin_beta = 2 * sin(theta) * cos(tau) - sin(alpha) beta = asin(bound(sin_beta)) # (24) sin_tau = sin(tau) cos_theta = cos(theta) if sin_tau == 0: psi = float('Nan') print ('WARNING: Diffcalc could not calculate a unique azimuth ' '(psi) as the scattering vector (Q) and the reference ' 'vector (n) are parallel') elif cos_theta == 0: psi = float('Nan') print ('WARNING: Diffcalc could not calculate a unique azimuth ' '(psi) because the scattering vector (Q) and xray beam are ' " are parallel and don't form a unique reference plane") else: cos_psi = ((cos(tau) * sin(theta) - sin(alpha)) / (sin_tau * cos_theta)) psi = acos(bound(cos_psi)) # (28) return {'theta': theta, 'qaz': qaz, 'alpha': alpha, 'naz': naz, 'tau': tau, 'psi': psi, 'beta': beta}
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 asynchronousMoveTo(self, newpos): pos = self.diffhw.getPosition() # a tuple (hkl_pos , _) = self._diffcalc.angles_to_hkl(pos) nref_hkl = [i[0] for i in self._diffcalc._ub.ubcalc.n_hkl.tolist()] pol, az_nref, sc = self._diffcalc._ub.ubcalc.calc_offset_for_hkl(hkl_pos, nref_hkl) if pol < SMALL: az_nref = 0 sc_nref_hkl = [sc * v for v in nref_hkl] _ubm = self._diffcalc._ub.ubcalc._get_UB() qvec = _ubm * matrix(hkl_pos).T qvec_rlu = sqrt(dot3(qvec, qvec)) * self._diffcalc._ub.ubcalc.get_hkl_plane_distance(nref_hkl) / (2.*pi) try: newpol = acos(bound(newpos / qvec_rlu)) except AssertionError: raise DiffcalcException("Scattering vector projection value of %.5f r.l.u. unreachable." % newpos) try: hkl_offset = self._diffcalc._ub.ubcalc.calc_hkl_offset(*sc_nref_hkl, pol=newpol, az=az_nref) (pos, _) = self._diffcalc.hkl_to_angles(*hkl_offset) except DiffcalcException as e: if DEBUG: raise else: raise DiffcalcException(e.message) self.diffhw.asynchronousMoveTo(pos)
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
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 _theta_and_qaz_from_detector_angles(delta, nu): # Equation 19: cos_2theta = cos(delta) * cos(nu) theta = acos(bound(cos_2theta)) / 2. qaz = atan2(tan(delta), sin(nu)) # qaz flips downward if delta > 90, so flip it back (kludge) if delta > pi / 2: qaz *= -1 return theta, qaz
def _calc_angle_between_naz_and_qaz(theta, alpha, tau): # Equation 30: top = cos(tau) - sin(alpha) * sin(theta) bottom = cos(alpha) * cos(theta) if is_small(bottom): if is_small(cos(alpha)): raise ValueError('cos(alpha) is too small') if is_small(cos(theta)): raise ValueError('cos(theta) is too small') return acos(bound(top / bottom))
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
def get_miscut_angle_axis(self, ubmatrix): y = self._get_surf_nphi() l = ubmatrix * y rotation_axis = self._tobj.transform(cross3(y, l), True) if abs(norm(rotation_axis)) < SMALL: rotation_axis = matrix('0; 0; 0') rotation_angle = 0 else: rotation_axis = rotation_axis * (1 / norm(rotation_axis)) cos_rotation_angle = bound(dot3(y, l) / norm(l)) rotation_angle = acos(cos_rotation_angle) * TODEG return rotation_angle, rotation_axis
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 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
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
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 _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 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]]) axis = self._tobj.transform(cross3(q_vec, hkl_nphi), True) 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]
def simulateMoveTo(self, newpos): pos = self.diffhw.getPosition() # a tuple (hkl_pos , _) = self._diffcalc.angles_to_hkl(pos) nref_hkl = [i[0] for i in self._diffcalc._ub.ubcalc.n_hkl.tolist()] pol, az_nref, sc = self._diffcalc._ub.ubcalc.calc_offset_for_hkl(hkl_pos, nref_hkl) if pol < SMALL: az_nref = 0 sc_nref_hkl = [sc * v for v in nref_hkl] _ubm = self._diffcalc._ub.ubcalc._get_UB() qvec = _ubm * matrix(hkl_pos).T qvec_rlu = sqrt(dot3(qvec, qvec)) * self._diffcalc._ub.ubcalc.get_hkl_plane_distance(nref_hkl) / (2.*pi) try: newpol = acos(bound(newpos / qvec_rlu)) except AssertionError: raise DiffcalcException("Scattering vector projection value of %.5f r.l.u. unreachable." % newpos) try: hkl_offset = self._diffcalc._ub.ubcalc.calc_hkl_offset(*sc_nref_hkl, pol=newpol, az=az_nref) (pos, params) = self._diffcalc.hkl_to_angles(*hkl_offset) except DiffcalcException as e: if DEBUG: raise else: raise DiffcalcException(e.message) width = max(len(k) for k in (params.keys() + list(self.diffhw.getInputNames()))) fmt = ' %' + str(width) + 's : % 9.4f' lines = ['simulated hkl: %9.4f %.4f %.4f' % (hkl_offset[0],hkl_offset[1],hkl_offset[2]), 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 _mu_and_qaz_from_eta_chi_phi(eta, chi, phi, theta, h_phi): h_phi_norm = normalised(h_phi) # (68,69) h1, h2, h3 = h_phi_norm[0, 0], h_phi_norm[1, 0], h_phi_norm[2, 0] a = sin(chi) * h2 * sin(phi) + sin(chi) * h1 * cos(phi) - cos(chi) * h3 b = (- cos(chi) * sin(eta) * h2 * sin(phi) - cos(eta) * h1 * sin(phi) + cos(eta) * h2 * cos(phi) - cos(chi) * sin(eta) * h1 * cos(phi) - sin(chi) * sin(eta) * h3) c = -sin(theta) sin_bit = bound(c / sqrt(a * a + b * b)) mu1 = asin(sin_bit) - atan2(b, a) mu2 = pi - asin(sin_bit) - atan2(b, a) mu1 = cut_at_minus_pi(mu1) mu2 = cut_at_minus_pi(mu2) # TODO: This special case should be *removed* when the gernal case has shown # toencompass it. It exists as fallback for a particular i16 experiment in # May 2013 --RobW. # if eta == chi == 0: # logger.debug("Testing against simplified equations for eta == chi == 0") # a = - h3 # b = - h1 * sin(phi) + h2 * cos(phi) # sin_bit = bound(c / sqrt(a * a + b * b)) # mu_simplified = pi - asin(sin_bit) - atan2(b, a) # mu_simplified = cut_at_minus_pi(mu_simplified) # if not ne(mu_simplified, mu): # raise AssertionError("mu_simplified != mu , %f!=%f" % (mu_simplified, mu)) [MU, _, _, ETA, CHI, PHI] = create_you_matrices(mu1, None, None, eta, chi, phi) h_lab = MU * ETA * CHI * PHI * h_phi # (11) qaz1 = atan2(h_lab[0, 0] , h_lab[2, 0]) [MU, _, _, ETA, CHI, PHI] = create_you_matrices(mu2, None, None, eta, chi, phi) h_lab = MU * ETA * CHI * PHI * h_phi # (11) qaz2 = atan2(h_lab[0, 0] , h_lab[2, 0]) return (mu1, qaz1) , (mu2, qaz2)
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
def _theta_and_qaz_from_detector_angles(delta, nu): # Equation 19: cos_2theta = cos(delta) * cos(nu) theta = acos(bound(cos_2theta)) / 2. qaz = atan2(tan(delta), sin(nu)) return theta, qaz
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