def polarization(self, ): """ Compute polarization correction factor. For a horizontally polarized beam (polarization vector parrallel to the lab-frame z direction) the polarization factor is normally defined as: p = 1-(cos(del)*sin(nu))^2 For a beam with mixed horizontal and vertical polarization: p = fh( 1-(cos(del)*sin(nu))^2 ) + (1-fh)(1-sin(del)^2) where fh is the fraction of horizontal polarization. Measured data is corrected for polarization as: Ic = Im * cp = Im/p """ fh = self.fh delta = self.gonio.angles['delta'] nu = self.gonio.angles['nu'] p = 1. - (cosd(delta) * sind(nu))**2. if fh != 1.0: p = fh * c_p + (1. - fh) * (1.0 - (sind(delta))**2.) if p == 0.: cp = 0. else: cp = 1. / p return cp
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 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_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_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 lorentz_stationary(self): """ Compute the Lorentz factor for a stationary (image) measurement. See Vlieg 1997 Measured data is corrected for Lorentz factor as: Ic = Im * cl """ beta = self.gonio.pangles['beta'] cl = sind(beta) return cl
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 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_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 _spec_or_angles(angles, calc_kappa=False): """ Angles defined by spec for the OR's. See specfile.py Notes: ------ Assume the following. If parsing angles from the P array: (generally shouldnt need this since angles are tagged with motor labels on read) angles = P if psic: angles = angles[0:5] elif kappa fourc angles = [angles[0:3], angles[8], angles[7]] If parsing angles from the G array: angles = G[x:y] where x:y depend on whether you are parsing out or0 or or1. See spec_G below We then assume the following: del = angles[0] eta = angles[1] chi = angles[2] phi = angles[3] nu = angles[4] mu = angles[5] Note: calc kappa, keta and kphi kap_alp = 50.031; keta = eta - asin(-tan(chi/2)/tan(kap_alp)) kphi = phi - asin(-tan(chi/2)/tan(kap_alp)) kappa = asin(sin(chi/2)/sin(kap_alp)) """ # angles from spec delta = angles[0] eta = angles[1] chi = angles[2] phi = angles[3] nu = angles[4] mu = angles[5] # kappa angles if calc_kappa: kap_alp = 50.031 keta = eta - arcsind(-tand(chi / 2.) / tand(kap_alp)) kphi = phi - arcsind(-tand(chi / 2.) / tand(kap_alp)) kappa = asind(sind(chi / 2.) / sind(kap_alp)) return { 'phi': phi, 'chi': chi, 'eta': eta, 'mu': mu, 'delta': delta, 'nu': nu, 'keta': keta, 'kphi': kphi, 'kappa': kappa } else: return { 'phi': phi, 'chi': chi, 'eta': eta, 'mu': mu, 'delta': delta, 'nu': nu }
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 rotate(self, angle): x = self.x y = self.y self.x = x * cosd(angle) - y * sind(angle) self.y = x * sind(angle) + y * cosd(angle) return self