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 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 repr_lines(self, ub_calculated, WIDTH=9): 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: 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) rotation_axis = cross3(matrix('0; 0; 1'), self.n_phi) if abs(norm(rotation_axis)) < SMALL: lines.append(" miscut:".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(" miscut:") 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: 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
def __str__(self): lines = [] if self._n_phi_configured is not None: lines.append("configured n_phi: " + self._pretty_vector(self._n_phi_configured)) elif self._n_hkl_configured is not None: lines.append("configured n_hkl: " + self._pretty_vector(self._n_hkl_configured)) else: raise AssertionError("Neither a manual n_phi nor n_hkl is configured") lines.append(" n_phi: " + self._pretty_vector(self.n_phi)) lines.append(" n_hkl: " + self._pretty_vector(self.n_hkl)) lines.append("") rotation_axis = cross3(matrix('0; 0; 1'), self.n_phi) if abs(norm(rotation_axis)) < SMALL: lines.append("no miscut") 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) uvw = rotation_axis.T.tolist()[0] lines.append("miscut angle : %.5f deg (phi axis to reference)" % (rotation_angle * TODEG)) u_repr = (', '.join(['% .5f' % el for el in uvw])) lines.append("miscut direction: [%s] (in phi frame)" % u_repr) lines.append("") lines.append("To change: set n_hkl_configured or n_phi_configured property.") return '\n'.join(lines)
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
def getPosition(self): 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 = self._diffcalc._ub.ubcalc.calc_offset_for_hkl(hkl_pos, nref_hkl)[0] _ubm = self._diffcalc._ub.ubcalc._get_UB() qvec = _ubm * matrix(hkl_pos).T sc = sqrt(dot3(qvec, qvec)) * self._diffcalc._ub.ubcalc.get_hkl_plane_distance(nref_hkl) / (2.*pi) res = sc * cos(pol) return res
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): """ 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 str_lines_u_angle_and_axis(self): lines = [] fmt = "% 9.5f % 9.5f % 9.5f" y = matrix('0; 0; 1') rotation_axis = cross3(y, self.U * y) if abs(norm(rotation_axis)) < SMALL: lines.append(" U 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(" angle:".ljust(WIDTH) + "% 9.5f" % (rotation_angle * TODEG)) lines.append(" axis:".ljust(WIDTH) + fmt % tuple((rotation_axis.T).tolist()[0])) return lines
def _fit_ub_matrix_uncon(self, *args): if args is None: raise DiffcalcException( "Please specify list of reference reflection indices.") if len(args) < 3: raise DiffcalcException( "Need at least 3 reference reflections to fit UB matrix.") x = [] y = [] for idx in args: try: hkl_vals, pos, en, _, _ = self.get_reflection(idx) except IndexError: raise DiffcalcException( "Cannot read reflection data for index %s" % str(idx)) pos.changeToRadians() x.append(hkl_vals) wl = 12.3984 / en y_tmp = self._strategy.calculate_q_phi(pos) * 2. * pi / wl y.append(y_tmp.T.tolist()[0]) xm = matrix(x) ym = matrix(y) b = (xm.T * xm).I * xm.T * ym b1, b2, b3 = matrix(b.tolist()[0]), matrix(b.tolist()[1]), matrix( b.tolist()[2]) e1 = b1 / norm(b1) e2 = b2 - e1 * dot3(b2.T, e1.T) e2 = e2 / norm(e2) e3 = b3 - e1 * dot3(b3.T, e1.T) - e2 * dot3(b3.T, e2.T) e3 = e3 / norm(e3) new_umatrix = matrix(e1.tolist() + e2.tolist() + e3.tolist()).T V = dot3(cross3(b1.T, b2.T), b3.T) a1 = cross3(b2.T, b3.T) * 2 * pi / V a2 = cross3(b3.T, b1.T) * 2 * pi / V a3 = cross3(b1.T, b2.T) * 2 * pi / V ax, bx, cx = norm(a1), norm(a2), norm(a3) alpha = acos(dot3(a2, a3) / (bx * cx)) * TODEG beta = acos(dot3(a1, a3) / (ax * cx)) * TODEG gamma = acos(dot3(a1, a2) / (ax * bx)) * TODEG lattice_name = self._state.crystal.getLattice()[0] return new_umatrix, (lattice_name, ax, bx, cx, alpha, beta, gamma)
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 calculate_UB_from_primary_only(self, idx=1): """ Calculate orientation matrix with the shortest absolute angle change. Default: use first reflection. """ # Algorithm from http://www.j3d.org/matrix_faq/matrfaq_latest.html # Get hkl and angle values for the first two reflections 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.get_reflection(idx) 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._U = matrix(m) self._UB = self._U * B self._state.configure_calc_type(manual_U=self._U, or0=idx) self.save()
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()