Skip to content

Commit

Permalink
Merge branch 'develop' into 358-passing-inputs
Browse files Browse the repository at this point in the history
  • Loading branch information
NicolaCourtier authored Jul 4, 2024
2 parents 9949478 + d495ed7 commit 8d09998
Show file tree
Hide file tree
Showing 10 changed files with 102 additions and 31 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,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.
Expand Down
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/spme_max_energy.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
# print(f"Optimised volumetric energy density: {final_cost:.2f} Wh.m-3")

# Define parameter set and model
parameter_set = pybop.ParameterSet.pybamm("Chen2020")
parameter_set = pybop.ParameterSet.pybamm("Chen2020", formation_concentrations=True)
model = pybop.lithium_ion.SPMe(parameter_set=parameter_set)

# Fitting parameters
Expand Down
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
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
18 changes: 12 additions & 6 deletions pybop/models/lithium_ion/base_echem.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,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 Expand Up @@ -289,6 +286,7 @@ def approximate_capacity(self, inputs: Inputs):
The nominal cell capacity is updated directly in the model's parameter set.
"""
inputs = self.parameters.verify(inputs)
self._parameter_set.update(inputs)

# Extract stoichiometries and compute mean values
(
Expand All @@ -300,13 +298,21 @@ def approximate_capacity(self, inputs: Inputs):
mean_sto_neg = (min_sto_neg + max_sto_neg) / 2
mean_sto_pos = (min_sto_pos + max_sto_pos) / 2

self._parameter_set.update(inputs)

# Calculate theoretical energy density
theoretical_energy = self._electrode_soh.calculate_theoretical_energy(
self._parameter_set
)

# Extract stoichiometries and compute mean values
(
min_sto_neg,
max_sto_neg,
min_sto_pos,
max_sto_pos,
) = self._electrode_soh.get_min_max_stoichiometries(self._parameter_set)
mean_sto_neg = (min_sto_neg + max_sto_neg) / 2
mean_sto_pos = (min_sto_pos + max_sto_pos) / 2

# Calculate average voltage
positive_electrode_ocp = self._parameter_set["Positive electrode OCP [V]"]
negative_electrode_ocp = self._parameter_set["Negative electrode OCP [V]"]
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
55 changes: 52 additions & 3 deletions pybop/parameters/parameter_set.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import json
import types
from typing import List

from pybamm import ParameterValues, parameter_sets
from pybamm import LithiumIonParameters, ParameterValues, parameter_sets


class ParameterSet:
Expand Down Expand Up @@ -34,6 +35,12 @@ def __setitem__(self, key, value):
def __getitem__(self, key):
return self.params[key]

def keys(self) -> List:
"""
A list of parameter names
"""
return list(self.params.keys())

def import_parameters(self, json_path=None):
"""
Imports parameters from a JSON file specified by the `json_path` attribute.
Expand Down Expand Up @@ -167,14 +174,16 @@ def is_json_serializable(self, value):
return False

@classmethod
def pybamm(cls, name):
def pybamm(cls, name, formation_concentrations=False):
"""
Retrieves a PyBaMM parameter set by name.
Parameters
----------
name : str
The name of the PyBaMM parameter set to retrieve.
set_formation_concentrations : bool, optional
If True, re-calculates the initial concentrations of lithium in the active material (default: False).
Returns
-------
Expand All @@ -187,4 +196,44 @@ def pybamm(cls, name):
if name not in list(parameter_sets):
raise ValueError(msg)

return ParameterValues(name).copy()
parameter_set = ParameterValues(name).copy()

if formation_concentrations:
set_formation_concentrations(parameter_set)

return parameter_set


def set_formation_concentrations(parameter_set):
"""
Compute the concentration of lithium in the positive electrode assuming that
all lithium in the active material originated from the positive electrode.
Parameters
----------
parameter_set : pybamm.ParameterValues
A PyBaMM parameter set containing standard lithium ion parameters.
"""
# Obtain the total amount of lithium in the active material
Q_Li_particles_init = parameter_set.evaluate(
LithiumIonParameters().Q_Li_particles_init
)

# Convert this total amount to a concentration in the positive electrode
c_init = (
Q_Li_particles_init
* 3600
/ (
parameter_set["Positive electrode active material volume fraction"]
* parameter_set["Positive electrode thickness [m]"]
* parameter_set["Electrode height [m]"]
* parameter_set["Electrode width [m]"]
* parameter_set["Faraday constant [C.mol-1]"]
)
)

# Update the initial lithium concentrations
parameter_set.update({"Initial concentration in negative electrode [mol.m-3]": 0})
parameter_set.update(
{"Initial concentration in positive electrode [mol.m-3]": c_init}
)
1 change: 1 addition & 0 deletions tests/unit/test_optimisation.py
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,7 @@ def test_halting(self, cost):
optim = pybop.Optimisation(cost=cost)

# Trigger threshold
optim.set_threshold(None)
optim.set_threshold(np.inf)
optim.run()
optim.set_max_unchanged_iterations()
Expand Down
13 changes: 13 additions & 0 deletions tests/unit/test_parameter_sets.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,3 +123,16 @@ def test_bpx_parameter_sets(self):
match="Parameter set already constructed, or path to bpx file not provided.",
):
bpx_parameters.import_from_bpx()

@pytest.mark.unit
def test_set_formation_concentrations(self):
parameter_set = pybop.ParameterSet.pybamm(
"Chen2020", formation_concentrations=True
)

assert (
parameter_set["Initial concentration in negative electrode [mol.m-3]"] == 0
)
assert (
parameter_set["Initial concentration in positive electrode [mol.m-3]"] > 0
)

0 comments on commit 8d09998

Please sign in to comment.