def column_check(infile, col_name, verbose=False): """ Given a cleaned Novonix data file, check if the col_name column exists. Parameters ----------- infile : string Name of the input Novonix file col_name : string Name of the column verbose : boolean True to print information out. Returns ------- column_exists : boolean True when the column name has been found in the header Examples --------- >>> import preparenovonix.novonix_variables as nv >>> import preparenovonix.novonix_add as prep >>> prep.column_check('example_data/example_data_prep.csv',nv.col_step) True """ icol = icolumn(infile, col_name) if icol > -1: if verbose: print("The file already has the column {}".format(col_name)) return True else: return False
def plot_vct(before_file, first_loop=0, plot_type="pdf", plot_show=False): """ Given two Novonix data files, pre and post-processing with preparenovonix, plot toghether their Voltage, Capacity, Step Number and Loop number versus time Parameters ----------- before_file : string Name of the pre-processed file first_loop : int Value for the Loop Number to start showing data plot_type : string 'pdf', 'png', 'jpg' for the format of the saved file plot_show : boolean True to show the plot. Notes ----- This code returns a plot. Examples --------- >>> from preparenovonix.compare import plot_vct >>> plot_vct('example_data/example_data.csv','example_data/example_data_prep.csv',first_loop=0,plot_type='pdf',plot_show=True) """ after_file = after_file_name(before_file) dirname, fname = os.path.split(os.path.abspath(after_file)) figname = os.path.join(dirname, "compare_vct." + plot_type) # Value of the loop to start the plot from val = first_loop ### Read the voltage, step number and loop number from # the processed file a_t = read_column(after_file, nv.col_t, outtype="float") a_v = read_column(after_file, nv.col_v, outtype="float") a_c = read_column(after_file, nv.col_c, outtype="float") a_s = read_column(after_file, nv.col_step, outtype="int") a_l = read_column(after_file, nv.loop_col, outtype="int") a_p = read_column(after_file, nv.line_col, outtype="int") # Find the column positions in the file icol_t = icolumn(after_file, nv.col_t) icol_v = icolumn(after_file, nv.col_v) icol_c = icolumn(after_file, nv.col_c) icol_step = icolumn(after_file, nv.col_step) ### Read the voltage and step number from the original file # Since there are failed tests, these need to be jumped data = "[Data]" ntests = 0 # Count the number of failed tests with open(before_file, "r") as ff: for line in ff: if line.strip(): # Jump empty lines if data in line: # Count failed tests for each start of file ntests += 1 idata = 0 vv = [] ss = [] tt = [] cc = [] with open(before_file, "r") as ff: for line in ff: if line.strip(): # Jump empty lines if data in line: # Count failed tests for each start of file idata += 1 if idata == ntests: # Read the header ff.readline() break for line in ff: tt.append(line.split(",")[icol_t]) vv.append(line.split(",")[icol_v]) cc.append(line.split(",")[icol_c]) ss.append(line.split(",")[icol_step]) b_t = np.asarray(tt, dtype=np.float64) b_v = np.asarray(vv, dtype=np.float64) b_c = np.asarray(cc, dtype=np.float64) b_s = np.asarray(ss, dtype=int) # Plot cols = ["navy", "salmon", "cornflowerblue", "darkred"] plt.figure(figsize=(8.0, 11.0)) gs = gridspec.GridSpec(4, 1) gs.update(wspace=0.0, hspace=0.0) fs = 15 # Plot only when the loop starts to be biggeer than val ind = np.where(a_l > val) if np.shape(ind)[1] < 1: print("WARNING compare.plot_vct: not enough data to be plotted") return astart = ind[0][0] first_a_t = a_t[astart] ind = np.where(b_t >= first_a_t) bstart = ind[0][0] # Loop number axl = plt.subplot(gs[3, :]) axl.set_xlabel(nv.col_t, fontsize=fs) axl.set_ylabel(nv.loop_col, fontsize=fs, color=cols[0]) axl.plot(a_t[astart::], a_l[astart::], cols[0], linewidth=2.5, label="Loop number") axp = axl.twinx() axp.set_ylabel("Protocol line", fontsize=fs, color=cols[2]) axp.plot( a_t[astart::], a_p[astart::], cols[2], linewidth=2.5, label="Protocol line" ) # Steps axs = plt.subplot(gs[2, :], sharex=axl) plt.setp(axs.get_xticklabels(), visible=False) axs.set_ylabel(nv.col_step, fontsize=fs) axs.plot(a_t[astart::], a_s[astart::], cols[0], linewidth=2.5, label="After") axs.plot(b_t[bstart::], b_s[bstart::], cols[1], linestyle="--", label="Before") # Voltage and capacity vs. time axv = plt.subplot(gs[:-2, :], sharex=axl) plt.setp(axv.get_xticklabels(), visible=False) axv.set_ylabel(nv.col_v, fontsize=fs) axv.plot( a_t[astart::], a_v[astart::], cols[0], linewidth=2.5, label="Potential after" ) axv.plot( b_t[bstart::], b_v[bstart::], cols[1], linestyle="--", label="Potential before" ) leg = axv.legend(loc=2, fontsize=fs - 2) ii = 0 for text in leg.get_texts(): text.set_color(cols[ii]) ii += 1 leg.draw_frame(False) axc = axv.twinx() axc.set_ylabel(nv.col_c, fontsize=fs) axc.plot( a_t[astart::], a_c[astart::], cols[2], linewidth=2.5, label="Capacity after" ) axc.plot( b_t[bstart::], b_c[bstart::], cols[3], linestyle="--", label="Capacity before" ) leg = axc.legend(loc=4, fontsize=fs - 2) ii = 2 for text in leg.get_texts(): text.set_color(cols[ii]) ii += 1 leg.draw_frame(False) plt.savefig(figname) if plot_show: plt.show() print("Plot: {}".format(figname))
def novonix_add_state(infile, verbose=False): """ Given a cleaned Novonix data file, it adds a 'State' column, which mimimcs Basytec format with: 0 = Start of a measurement type (charge/discharge, etc) 1 = Regular data point (measuring, no mode change) 2 = End of cycle (last point of the measurement) This values are determined by the change in the 'Step Number' from Novonix and the 'Step time', which goes to 0 with each new 'Step'. Parameters ----------- infile : string Name of the input Novonix file verbose : boolean Yes : print out some informative statements Notes ----- This code returns a Novonix file with an extra 'State' column. Examples --------- >>> import preparenovonix.novonix_add as prep >>> prep.novonix_add_state('example_data/example_data_prep.csv',verbose=True) The file example_data/example_data_prep.csv already has a State column """ # Check if the State column already exists col_exists = column_check(infile, nv.state_col, verbose=verbose) if col_exists: return # Find the Step Number column icol = icolumn(infile, nv.col_step) icolt = icolumn(infile, nv.col_tstep) # Read the input file header = [] fw = "fw" with open(infile, "r") as ff: # Read until the line with [Data] for line in ff: header.append(line) fw = line.rsplit() if fw[0] == "[Data]": break # Read the column names and add the 'State' one line = ff.readline() new_head = str(line.rstrip()) + ", " + nv.state_col + " \n" header.append(new_head) # Create a temporary file with the new header tmp_file = "tmp.csv" ihead = 0 with open(tmp_file, "w") as tf: for item in header: tf.write(str(item)) ihead += 1 # The State column state = [] data = [] last_step = -99 # Create a starting last state value last_t = 99.0 # Create a starting step time value steps = [] # Read the data adding values to the state for il, line in enumerate(ff): data.append(line) step = line.split(",")[icol] steps.append(step) stime = float(line.split(",")[icolt]) if step == last_step and stime > last_t: state.append(1) else: state.append(0) # Change the previous value if len(state) > 1: if state[-2] == 0: if last_t < nv.eps: # Single measurement state[-2] = -1 else: # Jump lines affected by software bug and # 2 single measurements in a row # (this can happen when current overshoots) state[-2] = -99 if verbose: # Line count starts with 1, thus (il+1) print( "WARNING line=", str(il + ihead), ", last step time=" + str(last_t), ": Measurement to be nv.ignored", ) if state[-3] == -1: # Avoid 2 single measurements in a row state[-3] = -99 if verbose: print( "WARNING Measurement to be nv.ignored: line=", str(il - 1 + ihead), ", last step time=" + str(last_t), ) elif state[-2] == 1: state[-2] = 2 else: sys.exit("STOP function novonix_add_state \n" + "REASON unexpected state \n" + " " + str(infile) + " \n") last_step = step last_t = stime state[-1] = 2 # Check the new column check_pass = state_check(state) if not check_pass: sys.exit("STOP novonix_add.novonix_add_state \n" + str(infile) + " \n") # Write the new data to the temporary file with open(tmp_file, "a") as tf: ii = 0 for idata in data: if state[ii] > -99: new_line = str(idata.rstrip()) + "," + str( state[ii]) + "\n" tf.write(new_line) ii += 1 # Replace the input file by the tmp_file, # which should be bigger. replace_file(tmp_file, infile, newbigger=True) if verbose: print("{} contains now a State column".format(infile)) return
def test_icolumn(): assert prep.icolumn(exfile_prep, nv.col_step) > -1 assert prep.icolumn(exfile_prep, nv.col_tstep) > -1 assert prep.icolumn(exfile_prep, "Random name") == -1
def cleannovonix(infile): """ Given a Novonix file remove blank lines, correct the header, remove failed tests if needed. Parameters ----------- infile : string Name of the input Novonix file Notes ----- This code returns a cleaned Novonix file Examples --------- >>> from preparenovonix.novonix_clean import cleannovonix >>> cleannovonix('example_data/example_data.csv') """ # Count the number of tests ntests = count_tests(infile) # Find the capacity colum icapacity = icolumn(infile, nv.col_c) # Find the run time column iruntime = icolumn(infile, nv.col_t) # Deal with the capacity of the failed tests last_capacity = capacity_failed_tests(icapacity, ntests, infile) # Start reading the last test # Remove blank lines if present in the header itest = 0 header = [] with open(infile, "r") as ff: for line in ff: if line.strip(): if summary in line: itest += 1 if itest == ntests: header.append(summary + " \n") break # Read until the line with [Data] for line in ff: if line.strip(): char1 = line.strip()[0] if char1 == "[": cleanhead = line.split("]") header.append(cleanhead[0] + "] \n") if cleanhead[0] == "[Data": break else: header.append(line) # From the data header, read the column names for line in ff: if line.strip(): break # Check that the number of data columns matches the header line_data1 = ff.readline() data = line_data1.split(",") header_data_columns(line, data, header) # Create a temporary file without blanck lines # and new header if needed tmp_file = "tmp.csv" with open(tmp_file, "w") as tf: for item in header: tf.write(str(item)) # Append the data jumping any line with time going backwards with open(tmp_file, "a") as tf: # Write the first data row if ntests > 1: # Modify the Capacity column in case of failed tests new_capacity = float(data[icapacity]) + float(last_capacity) data[icapacity] = str(new_capacity) new_line = data[0] for col in data[1:]: new_line = new_line + "," + col tf.write(new_line) else: tf.write(line_data1) # Write the rest of the data last_t = -1.0 for line in ff: columns = line.split(",") if float(columns[iruntime]) < last_t: continue last_t = float(columns[iruntime]) if ntests > 1: # Modify the Capacity column in case of failed tests new_capacity = float( columns[icapacity]) + float(last_capacity) columns[icapacity] = str(new_capacity) new_line = columns[0] for col in columns[1:]: new_line = new_line + "," + col tf.write(new_line) else: tf.write(line) # Replace the input file with the new one replace_file(tmp_file, infile, newbigger=False) return