Standardise data flow
This commit is contained in:
parent
95e411ac21
commit
514a20604b
2 changed files with 169 additions and 214 deletions
|
|
@ -1,40 +1,28 @@
|
||||||
|
from email.policy import default
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import matplotlib.pyplot as plt
|
import matplotlib.pyplot as plt
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
import nafuma.auxillary as aux
|
||||||
|
from sympy import re
|
||||||
|
|
||||||
def read_data(path, kind, options=None):
|
def read_data(data, options=None):
|
||||||
|
|
||||||
if kind == 'neware':
|
if data['kind'] == 'neware':
|
||||||
df = read_neware(path)
|
df = read_neware(data['path'])
|
||||||
cycles = process_neware_data(df, options=options)
|
cycles = process_neware_data(df=df, options=options)
|
||||||
|
|
||||||
elif kind == 'batsmall':
|
elif data['kind'] == 'batsmall':
|
||||||
df = read_batsmall(path)
|
df = read_batsmall(data['path'])
|
||||||
cycles = process_batsmall_data(df=df, options=options)
|
cycles = process_batsmall_data(df=df, options=options)
|
||||||
|
|
||||||
elif kind == 'biologic':
|
elif data['kind'] == 'biologic':
|
||||||
df = read_biologic(path)
|
df = read_biologic(data['path'])
|
||||||
cycles = process_biologic_data(df=df, options=options)
|
cycles = process_biologic_data(df=df, options=options)
|
||||||
|
|
||||||
return cycles
|
return cycles
|
||||||
|
|
||||||
def read_batsmall(path):
|
|
||||||
''' Reads BATSMALL-data into a DataFrame.
|
|
||||||
|
|
||||||
Input:
|
|
||||||
path (required): string with path to datafile
|
|
||||||
|
|
||||||
Output:
|
|
||||||
df: pandas DataFrame containing the data as-is, but without additional NaN-columns.'''
|
|
||||||
|
|
||||||
df = pd.read_csv(path, skiprows=2, sep='\t')
|
|
||||||
df = df.loc[:, ~df.columns.str.contains('^Unnamed')]
|
|
||||||
|
|
||||||
return df
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def read_neware(path, summary=False):
|
def read_neware(path, summary=False):
|
||||||
|
|
@ -43,6 +31,8 @@ def read_neware(path, summary=False):
|
||||||
type is .csv, it will just open the datafile and it does not matter if summary is False or not.'''
|
type is .csv, it will just open the datafile and it does not matter if summary is False or not.'''
|
||||||
from xlsx2csv import Xlsx2csv
|
from xlsx2csv import Xlsx2csv
|
||||||
|
|
||||||
|
# FIXME Do a check if a .csv-file already exists even if the .xlsx is passed
|
||||||
|
|
||||||
# Convert from .xlsx to .csv to make readtime faster
|
# Convert from .xlsx to .csv to make readtime faster
|
||||||
if path.split('.')[-1] == 'xlsx':
|
if path.split('.')[-1] == 'xlsx':
|
||||||
csv_details = ''.join(path.split('.')[:-1]) + '_details.csv'
|
csv_details = ''.join(path.split('.')[:-1]) + '_details.csv'
|
||||||
|
|
@ -66,6 +56,20 @@ def read_neware(path, summary=False):
|
||||||
return df
|
return df
|
||||||
|
|
||||||
|
|
||||||
|
def read_batsmall(path):
|
||||||
|
''' Reads BATSMALL-data into a DataFrame.
|
||||||
|
|
||||||
|
Input:
|
||||||
|
path (required): string with path to datafile
|
||||||
|
|
||||||
|
Output:
|
||||||
|
df: pandas DataFrame containing the data as-is, but without additional NaN-columns.'''
|
||||||
|
|
||||||
|
df = pd.read_csv(path, skiprows=2, sep='\t')
|
||||||
|
df = df.loc[:, ~df.columns.str.contains('^Unnamed')]
|
||||||
|
|
||||||
|
return df
|
||||||
|
|
||||||
|
|
||||||
def read_biologic(path):
|
def read_biologic(path):
|
||||||
''' Reads Bio-Logic-data into a DataFrame.
|
''' Reads Bio-Logic-data into a DataFrame.
|
||||||
|
|
@ -89,10 +93,6 @@ def read_biologic(path):
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def process_batsmall_data(df, options=None):
|
def process_batsmall_data(df, options=None):
|
||||||
''' Takes BATSMALL-data in the form of a DataFrame and cleans the data up and converts units into desired units.
|
''' Takes BATSMALL-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.
|
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.
|
||||||
|
|
@ -111,26 +111,25 @@ def process_batsmall_data(df, options=None):
|
||||||
'''
|
'''
|
||||||
|
|
||||||
required_options = ['splice_cycles', 'molecular_weight', 'reverse_discharge', 'units']
|
required_options = ['splice_cycles', 'molecular_weight', 'reverse_discharge', 'units']
|
||||||
default_options = {'splice_cycles': False, 'molecular_weight': None, 'reverse_discharge': False, 'units': None}
|
|
||||||
|
default_options = {
|
||||||
|
'splice_cycles': False,
|
||||||
|
'molecular_weight': None,
|
||||||
|
'reverse_discharge': False,
|
||||||
|
'units': None}
|
||||||
|
|
||||||
if not options:
|
|
||||||
options = default_options
|
|
||||||
else:
|
|
||||||
for option in required_options:
|
|
||||||
if option not in options.keys():
|
|
||||||
options[option] = default_options[option]
|
|
||||||
|
|
||||||
|
aux.update_options(options=options, required_options=required_options, default_options=default_options)
|
||||||
|
options['kind'] = 'batsmall'
|
||||||
|
|
||||||
# Complete set of new units and get the units used in the dataset, and convert values in the DataFrame from old to new.
|
# Complete set of new units and get the units used in the dataset, and convert values in the DataFrame from old to new.
|
||||||
new_units = set_units(units=options['units'])
|
set_units(options)
|
||||||
old_units = get_old_units(df, kind='batsmall')
|
options['old_units'] = get_old_units(df, options)
|
||||||
df = unit_conversion(df=df, new_units=new_units, old_units=old_units, kind='batsmall')
|
|
||||||
|
|
||||||
options['units'] = new_units
|
|
||||||
|
|
||||||
|
df = unit_conversion(df=df, options=options)
|
||||||
|
|
||||||
if options['splice_cycles']:
|
if options['splice_cycles']:
|
||||||
df = splice_cycles(df=df, kind='batsmall')
|
df = splice_cycles(df=df, options=options)
|
||||||
|
|
||||||
# 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
|
# 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[["comment"]] = df[["comment"]].fillna(value={'comment': ''})
|
||||||
|
|
@ -173,23 +172,21 @@ def process_batsmall_data(df, options=None):
|
||||||
cycles.append((chg_df, dchg_df))
|
cycles.append((chg_df, dchg_df))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return cycles
|
return cycles
|
||||||
|
|
||||||
|
|
||||||
def splice_cycles(df, kind):
|
def splice_cycles(df, options: dict) -> pd.DataFrame:
|
||||||
|
''' Splices two cycles together - if e.g. one charge cycle are split into several cycles due to change in parameters.
|
||||||
|
|
||||||
|
Incomplete, only accomodates BatSmall so far.'''
|
||||||
|
|
||||||
if kind == 'batsmall':
|
if options['kind'] == 'batsmall':
|
||||||
|
|
||||||
# Creates masks for charge and discharge curves
|
# Creates masks for charge and discharge curves
|
||||||
chg_mask = df['current'] >= 0
|
chg_mask = df['current'] >= 0
|
||||||
dchg_mask = df['current'] < 0
|
dchg_mask = df['current'] < 0
|
||||||
|
|
||||||
# Get the number of cycles in the dataset
|
# Loop through all the cycling steps, change the current and capacities in the
|
||||||
max_count = df["count"].max()
|
|
||||||
|
|
||||||
# Loop through all the cycling steps, change the current and capacities in the
|
|
||||||
for i in range(df["count"].max()):
|
for i in range(df["count"].max()):
|
||||||
sub_df = df.loc[df['count'] == i+1]
|
sub_df = df.loc[df['count'] == i+1]
|
||||||
sub_df_chg = sub_df.loc[chg_mask]
|
sub_df_chg = sub_df.loc[chg_mask]
|
||||||
|
|
@ -233,7 +230,7 @@ def splice_cycles(df, kind):
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def process_neware_data(df, options=None):
|
def process_neware_data(df, options={}):
|
||||||
|
|
||||||
""" Takes data from NEWARE in a DataFrame as read by read_neware() and converts units, adds columns and splits into cycles.
|
""" Takes data from NEWARE in a DataFrame as read by read_neware() and converts units, adds columns and splits into cycles.
|
||||||
|
|
||||||
|
|
@ -245,25 +242,26 @@ def process_neware_data(df, options=None):
|
||||||
molecular_weight: the molar mass (in g mol^-1) of the active material, to calculate the number of ions extracted. Assumes one electron per Li+/Na+-ion """
|
molecular_weight: the molar mass (in g mol^-1) of the active material, to calculate the number of ions extracted. Assumes one electron per Li+/Na+-ion """
|
||||||
|
|
||||||
required_options = ['units', 'active_material_weight', 'molecular_weight', 'reverse_discharge', 'splice_cycles']
|
required_options = ['units', 'active_material_weight', 'molecular_weight', 'reverse_discharge', 'splice_cycles']
|
||||||
default_options = {'units': None, 'active_material_weight': None, 'molecular_weight': None, 'reverse_discharge': False, 'splice_cycles': None}
|
|
||||||
|
default_options = {
|
||||||
|
'units': None,
|
||||||
|
'active_material_weight': None,
|
||||||
|
'molecular_weight': None,
|
||||||
|
'reverse_discharge': False,
|
||||||
|
'splice_cycles': None}
|
||||||
|
|
||||||
if not options:
|
|
||||||
options = default_options
|
aux.update_options(options=options, required_options=required_options, default_options=default_options)
|
||||||
else:
|
options['kind'] = 'neware'
|
||||||
for option in required_options:
|
|
||||||
if option not in options.keys():
|
|
||||||
options[option] = default_options[option]
|
|
||||||
|
|
||||||
|
|
||||||
# Complete set of new units and get the units used in the dataset, and convert values in the DataFrame from old to new.
|
# Complete set of new units and get the units used in the dataset, and convert values in the DataFrame from old to new.
|
||||||
new_units = set_units(units=options['units'])
|
set_units(options=options) # sets options['units']
|
||||||
old_units = get_old_units(df=df, kind='neware')
|
options['old_units'] = get_old_units(df=df, options=options)
|
||||||
|
|
||||||
df = add_columns(df=df, active_material_weight=options['active_material_weight'], molecular_weight=options['molecular_weight'], old_units=old_units, kind='neware')
|
df = add_columns(df=df, options=options) # adds columns to the DataFrame if active material weight and/or molecular weight has been passed in options
|
||||||
|
|
||||||
df = unit_conversion(df=df, new_units=new_units, old_units=old_units, kind='neware')
|
df = unit_conversion(df=df, options=options) # converts all units from the old units to the desired units
|
||||||
|
|
||||||
options['units'] = new_units
|
|
||||||
|
|
||||||
|
|
||||||
# Creates masks for charge and discharge curves
|
# Creates masks for charge and discharge curves
|
||||||
|
|
@ -288,6 +286,8 @@ def process_neware_data(df, options=None):
|
||||||
if chg_df.empty and dchg_df.empty:
|
if chg_df.empty and dchg_df.empty:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
|
||||||
|
# Reverses the discharge curve if specified
|
||||||
if options['reverse_discharge']:
|
if options['reverse_discharge']:
|
||||||
max_capacity = dchg_df['capacity'].max()
|
max_capacity = dchg_df['capacity'].max()
|
||||||
dchg_df['capacity'] = np.abs(dchg_df['capacity'] - max_capacity)
|
dchg_df['capacity'] = np.abs(dchg_df['capacity'] - max_capacity)
|
||||||
|
|
@ -310,35 +310,34 @@ def process_neware_data(df, options=None):
|
||||||
def process_biologic_data(df, options=None):
|
def process_biologic_data(df, options=None):
|
||||||
|
|
||||||
required_options = ['units', 'active_material_weight', 'molecular_weight', 'reverse_discharge', 'splice_cycles']
|
required_options = ['units', 'active_material_weight', 'molecular_weight', 'reverse_discharge', 'splice_cycles']
|
||||||
default_options = {'units': None, 'active_material_weight': None, 'molecular_weight': None, 'reverse_discharge': False, 'splice_cycles': None}
|
|
||||||
|
default_options = {
|
||||||
|
'units': None,
|
||||||
|
'active_material_weight': None,
|
||||||
|
'molecular_weight': None,
|
||||||
|
'reverse_discharge': False,
|
||||||
|
'splice_cycles': None}
|
||||||
|
|
||||||
if not options:
|
|
||||||
options = default_options
|
aux.update_options(options=options, required_options=required_options, default_options=default_options)
|
||||||
else:
|
options['kind'] = 'biologic'
|
||||||
for option in required_options:
|
|
||||||
if option not in options.keys():
|
|
||||||
options[option] = default_options[option]
|
|
||||||
|
|
||||||
# Pick out necessary columns
|
# Pick out necessary columns
|
||||||
df = df[['Ns changes', 'Ns', 'time/s', 'Ewe/V', 'Energy charge/W.h', 'Energy discharge/W.h', '<I>/mA', 'Capacity/mA.h', 'cycle number']].copy()
|
df = df[['Ns changes', 'Ns', 'time/s', 'Ewe/V', 'Energy charge/W.h', 'Energy discharge/W.h', '<I>/mA', 'Capacity/mA.h', 'cycle number']].copy()
|
||||||
|
|
||||||
# Complete set of new units and get the units used in the dataset, and convert values in the DataFrame from old to new.
|
# Complete set of new units and get the units used in the dataset, and convert values in the DataFrame from old to new.
|
||||||
new_units = set_units(units=options['units'])
|
set_units(options)
|
||||||
old_units = get_old_units(df=df, kind='biologic')
|
options['old_units'] = get_old_units(df=df, options=options)
|
||||||
|
|
||||||
df = add_columns(df=df, active_material_weight=options['active_material_weight'], molecular_weight=options['molecular_weight'], old_units=old_units, kind='biologic')
|
df = add_columns(df=df, options=options)
|
||||||
|
|
||||||
df = unit_conversion(df=df, new_units=new_units, old_units=old_units, kind='biologic')
|
|
||||||
|
|
||||||
options['units'] = new_units
|
|
||||||
|
|
||||||
|
df = unit_conversion(df=df, options=options)
|
||||||
|
|
||||||
# Creates masks for charge and discharge curves
|
# Creates masks for charge and discharge curves
|
||||||
chg_mask = (df['status'] == 1) & (df['status_change'] != 1)
|
chg_mask = (df['status'] == 1) & (df['status_change'] != 1)
|
||||||
dchg_mask = (df['status'] == 2) & (df['status_change'] != 1)
|
dchg_mask = (df['status'] == 2) & (df['status_change'] != 1)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Initiate cycles list
|
# Initiate cycles list
|
||||||
cycles = []
|
cycles = []
|
||||||
|
|
||||||
|
|
@ -376,62 +375,62 @@ def process_biologic_data(df, options=None):
|
||||||
return cycles
|
return cycles
|
||||||
|
|
||||||
|
|
||||||
def add_columns(df, active_material_weight, molecular_weight, old_units, kind):
|
def add_columns(df, options):
|
||||||
|
|
||||||
if kind == 'neware':
|
if options['kind'] == 'neware':
|
||||||
if active_material_weight:
|
if options['active_material_weight']:
|
||||||
df["SpecificCapacity({}/mg)".format(old_units["capacity"])] = df["Capacity({})".format(old_units['capacity'])] / (active_material_weight)
|
df["SpecificCapacity({}/mg)".format(options['old_units']["capacity"])] = df["Capacity({})".format(options['old_units']['capacity'])] / (options['active_material_weight'])
|
||||||
|
|
||||||
if molecular_weight:
|
if options['molecular_weight']:
|
||||||
faradays_constant = 96485.3365 # [F] = C mol^-1 = As mol^-1
|
faradays_constant = 96485.3365 # [F] = C mol^-1 = As mol^-1
|
||||||
seconds_per_hour = 3600 # s h^-1
|
seconds_per_hour = 3600 # s h^-1
|
||||||
f = faradays_constant / seconds_per_hour * 1000.0 # [f] = mAh mol^-1
|
f = faradays_constant / seconds_per_hour * 1000.0 # [f] = mAh mol^-1
|
||||||
|
|
||||||
df["IonsExtracted"] = (df["SpecificCapacity({}/mg)".format(old_units['capacity'])]*molecular_weight)*1000/f
|
df["IonsExtracted"] = (df["SpecificCapacity({}/mg)".format(options['old_units']['capacity'])]*options['molecular_weight'])*1000/f
|
||||||
|
|
||||||
|
|
||||||
if kind == 'biologic':
|
if options['kind'] == 'biologic':
|
||||||
if active_material_weight:
|
if options['active_material_weight']:
|
||||||
|
|
||||||
capacity = old_units['capacity'].split('h')[0] + '.h'
|
capacity = options['old_units']['capacity'].split('h')[0] + '.h'
|
||||||
|
|
||||||
|
|
||||||
df["SpecificCapacity({}/mg)".format(old_units["capacity"])] = df["Capacity/{}".format(capacity)] / (active_material_weight)
|
df["SpecificCapacity({}/mg)".format(options['old_units']["capacity"])] = df["Capacity/{}".format(capacity)] / (options['active_material_weight'])
|
||||||
|
|
||||||
if molecular_weight:
|
if options['molecular_weight']:
|
||||||
faradays_constant = 96485.3365 # [F] = C mol^-1 = As mol^-1
|
faradays_constant = 96485.3365 # [F] = C mol^-1 = As mol^-1
|
||||||
seconds_per_hour = 3600 # s h^-1
|
seconds_per_hour = 3600 # s h^-1
|
||||||
f = faradays_constant / seconds_per_hour * 1000.0 # [f] = mAh mol^-1
|
f = faradays_constant / seconds_per_hour * 1000.0 # [f] = mAh mol^-1
|
||||||
|
|
||||||
df["IonsExtracted"] = (df["SpecificCapacity({}/mg)".format(old_units['capacity'])]*molecular_weight)*1000/f
|
df["IonsExtracted"] = (df["SpecificCapacity({}/mg)".format(options['old_units']['capacity'])]*options['molecular_weight'])*1000/f
|
||||||
|
|
||||||
return df
|
return df
|
||||||
|
|
||||||
|
|
||||||
def unit_conversion(df, new_units, old_units, kind):
|
def unit_conversion(df, options):
|
||||||
from . import unit_tables
|
from . import unit_tables
|
||||||
|
|
||||||
if kind == 'batsmall':
|
if options['kind'] == 'batsmall':
|
||||||
|
|
||||||
df["TT [{}]".format(old_units["time"])] = df["TT [{}]".format(old_units["time"])] * unit_tables.time()[old_units["time"]].loc[new_units['time']]
|
df["TT [{}]".format(options['old_units']["time"])] = df["TT [{}]".format(options['old_units']["time"])] * unit_tables.time()[options['old_units']["time"]].loc[options['units']['time']]
|
||||||
df["U [{}]".format(old_units["voltage"])] = df["U [{}]".format(old_units["voltage"])] * unit_tables.voltage()[old_units["voltage"]].loc[new_units['voltage']]
|
df["U [{}]".format(options['old_units']["voltage"])] = df["U [{}]".format(options['old_units']["voltage"])] * unit_tables.voltage()[options['old_units']["voltage"]].loc[options['units']['voltage']]
|
||||||
df["I [{}]".format(old_units["current"])] = df["I [{}]".format(old_units["current"])] * unit_tables.current()[old_units["current"]].loc[new_units['current']]
|
df["I [{}]".format(options['old_units']["current"])] = df["I [{}]".format(options['old_units']["current"])] * unit_tables.current()[options['old_units']["current"]].loc[options['units']['current']]
|
||||||
df["C [{}/{}]".format(old_units["capacity"], old_units["mass"])] = df["C [{}/{}]".format(old_units["capacity"], old_units["mass"])] * (unit_tables.capacity()[old_units["capacity"]].loc[new_units["capacity"]] / unit_tables.mass()[old_units["mass"]].loc[new_units["mass"]])
|
df["C [{}/{}]".format(options['old_units']["capacity"], options['old_units']["mass"])] = df["C [{}/{}]".format(options['old_units']["capacity"], options['old_units']["mass"])] * (unit_tables.capacity()[options['old_units']["capacity"]].loc[options['units']["capacity"]] / unit_tables.mass()[options['old_units']["mass"]].loc[options['units']["mass"]])
|
||||||
|
|
||||||
df.columns = ['time', 'voltage', 'current', 'count', 'specific_capacity', 'comment']
|
df.columns = ['time', 'voltage', 'current', 'count', 'specific_capacity', 'comment']
|
||||||
|
|
||||||
|
|
||||||
if kind == 'neware':
|
if options['kind'] == 'neware':
|
||||||
df['Current({})'.format(old_units['current'])] = df['Current({})'.format(old_units['current'])] * unit_tables.current()[old_units['current']].loc[new_units['current']]
|
df['Current({})'.format(options['old_units']['current'])] = df['Current({})'.format(options['old_units']['current'])] * unit_tables.current()[options['old_units']['current']].loc[options['units']['current']]
|
||||||
df['Voltage({})'.format(old_units['voltage'])] = df['Voltage({})'.format(old_units['voltage'])] * unit_tables.voltage()[old_units['voltage']].loc[new_units['voltage']]
|
df['Voltage({})'.format(options['old_units']['voltage'])] = df['Voltage({})'.format(options['old_units']['voltage'])] * unit_tables.voltage()[options['old_units']['voltage']].loc[options['units']['voltage']]
|
||||||
df['Capacity({})'.format(old_units['capacity'])] = df['Capacity({})'.format(old_units['capacity'])] * unit_tables.capacity()[old_units['capacity']].loc[new_units['capacity']]
|
df['Capacity({})'.format(options['old_units']['capacity'])] = df['Capacity({})'.format(options['old_units']['capacity'])] * unit_tables.capacity()[options['old_units']['capacity']].loc[options['units']['capacity']]
|
||||||
df['Energy({})'.format(old_units['energy'])] = df['Energy({})'.format(old_units['energy'])] * unit_tables.energy()[old_units['energy']].loc[new_units['energy']]
|
df['Energy({})'.format(options['old_units']['energy'])] = df['Energy({})'.format(options['old_units']['energy'])] * unit_tables.energy()[options['old_units']['energy']].loc[options['units']['energy']]
|
||||||
df['CycleTime({})'.format(new_units['time'])] = df.apply(lambda row : convert_time_string(row['Relative Time(h:min:s.ms)'], unit=new_units['time']), axis=1)
|
df['CycleTime({})'.format(options['units']['time'])] = df.apply(lambda row : convert_time_string(row['Relative Time(h:min:s.ms)'], unit=options['units']['time']), axis=1)
|
||||||
df['RunTime({})'.format(new_units['time'])] = df.apply(lambda row : convert_datetime_string(row['Real Time(h:min:s.ms)'], reference=df['Real Time(h:min:s.ms)'].iloc[0], unit=new_units['time']), axis=1)
|
df['RunTime({})'.format(options['units']['time'])] = df.apply(lambda row : convert_datetime_string(row['Real Time(h:min:s.ms)'], reference=df['Real Time(h:min:s.ms)'].iloc[0], unit=options['units']['time']), axis=1)
|
||||||
columns = ['status', 'jump', 'cycle', 'steps', 'current', 'voltage', 'capacity', 'energy']
|
columns = ['status', 'jump', 'cycle', 'steps', 'current', 'voltage', 'capacity', 'energy']
|
||||||
|
|
||||||
if 'SpecificCapacity({}/mg)'.format(old_units['capacity']) in df.columns:
|
if 'SpecificCapacity({}/mg)'.format(options['old_units']['capacity']) in df.columns:
|
||||||
df['SpecificCapacity({}/mg)'.format(old_units['capacity'])] = df['SpecificCapacity({}/mg)'.format(old_units['capacity'])] * unit_tables.capacity()[old_units['capacity']].loc[new_units['capacity']] / unit_tables.mass()['mg'].loc[new_units["mass"]]
|
df['SpecificCapacity({}/mg)'.format(options['old_units']['capacity'])] = df['SpecificCapacity({}/mg)'.format(options['old_units']['capacity'])] * unit_tables.capacity()[options['old_units']['capacity']].loc[options['units']['capacity']] / unit_tables.mass()['mg'].loc[options['units']["mass"]]
|
||||||
columns.append('specific_capacity')
|
columns.append('specific_capacity')
|
||||||
|
|
||||||
if 'IonsExtracted' in df.columns:
|
if 'IonsExtracted' in df.columns:
|
||||||
|
|
@ -447,18 +446,18 @@ def unit_conversion(df, new_units, old_units, kind):
|
||||||
|
|
||||||
df.columns = columns
|
df.columns = columns
|
||||||
|
|
||||||
if kind == 'biologic':
|
if options['kind'] == 'biologic':
|
||||||
df['time/{}'.format(old_units['time'])] = df["time/{}".format(old_units["time"])] * unit_tables.time()[old_units["time"]].loc[new_units['time']]
|
df['time/{}'.format(options['old_units']['time'])] = df["time/{}".format(options['old_units']["time"])] * unit_tables.time()[options['old_units']["time"]].loc[options['units']['time']]
|
||||||
df["Ewe/{}".format(old_units["voltage"])] = df["Ewe/{}".format(old_units["voltage"])] * unit_tables.voltage()[old_units["voltage"]].loc[new_units['voltage']]
|
df["Ewe/{}".format(options['old_units']["voltage"])] = df["Ewe/{}".format(options['old_units']["voltage"])] * unit_tables.voltage()[options['old_units']["voltage"]].loc[options['units']['voltage']]
|
||||||
df["<I>/{}".format(old_units["current"])] = df["<I>/{}".format(old_units["current"])] * unit_tables.current()[old_units["current"]].loc[new_units['current']]
|
df["<I>/{}".format(options['old_units']["current"])] = df["<I>/{}".format(options['old_units']["current"])] * unit_tables.current()[options['old_units']["current"]].loc[options['units']['current']]
|
||||||
|
|
||||||
capacity = old_units['capacity'].split('h')[0] + '.h'
|
capacity = options['old_units']['capacity'].split('h')[0] + '.h'
|
||||||
df["Capacity/{}".format(capacity)] = df["Capacity/{}".format(capacity)] * (unit_tables.capacity()[old_units["capacity"]].loc[new_units["capacity"]])
|
df["Capacity/{}".format(capacity)] = df["Capacity/{}".format(capacity)] * (unit_tables.capacity()[options['old_units']["capacity"]].loc[options['units']["capacity"]])
|
||||||
|
|
||||||
columns = ['status_change', 'status', 'time', 'voltage', 'energy_charge', 'energy_discharge', 'current', 'capacity', 'cycle']
|
columns = ['status_change', 'status', 'time', 'voltage', 'energy_charge', 'energy_discharge', 'current', 'capacity', 'cycle']
|
||||||
|
|
||||||
if 'SpecificCapacity({}/mg)'.format(old_units['capacity']) in df.columns:
|
if 'SpecificCapacity({}/mg)'.format(options['old_units']['capacity']) in df.columns:
|
||||||
df['SpecificCapacity({}/mg)'.format(old_units['capacity'])] = df['SpecificCapacity({}/mg)'.format(old_units['capacity'])] * unit_tables.capacity()[old_units['capacity']].loc[new_units['capacity']] / unit_tables.mass()['mg'].loc[new_units["mass"]]
|
df['SpecificCapacity({}/mg)'.format(options['old_units']['capacity'])] = df['SpecificCapacity({}/mg)'.format(options['old_units']['capacity'])] * unit_tables.capacity()[options['old_units']['capacity']].loc[options['units']['capacity']] / unit_tables.mass()['mg'].loc[options['units']["mass"]]
|
||||||
columns.append('specific_capacity')
|
columns.append('specific_capacity')
|
||||||
|
|
||||||
if 'IonsExtracted' in df.columns:
|
if 'IonsExtracted' in df.columns:
|
||||||
|
|
@ -469,38 +468,43 @@ def unit_conversion(df, new_units, old_units, kind):
|
||||||
return df
|
return df
|
||||||
|
|
||||||
|
|
||||||
def set_units(units=None):
|
def set_units(options: dict) -> None:
|
||||||
|
|
||||||
# Complete the list of units - if not all are passed, then default value will be used
|
# Complete the list of units - if not all are passed, then default value will be used
|
||||||
required_units = ['time', 'current', 'voltage', 'capacity', 'mass', 'energy', 'specific_capacity']
|
required_units = ['time', 'current', 'voltage', 'capacity', 'mass', 'energy', 'specific_capacity']
|
||||||
default_units = {'time': 'h', 'current': 'mA', 'voltage': 'V', 'capacity': 'mAh', 'mass': 'g', 'energy': 'mWh', 'specific_capacity': None}
|
|
||||||
|
|
||||||
if not units:
|
|
||||||
units = default_units
|
|
||||||
|
|
||||||
if units:
|
|
||||||
for unit in required_units:
|
|
||||||
if unit not in units.keys():
|
|
||||||
units[unit] = default_units[unit]
|
|
||||||
|
|
||||||
units['specific_capacity'] = r'{} {}'.format(units['capacity'], units['mass']) + '$^{-1}$'
|
|
||||||
|
|
||||||
|
|
||||||
return units
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def get_old_units(df, kind):
|
|
||||||
|
|
||||||
if kind=='batsmall':
|
default_units = {
|
||||||
|
'time': 'h',
|
||||||
|
'current': 'mA',
|
||||||
|
'voltage': 'V',
|
||||||
|
'capacity': 'mAh',
|
||||||
|
'mass': 'g',
|
||||||
|
'energy': 'mWh',
|
||||||
|
'specific_capacity': None}
|
||||||
|
|
||||||
|
if not options['units']:
|
||||||
|
options['units'] = default_units
|
||||||
|
|
||||||
|
|
||||||
|
aux.update_options(options=options['units'], required_options=required_units, default_options=default_units)
|
||||||
|
|
||||||
|
options['units']['specific_capacity'] = r'{} {}'.format(options['units']['capacity'], options['units']['mass']) + '$^{-1}$'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def get_old_units(df: pd.DataFrame, options: dict) -> dict:
|
||||||
|
''' Reads a DataFrame with cycling data and determines which units have been used and returns these in a dictionary'''
|
||||||
|
|
||||||
|
if options['kind'] == 'batsmall':
|
||||||
|
|
||||||
time = df.columns[0].split()[-1].strip('[]')
|
time = df.columns[0].split()[-1].strip('[]')
|
||||||
voltage = df.columns[1].split()[-1].strip('[]')
|
voltage = df.columns[1].split()[-1].strip('[]')
|
||||||
current = df.columns[2].split()[-1].strip('[]')
|
current = df.columns[2].split()[-1].strip('[]')
|
||||||
capacity, mass = df.columns[4].split()[-1].strip('[]').split('/')
|
capacity, mass = df.columns[4].split()[-1].strip('[]').split('/')
|
||||||
old_units = {'time': time, 'current': current, 'voltage': voltage, 'capacity': capacity, 'mass': mass}
|
old_units = {'time': time, 'current': current, 'voltage': voltage, 'capacity': capacity, 'mass': mass}
|
||||||
|
|
||||||
if kind=='neware':
|
if options['kind']=='neware':
|
||||||
|
|
||||||
for column in df.columns:
|
for column in df.columns:
|
||||||
if 'Voltage' in column:
|
if 'Voltage' in column:
|
||||||
voltage = column.split('(')[-1].strip(')')
|
voltage = column.split('(')[-1].strip(')')
|
||||||
|
|
@ -514,7 +518,7 @@ def get_old_units(df, kind):
|
||||||
old_units = {'voltage': voltage, 'current': current, 'capacity': capacity, 'energy': energy}
|
old_units = {'voltage': voltage, 'current': current, 'capacity': capacity, 'energy': energy}
|
||||||
|
|
||||||
|
|
||||||
if kind=='biologic':
|
if options['kind'] == 'biologic':
|
||||||
|
|
||||||
for column in df.columns:
|
for column in df.columns:
|
||||||
if 'time' in column:
|
if 'time' in column:
|
||||||
|
|
@ -530,8 +534,6 @@ def get_old_units(df, kind):
|
||||||
|
|
||||||
old_units = {'voltage': voltage, 'current': current, 'capacity': capacity, 'energy': energy, 'time': time}
|
old_units = {'voltage': voltage, 'current': current, 'capacity': capacity, 'energy': energy, 'time': time}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return old_units
|
return old_units
|
||||||
|
|
||||||
def convert_time_string(time_string, unit='ms'):
|
def convert_time_string(time_string, unit='ms'):
|
||||||
|
|
|
||||||
|
|
@ -6,58 +6,57 @@ import numpy as np
|
||||||
import math
|
import math
|
||||||
|
|
||||||
import nafuma.electrochemistry as ec
|
import nafuma.electrochemistry as ec
|
||||||
|
import nafuma.plotting as btp
|
||||||
|
import nafuma.auxillary as aux
|
||||||
|
|
||||||
|
|
||||||
def plot_gc(path, kind, options=None):
|
def plot_gc(data, options=None):
|
||||||
|
|
||||||
# Prepare plot, and read and process data
|
|
||||||
fig, ax = prepare_gc_plot(options=options)
|
|
||||||
cycles = ec.io.read_data(path=path, kind=kind, options=options)
|
|
||||||
|
|
||||||
|
|
||||||
# Update options
|
# Update options
|
||||||
required_options = ['x_vals', 'y_vals', 'which_cycles', 'chg', 'dchg', 'colours', 'differentiate_charge_discharge', 'gradient']
|
required_options = ['x_vals', 'y_vals', 'which_cycles', 'charge', 'discharge', 'colours', 'differentiate_charge_discharge', 'gradient', 'rc_params', 'format_params']
|
||||||
default_options = {'x_vals': 'capacity', 'y_vals': 'voltage', 'which_cycles': 'all', 'chg': True, 'dchg': True, 'colours': None, 'differentiate_charge_discharge': True, 'gradient': False}
|
|
||||||
|
default_options = {
|
||||||
|
'x_vals': 'capacity', 'y_vals': 'voltage',
|
||||||
|
'which_cycles': 'all',
|
||||||
|
'charge': True, 'discharge': True,
|
||||||
|
'colours': None,
|
||||||
|
'differentiate_charge_discharge': True,
|
||||||
|
'gradient': False,
|
||||||
|
'rc_params': {},
|
||||||
|
'format_params': {}}
|
||||||
|
|
||||||
options = update_options(options=options, required_options=required_options, default_options=default_options)
|
options = aux.update_options(options=options, required_options=required_options, default_options=default_options)
|
||||||
|
|
||||||
|
|
||||||
|
# Prepare plot, and read and process data
|
||||||
|
|
||||||
|
fig, ax = btp.prepare_plot(options=options)
|
||||||
|
data['cycles'] = ec.io.read_data(data=data, options=options)
|
||||||
|
|
||||||
# Update list of cycles to correct indices
|
# Update list of cycles to correct indices
|
||||||
update_cycles_list(cycles=cycles, options=options)
|
update_cycles_list(cycles=data['cycles'], options=options)
|
||||||
|
|
||||||
colours = generate_colours(cycles=cycles, options=options)
|
colours = generate_colours(cycles=data['cycles'], options=options)
|
||||||
|
|
||||||
|
|
||||||
for i, cycle in enumerate(cycles):
|
for i, cycle in enumerate(data['cycles']):
|
||||||
if i in options['which_cycles']:
|
if i in options['which_cycles']:
|
||||||
if options['chg']:
|
if options['charge']:
|
||||||
cycle[0].plot(x=options['x_vals'], y=options['y_vals'], ax=ax, c=colours[i][0])
|
cycle[0].plot(x=options['x_vals'], y=options['y_vals'], ax=ax, c=colours[i][0])
|
||||||
|
|
||||||
if options['dchg']:
|
if options['discharge']:
|
||||||
cycle[1].plot(x=options['x_vals'], y=options['y_vals'], ax=ax, c=colours[i][1])
|
cycle[1].plot(x=options['x_vals'], y=options['y_vals'], ax=ax, c=colours[i][1])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
fig, ax = prettify_gc_plot(fig=fig, ax=ax, options=options)
|
fig, ax = btp.adjust_plot(fig=fig, ax=ax, options=options)
|
||||||
|
|
||||||
return cycles, fig, ax
|
return data['cycles'], fig, ax
|
||||||
|
|
||||||
|
|
||||||
def update_options(options, required_options, default_options):
|
|
||||||
|
|
||||||
if not options:
|
|
||||||
options = default_options
|
|
||||||
|
|
||||||
else:
|
def update_cycles_list(cycles, options: dict) -> None:
|
||||||
for option in required_options:
|
|
||||||
if option not in options.keys():
|
|
||||||
options[option] = default_options[option]
|
|
||||||
|
|
||||||
return options
|
|
||||||
|
|
||||||
def update_cycles_list(cycles, options):
|
|
||||||
|
|
||||||
if not options:
|
|
||||||
options['which_cycles']
|
|
||||||
|
|
||||||
if options['which_cycles'] == 'all':
|
if options['which_cycles'] == 'all':
|
||||||
options['which_cycles'] = [i for i in range(len(cycles))]
|
options['which_cycles'] = [i for i in range(len(cycles))]
|
||||||
|
|
@ -81,52 +80,6 @@ def update_cycles_list(cycles, options):
|
||||||
options['which_cycles'] = [i-1 for i in range(which_cycles[0], which_cycles[1]+1)]
|
options['which_cycles'] = [i-1 for i in range(which_cycles[0], which_cycles[1]+1)]
|
||||||
|
|
||||||
|
|
||||||
return options
|
|
||||||
|
|
||||||
|
|
||||||
def prepare_gc_plot(options=None):
|
|
||||||
|
|
||||||
|
|
||||||
# First take care of the options for plotting - set any values not specified to the default values
|
|
||||||
required_options = ['columns', 'width', 'height', 'format', 'dpi', 'facecolor']
|
|
||||||
default_options = {'columns': 1, 'width': 14, 'format': 'golden_ratio', 'dpi': None, 'facecolor': 'w'}
|
|
||||||
|
|
||||||
# If none are set at all, just pass the default_options
|
|
||||||
if not options:
|
|
||||||
options = default_options
|
|
||||||
options['height'] = options['width'] * (math.sqrt(5) - 1) / 2
|
|
||||||
options['figsize'] = (options['width'], options['height'])
|
|
||||||
|
|
||||||
|
|
||||||
# If options is passed, go through to fill out the rest.
|
|
||||||
else:
|
|
||||||
# Start by setting the width:
|
|
||||||
if 'width' not in options.keys():
|
|
||||||
options['width'] = default_options['width']
|
|
||||||
|
|
||||||
# Then set height - check options for format. If not given, set the height to the width scaled by the golden ratio - if the format is square, set the same. This should possibly allow for the tweaking of custom ratios later.
|
|
||||||
if 'height' not in options.keys():
|
|
||||||
if 'format' not in options.keys():
|
|
||||||
options['height'] = options['width'] * (math.sqrt(5) - 1) / 2
|
|
||||||
elif options['format'] == 'square':
|
|
||||||
options['height'] = options['width']
|
|
||||||
|
|
||||||
options['figsize'] = (options['width'], options['height'])
|
|
||||||
|
|
||||||
# After height and width are set, go through the rest of the options to make sure that all the required options are filled
|
|
||||||
for option in required_options:
|
|
||||||
if option not in options.keys():
|
|
||||||
options[option] = default_options[option]
|
|
||||||
|
|
||||||
fig, ax = plt.subplots(figsize=(options['figsize']), dpi=options['dpi'], facecolor=options['facecolor'])
|
|
||||||
|
|
||||||
linewidth = 1*options['columns']
|
|
||||||
axeswidth = 3*options['columns']
|
|
||||||
|
|
||||||
plt.rc('lines', linewidth=linewidth)
|
|
||||||
plt.rc('axes', linewidth=axeswidth)
|
|
||||||
|
|
||||||
return fig, ax
|
|
||||||
|
|
||||||
|
|
||||||
def prettify_gc_plot(fig, ax, options=None):
|
def prettify_gc_plot(fig, ax, options=None):
|
||||||
|
|
@ -166,7 +119,7 @@ def prettify_gc_plot(fig, ax, options=None):
|
||||||
'title': None
|
'title': None
|
||||||
}
|
}
|
||||||
|
|
||||||
update_options(options, required_options, default_options)
|
aux.update_options(options, required_options, default_options)
|
||||||
|
|
||||||
|
|
||||||
##################################################################
|
##################################################################
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue