{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Analysing GITT data\n", "\n", "PyProBE includes built-in analysis methods for pulsing experiments, which this example\n", "will demonstrate.\n", "\n", "First import the required libraries and data:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%capture\n", "%pip install matplotlib" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import matplotlib.pyplot as plt\n", "\n", "import pyprobe\n", "\n", "%matplotlib inline" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "info_dictionary = {\n", " \"Name\": \"Sample cell\",\n", " \"Chemistry\": \"NMC622\",\n", " \"Nominal Capacity [Ah]\": 0.04,\n", " \"Cycler number\": 1,\n", " \"Channel number\": 1,\n", "}\n", "data_directory = \"../../../tests/sample_data/neware\"\n", "\n", "# Create a cell object\n", "cell = pyprobe.Cell(info=info_dictionary)\n", "cell.add_procedure(\n", " procedure_name=\"Sample\",\n", " folder_path=data_directory,\n", " filename=\"sample_data_neware.parquet\",\n", ")\n", "print(cell.procedure[\"Sample\"].experiment_names)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We will plot the Break-in Cycles and Discharge Pulses:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fig, ax = plt.subplots()\n", "cell.procedure[\"Sample\"].experiment(\"Break-in Cycles\").plot(\n", " x=\"Time [hr]\", y=\"Voltage [V]\", ax=ax, label=\"Break-in Cycles\", color=\"blue\"\n", ")\n", "cell.procedure[\"Sample\"].experiment(\"Discharge Pulses\").plot(\n", " x=\"Time [hr]\", y=\"Voltage [V]\", ax=ax, label=\"Discharge Pulses\", color=\"red\"\n", ")\n", "ax.set_ylabel(\"Voltage [V]\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "State-of-charge is a useful metric when working with battery data, however it must be carefully defined. PyProBE doesn't automatically calculate a value for cell SOC until instructed to by the user for this reason.\n", "\n", "To add an SOC column to the data, we call `set_SOC()` on the procedure. We are going to provide an argument to `reference_charge`. This will be the final charge of the break-in cycles. This argument instructs PyProBE that the final data point of this charge is our 100% SOC reference." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "reference_charge = cell.procedure[\"Sample\"].experiment(\"Break-in Cycles\").charge(-1)\n", "cell.procedure[\"Sample\"].set_SOC(reference_charge=reference_charge)\n", "\n", "fig, ax = plt.subplots()\n", "cell.procedure[\"Sample\"].experiment(\"Break-in Cycles\").plot(\n", " x=\"Time [hr]\", y=\"SOC\", ax=ax, label=\"Break-in Cycles\", color=\"blue\"\n", ")\n", "cell.procedure[\"Sample\"].experiment(\"Discharge Pulses\").plot(\n", " x=\"Time [hr]\", y=\"SOC\", ax=ax, label=\"Discharge Pulses\", color=\"red\"\n", ")\n", "ax.set_ylabel(\"SOC\")\n", "plt.legend(loc=\"lower left\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Then we'll filter to only the pulsing experiment:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "pulsing_experiment = cell.procedure[\"Sample\"].experiment(\"Discharge Pulses\")\n", "\n", "fig, ax = plt.subplots()\n", "pulsing_experiment.plot(\n", " x=\"Experiment Time [hr]\",\n", " y=\"Voltage [V]\",\n", " ax=ax,\n", " label=\"Discharge Pulses\",\n", " color=\"red\",\n", ")\n", "ax.set_ylabel(\"Voltage [V]\")\n", "plt.legend(loc=\"lower left\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And then create our pulsing analysis object." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from pyprobe.analysis import pulsing\n", "\n", "pulse_object = pulsing.Pulsing(input_data=pulsing_experiment)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "With the pulsing object we can separate out individual pulses:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fig, ax = plt.subplots()\n", "pulse_object.input_data.plot(\n", " x=\"Experiment Time [hr]\",\n", " y=\"Voltage [V]\",\n", " label=\"Full Experiment\",\n", " color=\"blue\",\n", " ax=ax,\n", ")\n", "pulse_object.pulse(4).plot(\n", " x=\"Experiment Time [hr]\", y=\"Voltage [V]\", label=\"Pulse 5\", color=\"red\", ax=ax\n", ")\n", "ax.set_ylabel(\"Voltage [V]\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can also extract key parameters from the pulsing experiment, with the `get_resistances()` method." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "pulse_resistances = pulsing.get_resistances(input_data=pulsing_experiment)\n", "print(pulse_resistances.data)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The `get_resistances()` method can take an argument of a list of times at which to evaluate the resistance after the pulse, for instance at 10s after the pulse:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "pulse_resistances = pulsing.get_resistances(input_data=pulsing_experiment, r_times=[10])\n", "print(pulse_resistances.data)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As a result object, the pulse summary can also be plotted:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fig, ax = plt.subplots()\n", "pulse_resistances.plot(x=\"SOC\", y=\"R0 [Ohms]\", ax=ax, label=\"R0\", color=\"blue\")\n", "pulse_resistances.plot(x=\"SOC\", y=\"R_10s [Ohms]\", ax=ax, label=\"R_10s\", color=\"red\")\n", "ax.set_ylabel(\"Resistance [Ohms]\")" ] } ], "metadata": { "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.12.6" } }, "nbformat": 4, "nbformat_minor": 2 }