Merge pull request #3 from rasmusthog/rasmus_ec_standard_functions
Change EC-functions to same format as XRD
This commit is contained in:
commit
2ed2117eac
3 changed files with 202 additions and 214 deletions
|
|
@ -1,40 +1,28 @@
|
|||
from email.policy import default
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
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':
|
||||
df = read_neware(path)
|
||||
cycles = process_neware_data(df, options=options)
|
||||
if data['kind'] == 'neware':
|
||||
df = read_neware(data['path'])
|
||||
cycles = process_neware_data(df=df, options=options)
|
||||
|
||||
elif kind == 'batsmall':
|
||||
df = read_batsmall(path)
|
||||
elif data['kind'] == 'batsmall':
|
||||
df = read_batsmall(data['path'])
|
||||
cycles = process_batsmall_data(df=df, options=options)
|
||||
|
||||
elif kind == 'biologic':
|
||||
df = read_biologic(path)
|
||||
elif data['kind'] == 'biologic':
|
||||
df = read_biologic(data['path'])
|
||||
cycles = process_biologic_data(df=df, options=options)
|
||||
|
||||
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):
|
||||
|
|
@ -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.'''
|
||||
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
|
||||
if path.split('.')[-1] == 'xlsx':
|
||||
csv_details = ''.join(path.split('.')[:-1]) + '_details.csv'
|
||||
|
|
@ -66,6 +56,20 @@ def read_neware(path, summary=False):
|
|||
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):
|
||||
''' Reads Bio-Logic-data into a DataFrame.
|
||||
|
|
@ -89,10 +93,6 @@ def read_biologic(path):
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
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.
|
||||
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']
|
||||
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]
|
||||
default_options = {
|
||||
'splice_cycles': False,
|
||||
'molecular_weight': None,
|
||||
'reverse_discharge': False,
|
||||
'units': None}
|
||||
|
||||
|
||||
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.
|
||||
new_units = set_units(units=options['units'])
|
||||
old_units = get_old_units(df, kind='batsmall')
|
||||
df = unit_conversion(df=df, new_units=new_units, old_units=old_units, kind='batsmall')
|
||||
|
||||
options['units'] = new_units
|
||||
set_units(options)
|
||||
options['old_units'] = get_old_units(df, options)
|
||||
|
||||
df = unit_conversion(df=df, options=options)
|
||||
|
||||
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
|
||||
df[["comment"]] = df[["comment"]].fillna(value={'comment': ''})
|
||||
|
|
@ -173,22 +172,20 @@ def process_batsmall_data(df, options=None):
|
|||
cycles.append((chg_df, dchg_df))
|
||||
|
||||
|
||||
|
||||
|
||||
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.
|
||||
|
||||
if kind == 'batsmall':
|
||||
Incomplete, only accomodates BatSmall so far.'''
|
||||
|
||||
if options['kind'] == 'batsmall':
|
||||
|
||||
# Creates masks for charge and discharge curves
|
||||
chg_mask = df['current'] >= 0
|
||||
dchg_mask = df['current'] < 0
|
||||
|
||||
# Get the number of cycles in the dataset
|
||||
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()):
|
||||
sub_df = df.loc[df['count'] == i+1]
|
||||
|
|
@ -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.
|
||||
|
||||
|
|
@ -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 """
|
||||
|
||||
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}
|
||||
|
||||
if not options:
|
||||
options = default_options
|
||||
else:
|
||||
for option in required_options:
|
||||
if option not in options.keys():
|
||||
options[option] = default_options[option]
|
||||
default_options = {
|
||||
'units': None,
|
||||
'active_material_weight': None,
|
||||
'molecular_weight': None,
|
||||
'reverse_discharge': False,
|
||||
'splice_cycles': None}
|
||||
|
||||
|
||||
aux.update_options(options=options, required_options=required_options, default_options=default_options)
|
||||
options['kind'] = 'neware'
|
||||
|
||||
|
||||
# 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'])
|
||||
old_units = get_old_units(df=df, kind='neware')
|
||||
set_units(options=options) # sets options['units']
|
||||
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')
|
||||
|
||||
options['units'] = new_units
|
||||
df = unit_conversion(df=df, options=options) # converts all units from the old units to the desired units
|
||||
|
||||
|
||||
# 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:
|
||||
continue
|
||||
|
||||
|
||||
# Reverses the discharge curve if specified
|
||||
if options['reverse_discharge']:
|
||||
max_capacity = dchg_df['capacity'].max()
|
||||
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):
|
||||
|
||||
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}
|
||||
|
||||
if not options:
|
||||
options = default_options
|
||||
else:
|
||||
for option in required_options:
|
||||
if option not in options.keys():
|
||||
options[option] = default_options[option]
|
||||
default_options = {
|
||||
'units': None,
|
||||
'active_material_weight': None,
|
||||
'molecular_weight': None,
|
||||
'reverse_discharge': False,
|
||||
'splice_cycles': None}
|
||||
|
||||
|
||||
aux.update_options(options=options, required_options=required_options, default_options=default_options)
|
||||
options['kind'] = 'biologic'
|
||||
|
||||
# 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()
|
||||
|
||||
# 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'])
|
||||
old_units = get_old_units(df=df, kind='biologic')
|
||||
set_units(options)
|
||||
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 = unit_conversion(df=df, new_units=new_units, old_units=old_units, kind='biologic')
|
||||
|
||||
options['units'] = new_units
|
||||
df = add_columns(df=df, options=options)
|
||||
|
||||
df = unit_conversion(df=df, options=options)
|
||||
|
||||
# Creates masks for charge and discharge curves
|
||||
chg_mask = (df['status'] == 1) & (df['status_change'] != 1)
|
||||
dchg_mask = (df['status'] == 2) & (df['status_change'] != 1)
|
||||
|
||||
|
||||
|
||||
# Initiate cycles list
|
||||
cycles = []
|
||||
|
||||
|
|
@ -376,62 +375,62 @@ def process_biologic_data(df, options=None):
|
|||
return cycles
|
||||
|
||||
|
||||
def add_columns(df, active_material_weight, molecular_weight, old_units, kind):
|
||||
def add_columns(df, options):
|
||||
|
||||
if kind == 'neware':
|
||||
if active_material_weight:
|
||||
df["SpecificCapacity({}/mg)".format(old_units["capacity"])] = df["Capacity({})".format(old_units['capacity'])] / (active_material_weight)
|
||||
if options['kind'] == 'neware':
|
||||
if options['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
|
||||
seconds_per_hour = 3600 # s h^-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 active_material_weight:
|
||||
if options['kind'] == 'biologic':
|
||||
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
|
||||
seconds_per_hour = 3600 # s h^-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
|
||||
|
||||
|
||||
def unit_conversion(df, new_units, old_units, kind):
|
||||
def unit_conversion(df, options):
|
||||
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["U [{}]".format(old_units["voltage"])] = df["U [{}]".format(old_units["voltage"])] * unit_tables.voltage()[old_units["voltage"]].loc[new_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["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["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(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(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(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']
|
||||
|
||||
|
||||
if 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['Voltage({})'.format(old_units['voltage'])] = df['Voltage({})'.format(old_units['voltage'])] * unit_tables.voltage()[old_units['voltage']].loc[new_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['Energy({})'.format(old_units['energy'])] = df['Energy({})'.format(old_units['energy'])] * unit_tables.energy()[old_units['energy']].loc[new_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['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)
|
||||
if options['kind'] == 'neware':
|
||||
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(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(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(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(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(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']
|
||||
|
||||
if 'SpecificCapacity({}/mg)'.format(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"]]
|
||||
if 'SpecificCapacity({}/mg)'.format(options['old_units']['capacity']) in df.columns:
|
||||
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')
|
||||
|
||||
if 'IonsExtracted' in df.columns:
|
||||
|
|
@ -447,18 +446,18 @@ def unit_conversion(df, new_units, old_units, kind):
|
|||
|
||||
df.columns = columns
|
||||
|
||||
if 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["Ewe/{}".format(old_units["voltage"])] = df["Ewe/{}".format(old_units["voltage"])] * unit_tables.voltage()[old_units["voltage"]].loc[new_units['voltage']]
|
||||
df["<I>/{}".format(old_units["current"])] = df["<I>/{}".format(old_units["current"])] * unit_tables.current()[old_units["current"]].loc[new_units['current']]
|
||||
if options['kind'] == 'biologic':
|
||||
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(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(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'
|
||||
df["Capacity/{}".format(capacity)] = df["Capacity/{}".format(capacity)] * (unit_tables.capacity()[old_units["capacity"]].loc[new_units["capacity"]])
|
||||
capacity = options['old_units']['capacity'].split('h')[0] + '.h'
|
||||
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']
|
||||
|
||||
if 'SpecificCapacity({}/mg)'.format(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"]]
|
||||
if 'SpecificCapacity({}/mg)'.format(options['old_units']['capacity']) in df.columns:
|
||||
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')
|
||||
|
||||
if 'IonsExtracted' in df.columns:
|
||||
|
|
@ -469,37 +468,42 @@ def unit_conversion(df, new_units, old_units, kind):
|
|||
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
|
||||
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
|
||||
default_units = {
|
||||
'time': 'h',
|
||||
'current': 'mA',
|
||||
'voltage': 'V',
|
||||
'capacity': 'mAh',
|
||||
'mass': 'g',
|
||||
'energy': 'mWh',
|
||||
'specific_capacity': None}
|
||||
|
||||
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}$'
|
||||
if not options['units']:
|
||||
options['units'] = default_units
|
||||
|
||||
|
||||
return 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, kind):
|
||||
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':
|
||||
|
||||
if kind=='batsmall':
|
||||
time = df.columns[0].split()[-1].strip('[]')
|
||||
voltage = df.columns[1].split()[-1].strip('[]')
|
||||
current = df.columns[2].split()[-1].strip('[]')
|
||||
capacity, mass = df.columns[4].split()[-1].strip('[]').split('/')
|
||||
old_units = {'time': time, 'current': current, 'voltage': voltage, 'capacity': capacity, 'mass': mass}
|
||||
|
||||
if kind=='neware':
|
||||
if options['kind']=='neware':
|
||||
|
||||
for column in df.columns:
|
||||
if 'Voltage' in column:
|
||||
|
|
@ -514,7 +518,7 @@ def get_old_units(df, kind):
|
|||
old_units = {'voltage': voltage, 'current': current, 'capacity': capacity, 'energy': energy}
|
||||
|
||||
|
||||
if kind=='biologic':
|
||||
if options['kind'] == 'biologic':
|
||||
|
||||
for column in df.columns:
|
||||
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}
|
||||
|
||||
|
||||
|
||||
return old_units
|
||||
|
||||
def convert_time_string(time_string, unit='ms'):
|
||||
|
|
|
|||
|
|
@ -6,58 +6,87 @@ import numpy as np
|
|||
import math
|
||||
|
||||
import nafuma.electrochemistry as ec
|
||||
import nafuma.plotting as btp
|
||||
import nafuma.auxillary as aux
|
||||
|
||||
|
||||
def plot_gc(path, kind, 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)
|
||||
def plot_gc(data, options=None):
|
||||
|
||||
|
||||
# Update options
|
||||
required_options = ['x_vals', 'y_vals', 'which_cycles', 'chg', 'dchg', 'colours', 'differentiate_charge_discharge', 'gradient']
|
||||
default_options = {'x_vals': 'capacity', 'y_vals': 'voltage', 'which_cycles': 'all', 'chg': True, 'dchg': True, 'colours': None, 'differentiate_charge_discharge': True, 'gradient': False}
|
||||
required_options = ['x_vals', 'y_vals', 'which_cycles', 'charge', 'discharge', 'colours', 'differentiate_charge_discharge', 'gradient', 'rc_params', 'format_params']
|
||||
|
||||
options = update_options(options=options, required_options=required_options, default_options=default_options)
|
||||
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 = 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_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 options['chg']:
|
||||
if options['charge']:
|
||||
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])
|
||||
|
||||
|
||||
update_labels(options)
|
||||
print(options['xunit'])
|
||||
|
||||
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
|
||||
def update_labels(options):
|
||||
|
||||
else:
|
||||
for option in required_options:
|
||||
if option not in options.keys():
|
||||
options[option] = default_options[option]
|
||||
if 'xlabel' not in options.keys():
|
||||
options['xlabel'] = options['x_vals'].capitalize().replace('_', ' ')
|
||||
|
||||
return options
|
||||
if 'ylabel' not in options.keys():
|
||||
options['ylabel'] = options['y_vals'].capitalize().replace('_', ' ')
|
||||
|
||||
def update_cycles_list(cycles, options):
|
||||
|
||||
if not options:
|
||||
options['which_cycles']
|
||||
if 'xunit' not in options.keys():
|
||||
if options['x_vals'] == 'capacity':
|
||||
options['xunit'] = options['units']['capacity']
|
||||
elif options['x_vals'] == 'specific_capacity':
|
||||
options['xunit'] = f"{options['units']['capacity']} {options['units']['mass']}$^{{-1}}$"
|
||||
elif options['x_vals'] == 'time':
|
||||
options['xunit'] = options['units']['time']
|
||||
elif options['x_vals'] == 'ions':
|
||||
options['xunit'] = None
|
||||
|
||||
|
||||
if 'yunit' not in options.keys():
|
||||
if options['y_vals'] == 'voltage':
|
||||
options['yunit'] = options['units']['voltage']
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def update_cycles_list(cycles, options: dict) -> None:
|
||||
|
||||
if options['which_cycles'] == 'all':
|
||||
options['which_cycles'] = [i for i in range(len(cycles))]
|
||||
|
|
@ -81,52 +110,6 @@ def update_cycles_list(cycles, options):
|
|||
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):
|
||||
|
|
@ -166,7 +149,7 @@ def prettify_gc_plot(fig, ax, options=None):
|
|||
'title': None
|
||||
}
|
||||
|
||||
update_options(options, required_options, default_options)
|
||||
aux.update_options(options, required_options, default_options)
|
||||
|
||||
|
||||
##################################################################
|
||||
|
|
|
|||
|
|
@ -135,7 +135,10 @@ def adjust_plot(fig, ax, options):
|
|||
ax.set_ylabel('')
|
||||
|
||||
if not options['hide_x_labels']:
|
||||
if not options['xunit']:
|
||||
ax.set_xlabel(f'{options["xlabel"]}')
|
||||
else:
|
||||
ax.set_xlabel(f'{options["xlabel"]} [{options["xunit"]}]')
|
||||
else:
|
||||
ax.set_xlabel('')
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue