Notebook for cellpy batch processing

You can fill inn the MarkDown cells (the cells without "numbering") by double-clicking them. Also remember, press shift + enter to execute a cell.

A couple of useful links:

This notebook uses the following packages

  • python >= 3.6
  • cellpy >= 0.3.0
  • pandas
  • numpy
  • matplotlib
  • bokeh
  • pyviz (holoviews)

1. Key information about the current experiment

Experimental-id: xxx
Short-name: xxx
Project: project name
By: your name
Date: xx.xx.xxxx

2. Short summary of the experiment before processing

It is often helpful to formulate what you wanted to achieve with your experiment before actually going into depth of the data. I believe that it does not make you "biased" when processing your data, but instead sharpens your mind and motivates you to look more closely on your results. I might be wrong, off course. Then just skip filling in this part.

Main purpose

(State the main hypothesis for the current set of experiment)

Expected outcome

(What do you expect to find out? What kind of tests did you perform?)

Special considerations

(State if there are any special considerations for this experiment)

3. Processing data

Setting up everything


In [ ]:
%load_ext autoreload
%autoreload 2

In [ ]:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

import cellpy
from cellpy import prms
from cellpy import prmreader
from cellpy.utils import batch
import holoviews as hv

%matplotlib inline
hv.extension('bokeh')

In [ ]:
prmreader.info()

In [ ]:
######################################################################
##                                                                  ##
##                       development                                ##
##                                                                  ##
######################################################################
import sys
from pathlib import Path
from pprint import pprint

# Use these when working on my work PC:
test_data_path = r"C:\Scripting\MyFiles\development_cellpy\testdata"
out_data_path = r"C:\Scripting\Processing\Test\out"

if sys.platform=="darwin":
    # Use these when working on my MacBook:
    test_data_path = "/Users/jepe/scripting/cellpy/testdata"
    out_data_path = "/Users/jepe/cellpy_data"

test_data_path = Path(test_data_path)
out_data_path = Path(out_data_path)

print(" SETTING SOME PRMS ".center(80, "="))
prms.Paths["db_filename"] = "cellpy_db.xlsx"
prms.Paths["cellpydatadir"] = test_data_path / "hdf5"
prms.Paths["outdatadir"] = out_data_path
prms.Paths["rawdatadir"] = test_data_path / "data"
prms.Paths["db_path"] = test_data_path / "db"
prms.Paths["filelogdir"] = test_data_path / "log"
pprint(prms.Paths)

In [ ]:
## Uncomment this and run for checking your cellpy parameters.
# prmreader.info()

Creating pages and initialise the cellpy batch object

If you need to create Journal Pages, please provide appropriate names for the project and the experiment to allow cellpy to build the pages.


In [ ]:
# Please fill in here
project = "prebens_experiment"
name = "test"
batch_col = "b01"

Initialisation


In [ ]:
print(" INITIALISATION OF BATCH ".center(80, "="))
b = batch.init(name, project, batch_col=batch_col, log_level="INFO")

Set parameters


In [ ]:
# setting some prms
b.experiment.export_raw = False
b.experiment.export_cycles = False
b.experiment.export_ica = False
#b.experiment.all_in_memory = True  # store all data in memory, defaults to False

Run


In [ ]:
# load info from your db and write the journal pages
b.create_journal()
b.pages

In [ ]:
b.link()

In [ ]:
# create the apropriate folders
b.paginate()

In [ ]:
# load the data (and save .csv-files if you have set export_(raw/cycles/ica) = True)
# (this might take some time)
b.update()

In [ ]:
# collect summary-data (e.g. charge capacity vs cycle number) from each cell and export to .csv-file(s).
b.combine_summaries()
print(" FINISHED ".center(80, "-"))

4. Looking at the data

Summaries


In [ ]:
prms.Batch.summary_plot_height = 800
prms.Batch.summary_plot_width = 900

In [ ]:
# Plot the charge capacity and the C.E. (and resistance) vs. cycle number (standard plot)
b.plot_summaries()

In [ ]:
from bokeh.plotting import figure, show
from bokeh.models import ColumnDataSource, ColumnarDataSource

data = ColumnDataSource(pd.DataFrame(
    {
        "header_en": [1, 2, 3, 4],
        "annen": [22.1, 22.2, 22.3, 22.4],
        "siste": [1000, 2000, 3000, 4000]
    }
))


p = figure(title="Top Title with Toolbar", toolbar_location="above",
           plot_width=600, plot_height=300)
p.circle(x="header_en", y="annen", source=data)

show(p)

In [ ]:


In [ ]:


In [ ]:


In [ ]:


In [ ]:
b.summary_headers

In [ ]:
# Show the journal pages
# b.experiment.journal.pages.head()

In [ ]:
# Show the most important part of the journal pages
b.view

In [ ]:
# b.experiment.status()

In [ ]:
b.summaries.head()

In [ ]:
summaries = b.summaries.copy()

In [ ]:
from cellpy.parameters.internal_settings import (
    get_headers_summary,
    get_headers_normal,
    get_headers_step_table,
)

Cycles


In [ ]:
labels = b.experiment.cell_names
labels

In [ ]:
d = b.experiment.data['20160805_test001_47_cc']

In [ ]:
b.experiment.data.experiment.cell_data_frames

In [ ]:
%%opts Curve (color=hv.Palette('Magma'))
voltage_curves = dict()
for label in b.experiment.cell_names:
    d = b.experiment.data[label]
    curves = d.get_cap(label_cycle_number=True, interpolated=True, number_of_points=100)
    curve = hv.Curve(curves, kdims=["capacity", "cycle"], vdims="voltage").groupby("cycle").overlay().opts(show_legend=False)
    voltage_curves[label] = curve
NdLayout = hv.NdLayout(voltage_curves, kdims='label').cols(3)
NdLayout

In [ ]:
%%opts Curve (color=hv.Palette('Magma'))
ocv_curves = dict()
for label in b.experiment.cell_names:
    d = b.experiment.data[label]
    ocv_data = d.get_ocv(direction="up", number_of_points=40)
    ocv_curve = hv.Curve(ocv_data, kdims=["Step_Time", "Cycle_Index"], vdims="Voltage").groupby("Cycle_Index").overlay().opts(show_legend=False)
    ocv_curves[label] = ocv_curve

NdLayout = hv.NdLayout(ocv_curves, kdims='label').cols(3)
NdLayout

Selecting specific cells and investigating them


In [ ]:
# This will show you all your cell names
cell_labels = b.experiment.cell_names
cell_labels

In [ ]:
# This is how to select the data (CellpyData-objects)
data1 = b.experiment.data["20160805_test001_45_cc"]

Let's see how the smoothing (interpolation) method works


In [ ]:
# get voltage curves
df_cycles1 = data1.get_cap(method="back-and-forth",categorical_column=True, label_cycle_number=True, interpolated=False)

In [ ]:
# get interpolated voltage curves
df_cycles2 = data1.get_cap(
    method="back-and-forth", categorical_column=True, label_cycle_number=True, interpolated=True,
    dx=0.1, number_of_points=100,
)

In [ ]:
%%opts Scatter [width=600] (color="red", alpha=0.9, size=12)
single_curve = hv.Curve(df_cycles1, kdims=["capacity", "cycle"], vdims="voltage", label="not-smoothed").groupby("cycle")
single_scatter = hv.Scatter(df_cycles2, kdims=["capacity", "cycle"], vdims="voltage", label="smoothed").groupby("cycle")
single_scatter * single_curve

Using hvplot for plotting summaries

You can for example use hvplot for looking more at your summary data


In [ ]:
import hvplot.pandas

In [ ]:
# hvplot does not like infinities
s = b.summaries.replace([np.inf, -np.inf], np.nan)

In [ ]:
layout = s["coulombic_efficiency"].hvplot() + s["discharge_capacity"].hvplot() * s["charge_capacity"].hvplot()
layout.cols(1)

In [ ]:
s["cumulated_coulombic_efficiency"].hvplot()

Looking more in-depth and utilising advanced features

OCV relaxation points

Picking out 5 points on each OCV relaxation curve (distributed by last, last/2, last/2/2, ..., first).


In [ ]:
from cellpy.utils.batch_tools.batch_analyzers import OCVRelaxationAnalyzer
print(" analyzing ocv relaxation data ".center(80, "-"))
analyzer = OCVRelaxationAnalyzer()
analyzer.assign(b.experiment)
analyzer.direction = "down"
analyzer.do()
dfs = analyzer.last
df_file_one, _df_file_two = dfs

# keeping only the columns with voltages
ycols = [col for col in df_file_one.columns if col.find("point")>=0]

# removing the first ocv rlx (relaxation before starting cycling)
df = df_file_one.iloc[1:, :]
# tidy format
df = df.melt(id_vars = "cycle", var_name="point", value_vars=ycols, value_name="voltage")
curve = hv.Curve(df, kdims=["cycle", "point"], vdims="voltage").groupby("point").overlay().opts(xlim=(1,10), width=800)
scatter = hv.Scatter(df, kdims=["cycle", "point"], vdims="voltage").groupby("point").overlay().opts(
    xlim=(1,10), ylim=(0.7,1)
)

In [ ]:
layout = hv.Layout(curve * scatter)
layout.cols(1)

Looking closer at some summary-plots


In [ ]:
b.summary_columns

In [ ]:
discharge_capacity = b.summaries.discharge_capacity
charge_capacity = b.summaries.charge_capacity
coulombic_efficiency = b.summaries.coulombic_efficiency
ir_charge = b.summaries.ir_charge

In [ ]:
fig, (ax1, ax2) = plt.subplots(2, 1)
ax1.plot(discharge_capacity)
ax1.set_ylabel("capacity ")
ax2.plot(ir_charge)
ax2.set_xlabel("cycle")
ax2.set_ylabel("resistance")

5. Checking for more details per cycle

A. pick the CellpyData object for one of the cells


In [ ]:
# Lets check what cells we have
cell_labels = b.experiment.cell_names
cell_labels

In [ ]:
# OK, then I choose one of them
data = b.experiment.data["20160805_test001_45_cc"]

B. Get some voltage curves for some cycles and plot them

The method get_cap can be used to extract voltage curves.


In [ ]:
cap = data.get_cap(categorical_column=True)
cap.head()

In [ ]:
fig, ax = plt.subplots()
ax.plot(cap.capacity, cap.voltage)
ax.set_xlabel("capacity")
ax.set_ylabel("voltage")

In [ ]:
cv = data.get_cap(method="forth")
fig, ax = plt.subplots()
ax.set_xlabel("capacity")
ax.set_ylabel("voltage")
ax.plot(cv.capacity, cv.voltage)

In [ ]:
c4 = data.get_cap(cycle=4, method="forth-and-forth")
c10 = data.get_cap(cycle=10, method="forth-and-forth")
fig, ax = plt.subplots()
ax.set_xlabel("capacity")
ax.set_ylabel("voltage")
ax.plot(c4.capacity,c4.voltage, "ro", label="cycle 4")
ax.plot(c10.capacity,c10.voltage, "bs", label="cycle 22")
ax.legend();

Looking at some dqdv data

Get capacity cycles and make dqdv using the ica module


In [ ]:
from cellpy.utils import ica
v4, dqdv4 = ica.dqdv_cycle(
    data.get_cap(
        4, 
        categorical_column=True, 
        method = "forth-and-forth")
)

v10, dqdv10 = ica.dqdv_cycle(
    data.get_cap(
        10, 
        categorical_column=True, 
        method = "forth-and-forth")
)

plt.plot(v4,dqdv4, label="cycle 4")
plt.plot(v10, dqdv10, label="cycle 10")
plt.legend();

Put it in a for-loop for plotting many ica plots


In [ ]:
fig, ax = plt.subplots()
for cycle in data.get_cycle_numbers():
    d = data.get_cap(
            cycle, 
            categorical_column=True, 
            method = "forth-and-forth"
        )
    if not d.empty:
        v, dqdv = ica.dqdv_cycle(d)
        ax.plot(v, dqdv)
    else:
        print(f"cycle {cycle} seems to be missing or corrupted")

Get all the dqdv data in one go


In [ ]:
hv.extension('bokeh')

In [ ]:
tidy_ica = ica.dqdv_frames(data)
cycles = list(range(1,3)) + [10, 11, 12, 15]
tidy_ica = tidy_ica.loc[tidy_ica.cycle.isin(cycles), :]

In [ ]:
%%opts Curve [xlim=(0,1)] (color=hv.Palette('Magma'), alpha=0.9) NdOverlay [legend_position='right', width=800, height=500]
curve4 = (hv.Curve(tidy_ica, kdims=['voltage'], vdims=['dq', 'cycle'], label="Incremental capacity plot")
          .groupby("cycle")
          .overlay()
         )
curve4

In [ ]: