Exemple #1
0
def calc_surf_transform(nm):
    """
    This routine calculates the matrix, M, which transforms the
    indicies of vectors defined in the lab frame basis to the
    a surface frame defined such that:
        zs is along the surface normal (parrallel to nm)
        ys is the projection of the lab frame -y axis onto the surface
        xs is defined to make it a right handed orthonormal set
           (and is therefore in the surface plane)

    Parameters:
    -----------
    * nm is the surface normal vector, defined in the 
      laboratory m-frame (i.e. pointing in an arbitrary
      direction for a particular gonio setting)
      - see gonio_psic.py for more details.

    Notes:
    ------
    The surface transform is defined as follows:
        |e_xs|      |e_x|              |vx|     
        |e_ys| = F* |e_y|    and   F = |vy|   
        |e_zs|      |e_z|              |vz|    
    where e's are the (cartesian) basis vectors.  Given a
    vector [x,y,z] defined in the lab frame basis [e_x,e_y,e_z],
    we can compute the indicies of this vector in the surface
    basis [e_xs,e_ys,e_zs] from:
        |xs|      |x|
        |ys| = M* |y|
        |zs|      |z|
    where
        M = transpose(inv(F)) = inv(transpose(F))
        
    Note see lattice.py for more notes on general transforms, and see
    gonio_psic.py for notes on calc of the surface normal vector.  
    """
    v_z = nm / cartesian_mag(nm)

    v = num.array([0., -1., 0.])
    v_y = v - (num.dot(v, v_z) * v_z) / (cartesian_mag(v_z)**2.)
    v_y = v_y / cartesian_mag(v_y)

    v_x = num.cross(v_y, v_z)
    v_x = v_x / cartesian_mag(v_x)

    F = num.array([v_x, v_y, v_z])
    M = num.linalg.inv(F.transpose())

    return M
Exemple #2
0
def calc_surf_transform(nm):
    """
    This routine calculates the matrix, M, which transforms the
    indicies of vectors defined in the lab frame basis to the
    a surface frame defined such that:
        zs is along the surface normal (parrallel to nm)
        ys is the projection of the lab frame -y axis onto the surface
        xs is defined to make it a right handed orthonormal set
           (and is therefore in the surface plane)

    Parameters:
    -----------
    * nm is the surface normal vector, defined in the 
      laboratory m-frame (i.e. pointing in an arbitrary
      direction for a particular gonio setting)
      - see gonio_psic.py for more details.

    Notes:
    ------
    The surface transform is defined as follows:
        |e_xs|      |e_x|              |vx|     
        |e_ys| = F* |e_y|    and   F = |vy|   
        |e_zs|      |e_z|              |vz|    
    where e's are the (cartesian) basis vectors.  Given a
    vector [x,y,z] defined in the lab frame basis [e_x,e_y,e_z],
    we can compute the indicies of this vector in the surface
    basis [e_xs,e_ys,e_zs] from:
        |xs|      |x|
        |ys| = M* |y|
        |zs|      |z|
    where
        M = transpose(inv(F)) = inv(transpose(F))
        
    Note see lattice.py for more notes on general transforms, and see
    gonio_psic.py for notes on calc of the surface normal vector.  
    """
    v_z = nm / cartesian_mag(nm)

    v   = num.array([0.,-1.,0.])
    v_y = v - (num.dot(v,v_z) * v_z) / (cartesian_mag(v_z)**2.) 
    v_y = v_y / cartesian_mag(v_y)
    
    v_x = num.cross(v_y,v_z)
    v_x = v_x / cartesian_mag(v_x)

    F = num.array([v_x,v_y,v_z])
    M = num.linalg.inv(F.transpose())

    return M
def test1():
    # create a new psic instance
    psic = Psic(5.6,5.6,13,90,90,120,lam=1.3756)

    # diffr. angles:
    psic.set_angles(phi=65.78,chi=37.1005,eta=6.6400,
                    mu=0.0,nu=0.0,delta=46.9587)
    #calc tth, d and magnitude of Q
    print "\nh=",  psic.h
    print "tth =", psic.pangles['tth']
    print "tth =", psic.lattice.tth(psic.h)

    # reference vector/surface normal in [h,k,l]
    # n = [0, 0, 1]
    n = [-0.0348357, -0.00243595, 1]
    psic.set_n(n)
    #calc miscut
    print "\nmiscut=",psic.lattice.angle([0,0,1],n,recip=True)

    # test surf norm
    n_phi = num.dot(psic.UB,n)
    n_phi = n_phi/ num.fabs(cartesian_mag(n_phi))
    print "\nnphi: ", n_phi

    print "\nsigma_az=",psic.pangles['sigma_az']

    print "\ntau_az=",psic.pangles['tau_az']

    psic.calc_n(-psic.pangles['sigma_az'],-psic.pangles['tau_az'])
    print "calc n from sigma and tau az: ",psic.n 
    
    #calc miscut
    print "\nmiscut=",psic.lattice.angle([0,0,1],psic.n,recip=True)
    
    return psic
Exemple #4
0
    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_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
Exemple #6
0
    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_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
Exemple #8
0
    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_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
Exemple #10
0
    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_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
Exemple #12
0
def surface_intercept_bounds(k,v,diameter):
    """
    Find z-intercepts within bounds
    
    We assume that the plane is a circle of fixed diameter, 
    and scale back the intercept to the edge of the circle,
    This is done along by subtracting a vector that is parrelel
    to the in-plane projection of the k vector. The magnitude of
    the subtracted vector is set so the result has magnitude
    equal to the diameter.
    
    If that doesnt result in an intercept then just rescale
    the original intercept vector so its magnitude is the
    diameter.
    
    This is an approximate way to handle intercepts for
    circular samples, not recommended for use(!)
    """
    vi = surface_intercept(k,v)
    r = cartesian_mag(vi)
    if r <= diameter/2.:
        return vi
    # if r>diameter/2 need to scale it back
    # Therefore solve the following for mag
    #    vi_new = vi - ks*mag/norm(ks),
    #    mag(vi_new) = diameter/2
    # therefore end up with a quadratic:
    ks = num.array(k[0:2])
    a = 1.
    b = -2. * (v[0]*ks[0] + vi[1]*ks[1])/cartesian_mag(ks)
    c = cartesian_mag(vi)**2. - (diameter/2.)**2.
    arg = b**2. - 4.*a*c
    # make sure this method will result in a solution
    if arg > 0.: 
        mag1 = (-b + num.sqrt(arg))/(2.*a)
        mag2 = (-b - num.sqrt(arg))/(2.*a)
        vi1 = vi - ks*mag1/cartesian_mag(ks)
        vi2 = vi - ks*mag2/cartesian_mag(ks)
        # chose soln that makes smallest angle 
        # with the original intercept
        angle1 = num.fabs(cartesian_angle(vi,vi1))
        angle2 = num.fabs(cartesian_angle(vi,vi2))
        if angle1 < angle2:
            return vi1
        else:
            return vi2
    # otherwise just rescale the original vector
    else:
        vi = vi * (diameter/2.) / cartesian_mag(vi)
        return vi
Exemple #13
0
def surface_intercept_bounds(k, v, diameter):
    """
    Find z-intercepts within bounds
    
    We assume that the plane is a circle of fixed diameter, 
    and scale back the intercept to the edge of the circle,
    This is done along by subtracting a vector that is parrelel
    to the in-plane projection of the k vector. The magnitude of
    the subtracted vector is set so the result has magnitude
    equal to the diameter.
    
    If that doesnt result in an intercept then just rescale
    the original intercept vector so its magnitude is the
    diameter.
    
    This is an approximate way to handle intercepts for
    circular samples, not recommended for use(!)
    """
    vi = surface_intercept(k, v)
    r = cartesian_mag(vi)
    if r <= diameter / 2.:
        return vi
    # if r>diameter/2 need to scale it back
    # Therefore solve the following for mag
    #    vi_new = vi - ks*mag/norm(ks),
    #    mag(vi_new) = diameter/2
    # therefore end up with a quadratic:
    ks = num.array(k[0:2])
    a = 1.
    b = -2. * (v[0] * ks[0] + vi[1] * ks[1]) / cartesian_mag(ks)
    c = cartesian_mag(vi)**2. - (diameter / 2.)**2.
    arg = b**2. - 4. * a * c
    # make sure this method will result in a solution
    if arg > 0.:
        mag1 = (-b + num.sqrt(arg)) / (2. * a)
        mag2 = (-b - num.sqrt(arg)) / (2. * a)
        vi1 = vi - ks * mag1 / cartesian_mag(ks)
        vi2 = vi - ks * mag2 / cartesian_mag(ks)
        # chose soln that makes smallest angle
        # with the original intercept
        angle1 = num.fabs(cartesian_angle(vi, vi1))
        angle2 = num.fabs(cartesian_angle(vi, vi2))
        if angle1 < angle2:
            return vi1
        else:
            return vi2
    # otherwise just rescale the original vector
    else:
        vi = vi * (diameter / 2.) / cartesian_mag(vi)
        return vi
Exemple #14
0
def test1():
    # create a new psic instance
    psic = Psic(5.6, 5.6, 13, 90, 90, 120, lam=1.3756)

    # diffr. angles:
    psic.set_angles(phi=65.78,
                    chi=37.1005,
                    eta=6.6400,
                    mu=0.0,
                    nu=0.0,
                    delta=46.9587)
    #calc tth, d and magnitude of Q
    print "\nh=", psic.h
    print "tth =", psic.pangles['tth']
    print "tth =", psic.lattice.tth(psic.h)

    # reference vector/surface normal in [h,k,l]
    # n = [0, 0, 1]
    n = [-0.0348357, -0.00243595, 1]
    psic.set_n(n)
    #calc miscut
    print "\nmiscut=", psic.lattice.angle([0, 0, 1], n, recip=True)

    # test surf norm
    n_phi = num.dot(psic.UB, n)
    n_phi = n_phi / num.fabs(cartesian_mag(n_phi))
    print "\nnphi: ", n_phi

    print "\nsigma_az=", psic.pangles['sigma_az']

    print "\ntau_az=", psic.pangles['tau_az']

    psic.calc_n(-psic.pangles['sigma_az'], -psic.pangles['tau_az'])
    print "calc n from sigma and tau az: ", psic.n

    #calc miscut
    print "\nmiscut=", psic.lattice.angle([0, 0, 1], psic.n, recip=True)

    return psic
Exemple #15
0
def active_area(nm,ki=num.array([0.,1.,0.]),kr=num.array([0.,1.,0.]),
                beam=[],det=None,sample=1.,plot=False,fig=None):
    """
    Calc the area of overlap of beam, sample and detector
    surface polygon projections.

    Parameters:
    -----------
    * nm is the surface normal vector, defined in the laboratory frame.

    * ki and kr are vectors defined in the lab frame parallel to the
      incident beam and diffracted beam directions respectively
      (magnitudes are arbitrary)

    * beam, det and sample are lists that hold the the lab-frame (3D) 
      vectors defining the beam apperature, detector apperature
      and sample shape 
    
      If det = None then we ignore it and just compute spill-off correction

      If sample is a single number we take it as the diamter of a round sample
      mounted flat. Otherwise sample hould be a list of lab frame vectors that 
      describes the sample polygon.

      If sample == None, then we assume the sample is infinite in size    

    *  If plot = True then makes plot

    Note:
    -----
    The lab frame coordinate systems is defined such that:
        x is vertical (perpendicular, pointing to the ceiling of the hutch)
        y is directed along the incident beam path
        z make the system right handed and lies in the horizontal scattering plane
         (i.e. z is parallel to the phi axis)
    The center (0,0,0) of the lab frame is the rotation center of the instrument.


    Output:
    -------
    * A_beam = total area of the beam projection into the surface plane
    * A_int = area of sample illuminated within the detector projection 

    Use to correct scattering data for area effects, including spilloff, i.e.
      A_ratio = A_int/A_beam 
      Ic = I/A_ratio,   I = Idet/Io

    """
    # Calc surf system transformation matrix
    M = calc_surf_transform(nm)

    # calc k vectors in the surf frame (i.e. [xs,ys,zs])
    # first make them unit vectors, even though no real need to..
    ki = ki/cartesian_mag(ki)
    kr = kr/cartesian_mag(kr)
    ki_s = num.dot(M,ki)
    kr_s = num.dot(M,kr)

    #################################################################
    # Get the beam, detector and surface polygons.
    # Convert vectors to surface frame.
    # Then for beam and det project the vectors onto
    # the surface along the k vectors.  ie. calc the vector
    # intercepts with the surface plane
    # Each of the below polygons is a list of 2D vectors
    # ie in-plane [x_s, y_s] points 
    #################################################################
    # beam 
    if type(beam) == types.ListType:
        beam_poly = []
        if len(beam) < 3:
            print "Error in beam description"
            return None
        for v in beam:
            vs  = num.dot(M,v)
            int = surface_intercept(ki_s, vs)
            beam_poly.append(int)
    else:
        beam_poly=None
    # det 
    if type(det) == types.ListType:
        det_poly  = []
        if len(det) < 3:
            print "Error in det description"
            return None
        for v in det:
            vs  = num.dot(M,v)
            int = surface_intercept(kr_s, vs)
            det_poly.append(int)
    else:
        det_poly=None
    # sample 
    if type(sample) == types.ListType:
        sam_poly  = []
        if len(sample) < 3:
            print "Error in sample description"
            return None
        for v in sample:
            vs  = num.dot(M,v)
            if num.fabs(vs[2]) > 0.01:
                print "Warning sample hieght problem"
            sam_poly.append(vs[:2])
        sample_shape=True
    else:
        sam_poly = None
        sample_shape = False

    #####################################################################
    # depending on whether we have a sample shape description
    # or assume a round sample, pass along appropriate
    # data to compute areas.  Note all the polygons are lists
    # of 2D surface frame vectors (ie in plane vectors)
    #####################################################################
    if sample_shape == False:
        (A_beam,A_int) = _area_round(beam_poly,det_poly,diameter=sample,plot=plot,fig=fig)
    else:
        (A_beam,A_int) = _area_polygon(beam_poly,det_poly,sam_poly,plot=plot,fig=fig)
    return (A_beam, A_int)
Exemple #16
0
def active_area(nm,
                ki=num.array([0., 1., 0.]),
                kr=num.array([0., 1., 0.]),
                beam=[],
                det=None,
                sample=1.,
                plot=False,
                fig=None):
    """
    Calc the area of overlap of beam, sample and detector
    surface polygon projections.

    Parameters:
    -----------
    * nm is the surface normal vector, defined in the laboratory frame.

    * ki and kr are vectors defined in the lab frame parallel to the
      incident beam and diffracted beam directions respectively
      (magnitudes are arbitrary)

    * beam, det and sample are lists that hold the the lab-frame (3D) 
      vectors defining the beam apperature, detector apperature
      and sample shape 
    
      If det = None then we ignore it and just compute spill-off correction

      If sample is a single number we take it as the diamter of a round sample
      mounted flat. Otherwise sample hould be a list of lab frame vectors that 
      describes the sample polygon.

      If sample == None, then we assume the sample is infinite in size    

    *  If plot = True then makes plot

    Note:
    -----
    The lab frame coordinate systems is defined such that:
        x is vertical (perpendicular, pointing to the ceiling of the hutch)
        y is directed along the incident beam path
        z make the system right handed and lies in the horizontal scattering plane
         (i.e. z is parallel to the phi axis)
    The center (0,0,0) of the lab frame is the rotation center of the instrument.


    Output:
    -------
    * A_beam = total area of the beam projection into the surface plane
    * A_int = area of sample illuminated within the detector projection 

    Use to correct scattering data for area effects, including spilloff, i.e.
      A_ratio = A_int/A_beam 
      Ic = I/A_ratio,   I = Idet/Io

    """
    # Calc surf system transformation matrix
    M = calc_surf_transform(nm)

    # calc k vectors in the surf frame (i.e. [xs,ys,zs])
    # first make them unit vectors, even though no real need to..
    ki = ki / cartesian_mag(ki)
    kr = kr / cartesian_mag(kr)
    ki_s = num.dot(M, ki)
    kr_s = num.dot(M, kr)

    #################################################################
    # Get the beam, detector and surface polygons.
    # Convert vectors to surface frame.
    # Then for beam and det project the vectors onto
    # the surface along the k vectors.  ie. calc the vector
    # intercepts with the surface plane
    # Each of the below polygons is a list of 2D vectors
    # ie in-plane [x_s, y_s] points
    #################################################################
    # beam
    if type(beam) == types.ListType:
        beam_poly = []
        if len(beam) < 3:
            print "Error in beam description"
            return None
        for v in beam:
            vs = num.dot(M, v)
            int = surface_intercept(ki_s, vs)
            beam_poly.append(int)
    else:
        beam_poly = None
    # det
    if type(det) == types.ListType:
        det_poly = []
        if len(det) < 3:
            print "Error in det description"
            return None
        for v in det:
            vs = num.dot(M, v)
            int = surface_intercept(kr_s, vs)
            det_poly.append(int)
    else:
        det_poly = None
    # sample
    if type(sample) == types.ListType:
        sam_poly = []
        if len(sample) < 3:
            print "Error in sample description"
            return None
        for v in sample:
            vs = num.dot(M, v)
            if num.fabs(vs[2]) > 0.01:
                print "Warning sample hieght problem"
            sam_poly.append(vs[:2])
        sample_shape = True
    else:
        sam_poly = None
        sample_shape = False

    #####################################################################
    # depending on whether we have a sample shape description
    # or assume a round sample, pass along appropriate
    # data to compute areas.  Note all the polygons are lists
    # of 2D surface frame vectors (ie in plane vectors)
    #####################################################################
    if sample_shape == False:
        (A_beam, A_int) = _area_round(beam_poly,
                                      det_poly,
                                      diameter=sample,
                                      plot=plot,
                                      fig=fig)
    else:
        (A_beam, A_int) = _area_polygon(beam_poly,
                                        det_poly,
                                        sam_poly,
                                        plot=plot,
                                        fig=fig)
    return (A_beam, A_int)