{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"id": "expmkveO04pw"
},
"source": [
"## Parameter Estimation with AdamW in PyBOP\n",
"\n",
"In this notebook, we demonstrate an example of parameter estimation for a single-particle model using the AdamW optimiser [1][2]. The AdamW optimiser is an algorithm for gradient-based optimisation, combining the advantages of the Adaptive Gradient Algorithm (AdaGrad) and Root Mean Square Propagation (RMSProp).\n",
"\n",
"[[1]: Adam: A Method for Stochastic Optimization](https://arxiv.org/abs/1412.6980) \n",
"\n",
"[[2]: Decoupled Weight Decay Regularization](https://doi.org/10.48550/arXiv.1711.05101)\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 and upgrade dependencies:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "X87NUGPW04py",
"outputId": "0d785b07-7cff-4aeb-e60a-4ff5a669afbf"
},
"outputs": [],
"source": [
"%pip install --upgrade pip ipywidgets -q\n",
"%pip install pybop -q"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "jAvD5fk104p0"
},
"source": [
"### Importing Libraries\n",
"\n",
"With the environment set up, we can now import PyBOP alongside other libraries we will need:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "SQdt4brD04p1"
},
"outputs": [],
"source": [
"import numpy as np\n",
"\n",
"import pybop"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Let's fix the random seed in order to generate consistent output during development, although this does not need to be done in practice."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"np.random.seed(8)"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "5XU-dMtU04p2"
},
"source": [
"### Generate Synthetic Data\n",
"\n",
"To demonstrate parameter estimation, we first need some data. We will generate synthetic data using the PyBOP forward model, which requires defining a parameter set and the model itself.\n",
"\n",
"#### Defining Parameters and Model\n",
"\n",
"We start by creating an example parameter set and then instantiate the single-particle model (SPM):"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"parameter_set = pybop.ParameterSet.pybamm(\"Chen2020\")\n",
"model = pybop.lithium_ion.SPM(parameter_set=parameter_set)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Simulating Forward Model\n",
"\n",
"We can then simulate the model using the `predict` method, with a default constant current to generate voltage data."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "sBasxv8U04p3"
},
"outputs": [],
"source": [
"t_eval = np.arange(0, 900, 2)\n",
"values = model.predict(t_eval=t_eval)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Adding Noise to Voltage Data\n",
"\n",
"To make the parameter estimation more realistic, we add Gaussian noise to the data."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"sigma = 0.001\n",
"corrupt_values = values[\"Voltage [V]\"].data + np.random.normal(0, sigma, len(t_eval))"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "X8-tubYY04p_"
},
"source": [
"## Identify the Parameters"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "PQqhvSZN04p_"
},
"source": [
"We will now set up the parameter estimation process by defining the datasets for optimisation and selecting the model parameters we wish to estimate."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Creating Optimisation Dataset\n",
"\n",
"The dataset for optimisation is composed of time, current, and the noisy voltage data:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "zuvGHWID04p_"
},
"outputs": [],
"source": [
"dataset = pybop.Dataset(\n",
" {\n",
" \"Time [s]\": t_eval,\n",
" \"Current function [A]\": values[\"Current [A]\"].data,\n",
" \"Voltage [V]\": corrupt_values,\n",
" }\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "ffS3CF_704qA"
},
"source": [
"### Defining Parameters to Estimate\n",
"\n",
"We select the parameters for estimation and set up their prior distributions and bounds:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "WPCybXIJ04qA"
},
"outputs": [],
"source": [
"parameters = [\n",
" pybop.Parameter(\n",
" \"Negative electrode active material volume fraction\",\n",
" prior=pybop.Gaussian(0.6, 0.02),\n",
" bounds=[0.5, 0.8],\n",
" ),\n",
" pybop.Parameter(\n",
" \"Positive electrode active material volume fraction\",\n",
" prior=pybop.Gaussian(0.48, 0.02),\n",
" bounds=[0.4, 0.7],\n",
" ),\n",
"]"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "n4OHa-aF04qA"
},
"source": [
"### Setting up the Optimisation Problem\n",
"\n",
"With the datasets and parameters defined, we can set up the optimisation problem, its cost function, and the optimiser."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "etMzRtx404qA"
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"NOTE: Boundaries ignored by AdamW\n"
]
}
],
"source": [
"problem = pybop.FittingProblem(model, parameters, dataset)\n",
"cost = pybop.SumSquaredError(problem)\n",
"optim = pybop.Optimisation(cost, optimiser=pybop.AdamW)\n",
"optim.set_max_unchanged_iterations(40)\n",
"optim.set_max_iterations(150)"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "caprp-bV04qB"
},
"source": [
"### Running the Optimisation\n",
"\n",
"We proceed to run the AdamW optimisation algorithm to estimate the parameters:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "-9OVt0EQ04qB"
},
"outputs": [],
"source": [
"x, final_cost = optim.run()"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "-4pZsDmS04qC"
},
"source": [
"### Viewing the Estimated Parameters\n",
"\n",
"After the optimisation, we can examine the estimated parameter values:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "Hgz8SV4i04qC",
"outputId": "e1e42ae7-5075-4c47-dd68-1b22ecc170f6"
},
"outputs": [
{
"data": {
"text/plain": [
"array([0.76335438, 0.66225687])"
]
},
"execution_count": null,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"x # This will output the estimated parameters"
]
},
{
"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 target:"
]
},
{
"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": {
"image/svg+xml": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"pybop.quick_plot(problem, problem_inputs=x, title=\"Optimised Comparison\");"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Convergence and Parameter Trajectories\n",
"\n",
"To assess the optimisation process, we can plot the convergence of the cost function and the trajectories of the parameters:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "N5XYkevi04qD"
},
"outputs": [
{
"data": {
"image/svg+xml": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"image/svg+xml": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"pybop.plot_convergence(optim)\n",
"pybop.plot_parameters(optim);"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Cost Landscape\n",
"\n",
"Finally, we can visualise the cost landscape and the path taken by the optimiser:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [
{
"data": {
"image/svg+xml": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"image/svg+xml": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"# Plot the cost landscape\n",
"pybop.plot2d(cost, steps=15)\n",
"# Plot the cost landscape with optimisation path and updated bounds\n",
"bounds = np.asarray([[0.6, 0.9], [0.5, 0.8]])\n",
"pybop.plot2d(optim, bounds=bounds, steps=15);"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Conclusion\n",
"\n",
"This notebook illustrates how to perform parameter estimation using AdamW in PyBOP, providing insights into the optimisation process through various visualisations."
]
}
],
"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.11.9"
}
},
"nbformat": 4,
"nbformat_minor": 4
}