Skip to content

Commit

Permalink
Merge branch 'develop' into 339-updating-capacities
Browse files Browse the repository at this point in the history
  • Loading branch information
NicolaCourtier authored Jul 4, 2024
2 parents fb59602 + b49ede4 commit 074501f
Show file tree
Hide file tree
Showing 32 changed files with 93 additions and 64 deletions.
5 changes: 5 additions & 0 deletions .github/workflows/periodic_benchmarks.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ jobs:
runs-on: [self-hosted, macOS, ARM64]
if: github.repository == 'pybop-team/PyBOP'
steps:
- name: Cleanup build folder
run: |
rm -rf ./* || true
rm -rf ./.??* || true
- uses: actions/checkout@v4

- name: Install python & create virtualenv
Expand Down
5 changes: 5 additions & 0 deletions .github/workflows/scheduled_tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,11 @@ jobs:
matrix: ${{fromJson(needs.filter_pybamm_matrix.outputs.filtered_pybop_matrix)}}

steps:
- name: Cleanup build folder
run: |
rm -rf ./* || true
rm -rf ./.??* || true
- uses: actions/checkout@v4
- name: Install python & create virtualenv
shell: bash
Expand Down
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.4.8"
rev: "v0.5.0"
hooks:
- id: ruff
args: [--fix, --show-fixes]
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## Features

- [#379](https://github.com/pybop-team/PyBOP/pull/379) - Adds model.simulateS1 to weekly benchmarks.
- [#174](https://github.com/pybop-team/PyBOP/issues/174) - Adds new logo and updates Readme for accessibility.
- [#316](https://github.com/pybop-team/PyBOP/pull/316) - Adds Adam with weight decay (AdamW) optimiser, adds depreciation warning for pints.Adam implementation.
- [#271](https://github.com/pybop-team/PyBOP/issues/271) - Aligns the output of the optimisers via a generalisation of Result class.
Expand All @@ -25,6 +26,9 @@
## Bug Fixes

- [#339](https://github.com/pybop-team/PyBOP/issues/339) - Updates the calculation of the cyclable lithium capacity in the spme_max_energy example.
- [#387](https://github.com/pybop-team/PyBOP/issues/387) - Adds keys to ParameterSet and updates ECM OCV check.
- [#380](https://github.com/pybop-team/PyBOP/pull/380) - Restore self._boundaries construction for `pybop.PSO`
- [#372](https://github.com/pybop-team/PyBOP/pull/372) - Converts `np.array` to `np.asarray` for Numpy v2.0 support.
- [#165](https://github.com/pybop-team/PyBOP/issues/165) - Stores the attempted and best parameter values and the best cost for each iteration in the log attribute of the optimiser and updates the associated plots.
- [#354](https://github.com/pybop-team/PyBOP/issues/354) - Fixes the calculation of the gradient in the `RootMeanSquaredError` cost.
- [#347](https://github.com/pybop-team/PyBOP/issues/347) - Resets options between MSMR tests to cope with a bug in PyBaMM v23.9 which is fixed in PyBaMM v24.1.
Expand Down
10 changes: 10 additions & 0 deletions benchmarks/benchmark_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,13 @@ def time_model_simulate(self, model, parameter_set):
parameter_set (str): The name of the parameter set being used.
"""
self.problem._model.simulate(inputs=self.inputs, t_eval=self.t_eval)

def time_model_simulateS1(self, model, parameter_set):
"""
Benchmark the simulateS1 method of the model.
Args:
model (pybop.Model): The model class being benchmarked.
parameter_set (str): The name of the parameter set being used.
"""
self.problem._model.simulateS1(inputs=self.inputs, t_eval=self.t_eval)
2 changes: 1 addition & 1 deletion examples/notebooks/multi_model_identification.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -3958,7 +3958,7 @@
}
],
"source": [
"bounds = np.array([[5.5e-05, 8e-05], [7.5e-05, 9e-05]])\n",
"bounds = np.asarray([[5.5e-05, 8e-05], [7.5e-05, 9e-05]])\n",
"for optim in optims:\n",
" pybop.plot2d(optim, bounds=bounds, steps=10, title=optim.cost.problem.model.name)"
]
Expand Down
2 changes: 1 addition & 1 deletion examples/notebooks/multi_optimiser_identification.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -925,7 +925,7 @@
],
"source": [
"# Plot the cost landscape with optimisation path and updated bounds\n",
"bounds = np.array([[0.5, 0.8], [0.55, 0.8]])\n",
"bounds = np.asarray([[0.5, 0.8], [0.55, 0.8]])\n",
"for optim in optims:\n",
" pybop.plot2d(optim, bounds=bounds, steps=10, title=optim.name())"
]
Expand Down
2 changes: 1 addition & 1 deletion examples/notebooks/optimiser_calibration.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -677,7 +677,7 @@
],
"source": [
"# Plot the cost landscape with optimisation path and updated bounds\n",
"bounds = np.array([[0.6, 0.9], [0.5, 0.8]])\n",
"bounds = np.asarray([[0.6, 0.9], [0.5, 0.8]])\n",
"for optim, sigma in zip(optims, sigmas):\n",
" pybop.plot2d(optim, bounds=bounds, steps=10, title=f\"Sigma: {sigma}\")"
]
Expand Down
2 changes: 1 addition & 1 deletion examples/notebooks/spm_AdamW.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -530,7 +530,7 @@
"# Plot the cost landscape\n",
"pybop.plot2d(cost, steps=15)\n",
"# Plot the cost landscape with optimisation path and updated bounds\n",
"bounds = np.array([[0.6, 0.9], [0.5, 0.8]])\n",
"bounds = np.asarray([[0.6, 0.9], [0.5, 0.8]])\n",
"pybop.plot2d(optim, bounds=bounds, steps=15);"
]
},
Expand Down
2 changes: 1 addition & 1 deletion examples/scripts/ecm_CMAES.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,5 +101,5 @@
pybop.plot2d(cost, steps=15)

# Plot the cost landscape with optimisation path and updated bounds
bounds = np.array([[1e-4, 1e-2], [1e-5, 1e-2]])
bounds = np.asarray([[1e-4, 1e-2], [1e-5, 1e-2]])
pybop.plot2d(optim, bounds=bounds, steps=15)
3 changes: 1 addition & 2 deletions examples/scripts/exp_UKF.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import numpy as np
import pybamm

import pybop
from examples.standalone.model import ExponentialDecay

# Parameter set and model definition
parameter_set = pybamm.ParameterValues({"k": "[input]", "y0": "[input]"})
parameter_set = {"k": "[input]", "y0": "[input]"}
model = ExponentialDecay(parameter_set=parameter_set, n_states=1)

# Fitting parameters
Expand Down
2 changes: 1 addition & 1 deletion examples/scripts/spm_AdamW.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,5 +77,5 @@ def noise(sigma):
pybop.plot_parameters(optim)

# Plot the cost landscape with optimisation path
bounds = np.array([[0.5, 0.8], [0.4, 0.7]])
bounds = np.asarray([[0.5, 0.8], [0.4, 0.7]])
pybop.plot2d(optim, bounds=bounds, steps=15)
2 changes: 1 addition & 1 deletion examples/scripts/spm_IRPropMin.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,5 +51,5 @@
pybop.plot_parameters(optim)

# Plot the cost landscape with optimisation path
bounds = np.array([[0.5, 0.8], [0.4, 0.7]])
bounds = np.asarray([[0.5, 0.8], [0.4, 0.7]])
pybop.plot2d(optim, bounds=bounds, steps=15)
2 changes: 1 addition & 1 deletion examples/scripts/spm_MAP.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,5 +69,5 @@
pybop.plot2d(cost, steps=15)

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

# Plot the cost landscape with optimisation path
bounds = np.array([[0.55, 0.77], [0.48, 0.68]])
bounds = np.asarray([[0.55, 0.77], [0.48, 0.68]])
pybop.plot2d(optim, bounds=bounds, steps=15)
2 changes: 1 addition & 1 deletion examples/scripts/spm_NelderMead.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,5 +77,5 @@ def noise(sigma):
pybop.plot_parameters(optim)

# Plot the cost landscape with optimisation path
bounds = np.array([[0.5, 0.8], [0.4, 0.7]])
bounds = np.asarray([[0.5, 0.8], [0.4, 0.7]])
pybop.plot2d(optim, bounds=bounds, steps=15)
2 changes: 1 addition & 1 deletion examples/scripts/spm_descent.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,5 +57,5 @@
pybop.plot_parameters(optim)

# Plot the cost landscape with optimisation path
bounds = np.array([[0.5, 0.8], [0.4, 0.7]])
bounds = np.asarray([[0.5, 0.8], [0.4, 0.7]])
pybop.plot2d(optim, bounds=bounds, steps=15)
8 changes: 5 additions & 3 deletions examples/standalone/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ def __init__(
parameter_set: pybamm.ParameterValues = None,
n_states: int = 1,
):
super().__init__()
super().__init__(name=name, parameter_set=parameter_set)

self.n_states = n_states
if n_states < 1:
raise ValueError("The number of states (n_states) must be greater than 0")
Expand All @@ -38,10 +39,11 @@ def __init__(
)

self._unprocessed_model = self.pybamm_model
self.name = name

self.default_parameter_values = (
default_parameter_values if parameter_set is None else parameter_set
default_parameter_values
if self._parameter_set is None
else self._parameter_set
)
self._parameter_set = self.default_parameter_values
self._unprocessed_parameter_set = self._parameter_set
Expand Down
10 changes: 5 additions & 5 deletions pybop/costs/_likelihoods.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def set_sigma(self, sigma):
)

if not isinstance(sigma, np.ndarray):
sigma = np.array(sigma)
sigma = np.asarray(sigma)

if not np.issubdtype(sigma.dtype, np.number):
raise ValueError("Sigma must contain only numeric values")
Expand All @@ -74,7 +74,7 @@ def _evaluate(self, x, grad=None):
if len(y.get(key, [])) != len(self._target.get(key, [])):
return -np.float64(np.inf) # prediction doesn't match target

e = np.array(
e = np.asarray(
[
np.sum(
self._offset
Expand Down Expand Up @@ -102,7 +102,7 @@ def _evaluateS1(self, x, grad=None):
dl = self._dl * np.ones(self.n_parameters)
return -likelihood, -dl

r = np.array([self._target[signal] - y[signal] for signal in self.signal])
r = np.asarray([self._target[signal] - y[signal] for signal in self.signal])
likelihood = self._evaluate(x)
dl = np.sum((self.sigma2 * np.sum((r * dy.T), axis=2)), axis=1)
return likelihood, dl
Expand Down Expand Up @@ -148,7 +148,7 @@ def _evaluate(self, x, grad=None):
if len(y.get(key, [])) != len(self._target.get(key, [])):
return -np.float64(np.inf) # prediction doesn't match target

e = np.array(
e = np.asarray(
[
np.sum(
self._logpi
Expand Down Expand Up @@ -181,7 +181,7 @@ def _evaluateS1(self, x, grad=None):
dl = self._dl * np.ones(self.n_parameters)
return -likelihood, -dl

r = np.array([self._target[signal] - y[signal] for signal in self.signal])
r = np.asarray([self._target[signal] - y[signal] for signal in self.signal])
likelihood = self._evaluate(x)
dl = sigma ** (-2.0) * np.sum((r * dy.T), axis=2)
dsigma = -self.n_time_data / sigma + sigma**-(3.0) * np.sum(r**2, axis=1)
Expand Down
8 changes: 4 additions & 4 deletions pybop/costs/fitting_costs.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def _evaluate(self, x, grad=None):
if len(prediction.get(key, [])) != len(self._target.get(key, [])):
return np.float64(np.inf) # prediction doesn't match target

e = np.array(
e = np.asarray(
[
np.sqrt(np.mean((prediction[signal] - self._target[signal]) ** 2))
for signal in self.signal
Expand Down Expand Up @@ -87,7 +87,7 @@ def _evaluateS1(self, x):
de = self._de * np.ones(self.n_parameters)
return e, de

r = np.array([y[signal] - self._target[signal] for signal in self.signal])
r = np.asarray([y[signal] - self._target[signal] for signal in self.signal])
e = np.sqrt(np.mean(r**2, axis=1))
de = np.mean((r * dy.T), axis=2) / (e + np.finfo(float).eps)

Expand Down Expand Up @@ -159,7 +159,7 @@ def _evaluate(self, x, grad=None):
if len(prediction.get(key, [])) != len(self._target.get(key, [])):
return np.float64(np.inf) # prediction doesn't match target

e = np.array(
e = np.asarray(
[
np.sum(((prediction[signal] - self._target[signal]) ** 2))
for signal in self.signal
Expand Down Expand Up @@ -197,7 +197,7 @@ def _evaluateS1(self, x):
de = self._de * np.ones(self.n_parameters)
return e, de

r = np.array([y[signal] - self._target[signal] for signal in self.signal])
r = np.asarray([y[signal] - self._target[signal] for signal in self.signal])
e = np.sum(np.sum(r**2, axis=0), axis=0)
de = 2 * np.sum(np.sum((r * dy.T), axis=2), axis=1)

Expand Down
4 changes: 2 additions & 2 deletions pybop/models/base_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ def reinit(
if x is None:
x = self._built_model.y0

sol = pybamm.Solution([np.array([t])], [x], self._built_model, inputs)
sol = pybamm.Solution([np.asarray([t])], [x], self._built_model, inputs)

return TimeSeriesState(sol=sol, inputs=inputs, t=t)

Expand All @@ -303,7 +303,7 @@ def get_state(self, inputs: Inputs, t: float, x: np.ndarray) -> TimeSeriesState:
if self._built_model is None:
raise ValueError("Model must be built before calling get_state")

sol = pybamm.Solution([np.array([t])], [x], self._built_model, inputs)
sol = pybamm.Solution([np.asarray([t])], [x], self._built_model, inputs)

return TimeSeriesState(sol=sol, inputs=inputs, t=t)

Expand Down
6 changes: 3 additions & 3 deletions pybop/models/empirical/base_ecm.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,14 @@ def __init__(
# Correct OCP if set to default
if (
parameter_set is not None
and "Open-circuit voltage [V]" in parameter_set.params
and "Open-circuit voltage [V]" in parameter_set.keys()
):
default_ocp = self.pybamm_model.default_parameter_values[
"Open-circuit voltage [V]"
]
if parameter_set.params["Open-circuit voltage [V]"] == "default":
if parameter_set["Open-circuit voltage [V]"] == "default":
print("Setting open-circuit voltage to default function")
parameter_set.params["Open-circuit voltage [V]"] = default_ocp
parameter_set["Open-circuit voltage [V]"] = default_ocp

super().__init__(name=name, parameter_set=parameter_set)

Expand Down
5 changes: 1 addition & 4 deletions pybop/models/lithium_ion/base_echem.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,7 @@ def __init__(
solver=None,
**model_kwargs,
):
super().__init__(
name=name,
parameter_set=parameter_set,
)
super().__init__(name=name, parameter_set=parameter_set)

model_options = dict(build=False)
for key, value in model_kwargs.items():
Expand Down
2 changes: 1 addition & 1 deletion pybop/observers/observer.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ def get_current_covariance(self) -> Covariance:

def get_measure(self, x: TimeSeriesState) -> np.ndarray:
measures = [x.sol[s].data[-1] for s in self._signal]
return np.array([[m] for m in measures])
return np.asarray([[m] for m in measures])

def get_current_time(self) -> float:
"""
Expand Down
8 changes: 4 additions & 4 deletions pybop/observers/unscented_kalman.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ def observe(self, time: float, value: np.ndarray) -> float:
if value is None:
raise ValueError("Measurement must be provided.")
elif isinstance(value, np.floating):
value = np.array([value])
value = np.asarray([value])

dt = time - self.get_current_time()
if dt < 0:
Expand Down Expand Up @@ -201,7 +201,7 @@ def __init__(
zero_cols = np.logical_and(np.all(P0 == 0, axis=1), np.all(Rp == 0, axis=1))
zeros = np.logical_and(zero_rows, zero_cols)
ones = np.logical_not(zeros)
states = np.array(range(len(x0)))[ones]
states = np.asarray(range(len(x0)))[ones]
bool_mask = np.ix_(ones, ones)

S_filtered = linalg.cholesky(P0[ones, :][:, ones])
Expand Down Expand Up @@ -276,11 +276,11 @@ def gen_sigma_points(

# Define the weights of the sigma points
w_m0 = sigma / (L + sigma)
w_m = np.array([w_m0] + [1 / (2 * (L + sigma))] * (2 * L))
w_m = np.asarray([w_m0] + [1 / (2 * (L + sigma))] * (2 * L))

# Define the weights of the covariance of the sigma points
w_c0 = w_m0 + (1 - alpha**2 + beta)
w_c = np.array([w_c0] + [1 / (2 * (L + sigma))] * (2 * L))
w_c = np.asarray([w_c0] + [1 / (2 * (L + sigma))] * (2 * L))

return (points, w_m, w_c)

Expand Down
24 changes: 11 additions & 13 deletions pybop/optimisers/base_pints_optimiser.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,22 +134,20 @@ def _sanitise_inputs(self):

# Convert bounds to PINTS boundaries
if self.bounds is not None:
if issubclass(
self.pints_optimiser,
(PintsGradientDescent, PintsAdam, PintsNelderMead),
):
ignored_optimisers = (PintsGradientDescent, PintsAdam, PintsNelderMead)
if issubclass(self.pints_optimiser, ignored_optimisers):
print(f"NOTE: Boundaries ignored by {self.pints_optimiser}")
self.bounds = None
elif issubclass(self.pints_optimiser, PintsPSO):
if not all(
np.isfinite(value)
for sublist in self.bounds.values()
for value in sublist
):
raise ValueError(
"Either all bounds or no bounds must be set for Pints PSO."
)
else:
if issubclass(self.pints_optimiser, PintsPSO):
if not all(
np.isfinite(value)
for sublist in self.bounds.values()
for value in sublist
):
raise ValueError(
f"Either all bounds or no bounds must be set for {self.pints_optimiser.__name__}."
)
self._boundaries = PintsRectangularBoundaries(
self.bounds["lower"], self.bounds["upper"]
)
Expand Down
Loading

0 comments on commit 074501f

Please sign in to comment.