def test_colvariables(): result_a = col_variables('ARBIN') assert result_a == ('Cycle_Index', 'Data_Point', 'Voltage(V)', 'Current(A)', 'Discharge_Capacity(Ah)', 'Charge_Capacity(Ah)', 'Step_Index') result_b = col_variables('MACCOR') assert result_b == ('Cycle_Index', 'Rec', 'Voltage(V)', 'Current(A)', 'Cap(Ah)', 'Cap(Ah)', 'Md') try: result_x = col_variables('not a real datatype') except (AssertionError): pass else: raise Exception('Valid datatype not being checked')
def generate_model(df_clean, filename, peak_thresh, database): """Wrapper for the get_model_dfs function. Takes those results and adds them to the database with three new tables with the suffices: '-ModPoints', 'ModParams', and '-descriptors'.""" datatype = df_clean['datatype'].iloc[0] (cycle_ind_col, data_point_col, volt_col, curr_col, dis_cap_col, char_cap_col, charge_or_discharge) = col_variables(datatype) chargeloc_dict = {} param_df = pd.DataFrame(columns=[ 'Cycle', 'Model_Parameters_charge', 'Model_Parameters_discharge' ]) if len(df_clean[cycle_ind_col].unique()) > 1: length_list = [ len(df_clean[df_clean[cycle_ind_col] == cyc]) for cyc in df_clean[cycle_ind_col].unique() if cyc != 1 ] lenmax = max(length_list) else: length_list = 1 lenmax = len(df_clean) mod_pointsdf = pd.DataFrame() cycles_no_models = [] for cyc in df_clean[cycle_ind_col].unique(): try: new_df_mody, model_c_vals, model_d_vals, \ peak_heights_c, peak_heights_d = get_model_dfs( df_clean, datatype, cyc, lenmax, peak_thresh) mod_pointsdf = mod_pointsdf.append(new_df_mody) param_df = param_df.append( { 'Cycle': cyc, 'Model_Parameters_charge': str(model_c_vals), 'Model_Parameters_discharge': str(model_d_vals), 'charge_peak_heights': str(peak_heights_c), 'discharge_peak_heights': str(peak_heights_d) }, ignore_index=True) except Exception as e: cycles_no_models.append(cyc) # want this outside of for loop to update the db with the complete df of # new params update_database_newtable(mod_pointsdf, filename.split('.')[0] + '-ModPoints', database) # this will replace the data table in there if it exists already update_database_newtable(param_df, filename.split('.')[0] + 'ModParams', database) # the below also updates the database with the new descriptors after # evaluating the spit out dictionary and putting those parameters # into a nicely formatted datatable. param_dicts_to_df(filename.split('.')[0] + 'ModParams', database) if len(cycles_no_models) > 0: return 'That model has been added to the database.' \ + 'No model was generated for Cycle(s) ' + str(cycles_no_models) return 'That model has been added to the database'
def update_slider_max(filename): if filename is None: filename = 'ExampleData' database_sel = init_db else: filename = filename database_sel = database data, raw_data = pop_with_db(filename, database_sel) datatype = data['datatype'].iloc[0] (cycle_ind_col, data_point_col, volt_col, curr_col, dis_cap_col, char_cap_col, charge_or_discharge) = col_variables(datatype) return data['Cycle_Index'].max()
def peak_finder(df_run, cd, windowlength, polyorder, datatype, lenmax, peak_thresh): """Determines the index of each peak in a dQdV curve V_series = Pandas series of voltage data dQdV_series = Pandas series of differential capacity data cd = either 'c' for charge and 'd' for discharge. Output: i = list of indexes for each found peak""" (cycle_ind_col, data_point_col, volt_col, curr_col, dis_cap_col, char_cap_col, charge_or_discharge) = col_variables(datatype) V_series = df_run[volt_col] # this makes the peak finding smoothing independent of any smoothing that # has already occured. dQdV_series = df_run['Smoothed_dQ/dV'] sigx, sigy = cd_dataframe(V_series, dQdV_series, cd) # the below is to make sure the window length ends up an odd number - even # though we are basing it on the length of the df wl = lenmax / 20 wlint = int(round(wl)) if wlint % 2 == 0: windowlength_new = wlint + 1 else: windowlength_new = wlint if len(sigy) > windowlength_new and windowlength_new > polyorder: # has to be larger than 69 so that windowlength > 3 - necessary for sav # golay function sigy_smooth = scipy.signal.savgol_filter(sigy, windowlength_new, polyorder) else: sigy_smooth = sigy peak_thresh_ft = float(peak_thresh) i = peakutils.indexes(sigy_smooth, thres=peak_thresh_ft, min_dist=lenmax / 50) if i is not None and len(i) > 0: sigx_volts = list(sigx[i]) peak_heights = list(sigy[i]) else: sigx_volts = [] peak_heights = [] return i, sigx_volts, peak_heights
def pop_with_db(filename, database): """Returns dataframes that can be used to populate the app graphs. Finds the already existing file in the database and returns the cleaned version (as a dataframe) and the raw version (also as a dataframe).""" cleanset_name = get_filename_pref(filename) + 'CleanSet' rawset_name = get_filename_pref(filename) + 'Raw' if if_file_exists_in_db(database, filename): # then the file exists in the database and we can just read it df_clean = get_file_from_database(cleanset_name, database) df_raw = get_file_from_database(rawset_name, database) datatype = df_clean['datatype'].iloc[0] (cycle_ind_col, data_point_col, volt_col, curr_col, dis_cap_col, char_cap_col, charge_or_discharge) = col_variables(datatype) else: df_clean = None df_raw = None peakloc_dict = {} return df_clean, df_raw
def get_model_dfs(df_clean, datatype, cyc, lenmax, peak_thresh): """This is the wrapper for the model generation and fitting for the cycles. Returns dictionaries for the charge cycle model parameters and discharge cycle model parameters. These will each have at the least base gaussian parameter values, with the keys: 'base_amplitude', 'base_center', 'base_fwhm', 'base_height', and 'base_sigma'. The other keys are dependent on whether any peaks were found in the cycle. """ (cycle_ind_col, data_point_col, volt_col, curr_col, dis_cap_col, char_cap_col, charge_or_discharge) = col_variables(datatype) clean_charge, clean_discharge = sep_char_dis( df_clean[df_clean[cycle_ind_col] == cyc], datatype) windowlength = 9 polyorder = 3 i_charge, volts_i_ch, peak_heights_c = peak_finder(clean_charge, 'c', windowlength, polyorder, datatype, lenmax, peak_thresh) V_series_c = clean_charge[volt_col] dQdV_series_c = clean_charge['Smoothed_dQ/dV'] par_c, mod_c, indices_c = model_gen(V_series_c, dQdV_series_c, 'c', i_charge, cyc, peak_thresh) model_c = model_eval(V_series_c, dQdV_series_c, 'c', par_c, mod_c) if model_c is not None: mod_y_c = mod_c.eval(params=model_c.params, x=V_series_c) myseries_c = pd.Series(mod_y_c) myseries_c = myseries_c.rename('Model') model_c_vals = model_c.values new_df_mody_c = pd.concat([ myseries_c, V_series_c, dQdV_series_c, clean_charge[cycle_ind_col] ], axis=1) else: mod_y_c = None new_df_mody_c = None model_c_vals = None # now the discharge: i_discharge, volts_i_dc, peak_heights_d = peak_finder( clean_discharge, 'd', windowlength, polyorder, datatype, lenmax, peak_thresh) V_series_d = clean_discharge[volt_col] dQdV_series_d = clean_discharge['Smoothed_dQ/dV'] par_d, mod_d, indices_d = model_gen(V_series_d, dQdV_series_d, 'd', i_discharge, cyc, peak_thresh) model_d = model_eval(V_series_d, dQdV_series_d, 'd', par_d, mod_d) if model_d is not None: mod_y_d = mod_d.eval(params=model_d.params, x=V_series_d) myseries_d = pd.Series(mod_y_d) myseries_d = myseries_d.rename('Model') new_df_mody_d = pd.concat([ -myseries_d, V_series_d, dQdV_series_d, clean_discharge[cycle_ind_col] ], axis=1) model_d_vals = model_d.values else: mod_y_d = None new_df_mody_d = None model_d_vals = None if new_df_mody_c is not None or new_df_mody_d is not None: new_df_mody = pd.concat([new_df_mody_c, new_df_mody_d], axis=0) else: new_df_mody = None return new_df_mody, model_c_vals, model_d_vals,\ peak_heights_c, peak_heights_d
def update_figure2(filename, peak_thresh, n_clicks, show_gauss, desc_to_plot, cd_to_plot, peaknum_to_plot): """ This is a function to evaluate the model on a sample plot before updating the database""" if filename is None: filename = 'ExampleData' database_sel = init_db else: database_sel = database data, raw_data = pop_with_db(filename, database_sel) datatype = data['datatype'].iloc[0] (cycle_ind_col, data_point_col, volt_col, curr_col, dis_cap_col, char_cap_col, charge_or_discharge) = col_variables(datatype) selected_step = round(data[cycle_ind_col].max() / 2) + 1 # select a cycle in the middle of the set dff_data = data[data[cycle_ind_col] == selected_step] if len(data[cycle_ind_col].unique()) > 1: lenmax = max([ len(data[data[cycle_ind_col] == cyc]) for cyc in data[cycle_ind_col].unique() if cyc != 1 ]) else: lenmax = len(data) dff_raw = raw_data[raw_data[cycle_ind_col] == selected_step] peak_vals_df = get_file_from_database( filename.split('.')[0] + '-descriptors', database_sel) fig = plotly.subplots.make_subplots( rows=1, cols=2, subplot_titles=('Descriptors', 'Example Data for Model Tuning (Cycle ' + str(int(selected_step)) + ')'), shared_xaxes=True) marker = {'color': ['#0074D9']} if peak_vals_df is not None: if n_clicks is not None: # if the user has hit the update-model-button - remake model new_df_mody, model_c_vals, model_d_vals, peak_heights_c, peak_heights_d = get_model_dfs( dff_data, datatype, selected_step, lenmax, peak_thresh) dff_mod = new_df_mody c_sigma = model_c_vals['base_sigma'] c_center = model_c_vals['base_center'] c_amplitude = model_c_vals['base_amplitude'] c_fwhm = model_c_vals['base_fwhm'] c_height = model_c_vals['base_height'] d_sigma = model_d_vals['base_sigma'] d_center = model_d_vals['base_center'] d_amplitude = model_d_vals['base_amplitude'] d_fwhm = model_d_vals['base_fwhm'] d_height = model_d_vals['base_height'] else: # if user hasn't pushed the button, populate with original model # from database modset_name = filename.split('.')[0] + '-ModPoints' df_model = get_file_from_database(modset_name, database_sel) dff_mod = df_model[df_model[cycle_ind_col] == selected_step] filtpeakvals = peak_vals_df[peak_vals_df['c_cycle_number'] == selected_step] filtpeakvals = filtpeakvals.reset_index(drop=True) # grab values for the underlying gaussian in the charge: try: c_sigma = filtpeakvals['c_gauss_sigma'].iloc[0] c_center = filtpeakvals['c_gauss_center'].iloc[0] c_amplitude = filtpeakvals['c_gauss_amplitude'].iloc[0] c_fwhm = filtpeakvals['c_gauss_fwhm'].iloc[0] c_height = filtpeakvals['c_gauss_height'].iloc[0] except BaseException: # there may not be a model pass # grab values for the underlying discharge gaussian: try: d_sigma = filtpeakvals['d_gauss_sigma'].iloc[0] d_center = filtpeakvals['d_gauss_center'].iloc[0] d_amplitude = filtpeakvals['d_gauss_amplitude'].iloc[0] d_fwhm = filtpeakvals['d_gauss_fwhm'].iloc[0] d_height = filtpeakvals['d_gauss_height'].iloc[0] except BaseException: pass fig.append_trace( { 'x': dff_data[volt_col], 'y': dff_data['Smoothed_dQ/dV'], 'type': 'scatter', 'marker': marker, 'name': 'Smoothed Data' }, 1, 2) if len(peaknum_to_plot) > 0: for value in peaknum_to_plot: try: fig.append_trace( { 'x': peak_vals_df['c_cycle_number'], 'y': peak_vals_df[str(''.join(desc_to_plot)) + str(''.join(cd_to_plot)) + value], 'type': 'scatter', 'marker': marker, 'name': value }, 1, 1) except KeyError as e: None fig.append_trace( { 'x': dff_mod[volt_col], 'y': dff_mod['Model'], 'type': 'scatter', 'name': 'Model of One Cycle' }, 1, 2) # add if checkbox is selected to show polynomial baseline if 'show' in show_gauss: try: fig.append_trace( { 'x': dff_mod[volt_col], 'y': ((c_amplitude / (c_sigma * ((2 * 3.14159)**0.5))) * np.exp( (-(dff_mod[volt_col] - c_center)**2) / (2 * c_sigma**2))), 'type': 'scatter', 'name': 'Charge Gaussian Baseline' # plot the poly }, 1, 2) except BaseException: pass # add the plot of the discharge guassian: try: fig.append_trace( { 'x': dff_mod[volt_col], 'y': -((d_amplitude / (d_sigma * ((2 * 3.14159)**0.5))) * np.exp( (-(dff_mod[volt_col] - d_center)**2) / (2 * d_sigma**2))), 'type': 'scatter', 'name': 'Discharge Gaussian Baseline' # plot the poly }, 1, 2) except BaseException: pass fig['layout']['showlegend'] = True fig['layout']['xaxis1'].update(title='Cycle Number') fig['layout']['xaxis2'].update(title='Voltage (V)') fig['layout']['yaxis1'].update(title='Descriptor Value') fig['layout']['yaxis2'].update(title='dQ/dV', range=[ dff_data['Smoothed_dQ/dV'].min(), dff_data['Smoothed_dQ/dV'].max() ]) fig['layout']['height'] = 600 fig['layout']['margin'] = {'l': 40, 'r': 10, 't': 60, 'b': 200} return fig
def update_figure1(selected_step, filename, showmodel): fig = plotly.subplots.make_subplots(rows=1, cols=2, subplot_titles=('Raw Cycle', 'Smoothed Cycle'), shared_xaxes=True) marker = {'color': ['#0074D9']} if filename is None or filename == 'options': filename = 'ExampleData' database_sel = init_db else: database_sel = database data, raw_data = pop_with_db(filename, database_sel) datatype = data['datatype'].iloc[0] (cycle_ind_col, data_point_col, volt_col, curr_col, dis_cap_col, char_cap_col, charge_or_discharge) = col_variables(datatype) modset_name = filename.split('.')[0] + '-ModPoints' df_model = get_file_from_database(modset_name, database_sel) if df_model is not None: filt_mod = df_model[df_model[cycle_ind_col] == selected_step] if data is not None: filtered_data = data[data[cycle_ind_col] == selected_step] if raw_data is not None: raw_filtered_data = raw_data[raw_data[cycle_ind_col] == selected_step] for i in filtered_data[cycle_ind_col].unique(): if data is not None: dff = filtered_data[filtered_data[cycle_ind_col] == i] if raw_data is not None: dff_raw = raw_filtered_data[raw_filtered_data[cycle_ind_col] == i] if df_model is not None: dff_mod = filt_mod[filt_mod[cycle_ind_col] == i] if data is not None: fig.append_trace( { 'x': dff[volt_col], 'y': dff['Smoothed_dQ/dV'], 'type': 'scatter', 'marker': marker, 'name': 'Smoothed Data' }, 1, 2) if raw_data is not None: fig.append_trace( { 'x': dff_raw[volt_col], 'y': dff_raw['dQ/dV'], 'type': 'scatter', 'marker': marker, 'name': 'Raw Data' }, 1, 1) if df_model is not None and showmodel == 'showmodel': fig.append_trace( { 'x': dff_mod[volt_col], 'y': dff_mod['Model'], 'type': 'scatter', 'name': 'Model' }, 1, 2) fig['layout']['showlegend'] = False fig['layout']['xaxis1'].update(title='Voltage (V)') fig['layout']['xaxis2'].update(title='Voltage (V)') fig['layout']['yaxis1'].update(title='dQ/dV') fig['layout']['yaxis2'].update(title='dQ/dV') fig['layout']['height'] = 600 fig['layout']['margin'] = {'l': 40, 'r': 10, 't': 60, 'b': 200} return fig