Пример #1
0
def fit_temperature_stable(run_num,
                           df_info,
                           df_raw,
                           ycol,
                           ystd,
                           ystd_sf,
                           remove_data=True,
                           plotfile=None,
                           loss='linear'):
    # loss should be 'linear' or 'huber'
    # query raw data to get run
    df_i = df_info.iloc[run_num]
    df_ = df_raw.query(f'"{df_i.t0}" < index < "{df_i.tf}"').copy()
    df_['run_hours'] = (df_.index - df_.index[0]).total_seconds() / 60**2
    # if run is long enough, only fit first 100 hours
    if df_['run_hours'].max() > 100.:
        df_2 = df_.query('run_hours < 100')
    else:
        df_2 = df_
    # TESTING
    # df_2_ = df_2.copy()
    # df_2 = df_2.iloc[120:]
    # results = None; fig = None; ax1 = None; ax2 = None
    # df_ = df_.iloc[120:].copy()
    # df_['run_num'] = run_num
    # return results, df_, fig, ax1, ax2
    # END TESTING
    # create an array for weights if float supplied
    if type(ystd) != np.ndarray:
        # ystd = ystd*np.ones(len(df_2))
        ystd = ystd * df_2[ycol]  # fractional stddev. from manufacturer
    # setup lmfit model
    # y = A + B * np.exp(- x / C)
    model = lm.Model(mod_exp, independent_vars=['x'])
    params = lm.Parameters()
    params.add('A', value=0, vary=True)
    params.add('B', value=0, vary=True)
    params.add('C', value=1, min=0, vary=True)
    # fit
    result = model.fit(df_2[ycol].values,
                       x=df_2.run_hours.values,
                       params=params,
                       weights=1 / ystd,
                       scale_covar=False,
                       method='least_squares',
                       fit_kws={'loss': loss})
    # plot
    # label for fit
    # label= (r'$\underline{y = A + B e^{-x / C}}$'+'\n\n'+
    #         rf'$A = {result.params["A"].value:0.2f}$'+'\n'+
    #         rf'$B = {result.params["B"].value:0.2f}$'+'\n'+
    #         rf'$C = {result.params["C"].value:0.2f}$'+'\n'+
    #         rf'$\chi^2_\mathrm{{red.}} = {result.redchi:0.2f}$'+'\n\n')
    label = (r'$\underline{y = A + B e^{-x / C}}$' + '\n\n' +
             rf'$A = {result.params["A"].value:0.3f}$' +
             rf'$\pm{result.params["A"].stderr:0.3f}$' + '\n' +
             rf'$B = {result.params["B"].value:0.3f}$' +
             rf'$\pm{result.params["B"].stderr:0.3f}$' + '\n' +
             rf'$C = {result.params["C"].value:0.3f}$' +
             rf'$\pm{result.params["C"].stderr:0.3f}$' + '\n' +
             rf'$\chi^2_\mathrm{{red.}} = {result.redchi:0.2f}$' + '\n\n')
    # set up figure with two axes
    config_plots()
    fig = plt.figure()
    ax1 = fig.add_axes((0.12, 0.33, 0.8, 0.58))
    ax2 = fig.add_axes((0.12, 0.13, 0.8, 0.2))
    # plot data and fit
    # data
    if ystd_sf == 1:
        label_data = 'Data'
    else:
        label_data = r'Data (error $\times$' + f'{ystd_sf})'
    ax1.errorbar(df_2.index.values,
                 df_2[ycol].values,
                 yerr=ystd_sf * ystd,
                 fmt='o',
                 ls='none',
                 ms=2,
                 zorder=100,
                 label=label_data)
    # fit
    ax1.plot(df_2.index.values,
             result.best_fit,
             linewidth=2,
             color='red',
             zorder=99,
             label=label)
    # time constant
    x_ = df_2.index[0] + timedelta(hours=result.params["C"].value)
    ymin = np.min(df_2[ycol]) * 0.95
    ymax = np.max(df_2[ycol]) * 1.02
    ax1.plot([x_, x_], [ymin, ymax],
             '--',
             color='gray',
             zorder=101,
             label=rf'$C = {result.params["C"].value:0.3f}$ [Hours]')
    # calculate residual (data - fit)
    res = df_2[ycol].values - result.best_fit
    res_full = (df_[ycol].values -
                mod_exp(df_['run_hours'].values, **result.params))
    df_['stable_temp_res'] = res_full
    # quick histogram for residuals
    #_ = res_full
    _ = df_['NMR [T]'].values
    mean_full = np.mean(_)
    std_full = np.std(_)
    nstd = 2
    fig_h, ax_h = plt.subplots()
    n, bins, patches = ax_h.hist(_,
                                 bins=25,
                                 histtype='step',
                                 label='Measurements')
    #label='Fit Residuals')
    nm = np.max(n)
    ax_h.plot([mean_full - nstd * std_full, mean_full - nstd * std_full],
              [0, nm],
              'r--',
              label=f'{nstd} RMS from mean')
    ax_h.plot([mean_full + nstd * std_full, mean_full + nstd * std_full],
              [0, nm], 'r--')
    # ax_h.set_xlabel(r'Fit Residuals [$^\circ$C]')
    ax_h.set_xlabel(r'NMR [T]')
    ax_h.set_ylabel('Count')
    ax_h.set_yscale('log')
    ax_h.legend()
    fig_h.suptitle(
        f'Temperature Stability Fit Residuals: Exponential Decay\n' +
        f'Run Index {run_num}, {loss.capitalize()} Loss')
    if not plotfile is None:
        fig_h.savefig(plotfile + '_res_hist.pdf')
        fig_h.savefig(plotfile + '_res_hist.png')
    # calculate ylimit for ax2
    yl = 1.1 * (np.max(np.abs(res)) + ystd_sf * ystd[0])
    # plot residual
    # zero-line
    xmin = np.min(df_2.index.values)
    xmax = np.max(df_2.index.values)
    ax2.plot([xmin, xmax], [0, 0], 'k--', linewidth=2, zorder=98)
    # residual
    ax2.errorbar(df_2.index.values,
                 res,
                 yerr=ystd_sf * ystd,
                 fmt='o',
                 ls='none',
                 ms=2,
                 zorder=99)
    # formatting
    # set ylimit ax2
    ax2.set_ylim([-yl, yl])
    # remove ticklabels for ax1 xaxis
    ax1.set_xticklabels([])
    # axis labels
    ax2.set_xlabel('Datetime [2021 MM-DD hh:mm]')
    ax2.set_ylabel(r'(Data - Fit) [$^{\circ}$C]')
    ax1.set_ylabel(r'Yoke (center magnet) Temp. [$^{\circ}$C]')
    # force consistent x axis range for ax1 and ax2
    #tmin = np.min(df_2.index.values)
    #tmax = np.max(df_2.index.values)
    #ax1.set_xlim([tmin, tmax])
    #ax2.set_xlim([tmin, tmax])
    # turn on legend
    ax1.legend().set_zorder(102)
    # add title
    fig.suptitle(f'Temperature Stability Fit: Exponential Decay\n' +
                 f'Run Index {run_num}, {loss.capitalize()} Loss')
    # minor ticks
    ax1.yaxis.set_minor_locator(AutoMinorLocator())
    ax2.yaxis.set_minor_locator(AutoMinorLocator())
    # inward ticks and ticks on right and top
    ax1.tick_params(which='both',
                    direction='in',
                    top=True,
                    right=True,
                    bottom=True)
    # ax1.ticklabel_format(axis='y', useOffset=True)
    ax2.tick_params(which='both', direction='in', top=True, right=True)
    # tick label format consistent
    formatter = DateFormatter('%m-%d %H:%M')
    ax2.xaxis.set_major_formatter(formatter)
    # rotate label for dates
    ax2.xaxis.set_tick_params(rotation=15)
    # save figure
    #fig.tight_layout()
    if not plotfile is None:
        fig.savefig(plotfile + '.pdf')
        fig.savefig(plotfile + '.png')

    if remove_data:
        # remove first "time constant" of data
        hmax = df_.run_hours.max()
        tau = result.params["C"].value
        # special case (water chiller failed) -- only remove 1/2 time constant
        # if hmax/tau < 1:
        if hmax / tau < 1.5:
            # df_ = df_.query(f'run_hours > {tau/2}').copy()
            df_ = df_.query(f'run_hours > {tau/8}').copy()
        # else normal case -- remove one time constant
        else:
            df_ = df_.query(f'run_hours > {tau}').copy()
    # add run number to df
    df_['run_num'] = run_num
    return result, df_, fig, ax1, ax2
Пример #2
0
import numpy as np
import pandas as pd
import lmfit as lm
from scipy.interpolate import interp1d
import matplotlib.pyplot as plt
from plotting import config_plots

config_plots()

# pickle file
ddir = '/home/ckampa/data/hallprobecalib_extras/datafiles/magnet_ramp/'

# FEMM data
df_f0 = pd.read_csv(ddir+'gap75_B_vs_I_r0z0_0-300_results.txt', skiprows=8, names=['I','B'], delimiter=',')
df_fp = pd.read_csv(ddir+'gap75_B_vs_I_r0z37.5_results.txt', skiprows=8, names=['I', 'B'], delimiter=',')
df_f0 = df_f0.query('I <= 140').copy()
df_f0.eval('I = I*2', inplace=True)
df_fp = df_fp.query('I <= 140').copy()
df_fp.eval('I = I*2', inplace=True)

df_f0['B_ratio'] = df_f0.B / df_fp.B

# FEMM df
df_f = pd.read_csv(ddir+'gap75_B_vs_I_r0z0_0-300_results.txt', skiprows=8, names=['I','B'], delimiter=',')
df_f = df_f.query('I <= 140').copy()
df_f.eval('I = I*2', inplace=True)
# df = pd.read_pickle('/home/ckampa/Desktop/slow_controls_current.pkl')
df = pd.read_pickle(ddir+'ramp_2021-02-24_processed.pkl')
probe = '6A0000000D61333A'

# Is_set = np.array([224., 240.]) # testing equipment
def linear_temperature_regression(run_num, df, plotfile, xcol, ycol, ystd,
                                  ystd_sf, force_decreasing):
    # query preprocessed data to get run
    df_ = df.query(f'run_num == {run_num}').copy()
    # create an array for weights if float supplied
    if type(ystd) != np.ndarray:
        ystd = ystd*np.ones(len(df_))
    # setup lmfit model
    # y = A + B * x
    model = lm.Model(mod_lin, independent_vars=['x'])
    params = lm.Parameters()
    params.add('A', value=0, vary=True)
    params.add('B', value=0, vary=True)
    params.add('X0', value=T0, vary=False)
    # fit
    result = model.fit(df_[ycol].values, x=df_[xcol].values,
                       params=params, weights=1/ystd, scale_covar=False)
    # check if not decreasing and rerun
    if force_decreasing and (result.params['B'].value > 0.):
        params['B'].vary = False
        result = model.fit(df_[ycol].values, x=df_[xcol].values,
                           params=params, weights=1/ystd, scale_covar=False)
        # label for fit
        bv_str = f'{result.params["B"].value:0.3E}'
        ind = bv_str.find('E')
        b0 = bv_str[:ind]
        bsf = int(bv_str[ind+1:])
        be = f'{result.params["B"].stderr/10**bsf:0.3f}'

        label= (rf'$\underline{{y = A + B (x - {T0})}}$'+'\n'+
                rf'$A = {result.params["A"].value:0.7f}$'+'\n'+
                rf'$\pm {result.params["A"].stderr:0.7f}$'+'\n'+
                rf'$B =$'+'\n'+rf'$({b0}\pm{be})$'+
                rf'$\times 10^{{ {bsf} }}$'+' (fixed)\n'+
                # rf'$B = {result.params["B"].value:0.1f}$'+
                # rf'$\pm {result.params["B"].stderr:0.1f}$'+' (fixed)\n'+
                rf'$\chi^2_\mathrm{{red.}} = {result.redchi:0.2f}$'+'\n')
    else:
        # label for fit
        bv_str = f'{result.params["B"].value:0.3E}'
        ind = bv_str.find('E')
        b0 = bv_str[:ind]
        bsf = int(bv_str[ind+1:])
        be = f'{result.params["B"].stderr/10**bsf:0.3f}'

        label= (rf'$\underline{{y = A + B (x - {T0})}}$'+'\n'+
                rf'$A = {result.params["A"].value:0.7f}$'+'\n'+
                rf'$\pm {result.params["A"].stderr:0.7f}$'+'\n'+
                rf'$B =$'+'\n'+rf'$({b0}\pm{be})$'+
                rf'$\times 10^{{ {bsf} }}$'+'\n'+
                # rf'$B = {result.params["B"].value:0.3E}$'+
                # rf'$\pm {result.params["B"].stderr:0.3E}$'+'\n'+
                rf'$\chi^2_\mathrm{{red.}} = {result.redchi:0.2f}$'+'\n')
    # plot

    # set up figure with two axes
    config_plots()
    fig = plt.figure()
    ax1 = fig.add_axes((0.12, 0.3, 0.7, 0.6))
    ax2 = fig.add_axes((0.12, 0.08, 0.7, 0.185))
    # colorbar axis
    cb_ax = fig.add_axes([0.85, 0.15, 0.05, 0.7])
    # plot data and fit
    # data
    # with errorbars
    if ystd_sf == 1:
        label_data = 'Data'
    else:
        label_data = r'Data (error $\times$'+f'{ystd_sf})'
    ax1.errorbar(df_[xcol].values, df_[ycol].values, yerr=ystd_sf*ystd,
                 fmt='o', ls='none', ms=2, zorder=100, label=label_data)
    # scatter with color
    #sc = ax1.scatter(df_[xcol].values, df_[ycol].values, c=df_.index, s=1,
    #                 zorder=101)
    sc = ax1.scatter(df_[xcol].values, df_[ycol].values, c=df_.run_hours, s=1,
                     zorder=101)
    # fit
    ax1.plot(df_[xcol].values, result.best_fit, linewidth=2, color='red',
             zorder=99, label=label)
    # calculate residual (data - fit)
    res = df_[ycol].values - result.best_fit
    # calculate ylimit for ax2
    yl = 1.1*(np.max(np.abs(res)) + ystd_sf*ystd[0])
    # plot residual
    # zero-line
    xmin = np.min(df_[xcol].values)
    xmax = np.max(df_[xcol].values)
    ax2.plot([xmin, xmax], [0, 0], 'k--', linewidth=2, zorder=98)
    # residual
    ax2.errorbar(df_[xcol].values, res, yerr=ystd_sf*ystd, fmt='o', ls='none',
                 ms=2, zorder=99)
    #ax2.scatter(df_[xcol].values, res, c=df_.index, s=1, zorder=101)
    ax2.scatter(df_[xcol].values, res, c=df_.run_hours, s=1, zorder=101)
    # colorbar for ax1
    cb = fig.colorbar(sc, cax=cb_ax)
    cb.set_label('Time [Hours]')
    ## WITH DATETIME
    #cb.set_t
    # print(f'vmin = {sc.colorbar.vmin}')
    # print(f'vmax = {sc.colorbar.vmax}')
    # # change colobar ticks labels and locators
    # cb.set_ticks([sc.colorbar.vmin + t*(sc.colorbar.vmax-sc.colorbar.vmin)
    #              for t in cb.ax.get_yticks()])
    # cbtls = [mdates.datetime.datetime.fromtimestamp((sc.colorbar.vmin +
    #          t*(sc.colorbar.vmax-sc.colorbar.vmin))*1e-9).strftime('%c')
    #          for t in cb.ax.get_yticks()]
    # cb.set_ticklabels(cbtls)
    #cb.ax.set_yticklabels(df_.index.strftime('%m-%d %H:%M'))
    # formatting
    # kludge for NMR or Hall
    if ycol == 'NMR [T]':
        ylabel1 = r'$|B|_\mathrm{NMR}$ [T]'
        title_prefix = 'NMR'
    else:
        ylabel1 = r'$|B|_\mathrm{Hall}$ [T]'
        title_prefix = 'Hall Probe'
    # set ylimit ax2
    ax2.set_ylim([-yl, yl])
    # remove ticklabels for ax1 xaxis
    ax1.set_xticklabels([])
    # axis labels
    ax2.set_xlabel(r'Yoke (center magnet) Temp. [$^{\circ}$C]')
    ax2.set_ylabel('(Data - Fit) [T]')
    ax1.set_ylabel(ylabel1)
    # force consistent x axis range for ax1 and ax2
    tmin = np.min(df_[xcol].values)
    tmax = np.max(df_[xcol].values)
    range_t = tmax-tmin
    ax1.set_xlim([tmin-0.1*range_t, tmax+0.1*range_t])
    ax2.set_xlim([tmin-0.1*range_t, tmax+0.1*range_t])
    # turn on legend
    ax1.legend().set_zorder(102)
    # add title
    fig.suptitle(f'{title_prefix} vs. Temperature Regression: Linear Model\n'+
                 f'Run Index {run_num}')
    # minor ticks
    ax1.xaxis.set_minor_locator(AutoMinorLocator())
    ax2.xaxis.set_minor_locator(AutoMinorLocator())
    ax1.yaxis.set_minor_locator(AutoMinorLocator())
    ax2.yaxis.set_minor_locator(AutoMinorLocator())
    # inward ticks and ticks on right and top
    ax1.tick_params(which='both', direction='in', top=True, right=True,
                    bottom=True)
    ax2.tick_params(which='both', direction='in', top=True, right=True)
    # save figure
    #fig.tight_layout()
    fig.savefig(plotfile+'.pdf')
    fig.savefig(plotfile+'.png')
    return result, fig, ax1, ax2