Change unit conversion function and add time string conversions
This commit is contained in:
parent
2e910a2afb
commit
ed5504449f
1 changed files with 192 additions and 85 deletions
|
|
@ -19,15 +19,166 @@ def read_battsmall(path):
|
|||
|
||||
|
||||
|
||||
def unit_conversion(df, units):
|
||||
|
||||
C, m = units['C'].split('/')
|
||||
def read_neware(path, summary=False, active_material_weight=None, molecular_weight=None):
|
||||
''' Reads electrochemistry data, currently only from the Neware battery cycler. Will convert to .csv if the filetype is .xlsx,
|
||||
which is the file format the Neware provides for the backup data. In this case it matters if summary is False or not. If file
|
||||
type is .csv, it will just open the datafile and it does not matter if summary is False or not.'''
|
||||
|
||||
|
||||
# Convert from .xlsx to .csv to make readtime faster
|
||||
if filename.split('.')[-1] == 'xlsx':
|
||||
csv_details = ''.join(filename.split('.')[:-1]) + '_details.csv'
|
||||
csv_summary = ''.join(filename.split('.')[:-1]) + '_summary.csv'
|
||||
|
||||
Xlsx2csv(filename, outputencoding="utf-8").convert(csv_summary, sheetid=3)
|
||||
Xlsx2csv(filename, outputencoding="utf-8").convert(csv_details, sheetid=4)
|
||||
|
||||
if summary:
|
||||
df = pd.read_csv(csv_summary)
|
||||
else:
|
||||
df = pd.read_csv(csv_details)
|
||||
|
||||
elif filename.split('.')[-1] == 'csv':
|
||||
|
||||
df = pd.read_csv(filename)
|
||||
|
||||
|
||||
return df
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#def process_battsmall_data(df, t='ms', C='mAh/g', I='mA', U='V'):
|
||||
|
||||
def process_battsmall_data(df, units=None, splice_cycles=None, molecular_weight=None):
|
||||
''' Takes BATTSMALL-data in the form of a DataFrame and cleans the data up and converts units into desired units.
|
||||
Splits up into individual charge and discharge DataFrames per cycle, and outputs a list where each element is a tuple with the Chg and DChg-data. E.g. cycles[10][0] gives the charge data for the 11th cycle.
|
||||
|
||||
For this to work, the cycling program must be set to use the counter.
|
||||
|
||||
Input:
|
||||
df (required): A pandas DataFrame containing BATTSMALL-data, as obtained from read_battsmall().
|
||||
t (optional): Unit for time data. Defaults to ms.
|
||||
C (optional): Unit for specific capacity. Defaults to mAh/g.
|
||||
I (optional): Unit for current. Defaults mA.
|
||||
U (optional): Unit for voltage. Defaults to V.
|
||||
|
||||
Output:
|
||||
cycles: A list with
|
||||
'''
|
||||
|
||||
#########################
|
||||
#### UNIT CONVERSION ####
|
||||
#########################
|
||||
|
||||
# Complete the list of units - if not all are passed, then default value will be used
|
||||
required_units = ['t', 'I', 'U', 'C']
|
||||
default_units = {'t': 'h', 'I': 'mA', 'U': 'V', 'C': 'mAh/g'}
|
||||
|
||||
if not units:
|
||||
units = default_units
|
||||
|
||||
if units:
|
||||
for unit in required_units:
|
||||
if unit not in units.values():
|
||||
units[unit] = default_units[unit]
|
||||
|
||||
|
||||
# Get the units used in the data set
|
||||
t_prev = df.columns[0].split()[-1].strip('[]')
|
||||
U_prev = df.columns[1].split()[-1].strip('[]')
|
||||
I_prev = df.columns[2].split()[-1].strip('[]')
|
||||
C_prev, m_prev = df.columns[4].split()[-1].strip('[]').split('/')
|
||||
prev_units = {'t': t_prev, 'I': I_prev, 'U': U_prev, 'C': C_prev}
|
||||
|
||||
# Convert all units to the desired units.
|
||||
df = unit_conversion(df=df, units=units)
|
||||
|
||||
# Replace NaN with empty string in the Comment-column and then remove all steps where the program changes - this is due to inconsistent values for current
|
||||
df[["Comment"]] = df[["Comment"]].fillna(value={'Comment': ''})
|
||||
df = df[df["Comment"].str.contains("program")==False]
|
||||
|
||||
# Creates masks for charge and discharge curves
|
||||
chg_mask = df['I'] >= 0
|
||||
dchg_mask = df['I'] < 0
|
||||
|
||||
# Initiate cycles list
|
||||
cycles = []
|
||||
|
||||
# Loop through all the cycling steps, change the current and capacities in the
|
||||
for i in range(df["Z1"].max()):
|
||||
|
||||
sub_df = df.loc[df['Z1'] == i].copy()
|
||||
|
||||
sub_df.loc[dchg_mask, 'I'] *= -1
|
||||
sub_df.loc[dchg_mask, 'C'] *= -1
|
||||
|
||||
chg_df = sub_df.loc[chg_mask]
|
||||
dchg_df = sub_df.loc[dchg_mask]
|
||||
|
||||
# Continue to next iteration if the charge and discharge DataFrames are empty (i.e. no current)
|
||||
if chg_df.empty and dchg_df.empty:
|
||||
continue
|
||||
|
||||
cycles.append((chg_df, dchg_df))
|
||||
|
||||
|
||||
|
||||
|
||||
return cycles
|
||||
|
||||
|
||||
def process_neware_data(df, units=None, splice_cycles=None, active_material_weight=None, molecular_weight=None):
|
||||
|
||||
#########################
|
||||
#### UNIT CONVERSION ####
|
||||
#########################
|
||||
|
||||
# Complete the list of units - if not all are passed, then default value will be used
|
||||
required_units = ['t', 'I', 'U', 'C']
|
||||
default_units = {'t': 'h', 'I': 'mA', 'U': 'V', 'C': 'mAh/g'}
|
||||
|
||||
if not units:
|
||||
units = default_units
|
||||
|
||||
if units:
|
||||
for unit in required_units:
|
||||
if unit not in units.values():
|
||||
units[unit] = default_units[unit]
|
||||
|
||||
|
||||
|
||||
# Get the units used in the data set
|
||||
t_prev = 's' # default in
|
||||
U_prev = df.columns[1].split()[-1].strip('[]')
|
||||
I_prev = df.columns[2].split()[-1].strip('[]')
|
||||
C_prev, m_prev = df.columns[4].split()[-1].strip('[]').split('/')
|
||||
prev_units = {'t': t_prev, 'I': I_prev, 'U': U_prev, 'C': C_prev}
|
||||
|
||||
# Convert all units to the desired units.
|
||||
df = unit_conversion(df=df, units=units)
|
||||
|
||||
|
||||
|
||||
if active_material_weight:
|
||||
df["SpecificCapacity(mAh/g)"] = df["Capacity(mAh)"] / (active_material_weight / 1000)
|
||||
|
||||
if molecular_weight:
|
||||
faradays_constant = 96485.3365 # [F] = C mol^-1 = As mol^-1
|
||||
seconds_per_hour = 3600 # s h^-1
|
||||
f = faradays_constant / seconds_per_hour * 1000.0 # [f] = mAh mol^-1
|
||||
|
||||
df["IonsExtracted"] = (df["SpecificCapacity(mAh/g)"]*molecular_weight)/f
|
||||
|
||||
|
||||
def unit_conversion(df, units, prev_units, kind):
|
||||
|
||||
C, m = units['C'].split('/')
|
||||
C_prev, m_prev = prev_units['C'].split('/')
|
||||
|
||||
|
||||
# Define matrix for unit conversion for time
|
||||
|
|
@ -55,14 +206,11 @@ def unit_conversion(df, units):
|
|||
m_units_df = pd.DataFrame(m_units_df)
|
||||
m_units_df.index = ['kg', 'g', 'mg', 'ug']
|
||||
|
||||
|
||||
|
||||
|
||||
#print(df["TT [{}]".format(t_prev)])
|
||||
df["TT [{}]".format(t_prev)] = df["TT [{}]".format(t_prev)] * t_units_df[t_prev].loc[units['t']]
|
||||
df["U [{}]".format(U_prev)] = df["U [{}]".format(U_prev)] * U_units_df[U_prev].loc[units['U']]
|
||||
df["I [{}]".format(I_prev)] = df["I [{}]".format(I_prev)] * I_units_df[I_prev].loc[units['I']]
|
||||
df["C [{}/{}]".format(C_prev, m_prev)] = df["C [{}/{}]".format(C_prev, m_prev)] * (C_units_df[C_prev].loc[units['C']] / m_units_df[m_prev].loc[units['m']])
|
||||
df["C [{}/{}]".format(C_prev, m_prev)] = df["C [{}/{}]".format(C_prev, m_prev)] * (C_units_df[C_prev].loc[C] / m_units_df[m_prev].loc[m])
|
||||
|
||||
df.columns = ['TT', 'U', 'I', 'Z1', 'C', 'Comment']
|
||||
|
||||
|
|
@ -70,86 +218,60 @@ def unit_conversion(df, units):
|
|||
|
||||
return df
|
||||
|
||||
#def process_battsmall_data(df, t='ms', C='mAh/g', I='mA', U='V'):
|
||||
|
||||
def process_battsmall_data(df, units=None):
|
||||
''' Takes BATTSMALL-data in the form of a DataFrame and cleans the data up and converts units into desired units.
|
||||
Splits up into individual charge and discharge DataFrames per cycle, and outputs a list where each element is a tuple with the Chg and DChg-data. E.g. cycles[10][0] gives the charge data for the 11th cycle.
|
||||
|
||||
For this to work, the cycling program must be set to use the counter.
|
||||
|
||||
Input:
|
||||
df (required): A pandas DataFrame containing BATTSMALL-data, as obtained from read_battsmall().
|
||||
t (optional): Unit for time data. Defaults to ms.
|
||||
C (optional): Unit for specific capacity. Defaults to mAh/g.
|
||||
I (optional): Unit for current. Defaults mA.
|
||||
U (optional): Unit for voltage. Defaults to V.
|
||||
|
||||
Output:
|
||||
cycles: A list with
|
||||
'''
|
||||
|
||||
required_units = ['t', 'I', 'U', 'C']
|
||||
default_units = {'t': 'h', 'I': 'mA', 'U': 'V', 'C': 'mAh/g'}
|
||||
|
||||
if not units:
|
||||
units = default_units
|
||||
|
||||
if units:
|
||||
for unit in required_units:
|
||||
if unit not in units.values():
|
||||
units[unit] = default_units[unit]
|
||||
|
||||
|
||||
# Convert all units to the desired units.
|
||||
df = unit_conversion(df=df, units=units)
|
||||
def convert_time_string(time_string, unit='ms'):
|
||||
''' Convert time string from Neware-data with the format hh:mm:ss.xx to any given unit'''
|
||||
|
||||
# Replace NaN with empty string in the Comment-column and then remove all steps where the program changes - this is due to inconsistent values for current and
|
||||
df[["Comment"]] = df[["Comment"]].fillna(value={'Comment': ''})
|
||||
df = df[df["Comment"].str.contains("program")==False]
|
||||
h, m, s = time_string.split(':')
|
||||
ms = int(s)*1000 + int(m)*1000*60 + int(h)*1000*60*60
|
||||
|
||||
# Creates masks for
|
||||
chg_mask = df['I'] >= 0
|
||||
dchg_mask = df['I'] < 0
|
||||
factors = {'ms': 1, 's': 1/1000, 'min': 1/(1000*60), 'h': 1/(1000*60*60)}
|
||||
|
||||
# Initiate cycles list
|
||||
cycles = []
|
||||
t = ms*factors[unit]
|
||||
|
||||
# Loop through all the cycling steps, change the current and capacities in the
|
||||
for i in range(df["Z1"].max()):
|
||||
sub_df = df.loc[df['Z1'] == i].copy()
|
||||
sub_df.loc[dchg_mask, 'I'] *= -1
|
||||
sub_df.loc[dchg_mask, 'C'] *= -1
|
||||
|
||||
chg_df = sub_df.loc[chg_mask]
|
||||
dchg_df = sub_df.loc[dchg_mask]
|
||||
|
||||
|
||||
cycles.append((chg_df, dchg_df))
|
||||
|
||||
|
||||
return cycles
|
||||
return t
|
||||
|
||||
|
||||
|
||||
def plot_gc(cycles, which_cycles='all', chg=True, dchg=True, colours=None, x='C', y='U'):
|
||||
def convert_datetime_string(datetime_string, reference, unit='s'):
|
||||
''' Convert time string from Neware-data with the format yyy-mm-dd hh:mm:ss to any given unit'''
|
||||
|
||||
fig, ax = prepare_gc_plot()
|
||||
from datetime import datetime
|
||||
|
||||
# Parse the
|
||||
cur_date, cur_time = datetime_string.split()
|
||||
cur_y, cur_mo, cur_d = cur_date.split('-')
|
||||
cur_h, cur_m, cur_s = cur_time.split(':')
|
||||
cur_date = datetime(int(cur_y), int(cur_mo), int(cur_d), int(cur_h), int(cur_m), int(cur_s))
|
||||
|
||||
if which_cycles == 'all':
|
||||
which_cycles = [i for i, c in enumerate(cycles)]
|
||||
ref_date, ref_time = reference.split()
|
||||
ref_y, ref_mo, ref_d = ref_date.split('-')
|
||||
ref_h, ref_m, ref_s = ref_time.split(':')
|
||||
ref_date = datetime(int(ref_y), int(ref_mo), int(ref_d), int(ref_h), int(ref_m), int(ref_s))
|
||||
|
||||
if not colours:
|
||||
chg_colour = (40/255, 70/255, 75/255) # Dark Slate Gray #28464B
|
||||
dchg_colour = (239/255, 160/255, 11/255) # Marigold #EFA00B
|
||||
days = cur_date - ref_date
|
||||
|
||||
s = days.seconds
|
||||
|
||||
factors = {'ms': 1000, 's': 1, 'min': 1/(60), 'h': 1/(60*60)}
|
||||
|
||||
for i, cycle in cycles:
|
||||
if i in which_cycles:
|
||||
if chg:
|
||||
cycle[0].plot(ax=ax)
|
||||
t = s * factors[unit]
|
||||
|
||||
return t
|
||||
|
||||
def splice_cycles(first, second):
|
||||
|
||||
first_chg = first[0]
|
||||
first_dchg = first[1]
|
||||
first
|
||||
|
||||
second_chg = second[0]
|
||||
second_dchg = second[1]
|
||||
|
||||
chg_df = first[0].append(second[0])
|
||||
|
||||
return True
|
||||
|
||||
|
||||
|
||||
|
|
@ -158,18 +280,3 @@ def plot_gc(cycles, which_cycles='all', chg=True, dchg=True, colours=None, x='C'
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def prepare_gc_plot(figsize=(14,7), dpi=None):
|
||||
|
||||
fig, ax = plt.subplots(figsize=figsize, dpi=dpi)
|
||||
|
||||
|
||||
return fig, ax
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue