Skip to content

Commit

Permalink
Merged development into issue-23-blended-electrodes
Browse files Browse the repository at this point in the history
  • Loading branch information
ikorotkin committed Sep 11, 2023
2 parents c2afaf8 + 58930b3 commit 2a6a0a6
Show file tree
Hide file tree
Showing 3 changed files with 144 additions and 3 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Unreleased

- Added validation based on models: SPM, SPMe, DFN ([#34](https://github.com/pybamm-team/BPX/pull/34)). A warning will be produced if the user-defined model type does not match the parameter set (e.g., if the model is `SPM`, but the full DFN model parameters are provided).
- Added support for well-mixed, blended electrodes that contain more than one active material ([#33](https://github.com/pybamm-team/BPX/pull/33))

# [v0.3.1](https://github.com/pybamm-team/BPX/releases/tag/v0.3.1)
Expand Down
46 changes: 43 additions & 3 deletions bpx/schema.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
from typing import List, Literal, Union, Dict

from pydantic import BaseModel, Field, Extra
from pydantic import BaseModel, Field, Extra, root_validator

from bpx import Function, InterpolatedTable

from warnings import warn

FloatFunctionTable = Union[float, Function, InterpolatedTable]


Expand Down Expand Up @@ -158,12 +160,15 @@ class Electrolyte(ExtraBaseModel):
)


class Contact(ExtraBaseModel):
class ContactBase(ExtraBaseModel):
thickness: float = Field(
alias="Thickness [m]",
example=85.2e-6,
description="Contact thickness",
)


class Contact(ContactBase):
porosity: float = Field(
alias="Porosity",
example=0.47,
Expand Down Expand Up @@ -259,6 +264,14 @@ class ElectrodeBlended(Electrode):
particle: Dict[str, Particle] = Field(alias="Particle")


class ElectrodeSingleSPM(ContactBase, Particle):
pass


class ElectrodeBlendedSPM(ContactBase):
particle: Dict[str, Particle] = Field(alias="Particle")


class Experiment(ExtraBaseModel):
time: List[float] = Field(
alias="Time [s]",
Expand Down Expand Up @@ -301,9 +314,36 @@ class Parameterisation(ExtraBaseModel):
)


class ParameterisationSPM(ExtraBaseModel):
cell: Cell = Field(
alias="Cell",
)
negative_electrode: Union[ElectrodeSingleSPM, ElectrodeBlendedSPM] = Field(
alias="Negative electrode",
)
positive_electrode: Union[ElectrodeSingleSPM, ElectrodeBlendedSPM] = Field(
alias="Positive electrode",
)


class BPX(ExtraBaseModel):
header: Header = Field(
alias="Header",
)
parameterisation: Parameterisation = Field(alias="Parameterisation")
parameterisation: Union[ParameterisationSPM, Parameterisation] = Field(
alias="Parameterisation"
)
validation: Dict[str, Experiment] = Field(None, alias="Validation")

@root_validator(skip_on_failure=True)
def model_based_validation(cls, values):
model = values.get("header").model
parameter_class_name = values.get("parameterisation").__class__.__name__
allowed_combinations = [
("Parameterisation", "DFN"),
("Parameterisation", "SPMe"),
("ParameterisationSPM", "SPM"),
]
if (parameter_class_name, model) not in allowed_combinations:
warn(f"The model type {model} does not correspond to the parameter set")
return values
100 changes: 100 additions & 0 deletions tests/test_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,110 @@ def setUp(self):
},
}

# SPM parameter set
self.base_spm = {
"Header": {
"BPX": 1.0,
"Model": "SPM",
},
"Parameterisation": {
"Cell": {
"Ambient temperature [K]": 299.0,
"Initial temperature [K]": 299.0,
"Reference temperature [K]": 299.0,
"Electrode area [m2]": 2.0,
"External surface area [m2]": 2.2,
"Volume [m3]": 1.0,
"Number of electrode pairs connected in parallel to make a cell": 1,
"Nominal cell capacity [A.h]": 5.0,
"Lower voltage cut-off [V]": 2.0,
"Upper voltage cut-off [V]": 4.0,
},
"Negative electrode": {
"Particle radius [m]": 5.86e-6,
"Thickness [m]": 85.2e-6,
"Diffusivity [m2.s-1]": 3.3e-14,
"OCP [V]": {"x": [0, 0.1, 1], "y": [1.72, 1.2, 0.06]},
"Surface area per unit volume [m-1]": 383959,
"Reaction rate constant [mol.m-2.s-1]": 1e-10,
"Maximum concentration [mol.m-3]": 33133,
"Minimum stoichiometry": 0.01,
"Maximum stoichiometry": 0.99,
},
"Positive electrode": {
"Thickness [m]": 75.6e-6,
"Particle": {
"Primary": {
"Particle radius [m]": 5.22e-6,
"Diffusivity [m2.s-1]": 4.0e-15,
"OCP [V]": {"x": [0, 0.1, 1], "y": [1.72, 1.2, 0.06]},
"Surface area per unit volume [m-1]": 382184,
"Reaction rate constant [mol.m-2.s-1]": 1e-10,
"Maximum concentration [mol.m-3]": 63104.0,
"Minimum stoichiometry": 0.1,
"Maximum stoichiometry": 0.9,
},
"Secondary": {
"Particle radius [m]": 10.0e-6,
"Diffusivity [m2.s-1]": 4.0e-15,
"OCP [V]": {"x": [0, 0.1, 1], "y": [1.72, 1.2, 0.06]},
"Surface area per unit volume [m-1]": 382184,
"Reaction rate constant [mol.m-2.s-1]": 1e-10,
"Maximum concentration [mol.m-3]": 63104.0,
"Minimum stoichiometry": 0.1,
"Maximum stoichiometry": 0.9,
},
},
},
},
}

def test_simple(self):
test = copy.copy(self.base)
parse_obj_as(BPX, test)

def test_simple_spme(self):
test = copy.copy(self.base)
test["Header"]["Model"] = "SPMe"
parse_obj_as(BPX, test)

def test_simple_spm(self):
test = copy.copy(self.base_spm)
parse_obj_as(BPX, test)

def test_bad_model(self):
test = copy.copy(self.base)
test["Header"]["Model"] = "Wrong model type"
with self.assertRaises(ValidationError):
parse_obj_as(BPX, test)

def test_bad_dfn(self):
test = copy.copy(self.base_spm)
test["Header"]["Model"] = "DFN"
with self.assertWarnsRegex(
UserWarning,
"The model type DFN does not correspond to the parameter set",
):
parse_obj_as(BPX, test)

def test_bad_spme(self):
test = copy.copy(self.base_spm)
test["Header"]["Model"] = "SPMe"
with self.assertWarnsRegex(
UserWarning,
"The model type SPMe does not correspond to the parameter set",
):
parse_obj_as(BPX, test)

def test_bad_spm(self):
test = copy.copy(self.base)
test["Header"]["Model"] = "SPM"
with self.assertWarnsRegex(
UserWarning,
"The model type SPM does not correspond to the parameter set",
):
parse_obj_as(BPX, test)

def test_table(self):
test = copy.copy(self.base)
test["Parameterisation"]["Electrolyte"]["Conductivity [S.m-1]"] = {
Expand Down

0 comments on commit 2a6a0a6

Please sign in to comment.