Beispiel #1
0
    def test_compare_c_py_abeles(self):
        # test python and c are equivalent
        # but not the same file
        s = self.structure.slabs()[..., :4]

        # if not TEST_C_REFLECT:
        #     return
        assert_(_reflect.__file__ != _creflect.__file__)

        calc1 = _reflect.abeles(self.qvals, s)
        calc2 = _creflect.abeles(self.qvals, s)
        assert_almost_equal(calc1, calc2)
        calc1 = _reflect.abeles(self.qvals, s, scale=2.)
        calc2 = _creflect.abeles(self.qvals, s, scale=2.)
        assert_almost_equal(calc1, calc2)
        calc1 = _reflect.abeles(self.qvals, s, scale=0.5,
                                bkg=0.1)
        # threads = 1 is a non-threaded implementation
        calc2 = _creflect.abeles(self.qvals, s, scale=0.5,
                                 bkg=0.1, threads=1)
        # threads = 2 forces the calculation to go through multithreaded calcn,
        # even on single core processor
        calc3 = _creflect.abeles(self.qvals, s, scale=0.5,
                                 bkg=0.1, threads=2)
        assert_almost_equal(calc1, calc2)
        assert_almost_equal(calc1, calc3)
Beispiel #2
0
def _smeared_abeles_constant(q, w, resolution, threads=-1):
    """
    Fast resolution smearing for constant dQ/Q.

    Parameters
    ----------
    q: np.ndarray
        Q values to evaluate the reflectivity at
    w: np.ndarray
        Parameters for the reflectivity model
    resolution: float
        Percentage dq/q resolution. dq specified as FWHM of a resolution
        kernel.
    threads: int, optional
        Do you want to calculate in parallel? This option is only applicable if
        you are using the ``_creflect`` module. The option is ignored if using
        the pure python calculator, ``_reflect``.
    Returns
    -------
    reflectivity: np.ndarray
        The resolution smeared reflectivity
    """

    if resolution < 0.5:
        return refcalc.abeles(q, w, threads=threads)

    resolution /= 100
    gaussnum = 51
    gaussgpoint = (gaussnum - 1) / 2

    def gauss(x, s):
        return 1. / s / np.sqrt(2 * np.pi) * np.exp(-0.5 * x**2 / s / s)

    lowq = np.min(q)
    highq = np.max(q)
    if lowq <= 0:
        lowq = 1e-6

    start = np.log10(lowq) - 6 * resolution / _FWHM
    finish = np.log10(highq * (1 + 6 * resolution / _FWHM))
    interpnum = np.round(
        np.abs(1 * (np.abs(start - finish)) /
               (1.7 * resolution / _FWHM / gaussgpoint)))
    xtemp = np.linspace(start, finish, int(interpnum))
    xlin = np.power(10., xtemp)

    # resolution smear over [-4 sigma, 4 sigma]
    gauss_x = np.linspace(-1.7 * resolution, 1.7 * resolution, gaussnum)
    gauss_y = gauss(gauss_x, resolution / _FWHM)

    rvals = refcalc.abeles(xlin, w, threads=threads)
    smeared_rvals = np.convolve(rvals, gauss_y, mode='same')
    interpolator = InterpolatedUnivariateSpline(xlin, smeared_rvals)

    smeared_output = interpolator(q)
    smeared_output *= gauss_x[1] - gauss_x[0]
    return smeared_output
Beispiel #3
0
    def test_c_abeles(self):
        # test reflectivity calculation with values generated from Motofit
        calc = _creflect.abeles(self.qvals,
                                self.structure.slabs()[..., :4])
        assert_almost_equal(calc, self.rvals)

        # test for non-contiguous Q values
        tempq = self.qvals[0::5]
        assert_(tempq.flags['C_CONTIGUOUS'] is False)
        calc = _creflect.abeles(tempq, self.structure.slabs()[..., :4])
        assert_almost_equal(calc, self.rvals[0::5])
Beispiel #4
0
    def test_compare_c_py_abeles0(self):
        # test two layer system
        if not TEST_C_REFLECT:
            return
        layer0 = np.array([[0, 2.07, 0.01, 3], [0, 6.36, 0.1, 3]])
        calc1 = _reflect.abeles(self.qvals, layer0, scale=0.99, bkg=1e-8)
        calc2 = _creflect.abeles(self.qvals, layer0, scale=0.99, bkg=1e-8)
        assert_almost_equal(calc1, calc2)

        # test a negative background
        calc1 = _reflect.abeles(self.qvals, layer0, scale=0.99, bkg=-5e-7)
        calc2 = _creflect.abeles(self.qvals, layer0, scale=0.99, bkg=-5e-7)
        assert_almost_equal(calc1, calc2)
Beispiel #5
0
    def reflectivity(self, q, threads=0):
        """
        Calculate theoretical reflectivity of this structure

        Parameters
        ----------
        q : array-like
            Q values (Angstrom**-1) for evaluation
        threads : int, optional
            Specifies the number of threads for parallel calculation. This
            option is only applicable if you are using the ``_creflect``
            module. The option is ignored if using the pure python calculator,
            ``_reflect``. If `threads == 0` then all available processors are
            used.

        Notes
        -----
        Normally the reflectivity will be calculated using the Nevot-Croce
        approximation for Gaussian roughness between different layers. However,
        if individual components have non-Gaussian roughness (e.g. Tanh), then
        the overall reflectivity and SLD profile are calculated by
        micro-slicing. Micro-slicing involves calculating the specific SLD
        profile, dividing it up into small-slabs, and calculating the
        reflectivity from those. This normally takes much longer than the
        Nevot-Croce approximation. To speed the calculation up the
        `Structure.contract` property can be used.
        """
        return refcalc.abeles(q, self.slabs()[..., :4], threads=threads)
Beispiel #6
0
def _smearkernel(x, w, q, dq, threads):
    """
    Adaptive Gaussian quadrature integration

    Parameters
    ----------
    x : float
        Independent variable for integration.
    w : array-like
        The uniform slab model parameters in 'layer' form.
    q : float
        Nominal mean Q of normal distribution
    dq : float
        FWHM of a normal distribution.
    threads : int
        number of threads for parallel calculation
    Returns
    -------
    reflectivity : float
        Model reflectivity multiplied by the probability density function
        evaluated at a given distance, x, away from the mean Q value.
    """
    prefactor = 1 / np.sqrt(2 * np.pi)
    gauss = prefactor * np.exp(-0.5 * x * x)
    localq = q + x * dq / _FWHM
    return refcalc.abeles(localq, w, threads=threads) * gauss
Beispiel #7
0
def _smeared_abeles_fixed(qvals, w, dqvals, quad_order=17, threads=-1):
    """
    Resolution smearing that uses fixed order Gaussian quadrature integration
    for the convolution.

    Parameters
    ----------
    qvals : array-like
        The Q values for evaluation
    w : array-like
        The uniform slab model parameters in 'layer' form.
    dqvals : array-like
        dQ values corresponding to each value in `qvals`. Each dqval is the
        FWHM of a Gaussian approximation to the resolution kernel.
    quad-order : int, optional
        Specify the order of the Gaussian quadrature integration for the
        convolution.
    threads: int, optional
        Specifies the number of threads for parallel calculation. This
        option is only applicable if you are using the ``_creflect``
        module. The option is ignored if using the pure python calculator,
        ``_reflect``. If `threads == -1` then all available processors are
        used.

    Returns
    -------
    reflectivity : np.ndarray
        The smeared reflectivity
    """

    # The fixed order quadrature does not use scipy.integrate.fixed_quad.
    # That library function does one point at a time, whereas in this function
    # the integration is vectorised

    # get the gauss-legendre weights and abscissae
    abscissa, weights = gauss_legendre(quad_order)

    # get the normal distribution at that point
    prefactor = 1. / np.sqrt(2 * np.pi)

    def gauss(x):
        return np.exp(-0.5 * x * x)

    gaussvals = prefactor * gauss(abscissa * _INTLIMIT)

    # integration between -3.5 and 3.5 sigma
    va = qvals - _INTLIMIT * dqvals / _FWHM
    vb = qvals + _INTLIMIT * dqvals / _FWHM

    va = va[:, np.newaxis]
    vb = vb[:, np.newaxis]

    qvals_for_res = ((np.atleast_2d(abscissa) * (vb - va) + vb + va) / 2.)
    smeared_rvals = refcalc.abeles(qvals_for_res, w, threads=threads)

    smeared_rvals = np.reshape(smeared_rvals, (qvals.size, abscissa.size))

    smeared_rvals *= np.atleast_2d(gaussvals * weights)
    return np.sum(smeared_rvals, 1) * _INTLIMIT
Beispiel #8
0
 def test_compare_c_py_abeles2(self):
     # test two layer system
     if not TEST_C_REFLECT:
         return
     layer2 = np.array([[0, 2.07, 0.01, 3], [10, 3.47, 0.01, 3],
                        [100, 1.0, 0.01, 4], [0, 6.36, 0.1, 3]])
     calc1 = _reflect.abeles(self.qvals, layer2, scale=0.99, bkg=1e-8)
     calc2 = _creflect.abeles(self.qvals, layer2, scale=0.99, bkg=1e-8)
     assert_almost_equal(calc1, calc2)
Beispiel #9
0
    def test_c_abeles_reshape(self):
        # c reflectivity should be able to deal with multidimensional input
        if not TEST_C_REFLECT:
            return
        s = self.structure.slabs()[..., :4]

        reshaped_q = np.reshape(self.qvals, (2, 250))
        reshaped_r = self.rvals.reshape(2, 250)
        calc = _creflect.abeles(reshaped_q, s)
        assert_equal(reshaped_r.shape, calc.shape)
        assert_almost_equal(reshaped_r, calc, 15)
Beispiel #10
0
    def test_c_py_abeles_absorption(self):
        # https://github.com/andyfaff/refl1d_analysis/tree/master/notebooks
        q = np.linspace(0.008, 0.05, 500)
        depth = [0, 850, 0]
        rho = [2.067, 4.3, 6.]
        irho_zero = [0., 0.1, 0.]
        refnx_sigma = [np.nan, 35, 5.]

        w_zero = np.c_[depth, rho, irho_zero, refnx_sigma]
        py_abeles = _reflect.abeles(q, w_zero)
        c_abeles = _creflect.abeles(q, w_zero)
        assert_almost_equal(py_abeles, c_abeles)
Beispiel #11
0
    def reflectivity(self, q, threads=0):
        """
        Calculate theoretical reflectivity of this structure

        Parameters
        ----------
        q : array-like
            Q values (Angstrom**-1) for evaluation
        threads : int, optional
            Specifies the number of threads for parallel calculation. This
            option is only applicable if you are using the ``_creflect``
            module. The option is ignored if using the pure python calculator,
            ``_reflect``. If `threads == 0` then all available processors are
            used.
        """
        return refcalc.abeles(q, self.slabs[..., :4], threads=threads)
Beispiel #12
0
    def test_compare_refl1d(self):
        # refl1d calculated with:
        # from refl1d import abeles
        # x = np.linspace(0.005, 0.5, 1001)
        # z = abeles.refl(x / 2,
        #                 [0, 100, 200, 0],
        #                 [2.07, 3.45, 5., 6.],
        #                 irho=[0.0, 0.1, 0.01, 0],
        #                 sigma=[3, 1, 5, 0])
        # a = z.real ** 2 + z.imag ** 2

        layers = np.array([[0, 2.07, 0, 0],
                           [100, 3.45, 0.1, 3],
                           [200, 5.0, 0.01, 1],
                           [0, 6., 0, 5]])
        x = np.linspace(0.005, 0.5, 1001)
        calc1 = _reflect.abeles(x, layers)
        calc2 = _creflect.abeles(x, layers)
        refl1d = np.load(os.path.join(self.pth, 'refl1d.npy'))

        assert_almost_equal(calc1, refl1d)
        assert_almost_equal(calc2, refl1d)
Beispiel #13
0
def reflectivity(q, slabs, scale=1., bkg=0., dq=5., quad_order=17, threads=-1):
    r"""
    Abeles matrix formalism for calculating reflectivity from a stratified
    medium.

    Parameters
    ----------
    q : np.ndarray
        The qvalues required for the calculation.
        :math:`Q=\frac{4Pi}{\lambda}\sin(\Omega)`.
        Units = Angstrom**-1
    slabs : np.ndarray
        coefficients required for the calculation, has shape (2 + N, 4),
        where N is the number of layers

        - slabs[0, 0]
           ignored
        - slabs[N, 0]
           thickness of layer N
        - slabs[N+1, 0]
           ignored

        - slabs[0, 1]
           SLD_real of fronting (/1e-6 Angstrom**-2)
        - slabs[N, 1]
           SLD_real of layer N (/1e-6 Angstrom**-2)
        - slabs[-1, 1]
           SLD_real of backing (/1e-6 Angstrom**-2)

        - slabs[0, 2]
           SLD_imag of fronting (/1e-6 Angstrom**-2)
        - slabs[N, 2]
           iSLD_imag of layer N (/1e-6 Angstrom**-2)
        - slabs[-1, 2]
           iSLD_imag of backing (/1e-6 Angstrom**-2)

        - slabs[0, 3]
           ignored
        - slabs[N, 3]
           roughness between layer N-1/N
        - slabs[-1, 3]
           roughness between backing and layer N

    scale : float
        scale factor. All model values are multiplied by this value before
        the background is added
    bkg : float
        Q-independent constant background added to all model values.
    dq : float or np.ndarray, optional
        - `dq == 0`
           no resolution smearing is employed.
        - `dq` is a float
           a constant dQ/Q resolution smearing is employed.  For 5% resolution
           smearing supply 5.
        - `dq` is the same shape as q
           the array contains the FWHM of a Gaussian approximated resolution
           kernel. Point by point resolution smearing is employed.  Use this
           option if dQ/Q varies across your dataset.
        - `dq.ndim == q.ndim + 2` and `q.shape == dq[..., -3].shape`
           an individual resolution kernel is applied to each measurement
           point. This resolution kernel is a probability distribution function
           (PDF). `dqvals` will have the shape (qvals.shape, 2, M).  There are
           `M` points in the kernel. `dq[:, 0, :]` holds the q values for the
           kernel, `dq[:, 1, :]` gives the corresponding probability.
    quad_order: int, optional
        the order of the Gaussian quadrature polynomial for doing the
        resolution smearing. default = 17. Don't choose less than 13. If
        quad_order == 'ultimate' then adaptive quadrature is used. Adaptive
        quadrature will always work, but takes a _long_ time (2 or 3 orders
        of magnitude longer). Fixed quadrature will always take a lot less
        time. BUT it won't necessarily work across all samples. For
        example, 13 points may be fine for a thin layer, but will be
        atrocious at describing a multilayer with bragg peaks.
    threads: int, optional
        Specifies the number of threads for parallel calculation. This
        option is only applicable if you are using the ``_creflect``
        module. The option is ignored if using the pure python calculator,
        ``_reflect``. If `threads == -1` then all available processors are
        used.

    Example
    -------

    >>> from refnx.reflect import reflectivity
    >>> q = np.linspace(0.01, 0.5, 1000)
    >>> slabs = np.array([[0, 2.07, 0, 0],
    ...                   [100, 3.47, 0, 3],
    ...                   [500, -0.5, 0.00001, 3],
    ...                   [0, 6.36, 0, 3]])
    >>> print(reflectivity(q, slabs))

    """
    # constant dq/q smearing
    if isinstance(dq, numbers.Real) and float(dq) == 0:
        return refcalc.abeles(q, slabs, scale=scale, bkg=bkg, threads=threads)
    elif isinstance(dq, numbers.Real):
        dq = float(dq)
        return (scale *
                _smeared_abeles_constant(q, slabs, dq, threads=threads)) + bkg

    # point by point resolution smearing (each q point has different dq/q)
    if isinstance(dq, np.ndarray) and dq.size == q.size:
        dqvals_flat = dq.flatten()
        qvals_flat = q.flatten()

        # adaptive quadrature
        if quad_order == 'ultimate':
            smeared_rvals = (scale * _smeared_abeles_adaptive(
                qvals_flat, slabs, dqvals_flat, threads=threads) + bkg)
            return smeared_rvals.reshape(q.shape)
        # fixed order quadrature
        else:
            smeared_rvals = (
                scale * _smeared_abeles_fixed(qvals_flat,
                                              slabs,
                                              dqvals_flat,
                                              quad_order=quad_order,
                                              threads=threads) + bkg)
            return np.reshape(smeared_rvals, q.shape)

    # resolution kernel smearing
    elif (isinstance(dq, np.ndarray) and dq.ndim == q.ndim + 2
          and dq.shape[0:q.ndim] == q.shape):

        qvals_for_res = dq[:, 0, :]
        # work out the reflectivity at the kernel evaluation points
        smeared_rvals = refcalc.abeles(qvals_for_res, slabs, threads=threads)

        # multiply by probability
        smeared_rvals *= dq[:, 1, :]

        # now do simpson integration
        rvals = scipy.integrate.simps(smeared_rvals, x=dq[:, 0, :])

        return scale * rvals + bkg

    return None
Beispiel #14
0
 def test_c_abeles_multithreaded(self):
     _creflect.abeles(self.qvals, self.structure.slabs()[..., :4],
                      threads=4)