Example #1
0
 def norm_at(E, Qaxis, Ei):
     "compute normalization(Q) array for the given energy transfer"
     ki = conversion.e2k(Ei)
     Ef = Ei - E
     kf = conversion.e2k(Ef)
     Qmin = np.abs(ki - kf)
     Qmax = ki + kf
     rt = np.zeros(Qaxis.shape)
     in_range = (Qaxis < Qmax) * (Qaxis > Qmin)
     rt[in_range] = (Qaxis * (2 * np.pi / ki / kf))[in_range]
     return rt
Example #2
0
def dgs_setEi(sample, Ei):
    """set incident energy for DGS experiment
    
A sample has kernels with parameters that may be affected by Ei.
For example, a powder SQE kernel has Qrange and Erange. 
When this sample is put into a beam in a DGS instrument,
it is better to simulate only within the dynamical range determined
by Ei and instrument geometry.
Here we do some quick calculation to estimate the dynamical range
for the given Ei, without considering the instrument geometry.
The main purpose of this is to limit the simulation dynamical
range to speed up the simulation

    Ei: float. unit: meV
    """
    from mcni.utils import conversion
    ki = conversion.e2k(Ei) # \AA^-1
    Qrange = (0., 2*ki)
    Erange = (-Ei, Ei*.99)
    for excitation in sample.excitations:
        if 'Qrange' in excitation.__dict__:
            excitation.Qrange = ','.join(_to_str_tuple_with_unit(
                _combine_range(Qrange, _to_float_tuple(excitation.Qrange, '1./angstrom')),
                '1./angstrom'))
        if 'Erange' in excitation.__dict__:
            excitation.Erange = ','.join(_to_str_tuple_with_unit(
                _combine_range(Erange, _to_float_tuple(excitation.Erange, 'meV')),
                'meV'))
        continue
    return
Example #3
0
def test():
    class powdersqe:
        type = "powderSQE"
        SQEhist = "Al-iqe.h5"
        Erange = "-50*meV,50*meV"
        Qrange = "1/angstrom, 10/angstrom"

    class mysample:
        class lattice:
            constants = 1., 1., 1., 90., 90., 90.
            basis_vectors = [[1,0,0],[0,1,0],[0,0,1]]
        chemical_formula="V"
        class shape:
            class sphere:
                radius = 3
    mysample.excitations = [powdersqe]
    Ei = 30.; ki = C.e2k(Ei)
    sample.dgs_setEi(mysample, Ei)
    Qmin, Qmax = sample._to_float_tuple(mysample.excitations[0].Qrange, '1./angstrom')
    assert np.isclose(Qmin, 1.)
    assert np.isclose(Qmax, 2*ki)
    Emin, Emax = sample._to_float_tuple(mysample.excitations[0].Erange, 'meV')
    assert np.isclose(Emin, -Ei)
    assert np.isclose(Emax, Ei*.99)
    return
Example #4
0
def Eresidual(xtalori, hkl, Etarget, angles, Ei):
    """compute residual of energy transfer
    This method compute a series of psi angle and corresponding residual 
    (E - Etarget).
    We only need to simulate crystal orientation where the residual 
    is close to zero.
    """

    from mcni.utils import conversion as conv
    ki = conv.e2k(Ei)
    kiv = np.array([ki,0,0])
    
    r = np.zeros((len(angles), 2))
    for i, psi in enumerate(angles):
        xtalori.psi = psi / 180. * np.pi
        hkl2cartesian = xtalori.hkl2cartesian_mat()
        # cart2hkl = xtalori.cartesian2hkl_mat()
        Qcart = np.dot(hkl, hkl2cartesian)
        # print hkl, np.dot(Qcart, cart2hkl)
        # print Qcart
        kfv = kiv - Qcart
        kf = np.linalg.norm(kfv)
        Ef = conv.k2e(kf)
        E = Ei - Ef
        r[i] = psi, E-Etarget
        continue
    return r
Example #5
0
 def convolveSQE(self, IQE, Ei, *args):
     max_det_angle = 140. * np.pi / 180.
     a = self.calc_res_a(Ei, *args)
     E_new, Q, I_new = convolveSQE(a, IQE, Ei)
     mask = np.zeros(I_new.shape, dtype=bool)
     from mcni.utils import conversion
     ki = conversion.e2k(Ei)
     for iE in range(E_new.size):
         E1 = E_new[iE]
         if E1 > Ei:
             mask[:, iE] = 1
             continue
         Ef1 = Ei - E1
         kf1 = conversion.e2k(Ef1)
         Qmin = abs(ki - kf1)
         Qmax = np.sqrt(ki * ki + kf1 * kf1 -
                        2 * ki * kf1 * np.cos(max_det_angle))
         mask[Q < Qmin, iE] = 1
         mask[Q > Qmax, iE] = 1
     I_new[mask] = np.nan
     return E_new, Q, I_new
Example #6
0
def hklE2rtz(hkl, E, xtalori, Ei, r):
    """convert from hkl to cylinderical coordinate sytem
    """
    ki_scalar = conv.e2k(Ei)
    ki = [ki_scalar, 0, 0]
    Q = hkl2Q(hkl, xtalori)
    kf = ki - Q
    kf_dir = kf/np.linalg.norm(kf)
    scale_factor = r/np.linalg.norm(kf_dir[:, :3])
    position = kf_dir * scale_factor
    np.allclose(np.linalg.norm(position[:, :3]), r)
    theta = np.arctan2(position[:, 1], position[:, 0])
    return r, theta, position[:, 2]
Example #7
0
def sqe(
    E, g, Qmax=None, Qmin=0, dQ=None,
    T=300, M=50, N=5, starting_order=2, Emax=None,
    ):
    """compute sum of multiphonon SQE from dos
    S = \sum_{i=2,N} S_i(Q,E)
    
    Note: single phonon scattering is not included. only 2-phonons and up
    
    E,g: input DOS data
    energy axis is inferred from input DOS data
    Q axis is defined by Qmax, Qmin, and dQ
    T: temperature (Kelvin)
    M: atomic mass 
    N: maximum number of order for multi-phonon scattering
    starting_order: 2: start with 2 phonon scattering
    """
    dos_sample = len(E)
    e0 = E[0]
    de = E[1] - E[0]
    emax = E[-1]
    # expand E
    Emax = Emax or e0+de*3*dos_sample
    E = np.arange(e0, Emax, de)
    g = np.concatenate((g, np.zeros(len(E)-len(g))))
    # normalize
    int_g = np.sum(g) * de
    g/=int_g
    # Q axis
    if Qmax is None:
        from mcni.utils import conversion
        Qmax = conversion.e2k(emax) * 3
    if dQ is None:
        dQ = (Qmax-Qmin)/200
    Q = np.arange(Qmin, Qmax, dQ)
    
    # beta
    kelvin2mev = 0.0862
    beta = 1./(T*kelvin2mev)
    
    # compute S
    S = None
    for i, (Q,E,S1) in enumerate(iterSQESet(N, Q, dQ, E, de, M, g, beta)):
        if i < starting_order - 1: continue
        if S is None: S = S1; continue
        S += S1
        continue
    
    # sum over 2..N
    # S = S_set[starting_order-1:].sum(axis=0)
    return Q, E, S
Example #8
0
def computeKf(Ei, E, Q, log):
    ki = Conv.e2k(Ei);
    log.write( "* ki=%s\n" % (ki,) )
    kiv = np.array([ki, 0, 0])
    kfv = kiv - Q
    log.write( "* vectors ki=%s, kf=%s\n" % (kiv, kfv) )
    Ef = Ei - E
    # ** Verify the momentum and energy transfers **
    log.write( "These two numbers should be very close:\n")
    log.write( "  %s\n" % (Ei-Conv.k2e(np.linalg.norm(kfv)),) )
    log.write( "  %s\n" % (Ei-Ef,) )
    assert np.isclose(Ef, Conv.k2e(np.linalg.norm(kfv)))
    log.write( "  Ei=%s, Ef=%s\n" % (Ei,Ef) )
    return kfv, Ef
Example #9
0
def rtzE2hkl(r, theta, z, E, xtalori, Ei):
    """convert from cylinderical coordinate sytem to hkl
    """
    kf_dir = [r*np.cos(theta), r*np.sin(theta), z]
    kf_dir = np.array(kf_dir).T # (N,3)
    kf_dir /= np.linalg.norm(kf_dir, axis=-1)[:, np.newaxis] # (N,3)
    Ef = Ei - E # (N,)
    kf_scalar = (conv.SE2V*conv.V2K) * Ef**.5 # (N,)
    del Ef
    kf = kf_dir * kf_scalar[:, np.newaxis] # (N,3)
    del kf_scalar
    ki_scalar = conv.e2k(Ei) 
    ki = [ki_scalar, 0, 0]
    Q = ki - kf;  del ki, kf
    return Q2hkl(Q, xtalori)
Example #10
0
def compute_xE_curve(xaxis, hkl0, ex, mat, Ei):
    """given xaxis and ex, compute hkl = hkl0+ x*ex
    and then compute energy transfer E
    
    mat: convert hkl to Q
    """
    ki = Conv.e2k(Ei)
    kiv = np.array([ki, 0, 0])
    hkl = hkl0 + xaxis[:, np.newaxis] * ex
    Q = np.dot(hkl, mat) # NX3
    kfv = kiv - Q # NX3
    kf2 = np.sum(kfv**2, -1) # N
    kf = np.sqrt(kf2) # N

    # theta is the angle in the scattering plane (x-y)
    theta = np.arctan2(kfv[:, 1], kfv[:, 0]) # N
    # phi is the angle off the scattering plane. sin(phi) = z/r
    phi = np.arcsin(kfv[:, 2] / kf)
    Ef = kf2 * (Conv.K2V**2 * Conv.VS2E)
    E = Ei - Ef
    return E, theta, phi
Example #11
0
def total_scattering_cross_section(Ei, dp):
    """compute the total scattering cross section
    by powder diffraction
    from the given known list of peaks.
    This cross section is per unit cell.
    
    According to Squires, cross section by one Debye Scherrer cone is
    \sigma_{\tau} = 1/v_0 \lambda^3/(4sin(\theta/2)) \sum_{all \tau\prime of same length} |F_N(\tau\prime)|^2

    The total cross section is the sum of all such cones
    (number of cones is limited by Ei)
    
    Limitation: 
     * the diffraction peaks list usually could be limited
       if they are loaded from a file such as laz.
       Probably better be computed directly from the structure.
       When the peaks list is limited, the calculation
       would be wrong when Ei gets larger 
       (should see more diff peaks but they are not counted).
     * only  consider the ideal powder diffraction without
       vibrations. 
    """
    from mcni.utils import conversion as conv
    import numpy as np
    k = conv.e2k(Ei)
    l = np.pi * 2 / k
    ucvol = dp.structure.lattice.getVolume()
    # loop over peaks, for each peak that contributes
    # add its contribution
    sigma = 0
    for peak in dp.peaks:
        q = peak.q
        if q > 2*k: continue
        sinthetaover2 = q/2/k
        F_squared = peak.F_squared
        mul = peak.multiplicity
        sigma += l**3 / ucvol / 4 / sinthetaover2 * F_squared * mul
        continue
    return sigma
Example #12
0
def compute_hE_curve(hmin, hmax, dh, k, l, mat, Ei):
    """given k, l values, compute h, E
    
    mat: convert hkl to Q
    """
    ki = Conv.e2k(Ei)
    kiv = np.array([ki, 0, 0])
    h = np.arange(hmin, hmax, dh) # N
    hkl = np.vstack((h, np.repeat(k, h.size), np.repeat(l, h.size))).T # NX3
    Q = np.dot(hkl, mat) # NX3
    kfv = kiv - Q # NX3
    kf2 = np.sum(kfv**2, -1) # N
    kf = np.sqrt(kf2) # N
    
    # theta is the angle in the scattering plane (x-y)
    theta = np.arctan2(kfv[:, 1], kfv[:, 0]) # N
    # phi is the angle off the scattering plane. sin(phi) = z/r
    phi = np.arcsin(kfv[:, 2], kf)
    
    Ef = kf2 * (Conv.K2V**2 * Conv.VS2E)
    E = Ei - Ef
    return h, E, theta, phi
Example #13
0
def total_scattering_cross_section(Ei, dp):
    """compute the total scattering cross section
    by powder diffraction
    from the given known list of peaks.
    This cross section is per unit cell.
    
    According to Squires, cross section by one Debye Scherrer cone is
    \sigma_{\tau} = 1/v_0 \lambda^3/(4sin(\theta/2)) \sum_{all \tau\prime of same length} |F_N(\tau\prime)|^2

    The total cross section is the sum of all such cones
    (number of cones is limited by Ei)
    
    Limitation: 
     * the diffraction peaks list usually could be limited
       if they are loaded from a file such as laz.
       Probably better be computed directly from the structure.
       When the peaks list is limited, the calculation
       would be wrong when Ei gets larger 
       (should see more diff peaks but they are not counted).
     * only  consider the ideal powder diffraction without
       vibrations. 
    """
    from mcni.utils import conversion as conv
    import numpy as np
    k = conv.e2k(Ei)
    l = np.pi * 2 / k
    ucvol = dp.structure.lattice.getVolume()
    # loop over peaks, for each peak that contributes
    # add its contribution
    sigma = 0
    for peak in dp.peaks:
        q = peak.q
        if q > 2 * k: continue
        sinthetaover2 = q / 2 / k
        F_squared = peak.F_squared
        mul = peak.multiplicity
        sigma += l**3 / ucvol / 4 / sinthetaover2 * F_squared * mul
        continue
    return sigma
 def test1(self):
     'kernel orientation'
     # source
     from mcni.components.MonochromaticSource import MonochromaticSource
     import mcni, numpy as np
     Ei = 100
     from mcni.utils import conversion as Conv
     ki = Conv.e2k(Ei)
     vi = Conv.e2v(Ei)
     Qdir = np.array([np.sqrt(3)/2, 0, -1./2])
     Q = Qdir * 2
     kf = np.array([0,0,ki]) - Q
     Ef = Conv.k2e(np.linalg.norm(kf))
     E  = Ei-Ef
     dv = Qdir * Conv.k2v(Q)
     vf = np.array([0,0,vi]) - dv
     # print ki, Q, kf
     # print Ei, Ef, E
     neutron = mcni.neutron(r=(0,0,-1), v=(0,0,vi), prob=1)
     source = MonochromaticSource('s', neutron, dx=0.001, dy=0.001, dE=0)
     # sample
     from mccomponents.sample import samplecomponent
     scatterer = samplecomponent('sa', 'cyl/sampleassembly.xml' )
     # incident
     N = 1000
     neutrons = mcni.neutron_buffer(N)
     neutrons = source.process(neutrons)
     # print neutrons
     # scatter
     scatterer.process(neutrons)
     # print neutrons
     self.assertEqual(len(neutrons), N)
     for neutron in neutrons:
         np.allclose(neutron.state.velocity, vf)
         self.assert_(neutron.probability > 0)
         continue
     return
Example #15
0
def compute(sample_yml,
            Ei,
            dynamics,
            psi_scan,
            instrument,
            pixel,
            tofwidths,
            beamdivs,
            samplethickness,
            plot=False):
    # XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
    # should P be the T0 chopper?
    L_PM = mcvine.units.parse(
        instrument.L_m2fc
    ) / mcvine.units.meter  # P chopper to M chopper distance
    L_PS = mcvine.units.parse(
        instrument.L_m2s) / mcvine.units.meter  # P chopper to sample
    L_MS = L_PS - L_PM
    #
    R = mcvine.units.parse(instrument.detsys_radius) / mcvine.units.meter  #

    hkl0 = dynamics.hkl0
    hkl_dir = dynamics.hkl_dir  # projection
    psimin = psi_scan.min
    psimax = psi_scan.max
    dpsi = psi_scan.step

    # dynamics calculations
    E = dynamics.E
    dq = dynamics.dq
    hkl = hkl0 + dq * hkl_dir

    from mcni.utils import conversion as Conv
    vi = Conv.e2v(Ei)

    ti = L_PM / vi * 1e6  # microsecond
    Ef = Ei - E
    vf = Conv.e2v(Ef)

    # find the psi angle
    from mcvine.workflow.singlextal.io import loadXtalOriFromSampleYml
    xtalori = loadXtalOriFromSampleYml(sample_yml)
    from mcvine.workflow.singlextal.solve_psi import solve
    results = solve(xtalori,
                    Ei,
                    hkl,
                    E,
                    psimin,
                    psimax,
                    Nsegments=NSEGMENTS_SOLVE_PSI)
    from mcvine.workflow.singlextal.coords_transform import hkl2Q
    for r in results:
        xtalori.psi = r * np.pi / 180
        print("psi=%s, Q=%s" % (r, hkl2Q(hkl, xtalori)))
        print("hkl2Q=%r\n(Q = hkl dot hkl2Q)" %
              (xtalori.hkl2cartesian_mat(), ))
    # these are the psi angles that the particular point of interest will be measured
    # print results
    assert len(results)
    # XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
    # only select the first one. this is OK for most cases but there are cases where more than
    # one psi angles satisfy the condition
    psi = results[0]
    xtalori.psi = psi * np.pi / 180
    Q = hkl2Q(hkl, xtalori)
    hkl2Q_mat = xtalori.hkl2cartesian_mat()
    # print Q
    # print hkl2Q_mat
    #
    Q_len = np.linalg.norm(Q)
    # print Q_len
    ki = Conv.e2k(Ei)
    # print ki
    kiv = np.array([ki, 0, 0])
    kfv = kiv - Q
    # print kfv
    #
    # ** Verify the momentum and energy transfers **
    # print Ei-Conv.k2e(np.linalg.norm(kfv))
    # print Ei-Ef
    assert np.isclose(Ei - Ef, E)

    # ** Compute detector pixel position **
    z = kfv[2] / (kfv[0]**2 + kfv[1]**2)**.5 * R
    L_SD = (z**2 + R**2)**.5
    # print z, L_SD

    # ### Constants
    eV = 1.60218e-19
    meV = eV * 1e-3
    mus = 1.e-6
    hbar = 1.0545718e-34
    AA = 1e-10
    m = 1.6750e-24 * 1e-3  #kg
    from numpy import sin, cos

    # dE calcuation starts here
    # ## Differentials
    pE_pt = -m * (vi**3 / L_PM + vf**3 / L_SD * L_MS / L_PM)
    # convert to eV/microsecond
    pE_pt /= meV / mus
    # print pE_pt

    pE_ptMD = m * vf**3 / L_SD
    pE_ptMD /= meV / mus
    # print pE_ptMD

    pE_pLPM = m / L_PM * (vi**2 + vf**3 / vi * L_MS / L_SD)
    pE_pLPM /= meV
    # print pE_pLPM

    pE_pLMS = -m / L_SD * (vf**3 / vi)
    pE_pLMS /= meV
    # print pE_pLMS

    pE_pLSD = -m * vf * vf / L_SD
    pE_pLSD /= meV
    # print pE_pLSD

    # we don't need pE_pLSD, instead we need pE_pR and pE_pz. R and z are cylinder radius and z coordinate
    pE_pR = pE_pLSD * (R / L_SD)
    pE_pz = pE_pLSD * (z / L_SD)
    # print pE_pR, pE_pz

    # XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
    # ## ** Paramters: Estimate of standard deviations
    # tau_P = 10 # microsecond
    # tau_M = 8 # microsecond
    tau_P = tofwidths.P
    tau_M = tofwidths.M
    #
    # tau_D = 10 # microsecond
    tau_D = mcvine.units.parse(
        pixel.radius) / mcvine.units.meter * 2 / vf * 1e6  # microsecond

    # ## Calculations
    pE_p_vec = [pE_pt, pE_ptMD, pE_pLPM, pE_pLMS, pE_pR, pE_pz]
    pE_p_vec = np.array(pE_p_vec)
    J_E = pE_p_vec / E

    # print J_E
    sigma_t = (tau_P**2 + tau_M**2)**.5
    sigma_tMD = (tau_M**2 + tau_D**2)**.5
    # XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
    div = (beamdivs.theta**2 + beamdivs.phi**2)**.5  # a crude approx
    sigma_LPM = L_PM * div * div

    # mainly due to sample size
    sigma_LMS = samplethickness

    # mainly due to det tube diameter
    sigma_R = mcvine.units.parse(pixel.radius) / mcvine.units.meter * 2

    # pixel size
    sigma_z = mcvine.units.parse(pixel.height) / mcvine.units.meter

    sigma = np.array(
        [sigma_t, sigma_tMD, sigma_LPM, sigma_LMS, sigma_R, sigma_z])
    # print sigma
    sigma2 = sigma * sigma
    # print sigma2
    sigma2 = np.diag(sigma2)

    # print J_E
    # print np.dot(sigma2, J_E)
    cov = np.dot(J_E, np.dot(sigma2, J_E))

    # print cov, np.sqrt(cov)
    sigma_E = E * np.sqrt(cov)
    # print sigma_E

    # Not sure if this is right:
    #
    # * ** Note: this may be more like FWHM than sigma_E because of the approx I made **
    # * ** FWHM is 2.355 sigma **

    # ## Include Q
    # print "ti=",ti
    tf = L_SD / vf * 1e6
    # print "tf=",tf
    thetai = 0
    phii = 0
    # print "R=", R
    # print "Q=", Q

    eeta = np.arctan2(kfv[1], kfv[0])
    # print "eeta=", eeta

    pQx_pt = -m / hbar * (L_PM / ti / ti / mus / mus * cos(thetai) * cos(phii)
                          + R / tf / tf / mus / mus * L_MS / L_PM * cos(eeta))
    pQx_pt /= 1. / AA / mus
    # print pQx_pt

    pQx_ptMD = m / hbar * R / tf / tf * cos(eeta) / mus / mus
    pQx_ptMD /= 1. / AA / mus
    # print pQx_ptMD

    pQx_pLPM = m / hbar * (cos(thetai) * cos(phii) / ti + ti / tf / tf * R *
                           L_MS / L_PM / L_PM * cos(eeta)) / mus
    pQx_pLPM /= 1. / AA
    # print pQx_pLPM

    pQx_pLMS = -m / hbar * R / tf / tf * ti / L_PM * cos(eeta) / mus
    pQx_pLMS /= 1. / AA
    # print pQx_pLMS

    pQx_pR = -m / hbar / tf * cos(eeta) / mus
    pQx_pR /= 1. / AA
    # print pQx_pR

    pQx_peeta = m / hbar * R / tf * sin(eeta) / mus
    pQx_peeta /= 1. / AA
    # print pQx_peeta

    pQx_pthetai = -m / hbar * L_PM / ti * sin(thetai) * cos(phii) / mus
    pQx_pthetai /= 1. / AA
    # print pQx_pthetai

    pQx_pphii = -m / hbar * L_PM / ti * cos(thetai) * sin(phii) / mus
    pQx_pphii /= 1. / AA
    # print pQx_pphii

    pQx_p_vec = [
        pQx_pt, pQx_ptMD, pQx_pLPM, pQx_pLMS, pQx_pR, 0, pQx_peeta,
        pQx_pthetai, pQx_pphii
    ]
    pQx_p_vec = np.array(pQx_p_vec)
    J_Qx = pQx_p_vec / Q_len

    # **Qy**
    pQy_pt = -m / hbar * (L_PM / ti / ti * sin(thetai) * cos(phii) +
                          R / tf / tf * L_MS / L_PM * sin(eeta)) / mus / mus
    pQy_pt /= 1. / AA / mus
    # print pQy_pt

    pQy_ptMD = m / hbar * R / tf / tf * sin(eeta) / mus / mus
    pQy_ptMD /= 1. / AA / mus
    # print pQy_ptMD

    pQy_pLPM = m / hbar * (sin(thetai) * cos(phii) / ti + ti / tf / tf * R *
                           L_MS / L_PM / L_PM * sin(eeta)) / mus
    pQy_pLPM /= 1. / AA
    # print pQy_pLPM

    pQy_pLMS = -m / hbar * R / tf / tf * ti / L_PM * sin(eeta) / mus
    pQy_pLMS /= 1. / AA
    # print pQy_pLMS

    pQy_pR = -m / hbar / tf * sin(eeta) / mus
    pQy_pR /= 1. / AA
    # print pQy_pR

    pQy_peeta = -m / hbar * R / tf * cos(eeta) / mus
    pQy_peeta /= 1. / AA
    # print pQy_peeta

    pQy_pthetai = m / hbar * L_PM / ti * cos(thetai) * cos(phii) / mus
    pQy_pthetai /= 1. / AA
    # print pQy_pthetai

    pQy_pphii = -m / hbar * L_PM / ti * sin(thetai) * sin(phii) / mus
    pQy_pphii /= 1. / AA
    # print pQy_pphii

    pQy_p_vec = [
        pQy_pt, pQy_ptMD, pQy_pLPM, pQy_pLMS, pQy_pR, 0, pQy_peeta,
        pQy_pthetai, pQy_pphii
    ]
    pQy_p_vec = np.array(pQy_p_vec)
    J_Qy = pQy_p_vec / Q_len

    # ** Qz **
    pQz_pt = -m / hbar * (L_PM / ti / ti * sin(phii) +
                          z / tf / tf * L_MS / L_PM) / mus / mus
    pQz_pt /= 1. / AA / mus
    # print pQz_pt

    pQz_ptMD = m / hbar * z / tf / tf / mus / mus
    pQz_ptMD /= 1. / AA / mus
    # print pQz_ptMD

    pQz_pLPM = m / hbar * (sin(phii) / ti +
                           ti / tf / tf * z * L_MS / L_PM / L_PM) / mus
    pQz_pLPM /= 1. / AA
    # print pQz_pLPM

    pQz_pLMS = -m / hbar * z / tf / tf * ti / L_PM / mus
    pQz_pLMS /= 1. / AA
    # print pQz_pLMS

    pQz_pz = -m / hbar / tf / mus
    pQz_pz /= 1. / AA
    # print pQz_pz

    pQz_pphii = m / hbar * L_PM / ti * cos(phii) / mus
    pQz_pphii /= 1. / AA
    # print pQz_pphii

    pQz_p_vec = [
        pQz_pt, pQz_ptMD, pQz_pLPM, pQz_pLMS, 0, pQz_pz, 0, 0, pQz_pphii
    ]
    pQz_p_vec = np.array(pQz_p_vec)
    J_Qz = pQz_p_vec / Q_len

    # ** Here we need to extend the J vector for E to include the additional variables eeta, thetai, and phii **
    pE_p_vec = [pE_pt, pE_ptMD, pE_pLPM, pE_pLMS, pE_pR, pE_pz, 0, 0, 0]
    pE_p_vec = np.array(pE_p_vec)
    J_E = pE_p_vec / E
    J = np.array((J_Qx, J_Qy, J_Qz, J_E))

    # ## ** Parameters
    sigma_eeta = mcvine.units.parse(pixel.radius) / mcvine.units.parse(
        instrument.detsys_radius)
    # sigma_thetai = 0.01
    sigma_thetai = beamdivs.theta
    # sigma_phii = 0.01
    sigma_phii = beamdivs.phi
    sigma = np.array([
        sigma_t, sigma_tMD, sigma_LPM, sigma_LMS, sigma_R, sigma_z, sigma_eeta,
        sigma_thetai, sigma_phii
    ])
    sigma2 = sigma**2
    sigma2 = np.diag(sigma2)

    # print J.shape, sigma2.shape
    cov = np.dot(J, np.dot(sigma2, J.T))
    # print cov

    M = np.linalg.inv(cov)
    # print M

    # ## Ellipsoid
    # hkl = hkl0+hkl_dir*x
    # dh,dk,dl = dx2dhkl*dx
    dx2dhkl = np.array(hkl_dir)

    # dQ = dx * dx2dhkl dot hkl2Q
    # so dx2dQ = dx2dhkl * hkl2Q
    # dQ = dx * dx2dQ
    dx2dQ = np.dot(dx2dhkl, hkl2Q_mat)
    # print dx2dQ

    # [dQx,dQy,dQz,dE] = [dx dE] dot dxdE2dQdE
    L = dxdE2dQdE = np.array([list(dx2dQ) + [0], [0., 0., 0., 1]])

    # np.dot([1,1], dxdE2dQdE)

    # $ [dX1,\; dX2,\; dX3,\; dX4]\; M\; [dX1,\; dX2,\; dX3,\; dX4 ]^T = 2ln(2)$
    #
    # $ dX_i = \frac{dQ_i}{|Q|}$ for i = 1,2,3
    #
    # $ dX_4 = \frac{dE}{E}$
    #
    # Let
    # $ U = diag\big( \frac{1}{|Q|},\; \frac{1}{|Q|},\; \frac{1}{|Q|},\; 1/E \big) $
    #
    # $ [dx,\; dE]\; L U MU^TL^T [dx,\; dE ]^T = 2ln(2)$
    #
    # Let $N=L U MU^TL^T $
    # print Q_len, E
    U = np.diag([1. / Q_len, 1. / Q_len, 1. / Q_len, 1. / E])
    N = LUMUTLT = np.dot(L, np.dot(U, np.dot(M, np.dot(U.T, L.T))))
    # print N
    # print 2*np.log(2)
    r = np.linalg.eig(N)
    mR = r[1]
    lambdas = r[0]
    # print np.dot(mR, mR.T)
    # print np.dot(np.dot(mR.T, N), mR)

    # Make 4-D inverse covariance matrix:
    InvCov4D = UMUT = np.dot(U, np.dot(M, U.T))
    hklE2QE = scipy.linalg.block_diag(hkl2Q_mat, 1.)
    InvCov4D = np.dot(np.dot(hklE2QE, InvCov4D), hklE2QE.T)
    # print np.dot(cov, M) # should be Eye

    # $ u = [dx,\;dE]$
    #
    # $ u N u^T = 2ln(2)$        .... (1)
    #
    # Find eigen values ($\lambda_1$, $\lambda_2$) and eigne vectors ($e_1$, $e_2$, column vectors) of N,
    # and let
    #
    # $ R = [e_1,\;e_2] $
    #
    # Then
    #
    # $ N' = R^T N R = diag([\lambda_1, \lambda_2]) $
    #
    # or
    #
    # $ N = R N' R^T $
    #
    # With $N'$ we can rewrite (1) as
    #
    # $ u'N'{u'}^T = 2ln2 = \lambda_1 {u'}_1^2 + \lambda_2 {u'}_2^2 $
    #
    # where
    #
    # $ u' = u . R $

    # ${u'}_1 = \sqrt{2ln2/\lambda_1}*cos(\theta)$
    #
    # ${u'}_2 = \sqrt{2ln2/\lambda_2}*sin(\theta)$

    # In[ ]:

    RR = 2 * np.log(2)
    theta = np.arange(0, 360, 1.) * np.pi / 180
    u1p = np.sqrt(RR / lambdas[0]) * np.cos(theta)
    u2p = np.sqrt(RR / lambdas[1]) * np.sin(theta)
    up = np.array([u1p, u2p]).T

    # print up.shape
    u = np.dot(up, mR.T)

    if plot:
        from matplotlib import pyplot as plt
        plt.plot(u[:, 0], u[:, 1], '.')
        # plt.xlim(-.35, .1)
        # plt.ylim(-5., 5.)
        plt.show()
    # u: 2D ellipsoid coordinates
    # mR: 2D eigen vectors
    # lambdas: and 2D eigen values of the scaled inverse covariance
    # QxQyQzE_cov: 4D covariance matrix for Qx,Qy,Qz,E in instrument coordinate system
    # hklE_inv_cov: inverse 4D covariance matrix for hklE
    return dict(u=u,
                mR=mR,
                lambdas=lambdas,
                QxQyQzE_cov=cov,
                hklE_inv_cov=InvCov4D)
Example #16
0
 def updateSQEConvolution(self, uploaded_contents, uploaded_filename,
                          qgrid_dim, Nqsamples, temperature, Ei, *args):
     if uploaded_contents is None: return
     binfile = binfile_from_uploaded(uploaded_contents, uploaded_filename)
     if binfile.endswith('.zip'):
         zipfile = binfile
         # compute sqe
         Eaxis = np.linspace(-0. * Ei, .9 * Ei, 120)
         from mcni.utils import conversion
         Qmax = conversion.e2k(Ei) * 2
         Qaxis = np.linspace(0, Qmax, 100)
         from phonon import SQE_from_FCzip
         print temperature
         sqe = SQE_from_FCzip(Qaxis,
                              Eaxis,
                              zipfile,
                              Ei,
                              max_det_angle=140.,
                              T=temperature,
                              qgrid_dim=qgrid_dim,
                              Nqpoints=Nqsamples)
         os.unlink(zipfile)
     elif binfile.endswith('.h5'):
         import histogram.hdf as hh
         sqe = hh.load(binfile)
     # plot sqe
     z = sqe.I.T
     zmedian = np.median(z[z > 0])
     import plotly.graph_objs as go
     fig = go.Figure(data=[
         go.Heatmap(z=z,
                    x=sqe.Q,
                    y=sqe.E,
                    zmin=0.,
                    zmax=zmedian * 5,
                    colorscale='Viridis')
     ],
                     layout={
                         'title': 'Original',
                     })
     # convolve
     E_new, Q, I_new = self.convolveSQE(sqe, Ei, *args)
     z = I_new.T
     zmedian = np.median(z[z > 0])
     fig2 = go.Figure(data=[
         go.Heatmap(z=z,
                    x=Q,
                    y=E_new,
                    zmin=0.,
                    zmax=zmedian * 5,
                    colorscale='Viridis')
     ],
                      layout={
                          'title': 'Convolved',
                      })
     graph_style = {'height': '25em', 'width': '30em'}
     inline = {"display": "inline-flex"}
     return html.Div([
         html.Div([dcc.Graph(figure=fig, style=graph_style)], style=inline),
         html.Div([dcc.Graph(figure=fig2, style=graph_style)],
                  style=inline),
     ],
                     style={"display": "inline-flex"})
Example #17
0
def apply_corrections(I, Qbb, Ebb, N, mass, uc, doshist, T, Ei, max_det_angle):
    """apply remaining corrections to IQE. this needs to be combined with calcIQE"""
    from mcni.utils import conversion
    from multiphonon.forward.phonon import kelvin2mev
    from multiphonon.forward import phonon
    import histogram as H
    Q = (Qbb[1:] + Qbb[:-1]) / 2
    # correction 1
    ki = conversion.e2k(Ei)
    E = (Ebb[1:] + Ebb[:-1]) / 2
    Ef = Ei - E
    kf = conversion.e2k(Ef)
    v0 = uc.lattice.volume
    beta = 1. / (T * kelvin2mev)
    thermal_factor = 1. / 2 * (1. / np.tanh(E / 2 * beta) + 1)
    correction_E = thermal_factor * (2 * np.pi)**3 / v0 / (2 * ki * kf)
    correction_Q = 1. / Q
    # the additional 1/4pi comes from powder average. See notes
    correction = 1. / 4 / np.pi * np.outer(correction_Q, correction_E)
    I *= correction
    # # Correction 2 - DW
    dos_e = doshist.E if hasattr(doshist, 'E') else doshist.energy
    dos_dE = dos_e[1] - dos_e[0]
    dos_g = doshist.I / np.sum(doshist.I) / dos_dE
    DW2 = phonon.DWExp(Q,
                       M=mass,
                       E=dos_e,
                       g=dos_g,
                       beta=beta,
                       dE=dos_e[1] - dos_e[0])
    I *= np.exp(-DW2)[:, np.newaxis]
    IQEhist = H.histogram('IQE',
                          (H.axis('Q', boundaries=Qbb, unit='1./angstrom'),
                           H.axis('E', boundaries=Ebb, unit='meV')),
                          data=I)
    ## Normalize
    # first normalize by "event" count and make it density
    dQ = Q[1] - Q[0]
    dE = E[1] - E[0]
    IQEhist.I /= dQ * dE

    # Simulate "Vanadium" data
    def norm_at(E, Qaxis, Ei):
        "compute normalization(Q) array for the given energy transfer"
        ki = conversion.e2k(Ei)
        Ef = Ei - E
        kf = conversion.e2k(Ef)
        Qmin = np.abs(ki - kf)
        Qmax = ki + kf
        rt = np.zeros(Qaxis.shape)
        in_range = (Qaxis < Qmax) * (Qaxis > Qmin)
        rt[in_range] = (Qaxis * (2 * np.pi / ki / kf))[in_range]
        return rt

    norm_hist = IQEhist.copy()
    norm_hist.I[:] = 0
    for E_ in norm_hist.E:
        norm_hist[(), E_].I[:] = norm_at(E_, Q, Ei)
    # normalize
    IQEhist = IQEhist / norm_hist
    #
    ## Dynamical range
    DR_Qmin = ki - kf
    DR_Qmax = ((ki * ki + kf * kf -
                2 * ki * kf * np.cos(max_det_angle * np.pi / 180)))**.5
    I = IQEhist.I
    for iE, (E1, Qmin1, Qmax1) in enumerate(zip(E, DR_Qmin, DR_Qmax)):
        I[Q < Qmin1, iE] = np.nan
        I[Q > Qmax1, iE] = np.nan
    return IQEhist