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 _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 _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 _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 _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 _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 _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 _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)