Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update BPX version and make PyBaMM compatible with Pydantic V2 #4701

Merged
merged 20 commits into from
Dec 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

## Breaking changes

- Updated BPX to v0.5.0 and made changes for the switch to Pydantic V2 ([#4701](https://github.com/pybamm-team/PyBaMM/pull/4701))
- Summary variables now calculated only when called, accessed via a class in the same manner as other variables rather than a dictionary. ([#4621](https://github.com/pybamm-team/PyBaMM/pull/4621))
- The conda distribution (`pybamm`) now installs all optional dependencies available on conda-forge. Use the new `pybamm-base` conda
package to install PyBaMM with only the required dependencies. ([conda-forge/pybamm-feedstock#70](https://github.com/conda-forge/pybamm-feedstock/pull/70))
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ cite = [
]
# Battery Parameter eXchange format
bpx = [
"bpx==0.4.0",
"bpx>=0.5.0,<0.6.0",
]
# Low-overhead progress bars
tqdm = [
Expand Down
15 changes: 7 additions & 8 deletions src/pybamm/parameters/bpx.py
Original file line number Diff line number Diff line change
Expand Up @@ -398,8 +398,7 @@ def _conductivity(c_e, T, Ea, sigma_ref, constant=False):
# Add user-defined parameters, if any
user_defined = bpx.parameterisation.user_defined
if user_defined:
for name in user_defined.__dict__.keys():
value = getattr(user_defined, name)
for name, value in user_defined:
value = process_float_function_table(value, name)
if callable(value):
pybamm_dict[name] = partial(_callable_func, fun=value)
Expand Down Expand Up @@ -447,7 +446,7 @@ def _bpx_to_domain_param_dict(instance: BPX, pybamm_dict: dict, domain: Domain)
Turns a BPX instance in to a dictionary of parameters for PyBaMM for a given domain
"""
# Loop over fields in BPX instance and add to pybamm dictionary
for name, field in instance.__fields__.items():
for name, field in instance.model_fields.items():
value = getattr(instance, name)
# Handle blended electrodes, where the field is now an instance of
# ElectrodeBlended or ElectrodeBlendedSPM
Expand All @@ -460,16 +459,16 @@ def _bpx_to_domain_param_dict(instance: BPX, pybamm_dict: dict, domain: Domain)
for i, phase_name in enumerate(particle_instance.keys()):
phase_instance = particle_instance[phase_name]
# Loop over fields in phase instance and add to pybamm dictionary
for name, field in phase_instance.__fields__.items():
value = getattr(phase_instance, name)
for name_to_add, field_to_add in phase_instance.model_fields.items():
value = getattr(phase_instance, name_to_add)
pybamm_name = PHASE_NAMES[i] + _get_pybamm_name(
field.field_info.alias, domain
field_to_add.alias, domain
)
value = process_float_function_table(value, name)
value = process_float_function_table(value, name_to_add)
pybamm_dict[pybamm_name] = value
# Handle other fields, which correspond directly to parameters
else:
pybamm_name = _get_pybamm_name(field.field_info.alias, domain)
pybamm_name = _get_pybamm_name(field.alias, domain)
value = process_float_function_table(value, name)
pybamm_dict[pybamm_name] = value
return pybamm_dict
52 changes: 16 additions & 36 deletions tests/unit/test_parameters/test_bpx.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@
import copy
import numpy as np
import pytest
from typing import Any


class TestBPX:
def setup_method(self):
self.base = {
self.base: dict[str, Any] = {
"Header": {
"BPX": 1.0,
"Title": "Parametrisation example",
Expand Down Expand Up @@ -116,7 +117,7 @@ def test_bpx(self):
},
},
},
copy.copy(self.base),
copy.deepcopy(self.base),
]

model = pybamm.lithium_ion.DFN()
Expand All @@ -132,9 +133,6 @@ def test_bpx(self):
with tempfile.NamedTemporaryFile(
suffix=filename, delete=False, mode="w"
) as tmp:
# write to a temporary file so we can
# get the source later on using inspect.getsource
# (as long as the file still exists)
json.dump(obj, tmp)
tmp.flush()

Expand All @@ -153,13 +151,13 @@ def test_no_already_exists_in_BPX(self):
with tempfile.NamedTemporaryFile(
suffix="test.json", delete=False, mode="w"
) as test_file:
json.dump(copy.copy(self.base), test_file)
json.dump(copy.deepcopy(self.base), test_file)
test_file.flush()
params = pybamm.ParameterValues.create_from_bpx(test_file.name)
assert "check_already_exists" not in params.keys()

def test_constant_functions(self):
bpx_obj = copy.copy(self.base)
bpx_obj = copy.deepcopy(self.base)
bpx_obj["Parameterisation"]["Electrolyte"].update(
{
"Conductivity [S.m-1]": 1,
Expand All @@ -183,9 +181,6 @@ def test_constant_functions(self):
with tempfile.NamedTemporaryFile(
suffix=filename, delete=False, mode="w"
) as tmp:
# write to a tempory file so we can
# get the source later on using inspect.getsource
# (as long as the file still exists)
json.dump(bpx_obj, tmp)
tmp.flush()

Expand All @@ -210,7 +205,7 @@ def check_constant_output(func):
check_constant_output(De)

def test_table_data(self):
bpx_obj = copy.copy(self.base)
bpx_obj = copy.deepcopy(self.base)
data = {"x": [0, 1], "y": [0, 1]}
bpx_obj["Parameterisation"]["Electrolyte"].update(
{
Expand All @@ -237,9 +232,6 @@ def test_table_data(self):
with tempfile.NamedTemporaryFile(
suffix=filename, delete=False, mode="w"
) as tmp:
# write to a temporary file so we can
# get the source later on using inspect.getsource
# (as long as the file still exists)
json.dump(bpx_obj, tmp)
tmp.flush()

Expand All @@ -263,20 +255,17 @@ def test_table_data(self):
assert isinstance(dUdT, pybamm.Interpolant)

def test_bpx_soc_error(self):
bpx_obj = copy.copy(self.base)
bpx_obj = copy.deepcopy(self.base)
with pytest.raises(ValueError, match="Target SOC"):
pybamm.ParameterValues.create_from_bpx_obj(bpx_obj, target_soc=10)

def test_bpx_arrhenius(self):
bpx_obj = copy.copy(self.base)
bpx_obj = copy.deepcopy(self.base)

filename = "tmp.json"
with tempfile.NamedTemporaryFile(
suffix=filename, delete=False, mode="w"
) as tmp:
# write to a tempory file so we can
# get the source later on using inspect.getsource
# (as long as the file still exists)
json.dump(bpx_obj, tmp)
tmp.flush()

Expand Down Expand Up @@ -327,7 +316,7 @@ def arrhenius_assertion(pv, param_key, Ea_key):
arrhenius_assertion(pv, param_key, Ea_key)

def test_bpx_blended(self):
bpx_obj = copy.copy(self.base)
bpx_obj = copy.deepcopy(self.base)
bpx_obj["Parameterisation"]["Positive electrode"] = {
"Thickness [m]": 5.23e-05,
"Conductivity [S.m-1]": 0.789,
Expand Down Expand Up @@ -367,9 +356,6 @@ def test_bpx_blended(self):
with tempfile.NamedTemporaryFile(
suffix=filename, delete=False, mode="w"
) as tmp:
# write to a tempory file so we can
# get the source later on using inspect.getsource
# (as long as the file still exists)
json.dump(bpx_obj, tmp)
tmp.flush()

Expand All @@ -393,7 +379,7 @@ def test_bpx_blended(self):
sim.solve(calc_esoh=False)

def test_bpx_blended_error(self):
bpx_obj = copy.copy(self.base)
bpx_obj = copy.deepcopy(self.base)
bpx_obj["Parameterisation"]["Positive electrode"] = {
"Thickness [m]": 5.23e-05,
"Conductivity [S.m-1]": 0.789,
Expand Down Expand Up @@ -446,17 +432,14 @@ def test_bpx_blended_error(self):
with tempfile.NamedTemporaryFile(
suffix=filename, delete=False, mode="w"
) as tmp:
# write to a tempory file so we can
# get the source later on using inspect.getsource
# (as long as the file still exists)
json.dump(bpx_obj, tmp)
tmp.flush()

with pytest.raises(NotImplementedError, match="PyBaMM does not support"):
pybamm.ParameterValues.create_from_bpx(tmp.name)

def test_bpx_user_defined(self):
bpx_obj = copy.copy(self.base)
bpx_obj = copy.deepcopy(self.base)
data = {"x": [0, 1], "y": [0, 1]}
bpx_obj["Parameterisation"]["User-defined"] = {
"User-defined scalar parameter": 1.0,
Expand All @@ -468,9 +451,6 @@ def test_bpx_user_defined(self):
with tempfile.NamedTemporaryFile(
suffix=filename, delete=False, mode="w"
) as tmp:
# write to a tempory file so we can
# get the source later on using inspect.getsource
# (as long as the file still exists)
json.dump(bpx_obj, tmp)
tmp.flush()

Expand All @@ -488,21 +468,21 @@ def test_bpx_user_defined(self):
)

def test_bpx_activation_energy_default(self):
bpx_obj = copy.copy(self.base)
bpx_obj["Parameterisation"]["Negative electrode"][
bpx_obj = copy.deepcopy(self.base)
del bpx_obj["Parameterisation"]["Negative electrode"][
"Diffusivity activation energy [J.mol-1]"
] = None
]
with tempfile.NamedTemporaryFile(
suffix="test.json", delete=False, mode="w"
) as test_file:
json.dump(copy.copy(bpx_obj), test_file)
json.dump(copy.deepcopy(bpx_obj), test_file)
test_file.flush()
param = pybamm.ParameterValues.create_from_bpx(test_file.name)
assert param[
"Negative electrode diffusivity activation energy [J.mol-1]"
] == pytest.approx(0.0, rel=1e-12)

def test_bpx_from_obj(self):
bpx_obj = copy.copy(self.base)
bpx_obj = copy.deepcopy(self.base)
param = pybamm.ParameterValues.create_from_bpx_obj(bpx_obj)
assert isinstance(param, pybamm.ParameterValues)
Loading