예제 #1
0
파일: polygon.py 프로젝트: ttrainor/_tdl
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
예제 #2
0
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)
예제 #3
0
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)
예제 #4
0
    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
예제 #5
0
    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
예제 #6
0
파일: polygon.py 프로젝트: pombredanne/tdl
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
예제 #7
0
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)
예제 #8
0
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)
예제 #9
0
    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)
예제 #10
0
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
예제 #11
0
    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)
예제 #12
0
 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)
예제 #13
0
파일: lattice.py 프로젝트: FHe/tdl
 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)
예제 #14
0
 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
예제 #15
0
 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
예제 #16
0
    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
예제 #17
0
    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
예제 #18
0
    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
예제 #19
0
    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
예제 #20
0
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
예제 #21
0
 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))
예제 #22
0
파일: lattice.py 프로젝트: FHe/tdl
 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))
예제 #23
0
    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()
예제 #24
0
    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()