def trans_point(p, theta=0., scale=1.): """ simple in-plane rotation of points """ M = num.array([[cosd(theta), -sind(theta)], [sind(theta), cosd(theta)]]) pp = scale * num.dot(M, p) return pp
def calc_D(nu=0.0, delta=0.0): """ Calculate the detector rotation matrix. Angles are in degrees Notes: ------ D is the matrix that rotates a vector defined in the phi frame ie a vector defined with all angles zero => vphi. After rotation the lab frame coordinates of the vector => vm are given by: vm = D*vphi For example |0| kr_phi = (2pi/lam) |1| |0| Since kr is defined by the detector rotation, the lab frame coordinates of the kr vector after detector rotation are kr_m = D*kr_phi """ D1 = num.array([[cosd(delta), sind(delta), 0.], [-sind(delta), cosd(delta), 0.], [0., 0., 1.]]) D2 = num.array([[1., 0., 0.], [0., cosd(nu), -sind(nu)], [0., sind(nu), cosd(nu)]]) D = num.dot(D2, D1) return (D)
def calc_D(nu=0.0,delta=0.0): """ Calculate the detector rotation matrix. Angles are in degrees Notes: ------ D is the matrix that rotates a vector defined in the phi frame ie a vector defined with all angles zero => vphi. After rotation the lab frame coordinates of the vector => vm are given by: vm = D*vphi For example |0| kr_phi = (2pi/lam) |1| |0| Since kr is defined by the detector rotation, the lab frame coordinates of the kr vector after detector rotation are kr_m = D*kr_phi """ D1 = num.array([[cosd(delta), sind(delta), 0.], [-sind(delta), cosd(delta), 0.], [ 0. , 0. , 1.]]) D2 = num.array([[ 1., 0. , 0. ], [ 0., cosd(nu), -sind(nu)], [ 0., sind(nu), cosd(nu)]]) D = num.dot(D2,D1) return (D)
def _calc_omega(self): """ calc omega, this is the angle between Q and the plane which is perpendicular to the axis of the chi circle. Notes: ------ For nu=mu=0 this is the same as the four circle def: omega = 0.5*TTH - TH, where TTH is the detector motor (=del) and TH is the sample circle (=eta). Therefore, for mu=nu=0 and del=0.5*eta, omega = 0, which means that Q is in the plane perpendicular to the chi axis. Note check sign of results??? """ phi=self.angles['phi'] chi=self.angles['chi'] eta=self.angles['eta'] mu=self.angles['mu'] H = num.array([[ cosd(eta), sind(eta), 0.], [-sind(eta), cosd(eta), 0.], [ 0., 0., 1.]],float) M = num.array([[ 1., 0., 0. ], [ 0., cosd(mu), -sind(mu)], [ 0., sind(mu), cosd(mu)]],float) # check the mult order here!!!! # T = num.dot(H.transpose(),M.transpose()) T = num.dot(M.transpose(),H.transpose()) Qpp = num.dot(T,self.Q) #omega = -1.*cartesian_angle([Qpp[0], 0, Qpp[2]],Qpp) omega = cartesian_angle([Qpp[0], 0, Qpp[2]],Qpp) self.pangles['omega'] = omega
def _calc_omega(self): """ calc omega, this is the angle between Q and the plane which is perpendicular to the axis of the chi circle. Notes: ------ For nu=mu=0 this is the same as the four circle def: omega = 0.5*TTH - TH, where TTH is the detector motor (=del) and TH is the sample circle (=eta). Therefore, for mu=nu=0 and del=0.5*eta, omega = 0, which means that Q is in the plane perpendicular to the chi axis. Note check sign of results??? """ phi = self.angles['phi'] chi = self.angles['chi'] eta = self.angles['eta'] mu = self.angles['mu'] H = num.array([[cosd(eta), sind(eta), 0.], [-sind(eta), cosd(eta), 0.], [0., 0., 1.]], float) M = num.array([[1., 0., 0.], [0., cosd(mu), -sind(mu)], [0., sind(mu), cosd(mu)]], float) # check the mult order here!!!! # T = num.dot(H.transpose(),M.transpose()) T = num.dot(M.transpose(), H.transpose()) Qpp = num.dot(T, self.Q) #omega = -1.*cartesian_angle([Qpp[0], 0, Qpp[2]],Qpp) omega = cartesian_angle([Qpp[0], 0, Qpp[2]], Qpp) self.pangles['omega'] = omega
def trans_point(p, theta=0.0, scale=1.0): """ simple in-plane rotation of points """ M = num.array([[cosd(theta), -sind(theta)], [sind(theta), cosd(theta)]]) pp = scale * num.dot(M, p) return pp
def calc_kvecs(nu=0.0,delta=0.0,lam=1.0): """ Calculate psic ki, kr in the cartesian lab frame. nu and delta are in degrees, lam is in angstroms """ k = (2.* num.pi / lam) ki = k * num.array([0.,1.,0.],dtype=float) kr = k * num.array([sind(delta), cosd(nu)*cosd(delta), sind(nu)*cosd(delta)],dtype=float) return (ki,kr)
def calc_kvecs(nu=0.0, delta=0.0, lam=1.0): """ Calculate psic ki, kr in the cartesian lab frame. nu and delta are in degrees, lam is in angstroms """ k = (2. * num.pi / lam) ki = k * num.array([0., 1., 0.], dtype=float) kr = k * num.array( [sind(delta), cosd(nu) * cosd(delta), sind(nu) * cosd(delta)], dtype=float) return (ki, kr)
def calc_n(self, fchi=0.0, fphi=0.0): """ Calculate the hkl values of a reference vector given the chi and phi settings that align this vector with the eta axis. Notes: ------ This algorith is used, for example, to compute the surface normal from the (flat) chi and (flat) phi angles that leave an optical reflection in a fixed position during an eta rotation Note the vector is normalized such that the largest component is unity, ie n_hkl isn't a unit vector! """ # polar angles sig_az = -fchi tau_az = -fphi # this block converts the chi and phi values to correctly # defined polar coordinates, ie 0<= sig_az <= 180deg ..... if sig_az < 0.: sig_az = -1. * sig_az if tau_az < 0.: tau_az = 180. + tau_az elif tau_az > 0.: tau_az = tau_az - 180. # n in the unrotated lab frame (ie phi frame): # this is a unit vector! n_phi = num.array([ sind(sig_az) * cosd(tau_az), -sind(sig_az) * sind(tau_az), cosd(sig_az) ]) # n in HKL n_hkl = num.dot(num.linalg.inv(self.UB), n_phi) n_hkl = n_hkl / num.max(num.abs(n_hkl)) # note if l-component is negative, then its # pointing into the surface (ie assume positive L # is the positive direction away from the surface) # careful here!! if n_hkl[2] < 0.: n_hkl = -1. * n_hkl # set n which triggers recalc of # all the psuedo angles self.set_n(n_hkl)
def calc_Z(phi=0.0,chi=0.0,eta=0.0,mu=0.0): """ Calculate the psic goniometer rotation matrix Z for the 4 sample angles. Angles are in degrees Notes: ------ Z is the matrix that rotates a vector defined in the phi frame ie a vector defined with all angles zero => vphi. After rotation the lab frame coordinates of the vector => vm are given by: vm = Z*vphi """ P = num.array([[ cosd(phi), sind(phi), 0.], [-sind(phi), cosd(phi), 0.], [ 0., 0., 1.]],float) X = num.array([[ cosd(chi), 0., sind(chi)], [ 0., 1., 0.], [-sind(chi), 0., cosd(chi)]],float) H = num.array([[ cosd(eta), sind(eta), 0.], [-sind(eta), cosd(eta), 0.], [ 0., 0., 1.]],float) M = num.array([[ 1., 0., 0. ], [ 0., cosd(mu), -sind(mu)], [ 0., sind(mu), cosd(mu)]],float) Z = num.dot(num.dot(num.dot(M,H),X),P) return Z
def calc_n(self,fchi=0.0,fphi=0.0): """ Calculate the hkl values of a reference vector given the chi and phi settings that align this vector with the eta axis. Notes: ------ This algorith is used, for example, to compute the surface normal from the (flat) chi and (flat) phi angles that leave an optical reflection in a fixed position during an eta rotation Note the vector is normalized such that the largest component is unity, ie n_hkl isn't a unit vector! """ # polar angles sig_az = -fchi tau_az = -fphi # this block converts the chi and phi values to correctly # defined polar coordinates, ie 0<= sig_az <= 180deg ..... if sig_az < 0.: sig_az = -1.*sig_az if tau_az < 0.: tau_az = 180. + tau_az elif tau_az > 0.: tau_az = tau_az - 180. # n in the unrotated lab frame (ie phi frame): # this is a unit vector! n_phi = num.array([ sind(sig_az)*cosd(tau_az), -sind(sig_az)*sind(tau_az), cosd(sig_az) ]) # n in HKL n_hkl = num.dot(num.linalg.inv(self.UB),n_phi) n_hkl = n_hkl/ num.max(num.abs(n_hkl)) # note if l-component is negative, then its # pointing into the surface (ie assume positive L # is the positive direction away from the surface) # careful here!! if n_hkl[2] < 0.: n_hkl = -1.*n_hkl # set n which triggers recalc of # all the psuedo angles self.set_n(n_hkl)
def cartesian(self, shift=[0., 0., 0.]): """ Calculates a cartesian basis using: Va = a' is parallel to a Vb = b' is perpendicular to a' and in the a/b plane Vc = c' is perpendicular to the a'/c' plane A shift vector may be specified to shift the origin of the cartesian lattice relative to the original lattice origin (specify shift in fractional coordinates of the original lattice) """ (a, b, c, alp, bet, gam) = self.lattice.cell() (ar, br, cr, alpr, betr, gamr) = self.lattice.rcell() Va = [1. / a, 0., 0.] Vb = [-1. / (a * tand(gam)), 1. / (b * sind(gam)), 0.] Vc = [ar * cosd(betr), br * cosd(alpr), cr] self.basis(Va=Va, Vb=Vb, Vc=Vc, shift=shift)
def cartesian(self,shift=[0.,0.,0.]): """ Calculates a cartesian basis using: Va = a' is parallel to a Vb = b' is perpendicular to a' and in the a/b plane Vc = c' is perpendicular to the a'/c' plane A shift vector may be specified to shift the origin of the cartesian lattice relative to the original lattice origin (specify shift in fractional coordinates of the original lattice) """ (a,b,c,alp,bet,gam) = self.lattice.cell() (ar,br,cr,alpr,betr,gamr) = self.lattice.rcell() Va = [1./a, 0. , 0.] Vb = [-1./(a*tand(gam)), 1./(b*sind(gam)), 0.] Vc = [ar*cosd(betr), br*cosd(alpr), cr] self.basis(Va=Va,Vb=Vb,Vc=Vc,shift=shift)
def _calc_qaz(self): """ Calc qaz, the angle btwn Q and the yz plane """ nu = self.angles['nu'] delta = self.angles['delta'] qaz = num.arctan2(sind(delta), cosd(delta)*sind(nu) ) qaz = num.degrees(qaz) self.pangles['qaz'] = qaz
def _calc_qaz(self): """ Calc qaz, the angle btwn Q and the yz plane """ nu = self.angles['nu'] delta = self.angles['delta'] qaz = num.arctan2(sind(delta), cosd(delta) * sind(nu)) qaz = num.degrees(qaz) self.pangles['qaz'] = qaz
def _calc_tth(self): """ Calculate 2Theta, the scattering angle Notes: ------ This should be the same as: (ki,kr) = calc_kvecs(nu,delta,lambda) tth = cartesian_angle(ki,kr) You can also get this given h, the reciprocal lattice vector that is in the diffraction condition. E.g. h = self.calc_h() tth = self.lattice.tth(h) """ nu = self.angles['nu'] delta = self.angles['delta'] tth = arccosd(cosd(delta) * cosd(nu)) self.pangles['tth'] = tth
def _calc_tth(self): """ Calculate 2Theta, the scattering angle Notes: ------ This should be the same as: (ki,kr) = calc_kvecs(nu,delta,lambda) tth = cartesian_angle(ki,kr) You can also get this given h, the reciprocal lattice vector that is in the diffraction condition. E.g. h = self.calc_h() tth = self.lattice.tth(h) """ nu = self.angles['nu'] delta = self.angles['delta'] tth = arccosd(cosd(delta)*cosd(nu)) self.pangles['tth'] = tth
def _calc_psi(self): """ calc psi, this is the azmuthal angle of n wrt Q. ie for tau != 0, psi is the rotation of n about Q Notes: ------ Note this must be calc after tth, tau, and alpha! """ tau = self.pangles['tau'] tth = self.pangles['tth'] alpha = self.pangles['alpha'] #beta = self.calc_beta() #xx = (-cosd(tau)*sind(tth/2.) + sind(beta)) xx = (cosd(tau)*sind(tth/2.) - sind(alpha)) denom = (sind(tau)*cosd(tth/2.)) if denom == 0: self.pangles['psi'] = 0. return xx = xx /denom psi = arccosd( xx ) self.pangles['psi'] = psi
def _calc_psi(self): """ calc psi, this is the azmuthal angle of n wrt Q. ie for tau != 0, psi is the rotation of n about Q Notes: ------ Note this must be calc after tth, tau, and alpha! """ tau = self.pangles['tau'] tth = self.pangles['tth'] alpha = self.pangles['alpha'] #beta = self.calc_beta() #xx = (-cosd(tau)*sind(tth/2.) + sind(beta)) xx = (cosd(tau) * sind(tth / 2.) - sind(alpha)) denom = (sind(tau) * cosd(tth / 2.)) if denom == 0: self.pangles['psi'] = 0. return xx = xx / denom psi = arccosd(xx) self.pangles['psi'] = psi
def calc_Z(phi=0.0, chi=0.0, eta=0.0, mu=0.0): """ Calculate the psic goniometer rotation matrix Z for the 4 sample angles. Angles are in degrees Notes: ------ Z is the matrix that rotates a vector defined in the phi frame ie a vector defined with all angles zero => vphi. After rotation the lab frame coordinates of the vector => vm are given by: vm = Z*vphi """ P = num.array([[cosd(phi), sind(phi), 0.], [-sind(phi), cosd(phi), 0.], [0., 0., 1.]], float) X = num.array([[cosd(chi), 0., sind(chi)], [0., 1., 0.], [-sind(chi), 0., cosd(chi)]], float) H = num.array([[cosd(eta), sind(eta), 0.], [-sind(eta), cosd(eta), 0.], [0., 0., 1.]], float) M = num.array([[1., 0., 0.], [0., cosd(mu), -sind(mu)], [0., sind(mu), cosd(mu)]], float) Z = num.dot(num.dot(num.dot(M, H), X), P) return Z
def _calc_g(self): """ Calculate the metric tensors and recip lattice params self.g = real space metric tensor self.gr = recip space metric tensor """ (a, b, c, alp, bet, gam) = self.cell() # real metric tensor self.g = num.array([[a * a, a * b * cosd(gam), a * c * cosd(bet)], [b * a * cosd(gam), b * b, b * c * cosd(alp)], [c * a * cosd(bet), c * b * cosd(alp), c * c]]) # recip lattice metric tensor # and recip lattice params self.gr = num.linalg.inv(self.g) self.ar = num.sqrt(self.gr[0, 0]) self.br = num.sqrt(self.gr[1, 1]) self.cr = num.sqrt(self.gr[2, 2]) self.alphar = arccosd(self.gr[1, 2] / (self.br * self.cr)) self.betar = arccosd(self.gr[0, 2] / (self.ar * self.cr)) self.gammar = arccosd(self.gr[0, 1] / (self.ar * self.br))
def _calc_g(self): """ Calculate the metric tensors and recip lattice params self.g = real space metric tensor self.gr = recip space metric tensor """ (a,b,c,alp,bet,gam) = self.cell() # real metric tensor self.g = num.array([ [ a*a, a*b*cosd(gam), a*c*cosd(bet) ], [ b*a*cosd(gam), b*b, b*c*cosd(alp) ], [ c*a*cosd(bet), c*b*cosd(alp), c*c ] ]) # recip lattice metric tensor # and recip lattice params self.gr = num.linalg.inv(self.g) self.ar = num.sqrt(self.gr[0,0]) self.br = num.sqrt(self.gr[1,1]) self.cr = num.sqrt(self.gr[2,2]) self.alphar = arccosd(self.gr[1,2]/(self.br*self.cr)) self.betar = arccosd(self.gr[0,2]/(self.ar*self.cr)) self.gammar = arccosd(self.gr[0,1]/(self.ar*self.br))
def _calc_UB(self, ): """ Calculate the orientation matrix, U, from the primary and secondary reflectons and given lattice Note dont really ever use B by itself. so we should combine this and above to calc_UB and just store UB?? """ # use these, note they are used below on vectors # defined in the cartesian lab frame basis cross = num.cross norm = num.linalg.norm #Calculate the B matrix (a, b, c, alp, bet, gam) = self.lattice.cell() (ar, br, cr, alpr, betr, gamr) = self.lattice.rcell() B = num.array([[ar, br * cosd(gamr), cr * cosd(betr)], [0., br * sind(gamr), -cr * sind(betr) * cosd(alp)], [0., 0., 1. / c]]) self.B = B # calc Z and Q for the OR reflections Z1 = calc_Z(self.or0['phi'], self.or0['chi'], self.or0['eta'], self.or0['mu']) Q1 = calc_Q(self.or0['nu'], self.or0['delta'], self.or0['lam']) # Z2 = calc_Z(self.or1['phi'], self.or1['chi'], self.or1['eta'], self.or1['mu']) Q2 = calc_Q(self.or1['nu'], self.or1['delta'], self.or1['lam']) # calc the phi frame coords for diffraction vectors # note divide out 2pi since the diffraction condition # is 2pi*h = Q vphi_1 = num.dot(num.linalg.inv(Z1), (Q1 / (2. * num.pi))) vphi_2 = num.dot(num.linalg.inv(Z2), (Q2 / (2. * num.pi))) #calc cartesian coords of h vectors hc_1 = num.dot(self.B, self.or0['h']) hc_2 = num.dot(self.B, self.or1['h']) #So at this point the following should be true: # vphi_1 = U*hc_1 # vphi_2 = U*hc_2 # and we could use these relations to solve for U. # But U solved directly from above is likely not to be orthogonal # since the angles btwn (vphi_1 and vphi_2) and (hc_1 and hc_2) are # not exactly the same due to expt errors..... # Therefore, get an orthogonal solution for U from the below treatment #define the following normalized vectors from hc vectors tc_1 = hc_1 / norm(hc_1) tc_3 = cross(tc_1, hc_2) / norm(cross(tc_1, hc_2)) tc_2 = cross(tc_3, tc_1) / norm(cross(tc_3, tc_1)) #define tphi vectors from vphi vectors tphi_1 = vphi_1 / norm(vphi_1) tphi_3 = cross(tphi_1, vphi_2) / norm(cross(tphi_1, vphi_2)) tphi_2 = cross(tphi_3, tphi_1) / norm(cross(tphi_3, tphi_1)) #define the following matrices Tc = num.transpose(num.array([tc_1, tc_2, tc_3])) Tphi = num.transpose(num.array([tphi_1, tphi_2, tphi_3])) # calc orientation matrix U # note either of the below work since Tc is orthogonal #self.U = num.dot(Tphi, Tc.transpose()) self.U = num.dot(Tphi, num.linalg.inv(Tc)) # calc UB self.UB = num.dot(self.U, self.B) #update h and psuedo angles... self.set_angles()
def _calc_UB(self,): """ Calculate the orientation matrix, U, from the primary and secondary reflectons and given lattice Note dont really ever use B by itself. so we should combine this and above to calc_UB and just store UB?? """ # use these, note they are used below on vectors # defined in the cartesian lab frame basis cross = num.cross norm = num.linalg.norm #Calculate the B matrix (a,b,c,alp,bet,gam) = self.lattice.cell() (ar,br,cr,alpr,betr,gamr) = self.lattice.rcell() B = num.array([[ar, br*cosd(gamr), cr*cosd(betr) ], [0., br*sind(gamr), -cr*sind(betr)*cosd(alp)], [0., 0., 1./c ]]) self.B = B # calc Z and Q for the OR reflections Z1 = calc_Z(self.or0['phi'],self.or0['chi'],self.or0['eta'],self.or0['mu']) Q1 = calc_Q(self.or0['nu'],self.or0['delta'],self.or0['lam']) # Z2 = calc_Z(self.or1['phi'],self.or1['chi'],self.or1['eta'],self.or1['mu']) Q2 = calc_Q(self.or1['nu'],self.or1['delta'],self.or1['lam']) # calc the phi frame coords for diffraction vectors # note divide out 2pi since the diffraction condition # is 2pi*h = Q vphi_1 = num.dot(num.linalg.inv(Z1), (Q1/(2.*num.pi))) vphi_2 = num.dot(num.linalg.inv(Z2), (Q2/(2.*num.pi))) #calc cartesian coords of h vectors hc_1 = num.dot(self.B, self.or0['h']) hc_2 = num.dot(self.B, self.or1['h']) #So at this point the following should be true: # vphi_1 = U*hc_1 # vphi_2 = U*hc_2 # and we could use these relations to solve for U. # But U solved directly from above is likely not to be orthogonal # since the angles btwn (vphi_1 and vphi_2) and (hc_1 and hc_2) are # not exactly the same due to expt errors..... # Therefore, get an orthogonal solution for U from the below treatment #define the following normalized vectors from hc vectors tc_1 = hc_1 / norm(hc_1) tc_3 = cross(tc_1, hc_2) / norm(cross(tc_1, hc_2)) tc_2 = cross(tc_3, tc_1) / norm(cross(tc_3, tc_1)) #define tphi vectors from vphi vectors tphi_1 = vphi_1 / norm(vphi_1) tphi_3 = cross(tphi_1,vphi_2) / norm(cross(tphi_1,vphi_2)) tphi_2 = cross(tphi_3,tphi_1) / norm(cross(tphi_3,tphi_1)) #define the following matrices Tc = num.transpose(num.array([tc_1,tc_2,tc_3])) Tphi = num.transpose(num.array([tphi_1,tphi_2,tphi_3])) # calc orientation matrix U # note either of the below work since Tc is orthogonal #self.U = num.dot(Tphi, Tc.transpose()) self.U = num.dot(Tphi, num.linalg.inv(Tc)) # calc UB self.UB = num.dot(self.U,self.B) #update h and psuedo angles... self.set_angles()