def set_spec_G(self, G, preparsed=False): """ Take the spec G array for the psic geometry and set all the relevant orientation info... """ if not preparsed: (cell, or0, or1, n) = spec_psic_G(G) else: (cell, or0, or1, n) = G self.n = n self.or0 = or0 self.or1 = or1 self.lattice = Lattice(*cell) self._calc_UB()
def set_spec_G(self,G): """ Take the spec G array for the psic geometry and set all the relevant orientation info... """ (cell,or0,or1,n) = spec_psic_G(G) self.n = n self.or0 = or0 self.or1 = or1 self.lattice = Lattice(*cell) self._calc_UB()
def set_spec_G(self,G,preparsed=False): """ Take the spec G array for the psic geometry and set all the relevant orientation info... """ if not preparsed: (cell,or0,or1,n) = spec_psic_G(G) else: (cell,or0,or1,n) = G self.n = n self.or0 = or0 self.or1 = or1 self.lattice = Lattice(*cell) self._calc_UB()
def __init__(self,a=10.,b=10.,c=10.,alpha=90.,beta=90.,gamma=90.,lam=1.0): """ Initialize Parameters: ----------- * a,b,c in angstroms * alpha, beta, gamma in degrees, * lambda in angstroms """ # set lattice and lambda self.lattice = Lattice(a,b,c,alpha,beta,gamma,lam) # hold gonio angles self.angles={'phi':0.0,'chi':0.0,'eta':0.0,'mu':0.0, 'nu':0.0,'delta':0.0} # hold psuedo angles self.pangles = {} self.calc_psuedo = True # hold n (reference) vector in HKL # eg surface normal vector for psuedo angles self.n = num.array([0.,0.,1.],dtype=float) # Z and calc h self.Z = [] self.Q = [] self.ki = [] self.kr = [] self.h = [0.,0.,0.] # dummy primary reflection tth = self.lattice.tth([0.,0.,1.],lam=lam) self.or0={'h':num.array([0.,0.,1.]), 'phi':0.0,'chi':0.0,'eta':0.0,'mu':tth/2., 'nu':tth,'delta':0.0,'lam':lam} # dummy secondary reflection tth = self.lattice.tth([0.,1.,0.],lam=lam) self.or1={'h':num.array([0.,1.,0.]), 'phi':0.0,'chi':0.0,'eta':tth/2.,'mu':0.0, 'nu':0.0,'delta':tth,'lam':lam} # Compute OR matricies self.U = [] self.B = [] self.UB = [] self._calc_UB()
def __init__(self, a=10., b=10., c=10., alpha=90., beta=90., gamma=90., lam=1.0): """ Initialize Parameters: ----------- * a,b,c in angstroms * alpha, beta, gamma in degrees, * lambda in angstroms """ # set lattice and lambda self.lattice = Lattice(a, b, c, alpha, beta, gamma, lam) # hold gonio angles self.angles = { 'phi': 0.0, 'chi': 0.0, 'eta': 0.0, 'mu': 0.0, 'nu': 0.0, 'delta': 0.0 } # hold psuedo angles self.pangles = {} self.calc_psuedo = True # hold n (reference) vector in HKL # eg surface normal vector for psuedo angles self.n = num.array([0., 0., 1.], dtype=float) # Z and calc h self.Z = [] self.Q = [] self.ki = [] self.kr = [] self.h = [0., 0., 0.] # dummy primary reflection tth = self.lattice.tth([0., 0., 1.], lam=lam) self.or0 = { 'h': num.array([0., 0., 1.]), 'phi': 0.0, 'chi': 0.0, 'eta': 0.0, 'mu': tth / 2., 'nu': tth, 'delta': 0.0, 'lam': lam } # dummy secondary reflection tth = self.lattice.tth([0., 1., 0.], lam=lam) self.or1 = { 'h': num.array([0., 1., 0.]), 'phi': 0.0, 'chi': 0.0, 'eta': tth / 2., 'mu': 0.0, 'nu': 0.0, 'delta': tth, 'lam': lam } # Compute OR matricies self.U = [] self.B = [] self.UB = [] self._calc_UB()
class Psic: """ Orientation calculations for Psic geometry. The default dummy orientation matrix is set up assuming the sample is mounted such that (001) plane is perpendicular to the eta and phi rot axes (ie c-axis is parrallel to the eta and phi rot axes) and that the b-axis is parallel to the nu and mu rot axes (ie parrallel to the lab frame Z) """ ################################################### def __init__(self, a=10., b=10., c=10., alpha=90., beta=90., gamma=90., lam=1.0): """ Initialize Parameters: ----------- * a,b,c in angstroms * alpha, beta, gamma in degrees, * lambda in angstroms """ # set lattice and lambda self.lattice = Lattice(a, b, c, alpha, beta, gamma, lam) # hold gonio angles self.angles = { 'phi': 0.0, 'chi': 0.0, 'eta': 0.0, 'mu': 0.0, 'nu': 0.0, 'delta': 0.0 } # hold psuedo angles self.pangles = {} self.calc_psuedo = True # hold n (reference) vector in HKL # eg surface normal vector for psuedo angles self.n = num.array([0., 0., 1.], dtype=float) # Z and calc h self.Z = [] self.Q = [] self.ki = [] self.kr = [] self.h = [0., 0., 0.] # dummy primary reflection tth = self.lattice.tth([0., 0., 1.], lam=lam) self.or0 = { 'h': num.array([0., 0., 1.]), 'phi': 0.0, 'chi': 0.0, 'eta': 0.0, 'mu': tth / 2., 'nu': tth, 'delta': 0.0, 'lam': lam } # dummy secondary reflection tth = self.lattice.tth([0., 1., 0.], lam=lam) self.or1 = { 'h': num.array([0., 1., 0.]), 'phi': 0.0, 'chi': 0.0, 'eta': tth / 2., 'mu': 0.0, 'nu': 0.0, 'delta': tth, 'lam': lam } # Compute OR matricies self.U = [] self.B = [] self.UB = [] self._calc_UB() ################################################### def __repr__(self, ): """ display """ lout = self.lattice.__repr__() lout = "%sPrimary:\n h=%3.2f,k=%3.2f," % (lout, self.or0['h'][0], self.or0['h'][1]) lout = "%sl=%3.2f, lam=%6.6f\n" % (lout, self.or0['h'][2], self.or0['lam']) lout = "%s phi=%6.3f,chi=%6.3f," % (lout, self.or0['phi'], self.or0['chi']) lout = "%seta=%6.3f,mu=%6.3f," % (lout, self.or0['eta'], self.or0['mu']) lout = "%snu=%6.3f,delta=%6.3f\n" % (lout, self.or0['nu'], self.or0['delta']) # lout = "%sSecondary:\n h=%3.2f,k=%3.2f," % (lout, self.or1['h'][0], self.or1['h'][1]) lout = "%sl=%3.2f, lam=%6.6f\n" % (lout, self.or1['h'][2], self.or1['lam']) lout = "%s phi=%6.3f,chi=%6.3f," % (lout, self.or1['phi'], self.or1['chi']) lout = "%seta=%6.3f,mu=%6.3f," % (lout, self.or1['eta'], self.or1['mu']) lout = "%snu=%6.3f,delta=%6.3f\n" % (lout, self.or1['nu'], self.or1['delta']) # lout = "%sSetting:" % (lout) lout = "%s h=%3.2f,k=%3.2f,l=%3.2f\n" % (lout, self.h[0], self.h[1], self.h[2]) lout = "%s phi=%6.3f,chi=%6.3f," % (lout, self.angles['phi'], self.angles['chi']) lout = "%seta=%6.3f,mu=%6.3f," % (lout, self.angles['eta'], self.angles['mu']) lout = "%snu=%6.3f,delta=%6.3f\n" % (lout, self.angles['nu'], self.angles['delta']) # if self.calc_psuedo: lout = "%s TTH=%6.3f," % (lout, self.pangles['tth']) lout = "%sSIGMA_AZ=%6.3f," % (lout, self.pangles['sigma_az']) lout = "%sTAU_AZ=%6.3f," % (lout, self.pangles['tau_az']) lout = "%sN_AZ=%6.3f," % (lout, self.pangles['naz']) lout = "%sALPHA=%6.3f," % (lout, self.pangles['alpha']) lout = "%sBETA=%6.3f\n" % (lout, self.pangles['beta']) lout = "%s TAU=%6.3f," % (lout, self.pangles['tau']) lout = "%sPSI=%6.3f," % (lout, self.pangles['psi']) lout = "%sQ_AZ=%6.3f," % (lout, self.pangles['qaz']) lout = "%sOMEGA=%6.3f," % (lout, self.pangles['omega']) # return lout ################################################### def set_lat(self, a=None, b=None, c=None, alpha=None, beta=None, gamma=None, lam=None): """ Update lattice parameters and lambda Parameters: ----------- * a,b,c in angstroms * alpha, beta, gamma in degrees, * lambda in angstroms """ self.lattice.update(a=a, b=b, c=c, alpha=alpha, beta=beta, gamma=gamma, lam=lam) self._calc_UB() def set_spec_G(self, G, preparsed=False): """ Take the spec G array for the psic geometry and set all the relevant orientation info... """ if not preparsed: (cell, or0, or1, n) = spec_psic_G(G) else: (cell, or0, or1, n) = G self.n = n self.or0 = or0 self.or1 = or1 self.lattice = Lattice(*cell) self._calc_UB() ################################################### def set_or0(self, h=None, phi=None, chi=None, eta=None, mu=None, nu=None, delta=None, lam=None): """ Set / adjust the primary orientation reflection Parameters: ----------- * h is the hkl array of the reflection * the rest of the parameters are motor angles in degrees, * lam is the wavelength in angstroms If lam = None, then lambda defined for the lattice is used. """ if h != None: self.or0['h'] = num.array(h, dtype=float) if phi != None: self.or0['phi'] = float(phi) if chi != None: self.or0['chi'] = float(chi) if eta != None: self.or0['eta'] = float(eta) if mu != None: self.or0['mu'] = float(mu) if nu != None: self.or0['nu'] = float(nu) if delta != None: self.or0['delta'] = float(delta) if lam != None: self.or0['lam'] = float(lam) self._calc_UB() def set_or1(self, h=None, phi=None, chi=None, eta=None, mu=None, nu=None, delta=None, lam=None): """ Set / adjust the secondary orientation reflection Parameters: ----------- * h is the hkl array of the reflection * the rest of the parameters are motor angles in degrees, * lam is the wavelength in angstroms If lam = None, then lambda defined for the lattice is used. """ if h != None: self.or1['h'] = num.array(h, dtype=float) if phi != None: self.or1['phi'] = float(phi) if chi != None: self.or1['chi'] = float(chi) if eta != None: self.or1['eta'] = float(eta) if mu != None: self.or1['mu'] = float(mu) if nu != None: self.or1['nu'] = float(nu) if delta != None: self.or1['delta'] = float(delta) if lam != None: self.or1['lam'] = float(lam) self._calc_UB() def swap_or(self, ): """ Swap the primary and secondary reflection """ tmp = copy.copy(self.or0) self.or0 = copy.copy(self.or1) self.or1 = tmp self._calc_UB() ################################################### 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 set_angles(self, phi=None, chi=None, eta=None, mu=None, nu=None, delta=None): """ Set goniometer angles (all in degrees) """ if phi != None: self.angles['phi'] = float(phi) if chi != None: self.angles['chi'] = float(chi) if eta != None: self.angles['eta'] = float(eta) if mu != None: self.angles['mu'] = float(mu) if nu != None: self.angles['nu'] = float(nu) if delta != None: self.angles['delta'] = float(delta) # update h, also calc Z etc.. self._calc_h() # update psuedo self._update_psuedo() def _calc_h(self, ): """ Calculate the hkl values of the vector that is in the diffraction condition for the given set of angles. Notes: ------ Solve for hphi using Z and lab frame Q: hphi = inv(Z) * Q / (2*pi) then calc h from h = inv(UB)*hphi """ self.Z = calc_Z(phi=self.angles['phi'], chi=self.angles['chi'], eta=self.angles['eta'], mu=self.angles['mu']) (Q, ki, kr) = calc_Q(self.angles['nu'], self.angles['delta'], self.lattice.lam, ret_k=True) self.Q = Q self.ki = ki self.kr = kr hphi = num.dot(num.linalg.inv(self.Z), self.Q) / (2. * num.pi) h = num.dot(num.linalg.inv(self.UB), hphi) self.h = h ################################################### def set_n(self, n=[0, 0, 1]): """ Set n, the reference vector used for psuedo angles. The n vector is given in hkl values. see calc_n to determine n from chi and phi settings """ self.n = num.array(n, dtype=float) self._update_psuedo() 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) ################################################### ## Pseudo angles ################################################### def _update_psuedo(self): """ Compute psuedo angles Note: ----- use this to compute psuedo angles rather than individual calls. ie some psuedo angles depend on others so its important that the calc are executed in the correct order. Also important is that _calc_h is called before this... """ self.pangles = {} if self.calc_psuedo == True: self._calc_tth() self._calc_nm() self._calc_sigma_az() self._calc_tau_az() self._calc_naz() self._calc_alpha() self._calc_beta() self._calc_tau() self._calc_psi() self._calc_qaz() self._calc_omega() 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_nm(self): """ Calculate the rotated cartesian lab indicies of the reference vector n = nm. Note nm is normalized. Notes: ------ The reference vector n is given in recip lattice indicies (hkl) """ # calc n in the rotated lab frame and make a unit vector n = self.n Z = self.Z UB = self.UB nm = num.dot(num.dot(Z, UB), n) nm = nm / cartesian_mag(nm) self.nm = nm def _calc_sigma_az(self): """ sigma_az = angle between the z-axis and n in the phi frame """ # calc n in the lab frame (unrotated) and make a unit vector n_phi = num.dot(self.UB, self.n) n_phi = n_phi / cartesian_mag(n_phi) # note result of acosd is between 0 and pi # get correct sign from the sign of the x-component #sigma_az = num.sign(n_phi[0])*arccosd(n_phi[2]) sigma_az = arccosd(n_phi[2]) self.pangles['sigma_az'] = sigma_az def _calc_tau_az(self): """ tau_az = angle between the projection of n in the xy-plane and the x-axis in the phi frame """ # calc n in the lab frame (unrotated) and make a unit vector n_phi = num.dot(self.UB, self.n) n_phi = n_phi / cartesian_mag(n_phi) tau_az = num.arctan2(-n_phi[1], n_phi[0]) tau_az = tau_az * 180. / num.pi self.pangles['tau_az'] = tau_az def _calc_naz(self): """ calc naz, this is the angle btwn the reference vector n and the yz plane at the given angle settings """ # get norm reference vector in cartesian lab frame nm = self.nm naz = num.arctan2(nm[0], nm[2]) naz = num.degrees(naz) self.pangles['naz'] = naz def _calc_alpha(self): """ Calc alpha, ie incidence angle or angle btwn -1*k_in (which is parallel to lab-y) and the plane perp to the reference vector n. """ nm = self.nm ki = num.array([0., -1., 0.]) alpha = arcsind(num.dot(nm, ki)) self.pangles['alpha'] = alpha def _calc_beta(self): """ Calc beta, ie exit angle, or angle btwn k_r and the plane perp to the reference vector n Notes: ------ beta = arcsind(2*sind(tth/2)*cosd(tau)-sind(alpha)) """ # calc normalized kr #delta = self.angles['delta'] #nu = self.angles['nu'] #kr = num.array([sind(delta), # cosd(nu)*cosd(delta), # sind(nu)*cosd(delta)]) nm = self.nm kr = self.kr / cartesian_mag(self.kr) beta = arcsind(num.dot(nm, kr)) self.pangles['beta'] = beta def _calc_tau(self): """ Calc tau, this is the angle btwn n and the scattering-plane defined by ki and kr. ie the angle between n and Q Notes: ------ Can also calc from: tau = acos( cosd(alpha) * cosd(tth/2) * cosd(naz - qaz) ... + sind(alpha) * sind(tth/2) ) """ tau = cartesian_angle(self.Q, self.nm) self.pangles['tau'] = tau 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_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_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
class Psic: """ Orientation calculations for Psic geometry. The default dummy orientation matrix is set up assuming the sample is mounted such that (001) plane is perpendicular to the eta and phi rot axes (ie c-axis is parrallel to the eta and phi rot axes) and that the b-axis is parallel to the nu and mu rot axes (ie parrallel to the lab frame Z) """ ################################################### def __init__(self,a=10.,b=10.,c=10.,alpha=90.,beta=90.,gamma=90.,lam=1.0): """ Initialize Parameters: ----------- * a,b,c in angstroms * alpha, beta, gamma in degrees, * lambda in angstroms """ # set lattice and lambda self.lattice = Lattice(a,b,c,alpha,beta,gamma,lam) # hold gonio angles self.angles={'phi':0.0,'chi':0.0,'eta':0.0,'mu':0.0, 'nu':0.0,'delta':0.0} # hold psuedo angles self.pangles = {} self.calc_psuedo = True # hold n (reference) vector in HKL # eg surface normal vector for psuedo angles self.n = num.array([0.,0.,1.],dtype=float) # Z and calc h self.Z = [] self.Q = [] self.ki = [] self.kr = [] self.h = [0.,0.,0.] # dummy primary reflection tth = self.lattice.tth([0.,0.,1.],lam=lam) self.or0={'h':num.array([0.,0.,1.]), 'phi':0.0,'chi':0.0,'eta':0.0,'mu':tth/2., 'nu':tth,'delta':0.0,'lam':lam} # dummy secondary reflection tth = self.lattice.tth([0.,1.,0.],lam=lam) self.or1={'h':num.array([0.,1.,0.]), 'phi':0.0,'chi':0.0,'eta':tth/2.,'mu':0.0, 'nu':0.0,'delta':tth,'lam':lam} # Compute OR matricies self.U = [] self.B = [] self.UB = [] self._calc_UB() ################################################### def __repr__(self,): """ display """ lout = self.lattice.__repr__() lout = "%sPrimary:\n h=%3.2f,k=%3.2f," % (lout,self.or0['h'][0],self.or0['h'][1]) lout = "%sl=%3.2f, lam=%6.6f\n" % (lout,self.or0['h'][2],self.or0['lam']) lout = "%s phi=%6.3f,chi=%6.3f," % (lout,self.or0['phi'],self.or0['chi']) lout = "%seta=%6.3f,mu=%6.3f," % (lout,self.or0['eta'],self.or0['mu']) lout = "%snu=%6.3f,delta=%6.3f\n" % (lout,self.or0['nu'],self.or0['delta']) # lout = "%sSecondary:\n h=%3.2f,k=%3.2f," % (lout,self.or1['h'][0],self.or1['h'][1]) lout = "%sl=%3.2f, lam=%6.6f\n" % (lout,self.or1['h'][2],self.or1['lam']) lout = "%s phi=%6.3f,chi=%6.3f," % (lout,self.or1['phi'],self.or1['chi']) lout = "%seta=%6.3f,mu=%6.3f," % (lout,self.or1['eta'],self.or1['mu']) lout = "%snu=%6.3f,delta=%6.3f\n" % (lout,self.or1['nu'],self.or1['delta']) # lout = "%sSetting:" % (lout) lout = "%s h=%3.2f,k=%3.2f,l=%3.2f\n" % (lout,self.h[0],self.h[1],self.h[2]) lout = "%s phi=%6.3f,chi=%6.3f," % (lout,self.angles['phi'],self.angles['chi']) lout = "%seta=%6.3f,mu=%6.3f," % (lout,self.angles['eta'],self.angles['mu']) lout = "%snu=%6.3f,delta=%6.3f\n" % (lout,self.angles['nu'],self.angles['delta']) # if self.calc_psuedo: lout = "%s TTH=%6.3f," % (lout,self.pangles['tth']) lout = "%sSIGMA_AZ=%6.3f," % (lout,self.pangles['sigma_az']) lout = "%sTAU_AZ=%6.3f," % (lout,self.pangles['tau_az']) lout = "%sN_AZ=%6.3f," % (lout,self.pangles['naz']) lout = "%sALPHA=%6.3f," % (lout,self.pangles['alpha']) lout = "%sBETA=%6.3f\n" % (lout,self.pangles['beta']) lout = "%s TAU=%6.3f," % (lout,self.pangles['tau']) lout = "%sPSI=%6.3f," % (lout,self.pangles['psi']) lout = "%sQ_AZ=%6.3f," % (lout,self.pangles['qaz']) lout = "%sOMEGA=%6.3f," % (lout,self.pangles['omega']) # return lout ################################################### def set_lat(self,a=None,b=None,c=None,alpha=None, beta=None,gamma=None,lam=None): """ Update lattice parameters and lambda Parameters: ----------- * a,b,c in angstroms * alpha, beta, gamma in degrees, * lambda in angstroms """ self.lattice.update(a=a,b=b,c=c,alpha=alpha, beta=beta,gamma=gamma,lam=lam) self._calc_UB() def set_spec_G(self,G,preparsed=False): """ Take the spec G array for the psic geometry and set all the relevant orientation info... """ if not preparsed: (cell,or0,or1,n) = spec_psic_G(G) else: (cell,or0,or1,n) = G self.n = n self.or0 = or0 self.or1 = or1 self.lattice = Lattice(*cell) self._calc_UB() ################################################### def set_or0(self,h=None,phi=None,chi=None,eta=None, mu=None,nu=None,delta=None,lam=None): """ Set / adjust the primary orientation reflection Parameters: ----------- * h is the hkl array of the reflection * the rest of the parameters are motor angles in degrees, * lam is the wavelength in angstroms If lam = None, then lambda defined for the lattice is used. """ if h!=None: self.or0['h'] = num.array(h,dtype=float) if phi!=None: self.or0['phi']=float(phi) if chi!=None: self.or0['chi']=float(chi) if eta!=None: self.or0['eta']=float(eta) if mu!=None: self.or0['mu']=float(mu) if nu!=None: self.or0['nu']=float(nu) if delta!=None: self.or0['delta']=float(delta) if lam!= None: self.or0['lam']=float(lam) self._calc_UB() def set_or1(self,h=None,phi=None,chi=None,eta=None, mu=None,nu=None,delta=None,lam=None): """ Set / adjust the secondary orientation reflection Parameters: ----------- * h is the hkl array of the reflection * the rest of the parameters are motor angles in degrees, * lam is the wavelength in angstroms If lam = None, then lambda defined for the lattice is used. """ if h!=None: self.or1['h'] = num.array(h,dtype=float) if phi!=None: self.or1['phi']=float(phi) if chi!=None: self.or1['chi']=float(chi) if eta!=None: self.or1['eta']=float(eta) if mu!=None: self.or1['mu']=float(mu) if nu!=None: self.or1['nu']=float(nu) if delta!=None: self.or1['delta']=float(delta) if lam!= None: self.or1['lam']=float(lam) self._calc_UB() def swap_or(self,): """ Swap the primary and secondary reflection """ tmp = copy.copy(self.or0) self.or0 = copy.copy(self.or1) self.or1 = tmp self._calc_UB() ################################################### 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 set_angles(self,phi=None,chi=None,eta=None, mu=None,nu=None,delta=None): """ Set goniometer angles (all in degrees) """ if phi!=None: self.angles['phi']=float(phi) if chi!=None: self.angles['chi']=float(chi) if eta!=None: self.angles['eta']=float(eta) if mu!=None: self.angles['mu']=float(mu) if nu!=None: self.angles['nu']=float(nu) if delta!=None: self.angles['delta']=float(delta) # update h, also calc Z etc.. self._calc_h() # update psuedo self._update_psuedo() def _calc_h(self,): """ Calculate the hkl values of the vector that is in the diffraction condition for the given set of angles. Notes: ------ Solve for hphi using Z and lab frame Q: hphi = inv(Z) * Q / (2*pi) then calc h from h = inv(UB)*hphi """ self.Z = calc_Z(phi=self.angles['phi'],chi=self.angles['chi'], eta=self.angles['eta'],mu=self.angles['mu']) (Q,ki,kr) = calc_Q(self.angles['nu'], self.angles['delta'], self.lattice.lam,ret_k=True) self.Q=Q self.ki=ki self.kr=kr hphi = num.dot(num.linalg.inv(self.Z),self.Q) / (2.*num.pi) h = num.dot(num.linalg.inv(self.UB),hphi) self.h = h ################################################### def set_n(self,n=[0,0,1]): """ Set n, the reference vector used for psuedo angles. The n vector is given in hkl values. see calc_n to determine n from chi and phi settings """ self.n = num.array(n,dtype=float) self._update_psuedo() 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) ################################################### ## Pseudo angles ################################################### def _update_psuedo(self): """ Compute psuedo angles Note: ----- use this to compute psuedo angles rather than individual calls. ie some psuedo angles depend on others so its important that the calc are executed in the correct order. Also important is that _calc_h is called before this... """ self.pangles = {} if self.calc_psuedo == True: self._calc_tth() self._calc_nm() self._calc_sigma_az() self._calc_tau_az() self._calc_naz() self._calc_alpha() self._calc_beta() self._calc_tau() self._calc_psi() self._calc_qaz() self._calc_omega() 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_nm(self): """ Calculate the rotated cartesian lab indicies of the reference vector n = nm. Note nm is normalized. Notes: ------ The reference vector n is given in recip lattice indicies (hkl) """ # calc n in the rotated lab frame and make a unit vector n = self.n Z = self.Z UB = self.UB nm = num.dot(num.dot(Z,UB),n) nm = nm/cartesian_mag(nm) self.nm = nm def _calc_sigma_az(self): """ sigma_az = angle between the z-axis and n in the phi frame """ # calc n in the lab frame (unrotated) and make a unit vector n_phi = num.dot(self.UB,self.n) n_phi = n_phi/cartesian_mag(n_phi) # note result of acosd is between 0 and pi # get correct sign from the sign of the x-component #sigma_az = num.sign(n_phi[0])*arccosd(n_phi[2]) sigma_az = arccosd(n_phi[2]) self.pangles['sigma_az'] = sigma_az def _calc_tau_az(self): """ tau_az = angle between the projection of n in the xy-plane and the x-axis in the phi frame """ # calc n in the lab frame (unrotated) and make a unit vector n_phi = num.dot(self.UB,self.n) n_phi = n_phi/cartesian_mag(n_phi) tau_az = num.arctan2(-n_phi[1], n_phi[0]) tau_az = tau_az*180./num.pi self.pangles['tau_az'] = tau_az def _calc_naz(self): """ calc naz, this is the angle btwn the reference vector n and the yz plane at the given angle settings """ # get norm reference vector in cartesian lab frame nm = self.nm naz = num.arctan2( nm[0], nm[2] ) naz = num.degrees(naz) self.pangles['naz'] = naz def _calc_alpha(self): """ Calc alpha, ie incidence angle or angle btwn -1*k_in (which is parallel to lab-y) and the plane perp to the reference vector n. """ nm = self.nm ki = num.array([0.,-1.,0.]) alpha = arcsind(num.dot(nm,ki)) self.pangles['alpha'] = alpha def _calc_beta(self): """ Calc beta, ie exit angle, or angle btwn k_r and the plane perp to the reference vector n Notes: ------ beta = arcsind(2*sind(tth/2)*cosd(tau)-sind(alpha)) """ # calc normalized kr #delta = self.angles['delta'] #nu = self.angles['nu'] #kr = num.array([sind(delta), # cosd(nu)*cosd(delta), # sind(nu)*cosd(delta)]) nm = self.nm kr = self.kr / cartesian_mag(self.kr) beta = arcsind(num.dot(nm, kr)) self.pangles['beta'] = beta def _calc_tau(self): """ Calc tau, this is the angle btwn n and the scattering-plane defined by ki and kr. ie the angle between n and Q Notes: ------ Can also calc from: tau = acos( cosd(alpha) * cosd(tth/2) * cosd(naz - qaz) ... + sind(alpha) * sind(tth/2) ) """ tau = cartesian_angle(self.Q,self.nm) self.pangles['tau'] = tau 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_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_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