Skip to content

Commit

Permalink
Merge branch 'develop' into 238b-multi-fitting
Browse files Browse the repository at this point in the history
  • Loading branch information
NicolaCourtier authored Jul 23, 2024
2 parents 6654b1b + e6788e4 commit e51decc
Show file tree
Hide file tree
Showing 32 changed files with 1,462 additions and 225 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -314,3 +314,6 @@ $RECYCLE.BIN/
# Airspeed Velocity
*.asv/
results/

# Pycharm
*.idea/
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ ci:

repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: "v0.5.1"
rev: "v0.5.4"
hooks:
- id: ruff
args: [--fix, --show-fixes]
Expand Down
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
## Features

- [#364](https://github.com/pybop-team/PyBOP/pull/364) - Adds the MultiFittingProblem class and the multi_fitting example script.
- [#327](https://github.com/pybop-team/PyBOP/issues/327) - Adds the `WeightedCost` subclass, defines when to evaluate a problem and adds the `spm_weighted_cost` example script.
- [#393](https://github.com/pybop-team/PyBOP/pull/383) - Adds Minkowski and SumofPower cost classes, with an example and corresponding tests.
- [#403](https://github.com/pybop-team/PyBOP/pull/403/) - Adds lychee link checking action.
- [#313](https://github.com/pybop-team/PyBOP/pull/313/) - Fixes for PyBaMM v24.5, drops support for PyBaMM v23.9, v24.1

## Bug Fixes

Expand Down
609 changes: 609 additions & 0 deletions examples/notebooks/comparing_cost_functions.ipynb

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions examples/scripts/gitt.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,10 @@
model = pybop.lithium_ion.WeppnerHuggins(parameter_set=parameter_set)

parameters = pybop.Parameter(
"Positive electrode diffusivity [m2.s-1]",
"Positive particle diffusivity [m2.s-1]",
prior=pybop.Gaussian(5e-14, 1e-13),
bounds=[1e-16, 1e-11],
true_value=parameter_set["Positive electrode diffusivity [m2.s-1]"],
true_value=parameter_set["Positive particle diffusivity [m2.s-1]"],
)

problem = pybop.FittingProblem(
Expand Down
7 changes: 2 additions & 5 deletions examples/scripts/parameters/example_BPX.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"Header": {
"BPX": 0.1,
"Title": "Parameterisation example of an LFP|graphite 2 Ah cylindrical 18650 cell.",
"BPX": 0.4,
"Title": "Parameterisation example of an LFP|graphite 2 Ah cylindrical 18650 cell. File downloaded on 19/3/24 from https://github.com/FaradayInstitution/BPX/blob/main/examples/lfp_18650_cell_BPX.json",
"Description": "LFP|graphite 2 Ah cylindrical 18650 cell. Parameterisation by About:Energy Limited (aboutenergy.io), December 2022, based on cell cycling data, and electrode data gathered after cell teardown. Electrolyte properties from Nyman et al. 2008 (doi:10.1016/j.electacta.2008.04.023). Negative electrode entropic coefficient data are from O'Regan et al. 2022 (doi:10.1016/j.electacta.2022.140700). Positive electrode entropic coefficient data are from Gerver and Meyers 2011 (doi:10.1149/1.3591799). Other thermal properties are estimated.",
"Model": "DFN"
},
Expand Down Expand Up @@ -70,9 +70,6 @@
"Thickness [m]": 2e-05,
"Porosity": 0.47,
"Transport efficiency": 0.3222
},
"User-defined": {
"Source:": "An example BPX json file downloaded on 19/3/24 from https://github.com/FaradayInstitution/BPX/blob/main/examples/lfp_18650_cell_BPX.json"
}
}
}
2 changes: 1 addition & 1 deletion examples/scripts/spm_AdamW.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def noise(sigma):
problem = pybop.FittingProblem(
model, parameters, dataset, signal=signal, init_soc=init_soc
)
cost = pybop.RootMeanSquaredError(problem)
cost = pybop.Minkowski(problem, p=2)
optim = pybop.AdamW(
cost,
verbose=True,
Expand Down
45 changes: 31 additions & 14 deletions examples/scripts/spm_MAP.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,16 @@

import pybop

# Set variables
sigma = 0.002
init_soc = 0.7

# Construct and update initial parameter values
parameter_set = pybop.ParameterSet.pybamm("Chen2020")
parameter_set.update(
{
"Negative electrode active material volume fraction": 0.63,
"Positive electrode active material volume fraction": 0.51,
"Negative electrode active material volume fraction": 0.43,
"Positive electrode active material volume fraction": 0.56,
}
)

Expand All @@ -18,38 +22,51 @@
parameters = pybop.Parameters(
pybop.Parameter(
"Negative electrode active material volume fraction",
prior=pybop.Gaussian(0.6, 0.05),
bounds=[0.5, 0.8],
prior=pybop.Uniform(0.3, 0.8),
bounds=[0.3, 0.8],
initial_value=0.653,
true_value=parameter_set["Negative electrode active material volume fraction"],
),
pybop.Parameter(
"Positive electrode active material volume fraction",
prior=pybop.Gaussian(0.48, 0.05),
prior=pybop.Uniform(0.3, 0.8),
bounds=[0.4, 0.7],
initial_value=0.657,
true_value=parameter_set["Positive electrode active material volume fraction"],
),
)

# Generate data
sigma = 0.005
t_eval = np.arange(0, 900, 3)
values = model.predict(t_eval=t_eval)
corrupt_values = values["Voltage [V]"].data + np.random.normal(0, sigma, len(t_eval))
# Generate data and corrupt it with noise
experiment = pybop.Experiment(
[
(
"Discharge at 0.5C for 3 minutes (4 second period)",
"Charge at 0.5C for 3 minutes (4 second period)",
),
]
)
values = model.predict(
init_soc=init_soc, experiment=experiment, inputs=parameters.true_value()
)
corrupt_values = values["Voltage [V]"].data + np.random.normal(
0, sigma, len(values["Voltage [V]"].data)
)

# Form dataset
dataset = pybop.Dataset(
{
"Time [s]": t_eval,
"Time [s]": values["Time [s]"].data,
"Current function [A]": values["Current [A]"].data,
"Voltage [V]": corrupt_values,
}
)

# Generate problem, cost function, and optimisation class
problem = pybop.FittingProblem(model, parameters, dataset)
problem = pybop.FittingProblem(model, parameters, dataset, init_soc=init_soc)
cost = pybop.MAP(problem, pybop.GaussianLogLikelihoodKnownSigma, sigma0=sigma)
optim = pybop.AdamW(
cost,
sigma0=0.05,
max_unchanged_iterations=20,
min_iterations=20,
max_iterations=100,
Expand All @@ -61,7 +78,7 @@
print("Estimated parameters:", x)

# Plot the timeseries output
pybop.quick_plot(problem, problem_inputs=x[0:2], title="Optimised Comparison")
pybop.quick_plot(problem, problem_inputs=x, title="Optimised Comparison")

# Plot convergence
pybop.plot_convergence(optim)
Expand All @@ -73,5 +90,5 @@
pybop.plot2d(cost, steps=15)

# Plot the cost landscape with optimisation path
bounds = np.asarray([[0.55, 0.77], [0.48, 0.68]])
bounds = np.asarray([[0.35, 0.7], [0.45, 0.625]])
pybop.plot2d(optim, bounds=bounds, steps=15)
2 changes: 1 addition & 1 deletion examples/scripts/spm_SNES.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@

# Generate problem, cost function, and optimisation class
problem = pybop.FittingProblem(model, parameters, dataset)
cost = pybop.SumSquaredError(problem)
cost = pybop.SumofPower(problem, p=2)
optim = pybop.SNES(cost, max_iterations=100)

x, final_cost = optim.run()
Expand Down
6 changes: 3 additions & 3 deletions examples/scripts/spm_UKF.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

# Make a prediction with measurement noise
sigma = 0.001
t_eval = np.arange(0, 300, 2)
t_eval = np.arange(0, 900, 0.5)
values = model.predict(t_eval=t_eval)
corrupt_values = values["Voltage [V]"].data + np.random.normal(0, sigma, len(t_eval))

Expand All @@ -42,8 +42,8 @@
signal = ["Voltage [V]"]
n_states = model.n_states
n_signals = len(signal)
covariance = np.diag([0] * 19 + [sigma**2] + [0] * 19 + [sigma**2])
process_noise = np.diag([0] * 19 + [1e-6] + [0] * 19 + [1e-6])
covariance = np.diag([0] * 20 + [sigma**2] + [0] * 20 + [sigma**2])
process_noise = np.diag([0] * 20 + [1e-6] + [0] * 20 + [1e-6])
measurement_noise = np.diag([sigma**2])
observer = pybop.UnscentedKalmanFilterObserver(
parameters,
Expand Down
61 changes: 61 additions & 0 deletions examples/scripts/spm_weighted_cost.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import numpy as np

import pybop

# Parameter set and model definition
parameter_set = pybop.ParameterSet.pybamm("Chen2020")
model = pybop.lithium_ion.SPM(parameter_set=parameter_set)

# Fitting parameters
parameters = pybop.Parameters(
pybop.Parameter(
"Negative electrode active material volume fraction",
prior=pybop.Gaussian(0.68, 0.05),
bounds=[0.5, 0.8],
true_value=parameter_set["Negative electrode active material volume fraction"],
),
pybop.Parameter(
"Positive electrode active material volume fraction",
prior=pybop.Gaussian(0.58, 0.05),
bounds=[0.4, 0.7],
true_value=parameter_set["Positive electrode active material volume fraction"],
),
)

# Generate data
sigma = 0.001
t_eval = np.arange(0, 900, 3)
values = model.predict(t_eval=t_eval)
corrupt_values = values["Voltage [V]"].data + np.random.normal(0, sigma, len(t_eval))

# Form dataset
dataset = pybop.Dataset(
{
"Time [s]": t_eval,
"Current function [A]": values["Current [A]"].data,
"Voltage [V]": corrupt_values,
}
)

# Generate problem, cost function, and optimisation class
problem = pybop.FittingProblem(model, parameters, dataset)
cost1 = pybop.SumSquaredError(problem)
cost2 = pybop.RootMeanSquaredError(problem)
weighted_cost = pybop.WeightedCost(cost1, cost2, weights=[1, 100])

for cost in [weighted_cost, cost1, cost2]:
optim = pybop.IRPropMin(cost, max_iterations=60)

# Run the optimisation
x, final_cost = optim.run()
print("True parameters:", parameters.true_value())
print("Estimated parameters:", x)

# Plot the timeseries output
pybop.quick_plot(problem, problem_inputs=x, title="Optimised Comparison")

# Plot convergence
pybop.plot_convergence(optim)

# Plot the cost landscape with optimisation path
pybop.plot2d(optim, steps=15)
3 changes: 3 additions & 0 deletions pybop/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@
from .costs.fitting_costs import (
RootMeanSquaredError,
SumSquaredError,
Minkowski,
SumofPower,
ObserverCost,
)
from .costs.design_costs import (
Expand All @@ -98,6 +100,7 @@
GaussianLogLikelihoodKnownSigma,
MAP,
)
from .costs._weighted_cost import WeightedCost

#
# Optimiser class
Expand Down
2 changes: 0 additions & 2 deletions pybop/_experiment.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,4 @@ def __init__(
period,
temperature,
termination,
drive_cycles,
cccv_handling,
)
Loading

0 comments on commit e51decc

Please sign in to comment.