{ "cells": [ { "cell_type": "markdown", "metadata": { "id": "expmkveO04pw" }, "source": [ "## An Electrode Design Optimisation Example\n", "\n", "A design optimisation example loosely based on work by L.D. Couto available at [[1]](https://doi.org/10.1016/j.energy.2022.125966).\n", "\n", "The target is to maximise the gravimetric energy density over a range of possible design parameter values, including for example:\n", "\n", "cross-sectional area = height x width (only need change one), electrode widths, particle radii, volume fractions and separator width.\n", "\n", "### Setting up the Environment\n", "\n", "If you don't already have PyBOP installed, check out the [installation guide](https://pybop-docs.readthedocs.io/en/latest/installation.html) first.\n", "\n", "We begin by importing the necessary libraries. Let's also fix the random seed to generate consistent output during development." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "SQdt4brD04p1" }, "outputs": [], "source": [ "import numpy as np\n", "import pybamm\n", "from pybamm import Parameter\n", "\n", "import pybop\n", "\n", "pybop.plot.PlotlyManager().pio.renderers.default = \"notebook_connected\"\n", "\n", "np.random.seed(8) # users can remove this line" ] }, { "cell_type": "markdown", "metadata": { "id": "X8-tubYY04p_" }, "source": [ "## Optimising the parameters\n", "\n", "First, we define the model and parameter values to be used for the parameter optimisation." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "zuvGHWID04p_" }, "outputs": [], "source": [ "model = pybamm.lithium_ion.SPMe()\n", "pybop.pybamm.add_variable_to_model(model, \"Gravimetric energy density [W.h.kg-1]\")\n", "\n", "parameter_values = pybamm.ParameterValues(\"Chen2020\")\n", "pybop.pybamm.set_formation_concentrations(parameter_values)\n", "parameter_values.update(\n", " {\n", " \"Electrolyte density [kg.m-3]\": Parameter(\"Separator density [kg.m-3]\"),\n", " \"Negative electrode active material density [kg.m-3]\": Parameter(\n", " \"Negative electrode density [kg.m-3]\"\n", " ),\n", " \"Negative electrode carbon-binder density [kg.m-3]\": Parameter(\n", " \"Negative electrode density [kg.m-3]\"\n", " ),\n", " \"Positive electrode active material density [kg.m-3]\": Parameter(\n", " \"Positive electrode density [kg.m-3]\"\n", " ),\n", " \"Positive electrode carbon-binder density [kg.m-3]\": Parameter(\n", " \"Positive electrode density [kg.m-3]\"\n", " ),\n", " \"Cell mass [kg]\": pybop.pybamm.cell_mass(),\n", " }\n", ")" ] }, { "cell_type": "markdown", "metadata": { "id": "ffS3CF_704qA" }, "source": [ "Next, we define the model parameters for optimisation. Furthermore, PyBOP provides functionality to define a distribution for the parameters. The initial parameters values used in the optimisation will be randomly drawn from the prior distribution." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "WPCybXIJ04qA" }, "outputs": [], "source": [ "parameter_values.update(\n", " {\n", " \"Positive electrode thickness [m]\": pybop.Parameter(\n", " pybop.Gaussian(7.56e-05, 0.05e-05, truncated_at=[65e-06, 10e-05]),\n", " ),\n", " \"Positive particle radius [m]\": pybop.Parameter(\n", " pybop.Gaussian(5.22e-06, 0.05e-06, truncated_at=[2e-06, 9e-06]),\n", " ),\n", " }\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Next, we construct the experiment for design optimisation and the initial state-of-charge," ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "experiment = pybamm.Experiment([\"Discharge at 1C until 2.5 V (5 seconds period)\"])\n", "initial_soc = 0.7" ] }, { "cell_type": "markdown", "metadata": { "id": "n4OHa-aF04qA" }, "source": [ "We can now define the simulator, cost function, which in this example is based on the gravimetric energy density of the cell, and construct the problem." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "etMzRtx404qA" }, "outputs": [], "source": [ "simulator = pybop.pybamm.Simulator(\n", " model,\n", " parameter_values=parameter_values,\n", " protocol=experiment,\n", " initial_state={\"Initial SoC\": initial_soc},\n", " solver=pybamm.CasadiSolver(),\n", ")\n", "cost = pybop.DesignCost(target=\"Gravimetric energy density [W.h.kg-1]\")\n", "problem = pybop.Problem(simulator, cost)" ] }, { "cell_type": "markdown", "metadata": { "id": "eQiGurUV04qB" }, "source": [ "Next we construct an optimiser. This class provides the algorithm to optimise the parameters. For this example, we use particle swarm optimisation (PSO). Due to the computational requirements of the design optimisation methods, we limit the number of iterations for this example." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "N3FtAhrT04qB" }, "outputs": [], "source": [ "options = pybop.PintsOptions(verbose=True, max_iterations=15)\n", "optim = pybop.PSO(problem, options=options)" ] }, { "cell_type": "markdown", "metadata": { "id": "caprp-bV04qB" }, "source": [ "Finally, we run the optimisation and return the values obtained," ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "-9OVt0EQ04qB" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "| Iter: 1 | Evals: 6| Best Parameters: [8.32936135e-05 5.34874257e-06] | Best Cost: 272.4725741332926\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "| Iter: 2 | Evals: 12| Best Parameters: [8.32936135e-05 5.34874257e-06] | Best Cost: 272.4725741332926\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "| Iter: 3 | Evals: 18| Best Parameters: [8.46531639e-05 3.79767350e-06] | Best Cost: 278.4671427205715\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "| Iter: 4 | Evals: 24| Best Parameters: [8.46531639e-05 3.79767350e-06] | Best Cost: 278.4671427205715\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "| Iter: 5 | Evals: 29| Best Parameters: [8.46531639e-05 3.79767350e-06] | Best Cost: 278.4671427205715\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "| Iter: 6 | Evals: 32| Best Parameters: [8.46531639e-05 3.79767350e-06] | Best Cost: 278.4671427205715\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "| Iter: 7 | Evals: 37| Best Parameters: [8.55293313e-05 3.53706776e-06] | Best Cost: 278.642235228317\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "| Iter: 8 | Evals: 43| Best Parameters: [8.55293313e-05 3.53706776e-06] | Best Cost: 278.642235228317\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "| Iter: 9 | Evals: 49| Best Parameters: [8.55293313e-05 3.53706776e-06] | Best Cost: 278.642235228317\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "| Iter: 10 | Evals: 55| Best Parameters: [8.29860246e-05 2.58546828e-06] | Best Cost: 287.01440153288445\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "OptimisationResult:\n", " Best result from 1 run(s).\n", " Initial parameters: [7.56456024e-05 5.27456414e-06]\n", " Optimised parameters: [8.38277729e-05 2.13404650e-06]\n", " Best cost: 288.74937116651336\n", " Optimisation time: 65.29159212112427 seconds\n", " Number of iterations: 15\n", " Number of evaluations: 77\n", " Reason for stopping: Maximum number of iterations (15) reached.\n", "Initial gravimetric energy density: 254.67 W.h.kg-1\n", "Optimised gravimetric energy density: 288.75 W.h.kg-1\n" ] } ], "source": [ "result = optim.run()\n", "print(f\"Initial gravimetric energy density: {result.cost[0]:.2f} W.h.kg-1\")\n", "print(f\"Optimised gravimetric energy density: {result.best_cost:.2f} W.h.kg-1\")" ] }, { "cell_type": "markdown", "metadata": { "id": "KxKURtH704qC" }, "source": [ "## Plotting and visualisation\n", "\n", "PyBOP provides various plotting utilities to visualise the results of the optimisation.\n", "\n", "### Cost landscape\n", "\n", "We can visualise the cost landscape and the path taken by the optimiser:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 467 }, "id": "tJUJ80Ve04qD", "outputId": "855fbaa2-1e09-4935-eb1a-8caf7f99eb75" }, "outputs": [ { "data": { "text/html": [ " \n", " \n", " " ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "