Skip to content

Commit

Permalink
Merge pull request #117 from pybop-team/116-add-pints-optimisers
Browse files Browse the repository at this point in the history
Adds Pints' optimisers
  • Loading branch information
BradyPlanden authored Nov 23, 2023
2 parents 83ec94b + eee379d commit 5225cfc
Show file tree
Hide file tree
Showing 15 changed files with 507 additions and 110 deletions.
10 changes: 6 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
# [Unreleased](https://github.com/pybop-team/PyBOP)

- [#116](https://github.com/pybop-team/PyBOP/issues/116) - Adds PSO, SNES, XNES, ADAM, and IPropMin optimisers to PintsOptimisers() class

# [v23.11](https://github.com/pybop-team/PyBOP/releases/tag/v23.11)
- Initial release
- Adds Pints, NLOpt, and SciPy optimisers
- Adds SumofSquareError and RootMeanSquareError cost functions
- Adds Parameter and dataset classes
- Initial release
- Adds Pints, NLOpt, and SciPy optimisers
- Adds SumofSquareError and RootMeanSquareError cost functions
- Adds Parameter and dataset classes
22 changes: 17 additions & 5 deletions conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,29 @@ def pytest_addoption(parser):
parser.addoption(
"--unit", action="store_true", default=False, help="run unit tests"
)
parser.addoption(
"--examples", action="store_true", default=False, help="run examples tests"
)


def pytest_configure(config):
config.addinivalue_line("markers", "unit: mark test as a unit test")
config.addinivalue_line("markers", "examples: mark test as an example")


def pytest_collection_modifyitems(config, items):
def skip_marker(marker_name, reason):
skip = pytest.mark.skip(reason=reason)
for item in items:
if marker_name in item.keywords:
item.add_marker(skip)

if config.getoption("--unit"):
# --unit given in cli: do not skip unit tests
skip_marker("examples", "need --examples option to run")
return
skip_unit = pytest.mark.skip(reason="need --unit option to run")
for item in items:
if "unit" in item.keywords:
item.add_marker(skip_unit)

if config.getoption("--examples"):
skip_marker("unit", "need --unit option to run")
return

skip_marker("unit", "need --unit option to run")
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import matplotlib.pyplot as plt

parameter_set = pybop.ParameterSet("pybamm", "Chen2020")
model = pybop.lithium_ion.SPMe(parameter_set=parameter_set)
model = pybop.lithium_ion.SPM(parameter_set=parameter_set)

# Fitting parameters
parameters = [
Expand Down
55 changes: 55 additions & 0 deletions examples/scripts/spm_IRPropMin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import pybop
import numpy as np
import matplotlib.pyplot as plt

parameter_set = pybop.ParameterSet("pybamm", "Chen2020")
model = pybop.lithium_ion.SPM(parameter_set=parameter_set)

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

sigma = 0.001
t_eval = np.arange(0, 900, 2)
values = model.predict(t_eval=t_eval)
CorruptValues = values["Terminal voltage [V]"].data + np.random.normal(
0, sigma, len(t_eval)
)

dataset = [
pybop.Dataset("Time [s]", t_eval),
pybop.Dataset("Current function [A]", values["Current [A]"].data),
pybop.Dataset("Terminal voltage [V]", CorruptValues),
]

# Generate problem, cost function, and optimisation class
problem = pybop.Problem(model, parameters, dataset)
cost = pybop.SumSquaredError(problem)
optim = pybop.Optimisation(cost, optimiser=pybop.IRPropMin)
optim.set_max_iterations(100)

x, final_cost = optim.run()
print("Estimated parameters:", x)

# Show the generated data
simulated_values = problem.evaluate(x)

plt.figure(dpi=100)
plt.xlabel("Time", fontsize=12)
plt.ylabel("Values", fontsize=12)
plt.plot(t_eval, CorruptValues, label="Measured")
plt.fill_between(t_eval, simulated_values - sigma, simulated_values + sigma, alpha=0.2)
plt.plot(t_eval, simulated_values, label="Simulated")
plt.legend(bbox_to_anchor=(0.6, 1), loc="upper left", fontsize=12)
plt.tick_params(axis="both", labelsize=12)
plt.show()
55 changes: 55 additions & 0 deletions examples/scripts/spm_SNES.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import pybop
import numpy as np
import matplotlib.pyplot as plt

parameter_set = pybop.ParameterSet("pybamm", "Chen2020")
model = pybop.lithium_ion.SPM(parameter_set=parameter_set)

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

sigma = 0.001
t_eval = np.arange(0, 900, 2)
values = model.predict(t_eval=t_eval)
CorruptValues = values["Terminal voltage [V]"].data + np.random.normal(
0, sigma, len(t_eval)
)

dataset = [
pybop.Dataset("Time [s]", t_eval),
pybop.Dataset("Current function [A]", values["Current [A]"].data),
pybop.Dataset("Terminal voltage [V]", CorruptValues),
]

# Generate problem, cost function, and optimisation class
problem = pybop.Problem(model, parameters, dataset)
cost = pybop.SumSquaredError(problem)
optim = pybop.Optimisation(cost, optimiser=pybop.SNES)
optim.set_max_iterations(100)

x, final_cost = optim.run()
print("Estimated parameters:", x)

# Show the generated data
simulated_values = problem.evaluate(x)

plt.figure(dpi=100)
plt.xlabel("Time", fontsize=12)
plt.ylabel("Values", fontsize=12)
plt.plot(t_eval, CorruptValues, label="Measured")
plt.fill_between(t_eval, simulated_values - sigma, simulated_values + sigma, alpha=0.2)
plt.plot(t_eval, simulated_values, label="Simulated")
plt.legend(bbox_to_anchor=(0.6, 1), loc="upper left", fontsize=12)
plt.tick_params(axis="both", labelsize=12)
plt.show()
55 changes: 55 additions & 0 deletions examples/scripts/spm_XNES.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import pybop
import numpy as np
import matplotlib.pyplot as plt

parameter_set = pybop.ParameterSet("pybamm", "Chen2020")
model = pybop.lithium_ion.SPM(parameter_set=parameter_set)

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

sigma = 0.001
t_eval = np.arange(0, 900, 2)
values = model.predict(t_eval=t_eval)
CorruptValues = values["Terminal voltage [V]"].data + np.random.normal(
0, sigma, len(t_eval)
)

dataset = [
pybop.Dataset("Time [s]", t_eval),
pybop.Dataset("Current function [A]", values["Current [A]"].data),
pybop.Dataset("Terminal voltage [V]", CorruptValues),
]

# Generate problem, cost function, and optimisation class
problem = pybop.Problem(model, parameters, dataset)
cost = pybop.SumSquaredError(problem)
optim = pybop.Optimisation(cost, optimiser=pybop.XNES)
optim.set_max_iterations(100)

x, final_cost = optim.run()
print("Estimated parameters:", x)

# Show the generated data
simulated_values = problem.evaluate(x)

plt.figure(dpi=100)
plt.xlabel("Time", fontsize=12)
plt.ylabel("Values", fontsize=12)
plt.plot(t_eval, CorruptValues, label="Measured")
plt.fill_between(t_eval, simulated_values - sigma, simulated_values + sigma, alpha=0.2)
plt.plot(t_eval, simulated_values, label="Simulated")
plt.legend(bbox_to_anchor=(0.6, 1), loc="upper left", fontsize=12)
plt.tick_params(axis="both", labelsize=12)
plt.show()
59 changes: 59 additions & 0 deletions examples/scripts/spm_adam.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import pybop
import numpy as np
import matplotlib.pyplot as plt

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

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

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

# Dataset definition
dataset = [
pybop.Dataset("Time [s]", t_eval),
pybop.Dataset("Current function [A]", values["Current [A]"].data),
pybop.Dataset("Terminal voltage [V]", corrupt_values),
]

# Generate problem, cost function, and optimisation class
problem = pybop.Problem(model, parameters, dataset)
cost = pybop.SumSquaredError(problem)
optim = pybop.Optimisation(cost, optimiser=pybop.Adam)
optim.set_max_iterations(100)

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

# Show the generated data
simulated_values = problem.evaluate(x)

plt.figure(dpi=100)
plt.xlabel("Time", fontsize=12)
plt.ylabel("Values", fontsize=12)
plt.plot(t_eval, corrupt_values, label="Measured")
plt.fill_between(t_eval, simulated_values - sigma, simulated_values + sigma, alpha=0.2)
plt.plot(t_eval, simulated_values, label="Simulated")
plt.legend(bbox_to_anchor=(0.6, 1), loc="upper left", fontsize=12)
plt.tick_params(axis="both", labelsize=12)
plt.show()
55 changes: 55 additions & 0 deletions examples/scripts/spm_pso.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import pybop
import numpy as np
import matplotlib.pyplot as plt

parameter_set = pybop.ParameterSet("pybamm", "Chen2020")
model = pybop.lithium_ion.SPM(parameter_set=parameter_set)

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

sigma = 0.001
t_eval = np.arange(0, 900, 2)
values = model.predict(t_eval=t_eval)
CorruptValues = values["Terminal voltage [V]"].data + np.random.normal(
0, sigma, len(t_eval)
)

dataset = [
pybop.Dataset("Time [s]", t_eval),
pybop.Dataset("Current function [A]", values["Current [A]"].data),
pybop.Dataset("Terminal voltage [V]", CorruptValues),
]

# Generate problem, cost function, and optimisation class
problem = pybop.Problem(model, parameters, dataset)
cost = pybop.SumSquaredError(problem)
optim = pybop.Optimisation(cost, optimiser=pybop.PSO)
optim.set_max_iterations(100)

x, final_cost = optim.run()
print("Estimated parameters:", x)

# Show the generated data
simulated_values = problem.evaluate(x)

plt.figure(dpi=100)
plt.xlabel("Time", fontsize=12)
plt.ylabel("Values", fontsize=12)
plt.plot(t_eval, CorruptValues, label="Measured")
plt.fill_between(t_eval, simulated_values - sigma, simulated_values + sigma, alpha=0.2)
plt.plot(t_eval, simulated_values, label="Simulated")
plt.legend(bbox_to_anchor=(0.6, 1), loc="upper left", fontsize=12)
plt.tick_params(axis="both", labelsize=12)
plt.show()
12 changes: 10 additions & 2 deletions noxfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,22 @@
def unit(session):
session.run_always("pip", "install", "-e", ".")
session.install("pytest")
session.run("pytest", "--unit", "-v")
session.run("pytest", "--unit", "-v", "--showlocals")


@nox.session
def coverage(session):
session.run_always("pip", "install", "-e", ".")
session.install("pytest-cov")
session.run("pytest", "--unit", "-v", "--cov", "--cov-report=xml")
session.run(
"pytest",
"--unit",
"--examples",
"-v",
"--cov",
"--cov-report=xml",
"--showlocals",
)


@nox.session
Expand Down
10 changes: 9 additions & 1 deletion pybop/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,15 @@
from .optimisers.base_optimiser import BaseOptimiser
from .optimisers.nlopt_optimize import NLoptOptimize
from .optimisers.scipy_minimize import SciPyMinimize
from .optimisers.pints_optimisers import GradientDescent, CMAES
from .optimisers.pints_optimisers import (
GradientDescent,
Adam,
CMAES,
IRPropMin,
PSO,
SNES,
XNES,
)

#
# Parameter classes
Expand Down
Loading

0 comments on commit 5225cfc

Please sign in to comment.