示例#1
0
def set_params(peaks):
    """
    This module takes in the list of peaks from the peak detection modules, and then uses
    that to initialize parameters for a set of Pseudo-Voigt models that are not yet fit.
    There is a single model for every peak.

    Args:
        peaks (list): A list containing the x and y-values (in tuples) of the peaks.

    Returns:
        mod (lmfit.models.PseudoVoigtModel or lmfit.model.CompositeModel): This is an array of
                        the initialized Pseudo-Voigt models. The array contains all of the values
                        that are found in `pars` that are fed to an lmfit lorentzian model class.
        pars (lmfit.parameter.Parameters): An array containing the parameters for each peak
                        that were generated through the use of a Lorentzian fit. The pars
                        array contains a center value, a height, a sigma, and an amplitude
                        value. The center value is allowed to vary +- 10 wavenumber from
                        the peak max that was detected in scipy. Some wiggle room was allowed
                        to help mitigate problems from slight issues in the peakdetect
                        algorithm for peaks that might have relatively flat maxima. The height
                        value was allowed to vary between 0 and 1, as it is assumed the y-values
                        are normalized. Sigma is set to a maximum of 500, as we found that
                        giving it an unbound maximum led to a number of peaks that were
                        unrealistic for Raman spectra (ie, they were far too broad, and shallow,
                        to correspond to real data. Finally, the amplitude for the peak was set
                        to a minimum of 0, to prevent negatives.
    """
    # handling errors in inputs
    if not isinstance(peaks, list):
        raise TypeError('Passed value of `peaks` is not a list! Instead, it is: '
                        + str(type(peaks)))
    for i, _ in enumerate(peaks):
        if not isinstance(peaks[i], tuple):
            raise TypeError("""Passed value of `peaks[{}]` is not a tuple.
             Instead, it is: """.format(i) + str(type(peaks[i])))
    peak_list = []
    for i, _ in enumerate(peaks):
        prefix = 'p{}_'.format(i+1)
        peak = PseudoVoigtModel(prefix=prefix)
        if i == 0:
            pars = peak.make_params()
        else:
            pars.update(peak.make_params())
        pars[prefix+'center'].set(peaks[i][0], vary=False)
        pars[prefix+'height'].set(peaks[i][1], vary=False)
        pars[prefix+'sigma'].set(50, min=0, max=500)
        pars[prefix+'amplitude'].set(min=0)
        peak_list.append(peak)
        if i == 0:
            mod = peak_list[i]
        else:
            mod = mod + peak_list[i]
    return mod, pars
示例#2
0
 def add_peak(self, peak):
     """Adds a new Peak to the Model list."""
     if peak.region is not self._region:
         logger.error("peak with ID {} does not belong to region ID {}"
                      "".format(peak.ID, self._region.ID))
         raise ValueError("Peak does not belong to Region")
     if peak.model_name == "PseudoVoigt":
         model = PseudoVoigtModel(prefix=peak.prefix)
         # model.set_param_hint("fraction", vary=False, value=0.2)
         params = model.make_params()
         self._params += params
         fwname = "{}fwhm".format(peak.prefix)
         sigmaname = "{}sigma".format(peak.prefix)
         ampname = "{}amplitude".format(peak.prefix)
         centername = "{}center".format(peak.prefix)
         # alphaname = "{}fraction".format(peak.prefix)
         params[fwname].set(value=params[fwname].value, vary=True, min=0)
         params[sigmaname].set(expr="{}/2".format(fwname))
         params[ampname].set(min=0)
         params[centername].set(min=0)
     elif peak.model_name == "Doniach":
         model = MyDoniach(prefix=peak.prefix)
         params = model.make_params()
         self._params += params
         sigmaname = "{}sigma".format(peak.prefix)
         ampname = "{}amplitude".format(peak.prefix)
         centername = "{}center".format(peak.prefix)
         gammaname = "{}gamma".format(peak.prefix)
         params[sigmaname].set(min=0)
         params[ampname].set(min=0)
         params[centername].set(min=0)
         params[gammaname].set(vary=True)
     elif peak.model_name == "ConvDoniach":
         model = ConvolvedDoniach(prefix=peak.prefix)
         params = model.make_params()
         self._params += params
         sigmaname = "{}sigma".format(peak.prefix)
         ampname = "{}amplitude".format(peak.prefix)
         centername = "{}center".format(peak.prefix)
         gammaname = "{}gamma".format(peak.prefix)
         convname = "{}conv".format(peak.prefix)
         params[sigmaname].set(min=0)
         params[ampname].set(min=0)
         params[centername].set(min=0)
         params[gammaname].set(vary=True)
         params[convname].set(min=0)
     else:
         raise NotImplementedError("Only PseudoVoigt models supported")
     self._single_models[peak.prefix] = model
示例#3
0
    def test_model_nan_policy(self):
        x = np.linspace(0, 10, 201)
        np.random.seed(0)
        y = gaussian(x, 10.0, 6.15, 0.8)
        y += gaussian(x, 8.0, 6.35, 1.1)
        y += gaussian(x, 0.25, 6.00, 7.5)
        y += np.random.normal(size=len(x), scale=0.5)

        y[55] = y[91] = np.nan
        mod = PseudoVoigtModel()
        params = mod.make_params(amplitude=20, center=5.5,
                                 sigma=1, fraction=0.25)
        params['fraction'].vary = False
        # with raise, should get a ValueError
        result = lambda: mod.fit(y, params, x=x, nan_policy='raise')
        self.assertRaises(ValueError, result)

        # with propagate, should get no error, but bad results
        result = mod.fit(y, params, x=x, nan_policy='propagate')
        self.assertTrue(result.success)
        self.assertTrue(np.isnan(result.chisqr))
        self.assertTrue(np.isnan(result.aic))
        self.assertFalse(result.errorbars)
        self.assertTrue(result.params['amplitude'].stderr is None)
        self.assertTrue(abs(result.params['amplitude'].value - 20.0) < 0.001)

        # with omit, should get good results
        result = mod.fit(y, params, x=x, nan_policy='omit')
        self.assertTrue(result.success)
        self.assertTrue(result.chisqr > 2.0)
        self.assertTrue(result.aic < -100)
        self.assertTrue(result.errorbars)
        self.assertTrue(result.params['amplitude'].stderr > 0.1)
        self.assertTrue(abs(result.params['amplitude'].value - 20.0) < 5.0)
        self.assertTrue(abs(result.params['center'].value - 6.0) < 0.5)
示例#4
0
def make_model(peak_positions,
               fwhm=0.05,
               max_fwhm=0.5,
               pos_range=0.5,
               amplitude=1000.):
    n_peaks = len(peak_positions)
    pars = Parameters()

    bg = LinearModel(prefix='bg_')
    pars.update(bg.make_params(slope=0, intercept=0))

    mod = bg
    #pars['bg_intercept'].set(vary=True)
    #pars['bg_slope'].set(vary=True)

    for i in range(n_peaks):
        prefix = 'pk{}_'.format(i)
        peak = PseudoVoigtModel(prefix=prefix)
        # Set this zero
        pars.update(peak.make_params())
        pars[prefix + 'center'].set(peak_positions[i],
                                    min=peak_positions[i] - pos_range,
                                    max=peak_positions[i] + pos_range,
                                    vary=True)
        pars[prefix + 'sigma'].set(fwhm, min=0., max=max_fwhm, vary=True)
        pars[prefix + 'amplitude'].set(amplitude, min=0., vary=True)
        pars[prefix + 'fraction'].set(0.0, min=0., max=1., vary=True)
        mod += peak
    return mod, pars
示例#5
0
    def test_model_nan_policy(self):
        x = np.linspace(0, 10, 201)
        np.random.seed(0)
        y = gaussian(x, 10.0, 6.15, 0.8)
        y += gaussian(x, 8.0, 6.35, 1.1)
        y += gaussian(x, 0.25, 6.00, 7.5)
        y += np.random.normal(size=len(x), scale=0.5)

        y[55] = y[91] = np.nan
        mod = PseudoVoigtModel()
        params = mod.make_params(amplitude=20, center=5.5,
                                 sigma=1, fraction=0.25)
        params['fraction'].vary = False
        # with raise, should get a ValueError
        result = lambda: mod.fit(y, params, x=x, nan_policy='raise')
        self.assertRaises(ValueError, result)

        # with propagate, should get no error, but bad results
        result = mod.fit(y, params, x=x, nan_policy='propagate')
        self.assertTrue(result.success)
        self.assertTrue(np.isnan(result.chisqr))
        self.assertTrue(np.isnan(result.aic))
        self.assertFalse(result.errorbars)
        self.assertTrue(result.params['amplitude'].stderr is None)
        self.assertTrue(abs(result.params['amplitude'].value - 20.0) < 0.001)

        # with omit, should get good results
        result = mod.fit(y, params, x=x, nan_policy='omit')
        self.assertTrue(result.success)
        self.assertTrue(result.chisqr > 2.0)
        self.assertTrue(result.aic < -100)
        self.assertTrue(result.errorbars)
        self.assertTrue(result.params['amplitude'].stderr > 0.1)
        self.assertTrue(abs(result.params['amplitude'].value - 20.0) < 5.0)
        self.assertTrue(abs(result.params['center'].value - 6.0) < 0.5)
示例#6
0
def set_params(peaks):
    """
    This function takes in the list of peaks from the peak detection modules, and then uses
    that to initialize parameters for a set of Pseudo-Voigt models that are not yet fit.
    There is a single model for every peak.

    Args:
        peaks (list): A list containing tuples of the x_data (wavenumber) and y_data (counts)
                      values of the peaks.

    Returns:
        mod (lmfit.models.PseudoVoigtModel or lmfit.model.CompositeModel): This is an array of
                        the initialized pseudo-Voigt models. The array contains all of the values
                        that are found in `pars` that are fed to an lmfit lorentzian model class.
        pars (lmfit.parameter.Parameters): An array containing the parameters for each peak
                        that were generated through the use of a Lorentzian fit. The pars
                        array contains values for fraction, center, height, sigma, the full width
                        at half maximum (fwhm = 2*sigma), and amplitude.
    """
    # handling errors in inputs
    if not isinstance(peaks, list):
        raise TypeError(
            'Passed value of `peaks` is not a list! Instead, it is: ' +
            str(type(peaks)))
    for i, peak in enumerate(peaks):
        if not isinstance(peak, tuple):
            raise TypeError("""The {} value of `peaks` is not a tuple.
             Instead, it is: """.format(i) + str(type(peak)))
    peak_list = []
    for i, value in enumerate(peaks):
        prefix = 'p{}_'.format(i + 1)
        peak = PseudoVoigtModel(prefix=prefix)
        if i == 0:
            pars = peak.make_params()
        else:
            pars.update(peak.make_params())
        # constraints on profile desciptors
        pars[prefix + 'center'].set(value[0], vary=False)
        pars[prefix + 'height'].set(min=0.1 * value[1])
        pars[prefix + 'sigma'].set(10, min=1, max=100)
        pars[prefix + 'amplitude'].set(100 * value[1], min=0)
        peak_list.append(peak)
        if i == 0:
            mod = peak_list[i]
        else:
            mod = mod + peak_list[i]
    return mod, pars
示例#7
0
    def test_model_nan_policy(self):
        """Tests for nan_policy with NaN values in the input data."""
        x = np.linspace(0, 10, 201)
        np.random.seed(0)
        y = gaussian(x, 10.0, 6.15, 0.8)
        y += gaussian(x, 8.0, 6.35, 1.1)
        y += gaussian(x, 0.25, 6.00, 7.5)
        y += np.random.normal(size=len(x), scale=0.5)

        # with NaN values in the input data
        y[55] = y[91] = np.nan
        mod = PseudoVoigtModel()
        params = mod.make_params(amplitude=20,
                                 center=5.5,
                                 sigma=1,
                                 fraction=0.25)
        params['fraction'].vary = False

        # with raise, should get a ValueError
        result = lambda: mod.fit(y, params, x=x, nan_policy='raise')
        msg = (
            'NaN values detected in your input data or the output of your '
            'objective/model function - fitting algorithms cannot handle this!'
        )
        self.assertRaisesRegex(ValueError, msg, result)

        # with propagate, should get no error, but bad results
        result = mod.fit(y, params, x=x, nan_policy='propagate')
        self.assertTrue(result.success)
        self.assertTrue(np.isnan(result.chisqr))
        self.assertTrue(np.isnan(result.aic))
        self.assertFalse(result.errorbars)
        self.assertTrue(result.params['amplitude'].stderr is None)
        self.assertTrue(abs(result.params['amplitude'].value - 20.0) < 0.001)

        # with omit, should get good results
        result = mod.fit(y, params, x=x, nan_policy='omit')
        self.assertTrue(result.success)
        self.assertTrue(result.chisqr > 2.0)
        self.assertTrue(result.aic < -100)
        self.assertTrue(result.errorbars)
        self.assertTrue(result.params['amplitude'].stderr > 0.1)
        self.assertTrue(abs(result.params['amplitude'].value - 20.0) < 5.0)
        self.assertTrue(abs(result.params['center'].value - 6.0) < 0.5)

        # with 'wrong_argument', should get a ValueError
        err_msg = r"nan_policy must be 'propagate', 'omit', or 'raise'."
        with pytest.raises(ValueError, match=err_msg):
            mod.fit(y, params, x=x, nan_policy='wrong_argument')
示例#8
0
文件: section.py 项目: SHDShim/PeakPo
 def prepare_for_fitting(self, poly_order, maxwidth, centerrange):
     """
     :param x_center: numpy array of initial x values at picked centers
     :param y_center: numpy array of initial y values at picked centers
     :param fwhm: single float number for initial fwhm value
     """
     self.set_baseline(poly_order)
     baseline_mod = PolynomialModel(poly_order, prefix='b_')
     mod = baseline_mod
     pars = baseline_mod.make_params()
     peakinfo = {}
     for i in range(poly_order + 1):
         prefix = "b_c{0:d}".format(i)
         pars[prefix].set(value=self.baseline_in_queue[i]['value'],
                          vary=self.baseline_in_queue[i]['vary'])
     i = 0
     for peak in self.peaks_in_queue:
         prefix = "p{0:d}_".format(i)
         peak_mod = PseudoVoigtModel(prefix=prefix, )
         pars.update(peak_mod.make_params())
         pars[prefix + 'center'].set(value=peak['center'],
                                     min=peak['center'] - centerrange,
                                     max=peak['center'] + centerrange,
                                     vary=peak['center_vary'])
         pars[prefix + 'sigma'].set(value=peak['sigma'],
                                    min=0.0,
                                    vary=peak['sigma_vary'],
                                    max=maxwidth)
         pars[prefix + 'amplitude'].set(value=peak['amplitude'],
                                        min=0,
                                        vary=peak['amplitude_vary'])
         pars[prefix + 'fraction'].set(value=peak['fraction'],
                                       min=0.,
                                       max=1.,
                                       vary=peak['fraction_vary'])
         peakinfo[prefix + 'phasename'] = peak['phasename']
         peakinfo[prefix + 'h'] = peak['h']
         peakinfo[prefix + 'k'] = peak['k']
         peakinfo[prefix + 'l'] = peak['l']
         mod += peak_mod
         i += 1
     self.parameters = pars
     self.peakinfo = peakinfo
     self.fit_model = mod
示例#9
0
 def test_param_hint_explicit_value(self):
     # tests Github Issue 384
     pmod = PseudoVoigtModel()
     params = pmod.make_params(sigma=2, fraction=0.77)
     assert_allclose(params['fraction'].value, 0.77, rtol=0.01)
示例#10
0
def fitMo3d(filename, groupNo, regionNo, E_b_min, E_b_max, doplot=False):
    """Fit a Moly 3d signal using elemental and oxide components (two for each peak, 3/2 and 5/2).
    A shirley background is subtracted first.
    The elemental Mo components are fittet to the PseudoVoigt line shape.
    Oxide components are fitted to a Gaussian line shape.

    Parameters
    ----------
    filename : string
        location of the data file, expects an SpecsLab xml file.
    groupNo : int
        index of the group of spectra (from 0)
    regionNo : int
        index of the spectrum that should be fitted (from 0)
    E_b_min : float
        region boundary
    E_b_max : float
        region boundary
    doplot : bool
        wether or not to plot the fit and components (default False)
    """

    #load data from SpecsLab xml file and select region
    region = get_region(filename, groupNo, regionNo)

    # determine index of left and right boundary
    index1 = np.where(abs(region.x_be - (E_b_min)) < 1e-10)
    index2 = np.where(abs(region.x_be - (E_b_max)) < 1e-10)

    E = region.x_be[index1[0][0]:index2[0][0]]  #binding energy scale
    s = region.y_avg_counts_mcd[index1[0][0]:index2[0][0]]  #signal

    #signal with background substacted
    epsilon, sb, B = remove_shirley_background(s, E, 3, 1e-6)

    ######################
    # set up model components and set constraints for the fit
    ######################

    pre11 = 'Mo3d_32_1_'
    mod11 = PseudoVoigtModel(prefix=pre11)
    pars = mod11.make_params()
    pars.add('delta', value=3, min=2, max=4)
    pars[pre11 + 'amplitude'].set(9000, min=500, max=100000)
    pars[pre11 + 'center'].set(-231.2, min=-231.4, max=-231)
    pars[pre11 + 'sigma'].set(0.3, min=0.1, max=0.4)
    pars[pre11 + 'fraction'].set(0.5, min=0.1, max=1)

    mod21 = PseudoVoigtModel(prefix='Mo3d_52_1_')
    pars.update(mod21.make_params())
    pars['Mo3d_52_1_amplitude'].set(
        expr='3/2*Mo3d_32_1_amplitude')  #(12000, min=500, max=100000)
    pars['Mo3d_52_1_center'].set(expr='Mo3d_32_1_center+delta'
                                 )  #same distance between 3/2 and 5/2 peaks
    #pars['Mo3d_52_1_sigma'    ].set(expr='1.0*Mo3d_32_1_sigma') #all have same sigma value
    pars['Mo3d_52_1_fraction'].set(
        expr='1.0*Mo3d_32_1_fraction')  #(0.5, min=0.2, max=1)

    pre12 = 'MoO23d_32_2_'
    mod12 = GaussianModel(prefix=pre12)
    pars.update(mod12.make_params())
    pars[pre12 + 'amplitude'].set(2050, min=500, max=100000)
    pars[pre12 + 'center'].set(-234.5, min=-236, max=-234)
    pars[pre12 + 'sigma'].set(0.3, min=0.2, max=2)

    pre22 = 'MoO23d_52_2_'
    mod22 = GaussianModel(prefix=pre22)
    pars.update(mod22.make_params())
    pars[pre22 + 'amplitude'].set(
        expr='3/2*MoO23d_32_2_amplitude')  #(1050, min=500, max=100000)
    pars[pre22 + 'center'].set(expr='MoO23d_32_2_center+delta'
                               )  #same distance between 3/2 and 5/2 peaks
    pars[pre22 + 'sigma'].set(
        expr='1.0*MoO23d_32_2_sigma')  #all have same sigma value

    pre13 = 'MoO23d_32_3_'
    mod13 = GaussianModel(prefix=pre13)
    pars.update(mod13.make_params())
    pars[pre13 + 'amplitude'].set(2050, min=500, max=100000)
    pars[pre13 + 'center'].set(-235.5, min=-237, max=-234)
    pars[pre13 + 'sigma'].set(0.3, min=0.2, max=2)

    pre23 = 'MoO23d_52_3_'
    mod23 = GaussianModel(prefix=pre23)
    pars.update(mod23.make_params())
    pars[pre23 + 'amplitude'].set(
        expr='3/2*MoO23d_32_3_amplitude')  #(1050, min=500, max=100000)
    pars[pre23 + 'center'].set(expr='MoO23d_32_3_center+delta'
                               )  #same distance between 3/2 and 5/2 peaks
    pars[pre23 + 'sigma'].set(
        expr='1.0*MoO23d_32_3_sigma')  #all have same sigma value

    #composite model is a sum of the components
    mod = mod11 + mod21 + mod12 + mod22 + mod13 + mod23
    #perform the fit
    out = mod.fit(sb, pars, x=E)
    print(out.fit_report(min_correl=0.25))

    if (doplot):
        fig = plt.figure(None, figsize=(6, 4))
        #plot individual components
        comps = mod.eval_components(params=out.params, x=E)

        labels = [
            'Mo', 'Mo', 'MoO$_3$', 'MoO$_3$', 'MoO$_2$', 'MoO$_2$', 'MoO$_2$',
            'MoO$_2$'
        ]

        for n, key in enumerate(comps):
            if ((n % 2) == 0):
                plt.plot(-E,
                         comps[key] / 10**3,
                         color=colors[n],
                         label=labels[n])
            else:
                plt.plot(-E, comps[key] / 10**3, color=colors[n - 1])
                #plt.fill_between(-E, 0.0, comps[key]/10**3, facecolor=colors[int(i/2)], alpha=0.5)

        #plot measured signal (dots) and best fit (sum of components)
        plt.plot(-E, sb / 10**3, 'k.', label='measurement')
        #plt.plot( -E , s/10**3 , 'k-')
        #plt.plot( -E , B/10**3 , 'k--')
        plt.plot(-E, out.best_fit / 10**3, color=colors[1], label='total fit')
        ax = mpl.pyplot.gca()
        ax.set_xlim(241, -E_b_max)
        plt.xlabel('binding energy (eV)')
        plt.ylabel('counts (arb. u.)')

        print(out.best_values['MoO23d_32_2_center'] -
              out.best_values['Mo3d_32_1_center'])
        print(out.best_values['MoO23d_32_3_center'] -
              out.best_values['Mo3d_32_1_center'])

        ## reference : also plot pure Mo signal
        #load data from SpecsLab xml file and select region
        region = get_region(filename, 3, 2)

        # determine index of left and right boundary
        index1 = np.where(abs(region.x_be - (-235)) < 1e-10)
        index2 = np.where(abs(region.x_be - (E_b_max)) < 1e-10)

        E = region.x_be[index1[0][0]:index2[0][0]]  #binding energy scale
        s = region.y_avg_counts_mcd[index1[0][0]:index2[0][0]]  #signal

        epsilon, sb, B = remove_shirley_background(s, E, 3, 1e-6)
        plt.plot(-E, sb / (6 * 10**3), 'k--', label='pure Mo')
        plt.legend()

        fig.tight_layout()
示例#11
0
def fitpureMo3d(filename, groupNo, regionNo, E_b_min, E_b_max, doplot=False):
    """Fit a Moly 3d signal using elemental and oxide components (two for each peak, 3/2 and 5/2).
    A shirley background is subtracted first.
    The elemental Mo components are fittet to the PseudoVoigt line shape.
    Oxide components are fitted to a Gaussian line shape.

    Parameters
    ----------
    filename : string
        location of the data file, expects an SpecsLab xml file.
    groupNo : int
        index of the group of spectra (from 0)
    regionNo : int
        index of the spectrum that should be fitted (from 0)
    E_b_min : float
        region boundary
    E_b_max : float
        region boundary
    doplot : bool
        wether or not to plot the fit and components (default False)
    """

    #load data from SpecsLab xml file and select region
    region = get_region(filename, groupNo, regionNo)

    #Sb = VAMAS.VAMASExperiment('P006_Sb_surveyf.vms');
    #regions = [data[0][2], data[1][1], data[2][1]]
    print(region.x_be)

    # determine index of left and right boundary
    index1 = np.where(abs(region.x_be - (E_b_min)) < 1e-10)
    index2 = np.where(abs(region.x_be - (E_b_max)) < 1e-10)

    E = region.x_be[index1[0][0]:index2[0][0]]  #binding energy scale
    s = region.y_avg_counts_mcd[index1[0][0]:index2[0][0]]  #signal

    #signal with background substacted
    epsilon, sb, B = remove_shirley_background(s, E, 3, 1e-6)

    ######################
    # set up model components and set constraints for the fit
    ######################

    pre11 = 'Mo3d_32_1_'
    mod11 = PseudoVoigtModel(prefix=pre11)
    pars = mod11.make_params()
    pars.add('delta', value=3, min=2, max=5)
    pars[pre11 + 'amplitude'].set(9000, min=500, max=100000)
    pars[pre11 + 'center'].set(-231, min=-231, max=-230)
    pars[pre11 + 'sigma'].set(0.3, min=0.2, max=1)
    pars[pre11 + 'fraction'].set(0.5, min=0.2, max=1)

    mod21 = PseudoVoigtModel(prefix='Mo3d_52_1_')
    pars.update(mod21.make_params())
    pars['Mo3d_52_1_amplitude'].set(
        expr='3/2*Mo3d_32_1_amplitude')  #(12000, min=500, max=100000)
    pars['Mo3d_52_1_center'].set(expr='Mo3d_32_1_center+delta'
                                 )  #same distance between 3/2 and 5/2 peaks
    #pars['Mo3d_52_1_sigma'    ].set(expr='1.0*Mo3d_32_1_sigma') #all have same sigma value
    pars['Mo3d_52_1_fraction'].set(
        expr='1.0*Mo3d_32_1_fraction')  #(0.5, min=0.2, max=1)

    #composite model is a sum of the components
    mod = mod11 + mod21
    #perform the fit
    out = mod.fit(sb, pars, x=E)

    if (doplot):
        plt.figure()
        #plot individual components
        comps = mod.eval_components(params=out.params, x=E)
        i = 0
        for key in comps:
            plt.plot(E, comps[key] / 10**3, color=colors[i], label=key)
            plt.fill_between(E,
                             0.0,
                             comps[key] / 10**3,
                             facecolor=colors[i],
                             alpha=0.5)
            i = i + 1

        #plot measured signal (dots) and best fit (sum of components)
        plt.plot(E, sb / 10**3, 'k.')
        plt.plot(E, s / 10**3, 'k-')
        plt.plot(E, B / 10**3, 'k--')
        plt.plot(E, out.best_fit / 10**3, color=colors[1])
        plt.legend()
        ax = mpl.pyplot.gca()
        ax.set_xlim(E_b_min, E_b_max)
        plt.xlabel('binding energy (eV)')
        plt.ylabel('cps ($10^3$)')
    print(out.fit_report(min_correl=0.25))
示例#12
0
 def test_param_hint_explicit_value(self):
     # tests Github Issue 384
     pmod = PseudoVoigtModel()
     params = pmod.make_params(sigma=2, fraction=0.77)
     assert_allclose(params['fraction'].value, 0.77, rtol=0.01)
示例#13
0
def fit_sb_3d(inputs):
    """Fit an Antimony 3d signal using four Sb components (two for each peak,
    3/2 and 5/2). A shirley background is subtracted first.
    The Sb components are fittet to the PseudoVoigt line shape.
    An Oxygen component is fitted to a Gaussian line shape.

    Parameters
    ----------
    region : object
        region class object of the spectrum that should be fitted (from 0)
    e_b_min : float
        region boundary
    e_b_max : float
        region boundary
    doplot : bool
        wether or not to plot the fit and components (default False)
    """

    (region, e_b_min, e_b_max, name, doplot, title) = inputs
    print((region, e_b_min, e_b_max))

    # determine index of left and right boundary
    index1 = np.where(abs(region.x_be - (e_b_min)) < 1e-10)
    index2 = np.where(abs(region.x_be - (e_b_max)) < 1e-10)

    # select energy scale and signal within boundaries
    energy = region.x_be[index1[0][0]:index2[0][0]]
    signal = region.y_avg_counts_mcd[index1[0][0]:index2[0][0]]

    # signal with background substacted
    _, signal_b, background = remove_shirley_background(
        signal, energy, 3, 1e-6)

    ######################
    # set up model components and set constraints for the fit
    ######################

    pre11 = 'Sb3d_32_1_'
    mod11 = PseudoVoigtModel(prefix=pre11)
    pars = mod11.make_params()
    pars.add('delta', value=10, min=9,
             max=11)  # spin-split value, should be 9.4 eV
    pars.add('delta2', value=-1, min=-2.2,
             max=-0.9)  # chemical shift between the two Sb species
    pars[pre11 + 'amplitude'].set(9000, min=0, max=100000)
    pars[pre11 + 'center'].set(-535.65, min=-536, max=-535.5)
    pars[pre11 + 'sigma'].set(0.3, min=0.2, max=1)
    pars[pre11 + 'fraction'].set(0.5, min=0.2, max=1)
    pre12 = 'Sb3d_32_2_'
    mod12 = PseudoVoigtModel(prefix=pre12)
    pars.update(mod12.make_params())
    pars[pre12 + 'amplitude'].set(2050, min=0, max=100000)
    pars[pre12 + 'center'].set(expr='Sb3d_32_1_center+delta2')
    # all have same sigma value
    pars[pre12 + 'sigma'].set(expr='1.0*Sb3d_32_1_sigma')
    pars[pre12 + 'fraction'].set(expr='1.0*Sb3d_32_1_fraction')

    mod21 = PseudoVoigtModel(prefix='Sb3d_52_1_')
    pars.update(mod21.make_params())
    pars['Sb3d_52_1_amplitude'].set(expr='3/2*Sb3d_32_1_amplitude')
    # same distance between 3/2 and 5/2 peaks
    pars['Sb3d_52_1_center'].set(expr='Sb3d_32_1_center+delta')
    # all have same sigma value
    # pars['Sb3d_52_1_sigma'    ].set(expr='1.0*Sb3d_32_1_sigma')
    pars['Sb3d_52_1_fraction'].set(expr='1.0*Sb3d_32_1_fraction')
    mod22 = PseudoVoigtModel(prefix='Sb3d_52_2_')
    pars.update(mod22.make_params())
    pars['Sb3d_52_2_amplitude'].set(expr='3/2*Sb3d_32_2_amplitude')
    # same distance between 3/2 and 5/2 peaks
    pars['Sb3d_52_2_center'].set(expr='Sb3d_32_2_center+delta')
    # all have same sigma value
    # pars['Sb3d_52_2_sigma'    ].set(expr='1.0*Sb3d_32_1_sigma')
    pars['Sb3d_52_2_fraction'].set(expr='1.0*Sb3d_32_1_fraction')

    mod3 = GaussianModel(prefix='O1s_')
    pars.update(mod3.make_params())
    pars['O1s_amplitude'].set(2050, min=0, max=500000)
    pars['O1s_center'].set(-531.9, min=-532.5, max=-531)
    pars['O1s_sigma'].set(1.3, min=0.2, max=2)

    # composite model is a sum of the components
    mod = mod11 + mod12 + mod21 + mod22 + mod3
    # perform the fit
    out = mod.fit(signal_b, pars, x=energy)

    if doplot:
        fig = plt.figure(figsize=(6, 4))
        # plot individual components
        comps = mod.eval_components(params=out.params, x=energy)
        labels = ('Sb 3d 3/2 1', 'Sb 3d 3/2 2', 'Sb 3d 5/2 1', 'Sb 3d 5/2 2',
                  'O 1s')
        for i, key in enumerate(comps):
            plt.plot(-energy,
                     comps[key] / 10**3,
                     color=COLORS[i],
                     label=labels[i])
            plt.fill_between(-energy,
                             0.0,
                             comps[key] / 10**3,
                             facecolor=COLORS[i],
                             alpha=0.5)

        # plot measured signal (dots) and best fit (sum of components)
        plt.plot(-energy, signal_b / 10**3, 'k.')
        plt.plot(-energy, signal / 10**3, 'k-')
        plt.plot(-energy, background / 10**3, 'k--')
        plt.plot(-energy, out.best_fit / 10**3, color=COLORS[1])
        plt.legend()
        ax = plt.gca()
        ax.set_xlim(540, 520)
        ax.xaxis.set_ticks(np.arange(519, 540, 5.0))
        plt.xlabel('binding energy (eV)')
        plt.ylabel('counts ($10^3$/s)')

        if title is not None:
            plt.title(title)
            plt.savefig('Sb3d-fits/Sb3d-fit-%s.pdf' % title,
                        dpi=600,
                        bbox_inches='tight')
            #plt.close(fig)
    # fi

#       ### figures below are for debugging ###
#    plt.figure(33)
#    plt.plot(energy, signal_b/np.max(signal_b))
#    plt.figure(34)
#    plt.plot(energy, out.best_fit/np.max(out.best_fit))
#    plt.figure(35)
#    plt.plot(energy, signal_b/np.max(signal_b) - out.best_fit/np.max(out.best_fit))
#
#    comps = mod.eval_components(params=out.params, x=energy)
#    plt.figure(36)
#    a = comps['Sb3d_32_1_']
#    b = comps['Sb3d_52_1_']
#    c = comps['Sb3d_32_2_']
#    d = comps['Sb3d_52_2_']
#    maxpeak = np.max((np.amax(a), np.amax(b), np.amax(c), np.amax(d)))
#    plt.plot(energy, (a+b) / maxpeak)
#
#    plt.figure(37)
#    plt.plot(energy, (c+d) / maxpeak)

# print(out.fit_report(min_correl=0.25))
    return (name, out.best_values, np.sum(np.abs(signal_b - out.best_fit)))
示例#14
0
def fit_two_Psudo_Voigt(x_lst,y_lst,x_min_flt,x_max_flt,print_all_fits_bool,place_to_save_str):
    '''
    x_lst = x axis
    y_lst = spectra to fit
    first = beginning of fitting regions
    last = end of fitting region
    print_all_fits = Bool, do you want to save all plots
    place_to_save = string that is the filename where we're saving the data
    
    This takes the spectra and fits two Lorentzian curves to it. 
    Returns dictionary of fit values 
    Parameters have prefixes "one" for first V, "two" for second V, "c" for constant
    '''
    
    import numpy as np
    # for smoothing the curves
    import scipy.interpolate as interp #import splev 
    
    from lmfit.models import PseudoVoigtModel, ConstantModel
    
    # Restrict the fit
    x_fit = []
    y_fit = []
    
    for x,y in zip(x_lst, y_lst):
        if x_min_flt < x < x_max_flt:
            x_fit.append(float(x))
            y_fit.append(float(y))
    
    x_fit = np.asarray(x_fit)
    y_fit = np.asarray(y_fit)   
    
    # now we find the parameters using the - d^2/dx^2
    ysmooth = interp.interp1d(x_fit, y_fit, kind='cubic')
    # differentiate x 2
    yp = np.gradient(ysmooth(x_fit))
    ypp = np.gradient(yp)
    # we want the peaks of -d2/dx2 
    ypp = np.asarray([-x for x in ypp])
    
    '''
    *******************************************************
    Section of bad code that it'd take too long to do right
    *******************************************************
    '''
    # % of wavelength you want the peak centers to move 
    wiggle_room = .05
    w_guess = 3 # sigma
    pref1 = 'one'
    pref2 = 'two'
    prefo = 'off'
    
    # if the fancy shit doesn't work, this is how far in index
    # we shift the 2nd peak and max over
    doesnt_work_shift = 10
    '''
    *******************************************************
    Section of bad code that it'd take too long to do right
    *******************************************************
    '''
    
    # this is the money
    # defines the model that'll be fit
    peak1 = PseudoVoigtModel(prefix = pref1, independent_vars=['x'],nan_policy='raise')
    peak2 = PseudoVoigtModel(prefix = pref2, independent_vars=['x'],nan_policy='raise')
    offset = ConstantModel(prefix=prefo, independent_vars=['x'],nan_policy='raise')
    
    mod = peak1 + peak2 + offset
    
    # guess parameters
    x_max = x_fit[np.argmax(ypp)]
    y_max = y_fit[np.argmax(ypp)]
    
    # peak #1 
    # here we set up the peak fitting guess. Then the peak fitter will make a parameter object out of them
    mod.set_param_hint(pref1+'amplitude', value = 5*y_max, min=y_max*.8,max = y_max*6,vary=True)
    
    mod.set_param_hint(pref1+'center', value = x_max, min = x_max*(1-wiggle_room), max = x_max*(1+wiggle_room),vary=True)
    
    mod.set_param_hint(pref1+'sigma', value = w_guess, min = 0, max = 5*w_guess,vary=True)
    
    # Set Fraction
    mod.set_param_hint(pref1+'fraction', value = .5, min = 0, max = 1,vary=True)

    # Change gama maybe
    # mod.set_param_hint(pref1+'gamma', value = 1, vary=True)
    
    # peak #2
    x_trunk = []
    y_trunk = []
    ypp_trunk = []
    try:
        for a,b,c in zip(x_fit.tolist(),y_fit.tolist(),ypp.tolist()):
            '''
            BAD CODE MAKE THIS BETTER
            '''
            if x_max + 8 < a < x_max + 12:
                x_trunk.append(a)
                y_trunk.append(b)
                ypp_trunk.append(c)
        x_trunk = np.asarray(x_trunk)
        y_trunk = np.asarray(y_trunk)
        ypp_trunk = np.asarray(ypp_trunk)
        
        x_max_2 = x_trunk[np.argmax(ypp_trunk)]
        y_max_2 = y_trunk[np.argmax(ypp_trunk)]
        
    except ValueError:
        x_max_2 = x_trunk[np.argmax(ypp) + doesnt_work_shift]
        y_max_2 = y_trunk[np.argmax(ypp) + doesnt_work_shift]
        
    # add peak 2 paramaters 
    mod.set_param_hint(pref2+'amplitude', value = 4*y_max_2, min=y_max_2*.8,max = y_max_2*6,vary=True)
    # changed the bounds to be near other peak
    mod.set_param_hint(pref2+'center', value = x_max_2, min = x_max+8, max = x_max+14,vary=True)
    
    mod.set_param_hint(pref2+'sigma', value = w_guess/2, min = 0, max = w_guess ,vary=True)
    #mod.set_param_hint(pref2+'sigma', pref1 + 'sigma' < expr < pref1 + 'sigma')
    
    # Set Fraction
    mod.set_param_hint(pref2+'fraction', value = .5, min = 0, max = 1,vary=True)
    
    # Change gama maybe
    # mod.set_param_hint(pref2+'gamma', value = 1, vary=False)
    
    # constant offest
    mod.set_param_hint(prefo+'c', value = y_fit[-1], min = 0, max = 5*y_fit[-1],vary=False)
    
    # this does the fitting
    # the params = mod.ma... is what initializes the parameters
    result = mod.fit(y_fit, x=x_fit, params = mod.make_params())
    
    # If print all fits ... 
    if print_all_fits_bool:
        x_dense = np.arange(x_min_flt,x_max_flt,(x_max_flt-x_min_flt)/300.0).tolist()
        
        result.plot_fit(xlabel='Inv Cm', ylabel='counts',datafmt = 'xb', numpoints=len(x_fit)*10)
        
        '''
        Here we make paramaters for peak 1 and 2
        '''
        for x in result.best_values:
            if pref1 in x:
                peak1.set_param_hint(x, value = result.best_values[str(x)])
            elif pref2 in x:
                peak2.set_param_hint(x, value = result.best_values[str(x)])
            else:
                peak1.set_param_hint(x, value = result.best_values[str(x)])
                peak2.set_param_hint(x, value = result.best_values[str(x)])
        
        comp = [peak1.eval(x=yy, params=peak1.make_params()) for yy in x_dense]
        plt.plot(x_dense,comp, 'green', label = None)
        
        comp = [peak2.eval(x=yy, params=peak2.make_params()) for yy in x_dense]
        plt.plot(x_dense, comp, 'green', label= None)
        plt.title("Fit vs Data")
        plt.ylim(0, 1.1*np.max(y_fit))
        plt.legend()
        plt.savefig(place_to_save_str)
        plt.clf()
        
    return result.best_values
示例#15
0
def apply_old_model(x_data, y_data, peaks, plot_fits):
    """
    This function is used within the `superimpose_set` function to apply and existing fit to the
    next spectra in a decomposition series. It helps to improve consistency of fits that do not
    vary signficantly between residence times. This avoids bad fits where the peak shape can
    vary significantly between residence times.

    Args:
        x_data (list like): The x-values of the spectra for which the model will be fit.
        y_data (list like): The y-values of the spectra for which the model will be fit.
        peaks (list): A list containing tuples of pseudo-Voigt decriptors for each peak.
        plot_fits (boolean): A simple True/False boolean input that determins if the plot_fit
                      function should be used to display the resulting fit for visual inspection.

    Returns:
        fit_result (list): An array containing both the peak number, as well as the
                        fraction Lorentzian character, sigma, center, amplitude, full-width,
                        half-max, the height, and the area under the curve of the peaks. The
                        data for peak i can be accessed by the array positions shown here:
                            fit_result[i][0] = p[i]_fraction
                            fit_result[i][1] = p[i]_simga
                            fit_result[i][2] = p[i]_center
                            fit_result[i][3] = p[i]_amplitude
                            fit_result[i][4] = p[i]_fwhm
                            fit_result[i][5] = p[i]_height
                            fit_result[i][6] = p[i]_area under the curve
        residuals ():

    """
    # handling errors in inputs
    if not isinstance(x_data, (list, np.ndarray)):
        raise TypeError(
            'Passed value of `x_data` is not a list or numpy.ndarray! Instead, it is: '
            + str(type(x_data)))
    if not isinstance(y_data, (list, np.ndarray)):
        raise TypeError(
            'Passed value of `y_data` is not a list or numpy.ndarray! Instead, it is: '
            + str(type(y_data)))
    if not isinstance(peaks, list):
        raise TypeError(
            'Passed value of `peaks` is not a list! Instead, it is: ' +
            str(type(peaks)))
    if not isinstance(plot_fits, bool):
        raise TypeError(
            'Passed value of `plot_fits` is not a boolean! Instead, it is: ' +
            str(type(plot_fits)))
    # add old peaks to the model
    old_peak_list = []
    for i, old_peak in enumerate(peaks):
        prefix = 'p{}_'.format(i + 1)
        peak = PseudoVoigtModel(prefix=prefix)
        if i == 0:
            pars = peak.make_params()
        else:
            pars.update(peak.make_params())
        pars[prefix + 'fraction'].set(old_peak[0])
        pars[prefix + 'center'].set(old_peak[2],
                                    vary=True,
                                    min=(old_peak[2] - 10),
                                    max=(old_peak[2] + 10))
        pars[prefix + 'height'].set(min=0.1 * old_peak[5])
        pars[prefix + 'sigma'].set(old_peak[1],
                                   min=0.9 * old_peak[1],
                                   max=1.1 * old_peak[1])
        pars[prefix + 'amplitude'].set(old_peak[3], min=0)
        old_peak_list.append(peak)
        if i == 0:
            mod = old_peak_list[i]
        else:
            mod = mod + old_peak_list[i]
    # run the fit
    out = model_fit(x_data, y_data, mod, pars, report=False)
    # plot_fits option
    if plot_fits is True:
        plot_fit(x_data, y_data, out, plot_components=True)
    else:
        pass
    # save fit data
    fit_result, residuals = export_fit_data(x_data, y_data, out)
    # add 'user_added' label as 8th term to user added peaks
    for i in range(len(peaks), len(fit_result)):
        fit_result[i].append('user_added')
    # sort peaks by center location for saving
    fit_result = sorted(fit_result, key=lambda x: int(x[2]))
    return fit_result, residuals
示例#16
0
def build_custom_model(x_data, y_data, peaks, peaks_add, plot_fits):
    """
    This function is primarily utilized via the dataprep.adjust_peaks function. It allows a custom
    lmfit.model.CompositeModel to be generated using a list of accepted peak values and a list of
    new peak values to be added based on user experience and expertise. User input peaks are
    slightly less constrained than those automatically detected using local maximia and therefore
    their center (wavenumber) value may shift slighty to optimize the fit.

    Args:
        x_data (list like): The x-values of the spectra from which peaks will be detected.
        y_data (list like): The y-values of the spectra from which peaks will be detected.
        peaks (list): A list containing tuples of the x_data (wavenumber) and y_data (counts)
                      values of the peaks.
        peaks_add (list): A list of tuples containing user specified peak locations to be added to
                          the fit as well as interpolated values to provide an initial height guess.
        plot_fits (boolean): A simple True/False boolean input that determins if the plot_fit
                      function should be used to display the resulting fit for visual inspection.

    Returns:
        fit_result (list): An array containing both the peak number, as well as the
                        fraction Lorentzian character, sigma, center, amplitude, full-width,
                        half-max, and the height of the peaks. The data for peak i can be
                        accessed by the array positions shown here:
                            fit_result[i][0] = p[i]_fraction
                            fit_result[i][1] = p[i]_simga
                            fit_result[i][2] = p[i]_center
                            fit_result[i][3] = p[i]_amplitude
                            fit_result[i][4] = p[i]_fwhm
                            fit_result[i][5] = p[i]_height
                            fit_result[i][6] = p[i]_area under the curve

    """
    # handling errors in inputs
    if not isinstance(x_data, (list, np.ndarray)):
        raise TypeError(
            'Passed value of `x_data` is not a list or numpy.ndarray! Instead, it is: '
            + str(type(x_data)))
    if not isinstance(y_data, (list, np.ndarray)):
        raise TypeError(
            'Passed value of `y_data` is not a list or numpy.ndarray! Instead, it is: '
            + str(type(y_data)))
    if not isinstance(peaks, list):
        raise TypeError(
            'Passed value of `peaks` is not a list! Instead, it is: ' +
            str(type(peaks)))
    if not isinstance(peaks_add, list):
        raise TypeError(
            'Passed value of `peaks_add` is not a list! Instead, it is: ' +
            str(type(peaks_add)))
    if not isinstance(plot_fits, bool):
        raise TypeError(
            'Passed value of `plot_fits` is not a boolean! Instead, it is: ' +
            str(type(plot_fits)))
    # add new list of peaks to model
    # first starting with existing peaks
    old_peak_list = []
    for i, old_peak in enumerate(peaks):
        prefix = 'p{}_'.format(i + 1)
        peak = PseudoVoigtModel(prefix=prefix)
        if i == 0:
            pars = peak.make_params()
        else:
            pars.update(peak.make_params())
        pars[prefix + 'fraction'].set(old_peak[0])
        pars[prefix + 'center'].set(old_peak[2],
                                    vary=True,
                                    min=(old_peak[2] - 10),
                                    max=(old_peak[2] + 10))
        pars[prefix + 'height'].set(min=0.1 * old_peak[5])
        pars[prefix + 'sigma'].set(old_peak[1], min=1, max=150)
        pars[prefix + 'amplitude'].set(old_peak[3], min=0)
        old_peak_list.append(peak)
        if i == 0:
            mod = old_peak_list[i]
        else:
            mod = mod + old_peak_list[i]
    # then add new peaks with intial guesses
    new_peak_list = []
    for i, add_peak in enumerate(peaks_add):
        prefix = 'p{}_'.format(i + 1 + len(peaks))
        peak = PseudoVoigtModel(prefix=prefix)
        pars.update(peak.make_params())
        pars[prefix + 'center'].set(add_peak[0],
                                    vary=True,
                                    min=(add_peak[0] - 10),
                                    max=(add_peak[0] + 10))
        pars[prefix + 'height'].set(min=0.1 * add_peak[1])
        pars[prefix + 'sigma'].set(10, min=1, max=150)
        pars[prefix + 'amplitude'].set(20 * add_peak[1], min=0)
        new_peak_list.append(peak)
        mod = mod + new_peak_list[i]
    # run the fit
    out = model_fit(x_data, y_data, mod, pars, report=False)
    # plot_fits option
    if plot_fits is True:
        plot_fit(x_data, y_data, out, plot_components=True)
    else:
        pass
    # save fit data
    fit_result, residuals = export_fit_data(x_data, y_data, out)
    # add 'user_added' label as 8th term to user added peaks
    for i in range(len(peaks), len(fit_result)):
        fit_result[i].append('user_added')
    # sort peaks by center location for saving
    fit_result = sorted(fit_result, key=lambda x: int(x[2]))
    return fit_result, residuals