示例#1
0
    def __init__(self, shape, pixel_size, sample_detector, energy,
                 delta_energy):
        """ Creates instance of monochromatic (area) XRD detector.

        Creates XRD detector with correct geometry and experimental setup
        (i.e. sample to detector distance, energy and energy resolution).

        Inherits from Rings/Peaks classes, allowing for the
        calculation/estimation and visualisation of intensity profiles and
        Debye-Scherrer rings for materials or combinations of materials/phases.

        Args:
            shape (tuple): Detector shape (x, y) in pixels
            pixel_size (float): Pixel size (mm)
            sample_detector (float): Sample to detector distance (mm).
            energy (float): X-ray energy (keV)
            energy_sigma (float): Energy resolution (keV)
        """
        self.method = 'mono'
        self.energy = energy

        #  Instantiate position array (r, phi, 2theta, q) for detector
        y, x = np.ogrid[:float(shape[0]), :float(shape[1])]
        x, y = x - (shape[0] - 1) / 2, y - (shape[1] - 1) / 2
        r = (x**2 + y**2)**.5
        self.phi = np.arctan(y / x)
        self.phi[np.logical_and(x < 0, y > 0)] += np.pi
        self.phi[np.logical_and(x < 0, y < 0)] -= np.pi
        self.two_theta = np.arctan(r * pixel_size / sample_detector)
        self.q = tth_to_q(self.two_theta, energy)

        # Beam energy variation (used to estimate FWHM)
        tth = np.linspace(0, self.two_theta.max(), 100)
        fwhm_q = e_to_q(delta_energy, tth)
        fwhm_tth = q_to_tth(fwhm_q, energy)

        # FWHM should vary approx. with Caglioti polynomial
        self._fwhm = [0.01,
                      0.01]  #np.polyfit(np.tan(tth / 2), fwhm_tth **2, 3)

        # Flux wrt. energy/q - i.e. no variation for mono.
        self._flux = np.array([[0, 1], [self.q.max(), 1]])
        # assert np.array_equal(self._flux[:, 0], [0, self.q.max()])
        self.flux_q = interp1d(self._flux[:, 0], self._flux[:, 1])

        # Parameter store for save/reload
        self._det_param = {
            'shape': shape,
            'pixel_size': pixel_size,
            'sample_detector': sample_detector,
            'energy': energy,
            'delta_energy': delta_energy
        }

        # Empty dicts for storing peaks / materials
        self.a, self.fwhm, self.q0 = {}, {}, {}
        self.materials, self.hkl = {}, {}
        self._back = np.ones(1)
示例#2
0
 def fwhm_q(self, q, param=None):
     if param is None:
         param = self._fwhm
     if self.method == 'mono':
         theta = q_to_tth(q, self.energy) / 2
         fwhm_tth = np.polyval(param, np.tan(theta))**0.5
         return tth_to_q(fwhm_tth, self.energy)
     else:
         # print(np.polyval(param, q))
         return np.polyval(param, q)**0.5
示例#3
0
    def intensity_factors(self, material, b=1, q=None, plot=True, x_axis='q'):
        """ Calculates normalised intensity factors (with option for plotting).

        Finds intensity factors wrt. q based on material and diffraction setup.
        Either returns values or plots for visualisation.

        Args:
            material (str): Element symbol (compound formula)
            b (float): B factor
            q (np.ndarray): Values of q to calculate intensty factors at.
            plot (bool): Plot selector
            x_axis (str): Plot relative to 'q' or 'energy' / '2theta'

        Returns:
            tuple: Intensity factor components(i_lp, i_sf, i_tf, flux)
        """
        # Make decorator from this?
        valid = ['q'] + ['2theta' if self.method == 'mono' else 'energy']
        error = "Can't plot wrt. {} in {} mode".format(x_axis, self.method)
        assert x_axis in valid, error

        method = self.method

        if q is None and method == 'mono':
            x, y = self.q.shape[0] / 2, self.q.shape[1] / 2
            bins = (x**2 + y**2)**.5
            q = np.linspace(0, self.q.max(), bins)
        else:
            q = self.q if q is None else q
        # Intensity factors

        if method == 'mono':
            i_lp = lp_factor(q_to_tth(q, self.energy))
        else:
            i_lp = np.ones_like(q)
        i_sf = scattering_factor(material, q)  # consider adding complex
        i_tf = temp_factor(q, b)
        flux = self.flux_q(q)

        if plot:
            ind = np.argsort(q)[q > 2]
            q = q if x_axis == 'q' else self._convert(q)
            labels = ['flux', 'lorentz', 'scatter', 'temp']
            for i_f, label in zip([flux, i_lp, i_sf, i_tf], labels):
                plt.plot(q[ind], i_f[ind] / i_f[ind].max(), '-', label=label)
            total = (i_sf[ind]**2) * i_lp[ind] * i_tf[ind] * flux[ind]
            plt.plot(q[ind], total / np.max(total), 'k-.', label='total')
            plt.ylim([0, 1.05])
            legend = plt.legend()
            legend.get_frame().set_color('white')
            plt.ylabel('Relative Intensity Factor')
            plt.xlabel(self.label_dict[x_axis])
            plt.show()
        else:
            return i_lp, i_sf, i_tf, flux
示例#4
0
文件: detectors.py 项目: casimp/pyxpb
    def __init__(self, shape, pixel_size, sample_detector, energy,
                 delta_energy):
        """ Creates instance of monochromatic (area) XRD detector.

        Creates XRD detector with correct geometry and experimental setup
        (i.e. sample to detector distance, energy and energy resolution).

        Inherits from Rings/Peaks classes, allowing for the
        calculation/estimation and visualisation of intensity profiles and
        Debye-Scherrer rings for materials or combinations of materials/phases.

        Args:
            shape (tuple): Detector shape (x, y) in pixels
            pixel_size (float): Pixel size (mm)
            sample_detector (float): Sample to detector distance (mm).
            energy (float): X-ray energy (keV)
            energy_sigma (float): Energy resolution (keV)
        """
        self.method = 'mono'
        self.energy = energy

        #  Instantiate position array (r, phi, 2theta, q) for detector
        y, x = np.ogrid[:float(shape[0]), :float(shape[1])]
        x, y = x - (shape[0] - 1) / 2, y - (shape[1] - 1) / 2
        r = (x ** 2 + y ** 2) ** .5
        self.phi = np.arctan(y / x)
        self.phi[np.logical_and(x < 0, y > 0)] += np.pi
        self.phi[np.logical_and(x < 0, y < 0)] -= np.pi
        self.two_theta = np.arctan(r * pixel_size / sample_detector)
        self.q = tth_to_q(self.two_theta, energy)

        # Beam energy variation (used to estimate FWHM)
        tth = np.linspace(0, self.two_theta.max(), 100)
        fwhm_q = e_to_q(delta_energy, tth)
        fwhm_tth = q_to_tth(fwhm_q, energy)

        # FWHM should vary approx. with Caglioti polynomial
        self._fwhm = [0.01, 0.01]#np.polyfit(np.tan(tth / 2), fwhm_tth **2, 3)

        # Flux wrt. energy/q - i.e. no variation for mono.
        self._flux = np.array([[0, 1], [self.q.max(), 1]])
        # assert np.array_equal(self._flux[:, 0], [0, self.q.max()])
        self.flux_q = interp1d(self._flux[:, 0], self._flux[:, 1])

        # Parameter store for save/reload
        self._det_param = {'shape': shape, 'pixel_size': pixel_size,
                           'sample_detector': sample_detector,
                           'energy': energy, 'delta_energy': delta_energy}

        # Empty dicts for storing peaks / materials
        self.a, self.fwhm, self.q0 = {}, {}, {}
        self.materials, self.hkl = {}, {}
        self._back = np.ones(1)
示例#5
0
    def _convert(self, q):
        """ Helper function to convert q to 2theta (mono) or energy (edxd).

        Args:
            q (float, ndarray): q in A^-1

        Returns:
            float, ndarray: Energy (keV) or 2theta (rad)
        """
        if self.method == 'mono':
            return q_to_tth(q, self.energy) * 180 / np.pi
        else:
            return q_to_e(q, self.two_theta)