{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Cycle ageing example\n", "\n", "This notebook demonstrates how to use the `CycleAgeing` objective function to fit the\n", "SEI solvent diffusivity in the \"solvent-diffusion limited\" SEI model using synthetic cycling data." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import pybamm\n", "import ionworkspipeline as iwp\n", "import pandas as pd" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We begin by generating some synthetic cycling data. We choose the SPMe model and the \"solvent-diffusion limited\" SEI model and simulate 10 cycles of a CCCV charge/discharge cycle. For the data, we record the cycle number, the loss of lithium inventory, and the capacity. We choose the parameter values so that we get a large loss of lithium inventory even after only 10 cycles." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "model = pybamm.lithium_ion.SPMe({\"SEI\": \"solvent-diffusion limited\"})\n", "parameter_values = iwp.ParameterValues(\"Chen2020\")\n", "parameter_values.update(\n", " {\n", " \"SEI solvent diffusivity [m2.s-1]\": 2.5e-18,\n", " }\n", ")\n", "\n", "experiment = pybamm.Experiment(\n", " [\n", " (\n", " \"Discharge at 1C until 2.5 V\",\n", " \"Rest for 1 hour\",\n", " \"Charge at C/2 until 4.2 V\",\n", " \"Hold at 4.2 V until 10 mA\",\n", " \"Rest for 1 hour\",\n", " ),\n", " ]\n", " * 10\n", ")\n", "\n", "sim = iwp.Simulation(\n", " model,\n", " experiment=experiment,\n", " parameter_values=parameter_values,\n", ")\n", "\n", "sol = sim.solve()\n", "\n", "data = {\"Cycle number\": sol.summary_variables.cycle_number}\n", "for var in [\"Loss of lithium inventory [%]\", \"Capacity [A.h]\"]:\n", " data[var] = sol.summary_variables[var]\n", "\n", "# PyBaMM uses 1-based indexing for cycles but ionworks data format assumes cycles number\n", "# starts at zero\n", "data[\"Cycle number\"] = data[\"Cycle number\"] - 1\n", "\n", "data = pd.DataFrame(data)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can plot the synthetic data to see what it looks like." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "_ = data.plot(\n", " x=\"Cycle number\", y=[\"Loss of lithium inventory [%]\", \"Capacity [A.h]\"], kind=\"line\"\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Next, we create the objective function. We pass the data and the options to the `CycleAgeing` objective function. The options include the model, experiment, and the objective variables (i.e. the variables we want to fit to). The `CycleAgeing` objective runs the experiment and calculates the cycling data. By default, it uses the function `ionworkspipeline.data_fits.objectives.get_standard_summary_variables` to extract the summary variables from the solution. Custom summary variables can be used by passing a custom function to the `cycle_variables_function` option.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "objective = iwp.objectives.CycleAgeing(\n", " data,\n", " options={\n", " \"model\": model,\n", " \"experiment\": experiment,\n", " \"objective variables\": [\"Loss of lithium inventory [%]\"],\n", " },\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We then create the data fit. We pass the objective function and the parameters to the `DataFit` class. The parameters include the initial values and bounds for the parameters we want to fit." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "parameters = {\n", " \"SEI solvent diffusivity [m2.s-1]\": iwp.Parameter(\n", " \"SEI solvent diffusivity [m2.s-1]\",\n", " initial_value=1e-19,\n", " bounds=(1e-21, 1e-17),\n", " )\n", "}\n", "data_fit = iwp.DataFit(objective, parameters=parameters)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Next, we run the data fit. We pass the known parameters (i.e. those we are not fitting) to the data fit. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "known_parameters = {k: v for k, v in parameter_values.items() if k not in parameters}\n", "data_fit.run(known_parameters)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Finally, we plot the fit results." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "_ = data_fit.plot_fit_results()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3" } }, "nbformat": 4, "nbformat_minor": 2 }