Merge branch 'master' of github.uio.no:rasmusvt/beamtime
This commit is contained in:
commit
0048546767
7 changed files with 1020 additions and 99 deletions
|
|
@ -1 +1 @@
|
|||
from . import io, plot
|
||||
from . import io, plot, unit_tables
|
||||
|
|
|
|||
|
|
@ -1,10 +1,27 @@
|
|||
import pandas as pd
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
import os
|
||||
|
||||
|
||||
def read_battsmall(path):
|
||||
''' Reads BATTSMALL-data into a DataFrame.
|
||||
def read_data(path, kind, options=None):
|
||||
|
||||
if kind == 'neware':
|
||||
df = read_neware(path)
|
||||
cycles = process_neware_data(df, options=options)
|
||||
|
||||
elif kind == 'batsmall':
|
||||
df = read_batsmall(path)
|
||||
cycles = process_batsmall_data(df=df, options=options)
|
||||
|
||||
elif kind == 'biologic':
|
||||
df = read_biologic(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
|
||||
|
|
@ -19,67 +36,71 @@ def read_battsmall(path):
|
|||
|
||||
|
||||
|
||||
def unit_conversion(df, units):
|
||||
|
||||
C, m = units['C'].split('/')
|
||||
def read_neware(path, summary=False):
|
||||
''' 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.'''
|
||||
from xlsx2csv import Xlsx2csv
|
||||
|
||||
# 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('/')
|
||||
# Convert from .xlsx to .csv to make readtime faster
|
||||
if path.split('.')[-1] == 'xlsx':
|
||||
csv_details = ''.join(path.split('.')[:-1]) + '_details.csv'
|
||||
csv_summary = ''.join(path.split('.')[:-1]) + '_summary.csv'
|
||||
|
||||
if not os.path.isfile(csv_summary):
|
||||
Xlsx2csv(path, outputencoding="utf-8").convert(csv_summary, sheetid=3)
|
||||
|
||||
# Define matrix for unit conversion for time
|
||||
t_units_df = {'h': [1, 60, 3600, 3600000], 'min': [1/60, 1, 60, 60000], 's': [1/3600, 1/60, 1, 1000], 'ms': [1/3600000, 1/60000, 1/1000, 1]}
|
||||
t_units_df = pd.DataFrame(t_units_df)
|
||||
t_units_df.index = ['h', 'min', 's', 'ms']
|
||||
if not os.path.isfile(csv_details):
|
||||
Xlsx2csv(path, outputencoding="utf-8").convert(csv_details, sheetid=4)
|
||||
|
||||
# Define matrix for unit conversion for current
|
||||
I_units_df = {'A': [1, 1000, 1000000], 'mA': [1/1000, 1, 1000], 'uA': [1/1000000, 1/1000, 1]}
|
||||
I_units_df = pd.DataFrame(I_units_df)
|
||||
I_units_df.index = ['A', 'mA', 'uA']
|
||||
|
||||
# Define matrix for unit conversion for voltage
|
||||
U_units_df = {'V': [1, 1000, 1000000], 'mV': [1/1000, 1, 1000], 'uV': [1/1000000, 1/1000, 1]}
|
||||
U_units_df = pd.DataFrame(U_units_df)
|
||||
U_units_df.index = ['V', 'mV', 'uV']
|
||||
|
||||
# Define matrix for unit conversion for capacity
|
||||
C_units_df = {'Ah': [1, 1000, 1000000], 'mAh': [1/1000, 1, 1000], 'uAh': [1/1000000, 1/1000, 1]}
|
||||
C_units_df = pd.DataFrame(C_units_df)
|
||||
C_units_df.index = ['Ah', 'mAh', 'uAh']
|
||||
|
||||
# Define matrix for unit conversion for capacity
|
||||
m_units_df = {'kg': [1, 1000, 1000000, 1000000000], 'g': [1/1000, 1, 1000, 1000000], 'mg': [1/1000000, 1/1000, 1, 1000], 'ug': [1/1000000000, 1/1000000, 1/1000, 1]}
|
||||
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.columns = ['TT', 'U', 'I', 'Z1', 'C', 'Comment']
|
||||
if summary:
|
||||
df = pd.read_csv(csv_summary)
|
||||
else:
|
||||
df = pd.read_csv(csv_details)
|
||||
|
||||
elif path.split('.')[-1] == 'csv':
|
||||
df = pd.read_csv(path)
|
||||
|
||||
|
||||
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.
|
||||
|
||||
def read_biologic(path):
|
||||
''' Reads Bio-Logic-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.'''
|
||||
|
||||
with open(path, 'r') as f:
|
||||
lines = f.readlines()
|
||||
|
||||
header_lines = int(lines[1].split()[-1]) - 1
|
||||
|
||||
|
||||
df = pd.read_csv(path, sep='\t', skiprows=header_lines)
|
||||
df.dropna(inplace=True, axis=1)
|
||||
|
||||
return df
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
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.
|
||||
|
||||
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().
|
||||
df (required): A pandas DataFrame containing BATSMALL-data, as obtained from read_batsmall().
|
||||
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.
|
||||
|
|
@ -89,84 +110,426 @@ def process_battsmall_data(df, units=None):
|
|||
cycles: A list with
|
||||
'''
|
||||
|
||||
required_units = ['t', 'I', 'U', 'C']
|
||||
default_units = {'t': 'h', 'I': 'mA', 'U': 'V', 'C': 'mAh/g'}
|
||||
required_options = ['splice_cycles', 'molecular_weight', 'reverse_discharge', 'units']
|
||||
default_options = {'splice_cycles': None, '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]
|
||||
|
||||
|
||||
# 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
|
||||
|
||||
# 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['current'] >= 0
|
||||
dchg_mask = df['current'] < 0
|
||||
|
||||
# Initiate cycles list
|
||||
cycles = []
|
||||
|
||||
# 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].copy()
|
||||
|
||||
sub_df.loc[dchg_mask, 'current'] *= -1
|
||||
sub_df.loc[dchg_mask, 'specific_capacity'] *= -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
|
||||
|
||||
if options['reverse_discharge']:
|
||||
max_capacity = dchg_df['capacity'].max()
|
||||
dchg_df['capacity'] = np.abs(dchg_df['capacity'] - max_capacity)
|
||||
|
||||
if 'specific_capacity' in df.columns:
|
||||
max_capacity = dchg_df['specific_capacity'].max()
|
||||
dchg_df['specific_capacity'] = np.abs(dchg_df['specific_capacity'] - max_capacity)
|
||||
|
||||
if 'ions' in df.columns:
|
||||
max_capacity = dchg_df['ions'].max()
|
||||
dchg_df['ions'] = np.abs(dchg_df['ions'] - max_capacity)
|
||||
|
||||
cycles.append((chg_df, dchg_df))
|
||||
|
||||
|
||||
|
||||
|
||||
return cycles
|
||||
|
||||
|
||||
def process_neware_data(df, options=None):
|
||||
|
||||
""" Takes data from NEWARE in a DataFrame as read by read_neware() and converts units, adds columns and splits into cycles.
|
||||
|
||||
Input:
|
||||
df: pandas DataFrame containing NEWARE data as read by read_neware()
|
||||
units: dictionary containing the desired units. keywords: capacity, current, voltage, mass, energy, time
|
||||
splice_cycles: tuple containing index of cycles that should be spliced. Specifically designed to add two charge steps during the formation cycle with two different max voltages
|
||||
active_materiale_weight: weight of the active material (in mg) used in the cell.
|
||||
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]
|
||||
|
||||
|
||||
# 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')
|
||||
|
||||
df = add_columns(df=df, active_material_weight=options['active_material_weight'], molecular_weight=options['molecular_weight'], old_units=old_units, kind='neware')
|
||||
|
||||
df = unit_conversion(df=df, new_units=new_units, old_units=old_units, kind='neware')
|
||||
|
||||
options['units'] = new_units
|
||||
|
||||
|
||||
# Creates masks for charge and discharge curves
|
||||
chg_mask = df['status'] == 'CC Chg'
|
||||
dchg_mask = df['status'] == 'CC DChg'
|
||||
|
||||
# Initiate cycles list
|
||||
cycles = []
|
||||
|
||||
# Loop through all the cycling steps, change the current and capacities in the
|
||||
for i in range(df["cycle"].max()):
|
||||
|
||||
sub_df = df.loc[df['cycle'] == i].copy()
|
||||
|
||||
#sub_df.loc[dchg_mask, 'current'] *= -1
|
||||
#sub_df.loc[dchg_mask, 'capacity'] *= -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
|
||||
|
||||
if options['reverse_discharge']:
|
||||
max_capacity = dchg_df['capacity'].max()
|
||||
dchg_df['capacity'] = np.abs(dchg_df['capacity'] - max_capacity)
|
||||
|
||||
if 'specific_capacity' in df.columns:
|
||||
max_capacity = dchg_df['specific_capacity'].max()
|
||||
dchg_df['specific_capacity'] = np.abs(dchg_df['specific_capacity'] - max_capacity)
|
||||
|
||||
if 'ions' in df.columns:
|
||||
max_capacity = dchg_df['ions'].max()
|
||||
dchg_df['ions'] = np.abs(dchg_df['ions'] - max_capacity)
|
||||
|
||||
cycles.append((chg_df, dchg_df))
|
||||
|
||||
|
||||
|
||||
return cycles
|
||||
|
||||
|
||||
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]
|
||||
|
||||
# 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')
|
||||
|
||||
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
|
||||
|
||||
|
||||
# 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 = []
|
||||
|
||||
# Loop through all the cycling steps, change the current and capacities in the
|
||||
for i in range(int(df["cycle"].max())):
|
||||
|
||||
sub_df = df.loc[df['cycle'] == i].copy()
|
||||
|
||||
#sub_df.loc[dchg_mask, 'current'] *= -1
|
||||
#sub_df.loc[dchg_mask, 'capacity'] *= -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
|
||||
|
||||
if options['reverse_discharge']:
|
||||
max_capacity = dchg_df['capacity'].max()
|
||||
dchg_df['capacity'] = np.abs(dchg_df['capacity'] - max_capacity)
|
||||
|
||||
if 'specific_capacity' in df.columns:
|
||||
max_capacity = dchg_df['specific_capacity'].max()
|
||||
dchg_df['specific_capacity'] = np.abs(dchg_df['specific_capacity'] - max_capacity)
|
||||
|
||||
if 'ions' in df.columns:
|
||||
max_capacity = dchg_df['ions'].max()
|
||||
dchg_df['ions'] = np.abs(dchg_df['ions'] - max_capacity)
|
||||
|
||||
cycles.append((chg_df, dchg_df))
|
||||
|
||||
|
||||
|
||||
return cycles
|
||||
|
||||
|
||||
def add_columns(df, active_material_weight, molecular_weight, old_units, kind):
|
||||
|
||||
if kind == 'neware':
|
||||
if active_material_weight:
|
||||
df["SpecificCapacity({}/mg)".format(old_units["capacity"])] = df["Capacity({})".format(old_units['capacity'])] / (active_material_weight)
|
||||
|
||||
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({}/mg)".format(old_units['capacity'])]*molecular_weight)*1000/f
|
||||
|
||||
|
||||
if kind == 'biologic':
|
||||
if active_material_weight:
|
||||
|
||||
capacity = old_units['capacity'].split('h')[0] + '.h'
|
||||
|
||||
|
||||
df["SpecificCapacity({}/mg)".format(old_units["capacity"])] = df["Capacity/{}".format(capacity)] / (active_material_weight)
|
||||
|
||||
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({}/mg)".format(old_units['capacity'])]*molecular_weight)*1000/f
|
||||
|
||||
return df
|
||||
|
||||
|
||||
def unit_conversion(df, new_units, old_units, kind):
|
||||
from . import unit_tables
|
||||
|
||||
if 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.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)
|
||||
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"]]
|
||||
columns.append('specific_capacity')
|
||||
|
||||
if 'IonsExtracted' in df.columns:
|
||||
columns.append('ions')
|
||||
|
||||
|
||||
|
||||
columns.append('cycle_time')
|
||||
columns.append('time')
|
||||
|
||||
|
||||
df.drop(['Record number', 'Relative Time(h:min:s.ms)', 'Real Time(h:min:s.ms)'], axis=1, inplace=True)
|
||||
|
||||
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']]
|
||||
|
||||
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"]])
|
||||
|
||||
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"]]
|
||||
columns.append('specific_capacity')
|
||||
|
||||
if 'IonsExtracted' in df.columns:
|
||||
columns.append('ions')
|
||||
|
||||
df.columns = columns
|
||||
|
||||
return df
|
||||
|
||||
|
||||
def set_units(units=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
|
||||
|
||||
if units:
|
||||
for unit in required_units:
|
||||
if unit not in units.values():
|
||||
if unit not in units.keys():
|
||||
units[unit] = default_units[unit]
|
||||
|
||||
|
||||
# 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 and
|
||||
df[["Comment"]] = df[["Comment"]].fillna(value={'Comment': ''})
|
||||
df = df[df["Comment"].str.contains("program")==False]
|
||||
|
||||
# Creates masks for
|
||||
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]
|
||||
units['specific_capacity'] = r'{} {}'.format(units['capacity'], units['mass']) + '$^{-1}$'
|
||||
|
||||
|
||||
cycles.append((chg_df, dchg_df))
|
||||
|
||||
|
||||
return cycles
|
||||
return units
|
||||
|
||||
|
||||
|
||||
def plot_gc(cycles, which_cycles='all', chg=True, dchg=True, colours=None, x='C', y='U'):
|
||||
|
||||
fig, ax = prepare_gc_plot()
|
||||
|
||||
|
||||
if which_cycles == 'all':
|
||||
which_cycles = [i for i, c in enumerate(cycles)]
|
||||
|
||||
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
|
||||
|
||||
def get_old_units(df, kind):
|
||||
|
||||
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}
|
||||
|
||||
for i, cycle in cycles:
|
||||
if i in which_cycles:
|
||||
if chg:
|
||||
cycle[0].plot(ax=ax)
|
||||
if kind=='neware':
|
||||
|
||||
for column in df.columns:
|
||||
if 'Voltage' in column:
|
||||
voltage = column.split('(')[-1].strip(')')
|
||||
elif 'Current' in column:
|
||||
current = column.split('(')[-1].strip(')')
|
||||
elif 'Capacity' in column:
|
||||
capacity = column.split('(')[-1].strip(')')
|
||||
elif 'Energy' in column:
|
||||
energy = column.split('(')[-1].strip(')')
|
||||
|
||||
old_units = {'voltage': voltage, 'current': current, 'capacity': capacity, 'energy': energy}
|
||||
|
||||
|
||||
if kind=='biologic':
|
||||
|
||||
for column in df.columns:
|
||||
if 'time' in column:
|
||||
time = column.split('/')[-1]
|
||||
elif 'Ewe' in column:
|
||||
voltage = column.split('/')[-1]
|
||||
elif 'Capacity' in column:
|
||||
capacity = column.split('/')[-1].replace('.', '')
|
||||
elif 'Energy' in column:
|
||||
energy = column.split('/')[-1].replace('.', '')
|
||||
elif '<I>' in column:
|
||||
current = column.split('/')[-1]
|
||||
|
||||
old_units = {'voltage': voltage, 'current': current, 'capacity': capacity, 'energy': energy, 'time': time}
|
||||
|
||||
|
||||
|
||||
return old_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'''
|
||||
|
||||
h, m, s = time_string.split(':')
|
||||
ms = float(s)*1000 + int(m)*1000*60 + int(h)*1000*60*60
|
||||
|
||||
factors = {'ms': 1, 's': 1/1000, 'min': 1/(1000*60), 'h': 1/(1000*60*60)}
|
||||
|
||||
t = ms*factors[unit]
|
||||
|
||||
return t
|
||||
|
||||
|
||||
|
||||
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'''
|
||||
|
||||
def prepare_gc_plot(figsize=(14,7), dpi=None):
|
||||
from datetime import datetime
|
||||
|
||||
# Parse the
|
||||
current_date, current_time = datetime_string.split()
|
||||
current_year, current_month, current_day = current_date.split('-')
|
||||
current_hour, current_minute, current_second = current_time.split(':')
|
||||
current_date = datetime(int(current_year), int(current_month), int(current_day), int(current_hour), int(current_minute), int(current_second))
|
||||
|
||||
reference_date, reference_time = reference.split()
|
||||
reference_year, reference_month, reference_day = reference_date.split('-')
|
||||
reference_hour, reference_minute, reference_second = reference_time.split(':')
|
||||
reference_date = datetime(int(reference_year), int(reference_month), int(reference_day), int(reference_hour), int(reference_minute), int(reference_second))
|
||||
|
||||
days = current_date - reference_date
|
||||
|
||||
|
||||
s = days.days*24*60*60 + days.seconds
|
||||
|
||||
factors = {'ms': 1000, 's': 1, 'min': 1/(60), 'h': 1/(60*60)}
|
||||
|
||||
time = s * factors[unit]
|
||||
|
||||
return time
|
||||
|
||||
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
|
||||
|
||||
fig, ax = plt.subplots(figsize=figsize, dpi=dpi)
|
||||
|
||||
|
||||
return fig, ax
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1 +1,386 @@
|
|||
import matplotlib.pyplot as plt
|
||||
from matplotlib.ticker import (MultipleLocator, FormatStrFormatter,AutoMinorLocator)
|
||||
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
import math
|
||||
|
||||
import beamtime.electrochemistry as ec
|
||||
|
||||
|
||||
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)
|
||||
|
||||
|
||||
# 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}
|
||||
|
||||
options = update_options(options=options, required_options=required_options, default_options=default_options)
|
||||
|
||||
# Update list of cycles to correct indices
|
||||
update_cycles_list(cycles=cycles, options=options)
|
||||
|
||||
colours = generate_colours(cycles=cycles, options=options)
|
||||
|
||||
|
||||
for i, cycle in enumerate(cycles):
|
||||
if i in options['which_cycles']:
|
||||
if options['chg']:
|
||||
cycle[0].plot(x=options['x_vals'], y=options['y_vals'], ax=ax, c=colours[i][0])
|
||||
|
||||
if options['dchg']:
|
||||
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)
|
||||
|
||||
return cycles, fig, ax
|
||||
|
||||
|
||||
def update_options(options, required_options, default_options):
|
||||
|
||||
if not options:
|
||||
options = default_options
|
||||
|
||||
else:
|
||||
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':
|
||||
options['which_cycles'] = [i for i in range(len(cycles))]
|
||||
|
||||
|
||||
elif type(options['which_cycles']) == list:
|
||||
options['which_cycles'] = [i-1 for i in options['which_cycles']]
|
||||
|
||||
|
||||
# Tuple is used to define an interval - as elements tuples can't be assigned, I convert it to a list here.
|
||||
elif type(options['which_cycles']) == tuple:
|
||||
which_cycles = list(options['which_cycles'])
|
||||
|
||||
if which_cycles[0] <= 0:
|
||||
which_cycles[0] = 1
|
||||
|
||||
elif which_cycles[1] < 0:
|
||||
which_cycles[1] = len(cycles)
|
||||
|
||||
|
||||
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):
|
||||
|
||||
|
||||
##################################################################
|
||||
######################### UPDATE OPTIONS #########################
|
||||
##################################################################
|
||||
|
||||
# Define the required options
|
||||
required_options = [
|
||||
'columns',
|
||||
'xticks', 'yticks',
|
||||
'show_major_ticks', 'show_minor_ticks',
|
||||
'xlim', 'ylim',
|
||||
'hide_x_axis', 'hide_y_axis',
|
||||
'positions',
|
||||
'x_vals', 'y_vals',
|
||||
'xlabel', 'ylabel',
|
||||
'units', 'sizes',
|
||||
'title'
|
||||
]
|
||||
|
||||
|
||||
# Define the default options
|
||||
default_options = {
|
||||
'columns': 1,
|
||||
'xticks': None, 'yticks': None,
|
||||
'show_major_ticks': [True, True, True, True], 'show_minor_ticks': [True, True, True, True],
|
||||
'xlim': None,'ylim': None,
|
||||
'hide_x_axis': False, 'hide_y_axis': False,
|
||||
'positions': {'xaxis': 'bottom', 'yaxis': 'left'},
|
||||
'x_vals': 'specific_capacity', 'y_vals': 'voltage',
|
||||
'xlabel': None, 'ylabel': None,
|
||||
'units': None,
|
||||
'sizes': None,
|
||||
'title': None
|
||||
}
|
||||
|
||||
update_options(options, required_options, default_options)
|
||||
|
||||
|
||||
##################################################################
|
||||
########################## DEFINE SIZES ##########################
|
||||
##################################################################
|
||||
|
||||
# Define the required sizes
|
||||
required_sizes = [
|
||||
'labels',
|
||||
'legend',
|
||||
'title',
|
||||
'line', 'axes',
|
||||
'tick_labels',
|
||||
'major_ticks', 'minor_ticks']
|
||||
|
||||
|
||||
|
||||
# Define default sizes
|
||||
default_sizes = {
|
||||
'labels': 30*options['columns'],
|
||||
'legend': 30*options['columns'],
|
||||
'title': 30*options['columns'],
|
||||
'line': 3*options['columns'],
|
||||
'axes': 3*options['columns'],
|
||||
'tick_labels': 30*options['columns'],
|
||||
'major_ticks': 20*options['columns'],
|
||||
'minor_ticks': 10*options['columns']
|
||||
}
|
||||
|
||||
# Initialise dictionary if it doesn't exist
|
||||
if not options['sizes']:
|
||||
options['sizes'] = {}
|
||||
|
||||
|
||||
# Update dictionary with default values where none is supplied
|
||||
for size in required_sizes:
|
||||
if size not in options['sizes']:
|
||||
options['sizes'][size] = default_sizes[size]
|
||||
|
||||
|
||||
##################################################################
|
||||
########################## AXIS LABELS ###########################
|
||||
##################################################################
|
||||
|
||||
|
||||
if not options['xlabel']:
|
||||
options['xlabel'] = prettify_labels(options['x_vals']) + ' [{}]'.format(options['units'][options['x_vals']])
|
||||
|
||||
else:
|
||||
options['xlabel'] = options['xlabel'] + ' [{}]'.format(options['units'][options['x_vals']])
|
||||
|
||||
|
||||
if not options['ylabel']:
|
||||
options['ylabel'] = prettify_labels(options['y_vals']) + ' [{}]'.format(options['units'][options['y_vals']])
|
||||
|
||||
else:
|
||||
options['ylabel'] = options['ylabel'] + ' [{}]'.format(options['units'][options['y_vals']])
|
||||
|
||||
ax.set_xlabel(options['xlabel'], size=options['sizes']['labels'])
|
||||
ax.set_ylabel(options['ylabel'], size=options['sizes']['labels'])
|
||||
|
||||
##################################################################
|
||||
###################### TICK MARKS & LABELS #######################
|
||||
##################################################################
|
||||
|
||||
ax.tick_params(direction='in', which='major', bottom=options['show_major_ticks'][0], left=options['show_major_ticks'][1], top=options['show_major_ticks'][2], right=options['show_major_ticks'][0], length=options['sizes']['major_ticks'], width=options['sizes']['axes'])
|
||||
ax.tick_params(direction='in', which='minor', bottom=options['show_minor_ticks'][0], left=options['show_minor_ticks'][1], top=options['show_minor_ticks'][2], right=options['show_minor_ticks'][0], length=options['sizes']['minor_ticks'], width=options['sizes']['axes'])
|
||||
|
||||
|
||||
|
||||
# DEFINE AND SET TICK DISTANCES
|
||||
|
||||
from . import unit_tables
|
||||
|
||||
# Define default ticks and scale to desired units
|
||||
default_ticks = {
|
||||
'specific_capacity': [100 * (unit_tables.capacity()['mAh'].loc[options['units']['capacity']] / unit_tables.mass()['g'].loc[options['units']['mass']]), 50 * (unit_tables.capacity()['mAh'].loc[options['units']['capacity']] / unit_tables.mass()['g'].loc[options['units']['mass']])],
|
||||
'capacity': [0.1 * (unit_tables.capacity()['mAh'].loc[options['units']['capacity']]), 0.05 * (unit_tables.capacity()['mAh'].loc[options['units']['capacity']])],
|
||||
'voltage': [0.5 * (unit_tables.voltage()['V'].loc[options['units']['voltage']]), 0.25 * (unit_tables.voltage()['V'].loc[options['units']['voltage']])],
|
||||
'time': [10 * (unit_tables.time()['h'].loc[options['units']['time']]), 5 * (unit_tables.time()['h'].loc[options['units']['time']])]
|
||||
}
|
||||
|
||||
|
||||
if options['positions']['yaxis'] == 'right':
|
||||
ax.yaxis.set_label_position("right")
|
||||
ax.yaxis.tick_right()
|
||||
|
||||
|
||||
# Set default tick distances for x-axis if not specified
|
||||
if not options['xticks']:
|
||||
|
||||
major_xtick = default_ticks[options['x_vals']][0]
|
||||
minor_xtick = default_ticks[options['x_vals']][1]
|
||||
|
||||
# Otherwise apply user input
|
||||
else:
|
||||
major_xtick = options['xticks'][0]
|
||||
minor_xtick = options['xticks'][1]
|
||||
|
||||
|
||||
# Set default tick distances for x-axis if not specified
|
||||
if not options['yticks']:
|
||||
|
||||
major_ytick = default_ticks[options['y_vals']][0]
|
||||
minor_ytick = default_ticks[options['y_vals']][1]
|
||||
|
||||
# Otherwise apply user input
|
||||
else:
|
||||
major_xtick = options['yticks'][0]
|
||||
minor_xtick = options['yticks'][1]
|
||||
|
||||
|
||||
# Apply values
|
||||
ax.xaxis.set_major_locator(MultipleLocator(major_xtick))
|
||||
ax.xaxis.set_minor_locator(MultipleLocator(minor_xtick))
|
||||
|
||||
ax.yaxis.set_major_locator(MultipleLocator(major_ytick))
|
||||
ax.yaxis.set_minor_locator(MultipleLocator(minor_ytick))
|
||||
|
||||
|
||||
|
||||
|
||||
# SET FONTSIZE OF TICK LABELS
|
||||
|
||||
plt.xticks(fontsize=options['sizes']['tick_labels'])
|
||||
plt.yticks(fontsize=options['sizes']['tick_labels'])
|
||||
|
||||
##################################################################
|
||||
########################## AXES LIMITS ###########################
|
||||
##################################################################
|
||||
|
||||
if options['xlim']:
|
||||
plt.xlim(options['xlim'])
|
||||
|
||||
if options['ylim']:
|
||||
plt.ylim(options['ylim'])
|
||||
|
||||
##################################################################
|
||||
############################# TITLE ##############################
|
||||
##################################################################
|
||||
|
||||
if options['title']:
|
||||
ax.set_title(options['title'], size=options['sizes']['title'])
|
||||
|
||||
##################################################################
|
||||
############################# LEGEND #############################
|
||||
##################################################################
|
||||
|
||||
ax.get_legend().remove()
|
||||
|
||||
return fig, ax
|
||||
|
||||
|
||||
def prettify_labels(label):
|
||||
|
||||
labels_dict = {
|
||||
'capacity': 'Capacity',
|
||||
'specific_capacity': 'Specific capacity',
|
||||
'voltage': 'Voltage',
|
||||
'current': 'Current',
|
||||
'energy': 'Energy',
|
||||
'time': 'Time'
|
||||
}
|
||||
|
||||
return labels_dict[label]
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def generate_colours(cycles, options):
|
||||
|
||||
# Assign colours from the options dictionary if it is defined, otherwise use standard colours.
|
||||
if options['colours']:
|
||||
charge_colour = options['colours'][0]
|
||||
discharge_colour = options['colours'][1]
|
||||
|
||||
else:
|
||||
charge_colour = (40/255, 70/255, 75/255) # Dark Slate Gray #28464B, coolors.co
|
||||
discharge_colour = (239/255, 160/255, 11/255) # Marigold #EFA00B, coolors.co
|
||||
|
||||
if not options['differentiate_charge_discharge']:
|
||||
discharge_colour = charge_colour
|
||||
|
||||
|
||||
|
||||
# If gradient is enabled, find start and end points for each colour
|
||||
if options['gradient']:
|
||||
|
||||
add_charge = min([(1-x)*0.75 for x in charge_colour])
|
||||
add_discharge = min([(1-x)*0.75 for x in discharge_colour])
|
||||
|
||||
charge_colour_start = charge_colour
|
||||
charge_colour_end = [x+add_charge for x in charge_colour]
|
||||
|
||||
discharge_colour_start = discharge_colour
|
||||
discharge_colour_end = [x+add_discharge for x in discharge_colour]
|
||||
|
||||
|
||||
|
||||
# Generate lists of colours
|
||||
colours = []
|
||||
|
||||
for cycle_number in range(0, len(cycles)):
|
||||
if options['gradient']:
|
||||
weight_start = (len(cycles) - cycle_number)/len(cycles)
|
||||
weight_end = cycle_number/len(cycles)
|
||||
|
||||
charge_colour = [weight_start*start_colour + weight_end*end_colour for start_colour, end_colour in zip(charge_colour_start, charge_colour_end)]
|
||||
discharge_colour = [weight_start*start_colour + weight_end*end_colour for start_colour, end_colour in zip(discharge_colour_start, discharge_colour_end)]
|
||||
|
||||
colours.append([charge_colour, discharge_colour])
|
||||
|
||||
return colours
|
||||
|
|
|
|||
53
beamtime/electrochemistry/unit_tables.py
Normal file
53
beamtime/electrochemistry/unit_tables.py
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
import pandas as pd
|
||||
|
||||
def time():
|
||||
# Define matrix for unit conversion for time
|
||||
time = {'h': [1, 60, 3600, 3600000], 'min': [1/60, 1, 60, 60000], 's': [1/3600, 1/60, 1, 1000], 'ms': [1/3600000, 1/60000, 1/1000, 1]}
|
||||
time = pd.DataFrame(time)
|
||||
time.index = ['h', 'min', 's', 'ms']
|
||||
|
||||
return time
|
||||
|
||||
def current():
|
||||
# Define matrix for unit conversion for current
|
||||
current = {'A': [1, 1000, 1000000], 'mA': [1/1000, 1, 1000], 'uA': [1/1000000, 1/1000, 1]}
|
||||
current = pd.DataFrame(current)
|
||||
current.index = ['A', 'mA', 'uA']
|
||||
|
||||
return current
|
||||
|
||||
def voltage():
|
||||
# Define matrix for unit conversion for voltage
|
||||
voltage = {'V': [1, 1000, 1000000], 'mV': [1/1000, 1, 1000], 'uV': [1/1000000, 1/1000, 1]}
|
||||
voltage = pd.DataFrame(voltage)
|
||||
voltage.index = ['V', 'mV', 'uV']
|
||||
|
||||
return voltage
|
||||
|
||||
def capacity():
|
||||
# Define matrix for unit conversion for capacity
|
||||
capacity = {'Ah': [1, 1000, 1000000], 'mAh': [1/1000, 1, 1000], 'uAh': [1/1000000, 1/1000, 1]}
|
||||
capacity = pd.DataFrame(capacity)
|
||||
capacity.index = ['Ah', 'mAh', 'uAh']
|
||||
|
||||
return capacity
|
||||
|
||||
def mass():
|
||||
# Define matrix for unit conversion for capacity
|
||||
mass = {'kg': [1, 1000, 1000000, 1000000000], 'g': [1/1000, 1, 1000, 1000000], 'mg': [1/1000000, 1/1000, 1, 1000], 'ug': [1/1000000000, 1/1000000, 1/1000, 1]}
|
||||
mass = pd.DataFrame(mass)
|
||||
mass.index = ['kg', 'g', 'mg', 'ug']
|
||||
|
||||
return mass
|
||||
|
||||
|
||||
def energy():
|
||||
|
||||
energy = {'kWh': [1, 1000, 1000000], 'Wh': [1/1000, 1, 1000], 'mWh': [1/100000, 1/1000, 1]}
|
||||
energy = pd.DataFrame(energy)
|
||||
energy.index = ['kWh', 'Wh', 'mWh']
|
||||
|
||||
return energy
|
||||
|
||||
|
||||
|
||||
107
beamtime/requirements.txt
Normal file
107
beamtime/requirements.txt
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
# This file may be used to create an environment using:
|
||||
# $ conda create --name <env> --file <this file>
|
||||
# platform: win-64
|
||||
backcall=0.2.0=py_0
|
||||
beamtime=0.1=pypi_0
|
||||
blas=1.0=mkl
|
||||
bottleneck=1.3.2=py39h7cc1a96_1
|
||||
ca-certificates=2021.10.8=h5b45459_0
|
||||
cached-property=1.5.2=hd8ed1ab_1
|
||||
cached_property=1.5.2=pyha770c72_1
|
||||
certifi=2021.10.8=py39hcbf5309_0
|
||||
colorama=0.4.4=py_0
|
||||
cycler=0.10.0=py_2
|
||||
debugpy=1.4.1=py39hd77b12b_0
|
||||
decorator=4.4.2=py_0
|
||||
fabio=0.12.0=py39h5d4886f_0
|
||||
freetype=2.10.4=h546665d_1
|
||||
glymur=0.9.4=pyhd8ed1ab_0
|
||||
h5py=3.2.1=nompi_py39hf27771d_100
|
||||
hdf5=1.10.6=nompi_h5268f04_1114
|
||||
hdf5plugin=3.1.1=py39h71586dd_0
|
||||
icc_rt=2019.0.0=h0cc432a_1
|
||||
icu=68.1=h0e60522_0
|
||||
intel-openmp=2021.3.0=haa95532_3372
|
||||
ipykernel=6.4.1=py39haa95532_1
|
||||
ipython=7.27.0=py39hd4e2768_0
|
||||
ipython_genutils=0.2.0=pyhd3eb1b0_1
|
||||
jbig=2.1=h8d14728_2003
|
||||
jedi=0.18.0=py39haa95532_1
|
||||
jpeg=9d=h8ffe710_0
|
||||
jupyter_client=6.1.7=py_0
|
||||
jupyter_core=4.8.1=py39haa95532_0
|
||||
kiwisolver=1.3.2=py39h2e07f2f_0
|
||||
krb5=1.19.2=hbae68bd_2
|
||||
lcms2=2.12=h2a16943_0
|
||||
lerc=3.0=h0e60522_0
|
||||
libclang=11.1.0=default_h5c34c98_1
|
||||
libcurl=7.79.1=h789b8ee_1
|
||||
libdeflate=1.8=h8ffe710_0
|
||||
libiconv=1.16=he774522_0
|
||||
libpng=1.6.37=h1d00b33_2
|
||||
libssh2=1.10.0=h680486a_2
|
||||
libtiff=4.3.0=hd413186_2
|
||||
libxml2=2.9.12=hf5bbc77_0
|
||||
libxslt=1.1.33=h65864e5_2
|
||||
libzlib=1.2.11=h8ffe710_1013
|
||||
lxml=4.6.3=py39h4fd7cdf_0
|
||||
lz4-c=1.9.3=h8ffe710_1
|
||||
mako=1.1.5=pyhd8ed1ab_0
|
||||
markupsafe=2.0.1=py39hb82d6ee_0
|
||||
matplotlib=3.4.3=py39hcbf5309_1
|
||||
matplotlib-base=3.4.3=py39h581301d_1
|
||||
matplotlib-inline=0.1.2=pyhd3eb1b0_2
|
||||
mkl=2021.3.0=haa95532_524
|
||||
mkl-service=2.4.0=py39h2bbff1b_0
|
||||
mkl_fft=1.3.0=py39h277e83a_2
|
||||
mkl_random=1.2.2=py39hf11a4ad_0
|
||||
numexpr=2.7.3=py39hb80d3ca_1
|
||||
numpy=1.21.2=py39hfca59bb_0
|
||||
numpy-base=1.21.2=py39h0829f74_0
|
||||
olefile=0.46=pyh9f0ad1d_1
|
||||
openjpeg=2.4.0=hb211442_1
|
||||
openssl=1.1.1l=h8ffe710_0
|
||||
pandas=1.3.3=py39h6214cd6_0
|
||||
parso=0.8.0=py_0
|
||||
pickleshare=0.7.5=pyhd3eb1b0_1003
|
||||
pillow=8.3.2=py39h916092e_0
|
||||
pip=21.2.4=py39haa95532_0
|
||||
prompt-toolkit=3.0.8=py_0
|
||||
pyfai=0.20.0=hd8ed1ab_0
|
||||
pyfai-base=0.20.0=py39h2e25243_0
|
||||
pygments=2.7.1=py_0
|
||||
pyparsing=2.4.7=pyh9f0ad1d_0
|
||||
pyqt=5.12.3=py39hcbf5309_7
|
||||
pyqt-impl=5.12.3=py39h415ef7b_7
|
||||
pyqt5-sip=4.19.18=py39h415ef7b_7
|
||||
pyqtchart=5.12=py39h415ef7b_7
|
||||
pyqtwebengine=5.12.1=py39h415ef7b_7
|
||||
pyreadline=2.1=py39hcbf5309_1004
|
||||
python=3.9.7=h6244533_1
|
||||
python-dateutil=2.8.2=pyhd3eb1b0_0
|
||||
python_abi=3.9=2_cp39
|
||||
pytz=2021.3=pyhd3eb1b0_0
|
||||
pywin32=228=py39hbaba5e8_1
|
||||
pyzmq=22.2.1=py39hd77b12b_1
|
||||
qt=5.12.9=h5909a2a_4
|
||||
qtconsole=5.1.1=pyhd8ed1ab_0
|
||||
qtpy=1.11.2=pyhd8ed1ab_0
|
||||
scipy=1.7.1=py39hbe87c03_2
|
||||
setuptools=58.0.4=py39haa95532_0
|
||||
silx=0.15.2=hd8ed1ab_0
|
||||
silx-base=0.15.2=py39h2e25243_0
|
||||
six=1.16.0=pyhd3eb1b0_0
|
||||
sqlite=3.36.0=h2bbff1b_0
|
||||
tk=8.6.11=h8ffe710_1
|
||||
tornado=6.1=py39h2bbff1b_0
|
||||
traitlets=5.0.5=py_0
|
||||
tzdata=2021a=h5d7bf9c_0
|
||||
vc=14.2=h21ff451_1
|
||||
vs2015_runtime=14.27.29016=h5e58377_2
|
||||
wcwidth=0.2.5=py_0
|
||||
wheel=0.37.0=pyhd3eb1b0_1
|
||||
wincertstore=0.2=py39haa95532_2
|
||||
xlsx2csv=0.7.8=pypi_0
|
||||
xz=5.2.5=h62dcd97_1
|
||||
zlib=1.2.11=h8ffe710_1013
|
||||
zstd=1.5.0=h6255e5f_0
|
||||
|
|
@ -5,9 +5,20 @@ import os
|
|||
def rbkerbest():
|
||||
print("ROSENBORG!<3")
|
||||
|
||||
#def split_xanes_scan(filename, destination=None):
|
||||
|
||||
def split_xanes_scan(filename, destination=None):
|
||||
# with open(filename, 'r') as f:
|
||||
|
||||
|
||||
##Better to make a new function that loops through the files, and performing the split_xanes_scan on
|
||||
|
||||
|
||||
def split_xanes_scan(root, destination=None, replace=False):
|
||||
#root is the path to the beamtime-folder
|
||||
#destination should be the path to the processed data
|
||||
|
||||
#insert a for-loop to go through all the folders.dat-files in the folder root\xanes\raw
|
||||
|
||||
with open(filename, 'r') as f:
|
||||
lines = f.readlines()
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,2 @@
|
|||
#hello
|
||||
#yeah
|
||||
Loading…
Add table
Add a link
Reference in a new issue