Example #1
0
    def __init__(self, p0=0.3, n=1e6):
        self.g = data_generator(tmax=10)
        self.p0 = p0
        self.n = n

        g = self.g
        self.pexp = pulsed_exp(lifetime=g.lifetime, pulse_len=g.pulse_len)
        self.sexp = pulsed_strexp(lifetime=g.lifetime, pulse_len=g.pulse_len)
        self.T1 = np.logspace(np.log10(g.lifetime * 0.01),
                              np.log10(g.lifetime * 100), 1000)
        self.kernel_fn = lambda x, w: self.pexp(x, w, 1)
Example #2
0
    def get_fn(self, fn_name, ncomp=1, pulse_len=-1, lifetime=-1):
        """
            Get the fitting function used.
            
                fn_name: string of the function name users will select. 
                ncomp: number of components, ex if 2, then return exp+exp
                pulse_len: duration of beam on in s
                lifetime: lifetime of probe in s
            
            Returns python function(x,*pars)
        """

        # set fitting function
        if fn_name == 'Lorentzian':
            fn = fns.lorentzian
            self.mode = 1
        elif fn_name == 'BiLorentzian':
            fn = fns.bilorentzian
            self.mode = 1
        elif fn_name == 'Gaussian':
            fn = fns.gaussian
            self.mode = 1
        elif fn_name == 'Exp':
            fn = fns.pulsed_exp(lifetime, pulse_len)
            self.mode = 2
        elif fn_name == 'Bi Exp':
            fn = fns.pulsed_biexp(lifetime, pulse_len)
            self.mode = 2
        elif fn_name == 'Str Exp':
            fn = fns.pulsed_strexp(lifetime, pulse_len)
            self.mode = 2
        else:
            raise RuntimeError('Fitting function not found.')

        # Make final function based on number of components
        fnlist = [fn] * ncomp

        if self.mode == 1:
            fnlist.append(lambda x, b: b)
        fn = fns.get_fn_superpos(fnlist)

        return fn
Example #3
0
    def _setup(self):

        # get data
        dat = bd.bdata(self.run, self.year)
        self.x, self.y, self.yerr = dat.asym('c', rebin=self.rebin)

        # remove zero error values
        idx = self.yerr != 0
        self.x = self.x[idx]
        self.y = self.y[idx]
        self.yerr = self.yerr[idx]

        # get function
        f = pulsed_exp(lifetime=bd.life[self.probe],
                       pulse_len=dat.get_pulse_s())
        self.fn = lambda x, w: f(x, w, 1)

        # build error matrix
        self.S = np.diag(1 / self.yerr)

        # set the kernel
        self.K = np.array([self.fn(self.x, i) for i in self.lamb]).T
Example #4
0
    def __init__(self, filename, nproc=4):
        """
            filename:   yaml or csv or without extension
            nproc:      number of processors
        """

        # get file names
        filename = os.path.splitext(filename)[0]
        yamlfile = filename + '.yaml'
        csvfile = filename + '.csv'

        # get run settings
        with open(yamlfile, 'r') as fid:
            run_settings = yaml.safe_load(fid)

        # get run data
        df = pd.read_csv(csvfile, comment='#')
        df = df[['t', self.asym_type, self.asym_err]]

        # drop bad rows and errors of zero
        df[self.asym_err].loc[df[self.asym_err] == 0] = np.nan
        df.dropna(inplace=True)

        # make kernel and fit functions
        lifetime = run_settings['lifetime (s)']
        pulse_len = run_settings['beam pulse (s)']
        self.pexp = pulsed_exp(pulse_len=pulse_len, lifetime=lifetime)
        self.sexp = pulsed_strexp(pulse_len=pulse_len, lifetime=lifetime)

        # generate the T1 range
        log10_T1_min = np.log10(lifetime * 1e-2)
        log10_T1_max = np.log10(lifetime * 1e2)
        self.T1 = np.logspace(log10_T1_min, log10_T1_max, self.nT1, base=10.0)

        # make ilt object
        super().__init__(df['t'].values, df[self.asym_type].values,
                         df[self.asym_err].values,
                         lambda x, w: self.pexp(x, w, 1), self.T1, nproc)
Example #5
0
 def run(self):
     """
         Run the function placer
         
         comp = component number to run
     """
     
     try:
         del self.fplace
     except AttributeError:
         pass
     else:
         self.fig.axes[0].cla()
         self.fig.axes[0].errorbar(*self.xy,fmt='.')
     
     # ensure matplotlib signals work. Not sure why this is needed.
     self.fig.tight_layout()
     
     # get paramters, translating the names
     p0 = []
     if self.n_components > 1:
         for i in range(self.n_components):
             p0.append({self.parmap[k.split('_')[0]]:self.p0[k] \
                         for k in self.p0.keys() if 'base' in k or str(i) in k})
     else:
         p0.append({self.parmap[k]:self.p0[k] for k in self.p0.keys()})
     
     # get fitting function 
     if self.fname == 'Lorentzian':
         fn = fns.lorentzian     # freq,peak,width,amp
     # ~ elif self.fname == 'BiLorentzian':
         # ~ fn = fns.bilorentzian   # freq, peak,widthA,ampA,widthB,ampB
     elif self.fname == 'Gaussian':
         fn = lambda freq,peak,width,amp : fns.gaussian(freq,peak,width,amp)
             
     elif self.fname in ('Exp', 'Str Exp'):
         
         # get function
         pulse = self.data.get_pulse_s()
         lifetime = bd.life[self.bfit.probe_species.get()]
     
         if self.fname == 'Exp':
             f1 = fns.pulsed_exp(lifetime=lifetime,pulse_len=pulse)
             
             if self.bfit.probe_species.get() == 'Mg31':
                 fn = lambda x,lam,amp : fa_31Mg(x,pulse)*f1(x,lam,amp)
             else:
                 fn = lambda x,lam,amp : f1(x,lam,amp)
         
         elif self.fname == 'Str Exp':
             f1 = fns.pulsed_strexp(lifetime=lifetime,pulse_len=pulse)
             
             if self.bfit.probe_species.get() == 'Mg31':
                 fn = lambda x,lam,amp,beta : fa_31Mg(x,pulse)*f1(x,lam,beta,amp)
             else:
                 fn = lambda x,lam,amp,beta : f1(x,lam,beta,amp)
     else:
         self.cancel()
         errormsg = 'Function "%s" not implemented in P0 Finder' % self.fname
         self.logger.warning(errormsg)
         messagebox.showerror("Error",errormsg)
         raise RuntimeError(errormsg)
     
     self.fig.canvas.mpl_connect('close_event',self.cancel)
     
     self.fplace = FunctionPlacer(fig=self.fig,
                                  data=self.data,
                                  fn_single=fn,
                                  ncomp=self.n_components,
                                  p0=p0,
                                  fnname=self.fname,
                                  asym_mode=self.bfit.get_asym_mode(self.bfit.fit_files),
                                  endfn=self.endfn)
Example #6
0
    def get_fn(self, fn_name, ncomp=1, pulse_len=-1, lifetime=-1, constr=None):
        """
            Get the fitting function used.
            
                fn_name: string of the function name users will select. 
                ncomp: number of components, ex if 2, then return exp+exp
                pulse_len: duration of beam on in s
                lifetime: lifetime of probe in s
            
            Returns python function(x, *pars)
        """

        # set fitting function
        if fn_name == 'Lorentzian':
            fn = fns.lorentzian
            self.mode = 1
        elif fn_name == 'BiLorentzian':
            fn = fns.bilorentzian
            self.mode = 1
        elif fn_name == 'QuadLorentz':
            fn =  lambda freq, nu_0, nu_q, eta, theta, phi, \
              amp0, amp1, amp2, amp3, fwhm: \
                    fns.quadlorentzian(freq, nu_0, nu_q, eta, theta, phi, \
                amp0, amp1, amp2, amp3, \
                      fwhm, fwhm, fwhm, fwhm, \
                      I=self.spin[self.probe_species])
            self.mode = 1
        elif fn_name == 'Gaussian':
            fn = fns.gaussian
            self.mode = 1
        elif fn_name == 'Exp':
            fn = fns.pulsed_exp(lifetime, pulse_len)
            self.mode = 2
        elif fn_name == 'Bi Exp':
            fn = fns.pulsed_biexp(lifetime, pulse_len)
            self.mode = 2
        elif fn_name == 'Str Exp':
            fn = fns.pulsed_strexp(lifetime, pulse_len)
            self.mode = 2
        else:
            raise RuntimeError('Fitting function not found.')

        # add corrections for probe daughters
        if self.mode == 2 and self.probe_species == 'Mg31':
            fn = fns.decay_corrected_fn(fa_31Mg, fn, beam_pulse=pulse_len)

        # Make superimposed function based on number of components
        fnlist = [fn] * ncomp

        if self.mode == 1:
            fnlist.append(lambda x, b: b)

        fn = fns.get_fn_superpos(fnlist)

        # set parameter constraints
        if constr and constr is not None:
            par_names_orig = self.gen_param_names(fn_name, ncomp)
            par_names_constr = self.gen_param_names(fn_name, ncomp, constr)
            fn = fns.get_constrained_fn(fn, par_names_orig, par_names_constr,
                                        constr)

        return fn
Example #7
0
    def run(self):
        """
            Run the function placer
            
            comp = component number to run
        """

        try:
            del self.fplace
        except AttributeError:
            pass
        else:
            self.fig.axes[0].cla()
            self.fig.axes[0].errorbar(*self.xy, fmt='.')

        # ensure matplotlib signals work. Not sure why this is needed.
        self.fig.tight_layout()

        # get paramters, translating the names
        p0 = self.split_components(self.p0)
        blo = self.split_components(self.blo)
        bhi = self.split_components(self.bhi)

        # get fitting function
        if self.fname == 'Lorentzian':
            fn = fns.lorentzian  # freq, peak, fwhm, amp
        elif self.fname == 'BiLorentzian':
            fn = lambda freq, peak, fwhm, amp: fns.bilorentzian(
                freq, peak, fwhm, amp / 3, fwhm / 3, amp * 2 / 3)
        elif self.fname == 'Gaussian':
            fn = lambda freq, peak, fwhm, amp: fns.gaussian(
                freq, peak, fwhm, amp)
        elif self.fname == 'QuadLorentz':
            fn = lambda freq, nu_0, nu_q, eta, theta, phi, \
                        amp0, amp1, amp2, amp3, fwhm: \
                        fns.quadlorentzian(freq, nu_0, nu_q, eta, theta, phi, \
                        amp0, amp1, amp2, amp3, \
                        fwhm, fwhm, fwhm, fwhm,
                        I = self.fitter.spin[self.fitter.probe_species])

        elif self.fname in ('Exp', 'Str Exp'):

            # get function
            pulse = self.data.pulse_s
            lifetime = bd.life[self.bfit.probe_species.get()]

            if self.fname == 'Exp':
                f1 = fns.pulsed_exp(lifetime=lifetime, pulse_len=pulse)

                if self.bfit.probe_species.get() == 'Mg31':
                    fn = lambda x, lam, amp: fa_31Mg(x, pulse) * f1(
                        x, lam, amp)
                else:
                    fn = lambda x, lam, amp: f1(x, lam, amp)

            elif self.fname == 'Str Exp':
                f1 = fns.pulsed_strexp(lifetime=lifetime, pulse_len=pulse)

                if self.bfit.probe_species.get() == 'Mg31':
                    fn = lambda x, lam, amp, beta: fa_31Mg(x, pulse) * f1(
                        x, lam, beta, amp)
                else:
                    fn = lambda x, lam, amp, beta: f1(x, lam, beta, amp)
        else:
            self.cancel()
            errormsg = 'Function "%s" not implemented in P0 Finder' % self.fname
            self.logger.warning(errormsg)
            messagebox.showerror("Error", errormsg)
            raise RuntimeError(errormsg)

        self.fig.canvas.mpl_connect('close_event', self.cancel)

        self.fplace = FunctionPlacer(
            fig=self.fig,
            data=self.data,
            fn_single=fn,
            ncomp=self.n_components,
            p0=p0,
            fnname=self.fname,
            asym_mode=self.bfit.get_asym_mode(self.bfit.fit_files),
            endfn=self.endfn,
            spin=self.fitter.spin[self.fitter.probe_species])
def test_numeric_integration(tolerance=1e-5,
                             n_samples=50,
                             print_summary=False):

    # constants appropriate for most β-NMR data taken at TRIUMF
    nuclear_lifetime = bd.life["Li8"]
    pulse_duration = 3.0 * nuclear_lifetime

    # create the SLR functions
    fcn_exp = pulsed_exp(nuclear_lifetime, pulse_duration)
    fcn_strexp = pulsed_strexp(nuclear_lifetime, pulse_duration)

    # generate random values for the SLR fit function parameters that are uniformly
    # distributed between typical bounds for real data
    relaxation_rates = np.random.uniform(1e-2 * nuclear_lifetime,
                                         1e2 * nuclear_lifetime, n_samples)
    amplitudes = np.random.uniform(0.00, 0.15, n_samples)

    # stretching exponent
    beta = 1.0

    # machine precision for floating point values
    epsilon = np.finfo(float).eps

    # create an empty DataFrame to hold all of the results
    df = pd.DataFrame(columns=[
        "Time (s)",
        "Amplitude",
        "Rate (1/s)",
        "Difference",
        "Absolute Difference",
        "Tolerance",
        "Tolerance Exceeded",
        "Epsilon",
        "Epsilon Exceeded",
    ])

    # loop over the random function parameters
    for rate, amplitude in zip(relaxation_rates, amplitudes):

        # generate series of random times to evaluate the SLR fit functions that are
        # uniformly distributed between typical bounds for real data
        time = np.random.uniform(0.0, pulse_duration + 10 * nuclear_lifetime,
                                 n_samples)

        # evaluate the difference
        difference = fcn_exp(time, rate, amplitude) - fcn_strexp(
            time, rate, beta, amplitude)

        # fill the DataFrame row-by-row
        # https://stackoverflow.com/a/42837693
        for t, d in zip(time, difference):
            df = df.append(
                {
                    "Time (s)": t,
                    "Amplitude": amplitude,
                    "Rate (1/s)": rate,
                    "Difference": d,
                    "Absolute Difference": np.abs(d),
                    "Tolerance": tolerance,
                    "Tolerance Exceeded": np.abs(d) > tolerance,
                    "Epsilon": epsilon,
                    "Epsilon Exceeded": np.abs(d) < epsilon,
                },
                ignore_index=True,
            )

    # optionally print a summary of the results
    if print_summary:
        print("")
        print("-------")
        print("Summary of `test_numeric_integration()`")
        print("-------")
        print("")
        print("Tolerance        = %g" % tolerance)
        print("Machine Epsilon  = %g" % epsilon)
        print("")
        print("Absolute Difference:")
        print(" - Max  = %g" % df["Absolute Difference"].max())
        print(" - Min  = %g" % df["Absolute Difference"].min())
        print(" - Mean = %g" % df["Absolute Difference"].mean())
        print("")
        print("Column Data:")
        print("")
        print(df["Tolerance Exceeded"].value_counts())
        print("")
        print(df["Epsilon Exceeded"].value_counts())
        print("")

    # finally, check all values and raise an error if the tolerance is exceeded
    for i in df.index:
        if df["Tolerance Exceeded"][i]:
            raise AssertionError(
                "Absolute difference greater than tolerance!\n\n" +
                "\t|%.4e| > %.4e\n\n" % (df["Difference"][i], tolerance) +
                "\ttime      = %.4e s\n" % df["Time (s)"][i] +
                "\tamplitude = %.4e\n" % df["Amplitude"][i] +
                "\trate      = %.4e 1/s" % df["Rate (1/s)"][i])
Example #9
0
# basic dev testing the ilt 
# Derek Fujimoto
# Feb 2020

from bILT.src.ilt import *
from bfit.fitting.functions import pulsed_exp 
import bdata as bd
import numpy as np

x,y,dy = bd.bdata(40214, year=2009).asym('c') 
T1 = np.logspace(np.log(0.01 * 1.2096), np.log(100.0 * 1.2096), 100)
alpha = np.logspace(2, 5, 50)

f = pulsed_exp(1.21,4)
fn = lambda x,w: f(x,w,1) 
I = ilt(x,y,dy,fn,T1,nproc=4)
I.fit(alpha)

# ~ plt.figure()
# ~ I.draw_fit(10)

# ~ plt.figure()
# ~ I.draw_weights(10)

# ~ plt.figure()
# ~ I.draw_logdist(10)

# ~ plt.figure()
# ~ I.draw_Lcurve()

# ~ plt.figure()
Example #10
0
        ax1.legend()
        ax1.set_title("")
        ax2.set_title("")

        return (fig, ax1, ax2)


# EXPONENTIAL ================================================================

if 0:
    print("Running EXPONENTIAL")

    # setup
    T1_tst = 2  # T1 in this test
    f = lambda t: np.exp(-t / T1_tst)
    fit = pulsed_exp(lifetime=bd.life.Li8, pulse_len=4)

    # run and draw
    test1 = test()
    test1.run(f, fit, 5)
    fig, ax1, ax2 = test1.draw()

    ax2.axvline(1 / T1_tst, color='k', ls='--', lw=2, label='True 1/$T_1$')
    ax2.axvline(test1.par[0], color='r', ls=':', lw=2, label='Fitted 1/$T_1$')
    fig.suptitle("Exponential Relaxation")
    ax2.legend()

# BIEXPONENTIAL - well separated ==============================================

if 0:
    print("Running BIEXPONENTIAL - welll separated")