{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"id": "expmkveO04pw"
},
"source": [
"## An Electrode Design Optimisation Example\n",
"\n",
"NOTE: This is a brittle example, the classes and methods below will be integrated into PyBOP in a future release.\n",
"\n",
"A design optimisation example loosely based on work by L.D. Couto available at 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",
"Before we begin, we need to ensure that we have all the necessary tools. We will install PyBOP from its development branch and upgrade some dependencies:"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"execution": {
"iopub.execute_input": "2024-04-14T18:57:35.623967Z",
"iopub.status.busy": "2024-04-14T18:57:35.623399Z",
"iopub.status.idle": "2024-04-14T18:57:41.585471Z",
"shell.execute_reply": "2024-04-14T18:57:41.584895Z"
},
"id": "X87NUGPW04py",
"outputId": "0d785b07-7cff-4aeb-e60a-4ff5a669afbf"
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Note: you may need to restart the kernel to use updated packages.\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"Note: you may need to restart the kernel to use updated packages.\n"
]
}
],
"source": [
"%pip install --upgrade pip ipywidgets pybamm -q\n",
"%pip install pybop -q"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "jAvD5fk104p0"
},
"source": [
"Next, we import the added packages plus any additional dependencies,"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"execution": {
"iopub.execute_input": "2024-04-14T18:57:41.587953Z",
"iopub.status.busy": "2024-04-14T18:57:41.587606Z",
"iopub.status.idle": "2024-04-14T18:57:46.230723Z",
"shell.execute_reply": "2024-04-14T18:57:46.230142Z"
},
"id": "SQdt4brD04p1"
},
"outputs": [],
"source": [
"import pybop"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "X8-tubYY04p_"
},
"source": [
"## Optimising the Parameters"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "PQqhvSZN04p_"
},
"source": [
"First, we define the model to be used for the parameter optimisation,"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"execution": {
"iopub.execute_input": "2024-04-14T18:57:46.244423Z",
"iopub.status.busy": "2024-04-14T18:57:46.243274Z",
"iopub.status.idle": "2024-04-14T18:57:46.344865Z",
"shell.execute_reply": "2024-04-14T18:57:46.344504Z"
},
"id": "zuvGHWID04p_"
},
"outputs": [],
"source": [
"parameter_set = pybop.ParameterSet.pybamm(\"Chen2020\")\n",
"model = pybop.lithium_ion.SPMe(parameter_set=parameter_set)"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "ffS3CF_704qA"
},
"source": [
"Next, we define the model parameters for optimisation. Furthermore, PyBOP provides functionality to define a prior for the parameters. The initial parameters values used in the optimisation will be randomly drawn from the prior distribution."
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"execution": {
"iopub.execute_input": "2024-04-14T18:57:46.346645Z",
"iopub.status.busy": "2024-04-14T18:57:46.346525Z",
"iopub.status.idle": "2024-04-14T18:57:46.348677Z",
"shell.execute_reply": "2024-04-14T18:57:46.348288Z"
},
"id": "WPCybXIJ04qA"
},
"outputs": [],
"source": [
"parameters = [\n",
" pybop.Parameter(\n",
" \"Positive electrode thickness [m]\",\n",
" prior=pybop.Gaussian(7.56e-05, 0.05e-05),\n",
" bounds=[65e-06, 10e-05],\n",
" ),\n",
" pybop.Parameter(\n",
" \"Positive particle radius [m]\",\n",
" prior=pybop.Gaussian(5.22e-06, 0.05e-06),\n",
" bounds=[2e-06, 9e-06],\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": 5,
"metadata": {
"execution": {
"iopub.execute_input": "2024-04-14T18:57:46.349880Z",
"iopub.status.busy": "2024-04-14T18:57:46.349789Z",
"iopub.status.idle": "2024-04-14T18:57:46.351626Z",
"shell.execute_reply": "2024-04-14T18:57:46.351281Z"
}
},
"outputs": [],
"source": [
"experiment = pybop.Experiment(\n",
" [\"Discharge at 1C until 2.5 V (5 seconds period)\"],\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "n4OHa-aF04qA"
},
"source": [
"We can now define the output signal, the problem (which combines the model with the dataset) and construct a cost function which in this example is the `GravimetricEnergyDensity()` used to maximise the gravimetric energy density of the cell."
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"execution": {
"iopub.execute_input": "2024-04-14T18:57:46.353083Z",
"iopub.status.busy": "2024-04-14T18:57:46.352995Z",
"iopub.status.idle": "2024-04-14T18:57:46.892665Z",
"shell.execute_reply": "2024-04-14T18:57:46.892318Z"
},
"id": "etMzRtx404qA"
},
"outputs": [],
"source": [
"problem = pybop.DesignProblem(model, parameters, experiment, init_soc=1.0)\n",
"cost = pybop.GravimetricEnergyDensity(problem)"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "eQiGurUV04qB"
},
"source": [
"Let's construct PyBOP's optimisation class. This class provides the methods needed to fit the forward model. 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 to 5 for this example."
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"execution": {
"iopub.execute_input": "2024-04-14T18:57:46.894578Z",
"iopub.status.busy": "2024-04-14T18:57:46.894454Z",
"iopub.status.idle": "2024-04-14T18:57:46.896416Z",
"shell.execute_reply": "2024-04-14T18:57:46.896162Z"
},
"id": "N3FtAhrT04qB"
},
"outputs": [],
"source": [
"optim = pybop.Optimisation(cost, optimiser=pybop.PSO, verbose=True)\n",
"optim.set_max_iterations(15)"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "caprp-bV04qB"
},
"source": [
"Finally, we run the optimisation and return the values obtained,"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"execution": {
"iopub.execute_input": "2024-04-14T18:57:46.897807Z",
"iopub.status.busy": "2024-04-14T18:57:46.897703Z",
"iopub.status.idle": "2024-04-14T18:58:05.954495Z",
"shell.execute_reply": "2024-04-14T18:58:05.953904Z"
},
"id": "-9OVt0EQ04qB"
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Halt: Maximum number of iterations (15) reached.\n",
"Estimated parameters: [6.50259261e-05 2.18540132e-06]\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"Initial gravimetric energy density: 386.97 Wh.kg-1\n",
"Optimised gravimetric energy density: 411.23 Wh.kg-1\n"
]
}
],
"source": [
"x, final_cost = optim.run()\n",
"print(\"Estimated parameters:\", x)\n",
"print(f\"Initial gravimetric energy density: {-cost(cost.x0):.2f} Wh.kg-1\")\n",
"print(f\"Optimised gravimetric energy density: {-final_cost:.2f} Wh.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."
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "-cWCOiqR04qC"
},
"source": [
"### Comparing System Response\n",
"\n",
"We can quickly plot the system's response using the estimated parameters compared to the initial parameters:\n"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {
"execution": {
"iopub.execute_input": "2024-04-14T18:58:05.957518Z",
"iopub.status.busy": "2024-04-14T18:58:05.957344Z",
"iopub.status.idle": "2024-04-14T18:58:06.874737Z",
"shell.execute_reply": "2024-04-14T18:58:06.874105Z"
},
"id": "ZVfozY0A04qC"
},
"outputs": [
{
"data": {
"image/svg+xml": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"if cost.update_capacity:\n",
" problem._model.approximate_capacity(x)\n",
"pybop.quick_plot(problem, parameter_values=x, title=\"Optimised Comparison\");"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "ntIvAJmA04qD"
},
"source": [
"### Cost Landscape\n",
"\n",
"Finally, we can visualise the cost landscape and the path taken by the optimiser:\n"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 467
},
"execution": {
"iopub.execute_input": "2024-04-14T18:58:06.877016Z",
"iopub.status.busy": "2024-04-14T18:58:06.876676Z",
"iopub.status.idle": "2024-04-14T18:58:16.428669Z",
"shell.execute_reply": "2024-04-14T18:58:16.428218Z"
},
"id": "tJUJ80Ve04qD",
"outputId": "855fbaa2-1e09-4935-eb1a-8caf7f99eb75"
},
"outputs": [
{
"data": {
"image/svg+xml": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"if len(x) == 2:\n",
" pybop.plot2d(optim, steps=6)"
]
}
],
"metadata": {
"colab": {
"provenance": []
},
"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",
"version": "3.12.2"
},
"widgets": {
"application/vnd.jupyter.widget-state+json": {
"06f2374f91c8455bb63252092512f2ed": {
"model_module": "@jupyter-widgets/base",
"model_module_version": "2.0.0",
"model_name": "LayoutModel",
"state": {
"_model_module": "@jupyter-widgets/base",
"_model_module_version": "2.0.0",
"_model_name": "LayoutModel",
"_view_count": null,
"_view_module": "@jupyter-widgets/base",
"_view_module_version": "2.0.0",
"_view_name": "LayoutView",
"align_content": null,
"align_items": null,
"align_self": null,
"border_bottom": null,
"border_left": null,
"border_right": null,
"border_top": null,
"bottom": null,
"display": null,
"flex": null,
"flex_flow": null,
"grid_area": null,
"grid_auto_columns": null,
"grid_auto_flow": null,
"grid_auto_rows": null,
"grid_column": null,
"grid_gap": null,
"grid_row": null,
"grid_template_areas": null,
"grid_template_columns": null,
"grid_template_rows": null,
"height": null,
"justify_content": null,
"justify_items": null,
"left": null,
"margin": null,
"max_height": null,
"max_width": null,
"min_height": null,
"min_width": null,
"object_fit": null,
"object_position": null,
"order": null,
"overflow": null,
"padding": null,
"right": null,
"top": null,
"visibility": null,
"width": null
}
},
"423bffea3a1c42b49a9ad71218e5811b": {
"model_module": "@jupyter-widgets/base",
"model_module_version": "2.0.0",
"model_name": "LayoutModel",
"state": {
"_model_module": "@jupyter-widgets/base",
"_model_module_version": "2.0.0",
"_model_name": "LayoutModel",
"_view_count": null,
"_view_module": "@jupyter-widgets/base",
"_view_module_version": "2.0.0",
"_view_name": "LayoutView",
"align_content": null,
"align_items": null,
"align_self": null,
"border_bottom": null,
"border_left": null,
"border_right": null,
"border_top": null,
"bottom": null,
"display": null,
"flex": null,
"flex_flow": null,
"grid_area": null,
"grid_auto_columns": null,
"grid_auto_flow": null,
"grid_auto_rows": null,
"grid_column": null,
"grid_gap": null,
"grid_row": null,
"grid_template_areas": null,
"grid_template_columns": null,
"grid_template_rows": null,
"height": null,
"justify_content": null,
"justify_items": null,
"left": null,
"margin": null,
"max_height": null,
"max_width": null,
"min_height": null,
"min_width": null,
"object_fit": null,
"object_position": null,
"order": null,
"overflow": null,
"padding": null,
"right": null,
"top": null,
"visibility": null,
"width": null
}
},
"56ff19291e464d63b23e63b8e2ac9ea3": {
"model_module": "@jupyter-widgets/controls",
"model_module_version": "2.0.0",
"model_name": "SliderStyleModel",
"state": {
"_model_module": "@jupyter-widgets/controls",
"_model_module_version": "2.0.0",
"_model_name": "SliderStyleModel",
"_view_count": null,
"_view_module": "@jupyter-widgets/base",
"_view_module_version": "2.0.0",
"_view_name": "StyleView",
"description_width": "",
"handle_color": null
}
},
"646a8670cb204a31bb56bc2380898093": {
"model_module": "@jupyter-widgets/base",
"model_module_version": "2.0.0",
"model_name": "LayoutModel",
"state": {
"_model_module": "@jupyter-widgets/base",
"_model_module_version": "2.0.0",
"_model_name": "LayoutModel",
"_view_count": null,
"_view_module": "@jupyter-widgets/base",
"_view_module_version": "2.0.0",
"_view_name": "LayoutView",
"align_content": null,
"align_items": null,
"align_self": null,
"border_bottom": null,
"border_left": null,
"border_right": null,
"border_top": null,
"bottom": null,
"display": null,
"flex": null,
"flex_flow": null,
"grid_area": null,
"grid_auto_columns": null,
"grid_auto_flow": null,
"grid_auto_rows": null,
"grid_column": null,
"grid_gap": null,
"grid_row": null,
"grid_template_areas": null,
"grid_template_columns": null,
"grid_template_rows": null,
"height": null,
"justify_content": null,
"justify_items": null,
"left": null,
"margin": null,
"max_height": null,
"max_width": null,
"min_height": null,
"min_width": null,
"object_fit": null,
"object_position": null,
"order": null,
"overflow": null,
"padding": null,
"right": null,
"top": null,
"visibility": null,
"width": null
}
},
"7d46516469314b88be3500e2afcafcf6": {
"model_module": "@jupyter-widgets/output",
"model_module_version": "1.0.0",
"model_name": "OutputModel",
"state": {
"_dom_classes": [],
"_model_module": "@jupyter-widgets/output",
"_model_module_version": "1.0.0",
"_model_name": "OutputModel",
"_view_count": null,
"_view_module": "@jupyter-widgets/output",
"_view_module_version": "1.0.0",
"_view_name": "OutputView",
"layout": "IPY_MODEL_646a8670cb204a31bb56bc2380898093",
"msg_id": "",
"outputs": [],
"tabbable": null,
"tooltip": null
}
},
"8d003c14da5f4fa68284b28c15cee6e6": {
"model_module": "@jupyter-widgets/controls",
"model_module_version": "2.0.0",
"model_name": "VBoxModel",
"state": {
"_dom_classes": [
"widget-interact"
],
"_model_module": "@jupyter-widgets/controls",
"_model_module_version": "2.0.0",
"_model_name": "VBoxModel",
"_view_count": null,
"_view_module": "@jupyter-widgets/controls",
"_view_module_version": "2.0.0",
"_view_name": "VBoxView",
"box_style": "",
"children": [
"IPY_MODEL_aef2fa7adcc14ad0854b73d5910ae3b4",
"IPY_MODEL_7d46516469314b88be3500e2afcafcf6"
],
"layout": "IPY_MODEL_423bffea3a1c42b49a9ad71218e5811b",
"tabbable": null,
"tooltip": null
}
},
"aef2fa7adcc14ad0854b73d5910ae3b4": {
"model_module": "@jupyter-widgets/controls",
"model_module_version": "2.0.0",
"model_name": "FloatSliderModel",
"state": {
"_dom_classes": [],
"_model_module": "@jupyter-widgets/controls",
"_model_module_version": "2.0.0",
"_model_name": "FloatSliderModel",
"_view_count": null,
"_view_module": "@jupyter-widgets/controls",
"_view_module_version": "2.0.0",
"_view_name": "FloatSliderView",
"behavior": "drag-tap",
"continuous_update": true,
"description": "t",
"description_allow_html": false,
"disabled": false,
"layout": "IPY_MODEL_06f2374f91c8455bb63252092512f2ed",
"max": 1.1333333333333333,
"min": 0,
"orientation": "horizontal",
"readout": true,
"readout_format": ".2f",
"step": 0.011333333333333332,
"style": "IPY_MODEL_56ff19291e464d63b23e63b8e2ac9ea3",
"tabbable": null,
"tooltip": null,
"value": 0
}
}
}
}
},
"nbformat": 4,
"nbformat_minor": 0
}