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

Sei composite multiparticle #4693

Draft
wants to merge 14 commits into
base: develop
Choose a base branch
from
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## Features

- Made composite electrode model compatible with particle size distribution ([#4687](https://github.com/pybamm-team/PyBaMM/pull/4687))
- Added `Symbol.post_order()` method to return an iterable that steps through the tree in post-order fashion. ([#4684](https://github.com/pybamm-team/PyBaMM/pull/4684))
- Added two more submodels (options) for the SEI: Lars von Kolzenberg (2020) model and Tunneling Limit model ([#4394](https://github.com/pybamm-team/PyBaMM/pull/4394))

Expand Down
4 changes: 2 additions & 2 deletions docs/source/examples/notebooks/models/using-submodels.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -527,10 +527,10 @@
" model.param, \"positive\", model.options, cracks=True\n",
")\n",
"model.submodels[\"Negative lithium plating\"] = pybamm.lithium_plating.NoPlating(\n",
" model.param, \"Negative\"\n",
" model.param, \"negative\"\n",
")\n",
"model.submodels[\"Positive lithium plating\"] = pybamm.lithium_plating.NoPlating(\n",
" model.param, \"Positive\"\n",
" model.param, \"positive\"\n",
")"
]
},
Expand Down
55 changes: 49 additions & 6 deletions src/pybamm/expression_tree/averages.py
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,10 @@
return yz_average(x_average(symbol))


def xyzs_average(symbol: pybamm.Symbol) -> pybamm.Symbol:
return xyz_average(size_average(symbol))


def r_average(symbol: pybamm.Symbol) -> pybamm.Symbol:
"""
Convenience function for creating an average in the r-direction.
Expand Down Expand Up @@ -373,7 +377,15 @@
# If symbol doesn't have a domain, or doesn't have "negative particle size"
# or "positive particle size" as a domain, it's average value is itself
if symbol.domain == [] or not any(
domain in [["negative particle size"], ["positive particle size"]]
domain
in [
["negative particle size"],
["positive particle size"],
["negative primary particle size"],
["positive primary particle size"],
["negative secondary particle size"],
["positive secondary particle size"],
]
for domain in list(symbol.domains.values())
):
return symbol
Expand All @@ -394,13 +406,44 @@
else:
if f_a_dist is None:
geo = pybamm.geometric_parameters
R = pybamm.SpatialVariable(
"R", domains=symbol.domains, coord_sys="cartesian"
)
if ["negative particle size"] in symbol.domains.values():
if "negative" in symbol.domain[0]:
if "primary" in symbol.domain[0]:
R = pybamm.SpatialVariable(

Check warning on line 411 in src/pybamm/expression_tree/averages.py

View check run for this annotation

Codecov / codecov/patch

src/pybamm/expression_tree/averages.py#L411

Added line #L411 was not covered by tests
"R_n_prim", domains=symbol.domains, coord_sys="cartesian"
)
elif "secondary" in symbol.domain[0]:
R = pybamm.SpatialVariable(

Check warning on line 415 in src/pybamm/expression_tree/averages.py

View check run for this annotation

Codecov / codecov/patch

src/pybamm/expression_tree/averages.py#L415

Added line #L415 was not covered by tests
"R_n_sec", domains=symbol.domains, coord_sys="cartesian"
)
else:
R = pybamm.SpatialVariable(
"R_n", domains=symbol.domains, coord_sys="cartesian"
)
else:
if "primary" in symbol.domain[0]:
R = pybamm.SpatialVariable(

Check warning on line 424 in src/pybamm/expression_tree/averages.py

View check run for this annotation

Codecov / codecov/patch

src/pybamm/expression_tree/averages.py#L424

Added line #L424 was not covered by tests
"R_p_prim", domains=symbol.domains, coord_sys="cartesian"
)
elif "secondary" in symbol.domain[0]:
R = pybamm.SpatialVariable(

Check warning on line 428 in src/pybamm/expression_tree/averages.py

View check run for this annotation

Codecov / codecov/patch

src/pybamm/expression_tree/averages.py#L428

Added line #L428 was not covered by tests
"R_p_sec", domains=symbol.domains, coord_sys="cartesian"
)
else:
R = pybamm.SpatialVariable(
"R_p", domains=symbol.domains, coord_sys="cartesian"
)
if ["negative particle size"] in symbol.domains.values() or [
"negative primary particle size"
] in symbol.domains.values():
f_a_dist = geo.n.prim.f_a_dist(R)
elif ["positive particle size"] in symbol.domains.values():
elif ["negative secondary particle size"] in symbol.domains.values():
f_a_dist = geo.n.sec.f_a_dist(R)

Check warning on line 440 in src/pybamm/expression_tree/averages.py

View check run for this annotation

Codecov / codecov/patch

src/pybamm/expression_tree/averages.py#L440

Added line #L440 was not covered by tests
elif ["positive particle size"] in symbol.domains.values() or [
"positive primary particle size"
] in symbol.domains.values():
f_a_dist = geo.p.prim.f_a_dist(R)
elif ["positive secondary particle size"] in symbol.domains.values():
f_a_dist = geo.p.sec.f_a_dist(R)

Check warning on line 446 in src/pybamm/expression_tree/averages.py

View check run for this annotation

Codecov / codecov/patch

src/pybamm/expression_tree/averages.py#L445-L446

Added lines #L445 - L446 were not covered by tests
return SizeAverage(symbol, f_a_dist)


Expand Down
24 changes: 24 additions & 0 deletions src/pybamm/geometry/battery_geometry.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,18 @@
"negative particle size": {"R_n": {"min": R_min_n, "max": R_max_n}},
}
)
phases = int(options.negative["particle phases"])
if phases >= 2:
geometry.update(

Check warning on line 88 in src/pybamm/geometry/battery_geometry.py

View check run for this annotation

Codecov / codecov/patch

src/pybamm/geometry/battery_geometry.py#L88

Added line #L88 was not covered by tests
{
"negative primary particle size": {
"R_n_prim": {"min": R_min_n, "max": R_max_n}
},
"negative secondary particle size": {
"R_n_sec": {"min": R_min_n, "max": R_max_n}
},
}
)
if (
options is not None
and options.positive["particle size"] == "distribution"
Expand All @@ -95,6 +107,18 @@
"positive particle size": {"R_p": {"min": R_min_p, "max": R_max_p}},
}
)
phases = int(options.positive["particle phases"])
if phases >= 2:
geometry.update(

Check warning on line 112 in src/pybamm/geometry/battery_geometry.py

View check run for this annotation

Codecov / codecov/patch

src/pybamm/geometry/battery_geometry.py#L112

Added line #L112 was not covered by tests
{
"positive primary particle size": {
"R_p_prim": {"min": R_min_p, "max": R_max_p}
},
"positive secondary particle size": {
"R_p_sec": {"min": R_min_p, "max": R_max_p}
},
}
)

# Add current collector domains
current_collector_dimension = options["dimensionality"]
Expand Down
39 changes: 39 additions & 0 deletions src/pybamm/geometry/standard_spatial_vars.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@
},
coord_sys="cartesian",
)

R_p = pybamm.SpatialVariable(
"R_p",
domain=["positive particle size"],
Expand All @@ -108,6 +109,44 @@
coord_sys="cartesian",
)

R_n_prim = pybamm.SpatialVariable(
"R_n_prim",
domain=["negative primary particle size"],
auxiliary_domains={
"secondary": "negative electrode",
"tertiary": "current collector",
},
coord_sys="cartesian",
)
R_p_prim = pybamm.SpatialVariable(
"R_p_prim",
domain=["positive primary particle size"],
auxiliary_domains={
"secondary": "positive electrode",
"tertiary": "current collector",
},
coord_sys="cartesian",
)

R_n_sec = pybamm.SpatialVariable(
"R_n_sec",
domain=["negative secondary particle size"],
auxiliary_domains={
"secondary": "negative electrode",
"tertiary": "current collector",
},
coord_sys="cartesian",
)
R_p_sec = pybamm.SpatialVariable(
"R_p_sec",
domain=["positive secondary particle size"],
auxiliary_domains={
"secondary": "positive electrode",
"tertiary": "current collector",
},
coord_sys="cartesian",
)

# Domains at cell edges
x_n_edge = pybamm.SpatialVariableEdge(
"x_n",
Expand Down
26 changes: 19 additions & 7 deletions src/pybamm/models/full_battery_models/base_battery_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -530,9 +530,14 @@ def __init__(self, extra_options):
"Heat of mixing submodels do not yet support particle-size "
"distributions."
)
if options["lithium plating"] != "none":
if options["SEI porosity change"] == "true":
raise NotImplementedError(
"SEI porosity change submodels do not yet support particle-size "
"distributions."
)
if options["lithium plating porosity change"] == "true":
raise NotImplementedError(
"Lithium plating submodels do not yet support particle-size "
"Lithium plating porosity change submodels do not yet support particle-size "
"distributions."
)
if options["particle"] in ["quadratic profile", "quartic profile"]:
Expand All @@ -550,10 +555,6 @@ def __init__(self, extra_options):
"Particle shape must be 'spherical' for particle-size distribution"
" submodels."
)
if options["SEI"] != "none":
raise NotImplementedError(
"SEI submodels do not yet support particle-size distributions."
)
if options["stress-induced diffusion"] == "true":
raise NotImplementedError(
"stress-induced diffusion cannot yet be included in "
Expand Down Expand Up @@ -623,7 +624,6 @@ def __init__(self, extra_options):
if options["particle phases"] not in ["1", ("1", "1")]:
if not (
options["surface form"] != "false"
and options["particle size"] == "single"
and options["particle"] == "Fickian diffusion"
):
raise pybamm.OptionError(
Expand Down Expand Up @@ -868,6 +868,10 @@ def default_var_pts(self):
"z": 10,
"R_n": 30,
"R_p": 30,
"R_n_prim": 30,
"R_p_prim": 30,
"R_n_sec": 30,
"R_p_sec": 30,
}
# Reduce the default points for 2D current collectors
if self.options["dimensionality"] == 2:
Expand All @@ -888,6 +892,10 @@ def default_submesh_types(self):
"positive secondary particle": pybamm.Uniform1DSubMesh,
"negative particle size": pybamm.Uniform1DSubMesh,
"positive particle size": pybamm.Uniform1DSubMesh,
"negative primary particle size": pybamm.Uniform1DSubMesh,
"positive primary particle size": pybamm.Uniform1DSubMesh,
"negative secondary particle size": pybamm.Uniform1DSubMesh,
"positive secondary particle size": pybamm.Uniform1DSubMesh,
}
if self.options["dimensionality"] == 0:
base_submeshes["current collector"] = pybamm.SubMesh0D
Expand All @@ -910,6 +918,10 @@ def default_spatial_methods(self):
"positive secondary particle": pybamm.FiniteVolume(),
"negative particle size": pybamm.FiniteVolume(),
"positive particle size": pybamm.FiniteVolume(),
"negative primary particle size": pybamm.FiniteVolume(),
"positive primary particle size": pybamm.FiniteVolume(),
"negative secondary particle size": pybamm.FiniteVolume(),
"positive secondary particle size": pybamm.FiniteVolume(),
}
if self.options["dimensionality"] == 0:
# 0D submesh - use base spatial method
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,19 @@
R = self.phase_param.R
elif domain_options["particle size"] == "distribution":
if self.domain == "negative":
R_ = pybamm.standard_spatial_vars.R_n
if self.phase_name == "primary ":
R_ = pybamm.standard_spatial_vars.R_n_prim

Check warning on line 85 in src/pybamm/models/submodels/active_material/base_active_material.py

View check run for this annotation

Codecov / codecov/patch

src/pybamm/models/submodels/active_material/base_active_material.py#L85

Added line #L85 was not covered by tests
elif self.phase_name == "secondary ":
R_ = pybamm.standard_spatial_vars.R_n_sec

Check warning on line 87 in src/pybamm/models/submodels/active_material/base_active_material.py

View check run for this annotation

Codecov / codecov/patch

src/pybamm/models/submodels/active_material/base_active_material.py#L87

Added line #L87 was not covered by tests
else:
R_ = pybamm.standard_spatial_vars.R_n
elif self.domain == "positive":
R_ = pybamm.standard_spatial_vars.R_p
if self.phase_name == "primary ":
R_ = pybamm.standard_spatial_vars.R_p_prim

Check warning on line 92 in src/pybamm/models/submodels/active_material/base_active_material.py

View check run for this annotation

Codecov / codecov/patch

src/pybamm/models/submodels/active_material/base_active_material.py#L92

Added line #L92 was not covered by tests
elif self.phase_name == "secondary ":
R_ = pybamm.standard_spatial_vars.R_p_sec

Check warning on line 94 in src/pybamm/models/submodels/active_material/base_active_material.py

View check run for this annotation

Codecov / codecov/patch

src/pybamm/models/submodels/active_material/base_active_material.py#L94

Added line #L94 was not covered by tests
else:
R_ = pybamm.standard_spatial_vars.R_p
R = pybamm.size_average(R_)

R_av = pybamm.x_average(R)
Expand Down
49 changes: 40 additions & 9 deletions src/pybamm/models/submodels/interface/base_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@
self.reaction_name = self.phase_name + self.reaction_name

self.reaction = reaction
domain_options = getattr(self.options, domain)
self.size_distribution = domain_options["particle size"] == "distribution"

def _get_exchange_current_density(self, variables):
"""
Expand Down Expand Up @@ -93,8 +95,10 @@
# "current collector"
c_e = pybamm.PrimaryBroadcast(c_e, ["current collector"])
# broadcast c_e, T onto "particle size"
c_e = pybamm.PrimaryBroadcast(c_e, [f"{domain} particle size"])
T = pybamm.PrimaryBroadcast(T, [f"{domain} particle size"])
c_e = pybamm.PrimaryBroadcast(
c_e, [f"{domain} {phase_name}particle size"]
)
T = pybamm.PrimaryBroadcast(T, [f"{domain} {phase_name}particle size"])

else:
c_s_surf = variables[
Expand Down Expand Up @@ -215,13 +219,23 @@

# Size average. For j variables that depend on particle size, see
# "_get_standard_size_distribution_interfacial_current_variables"
if j.domain in [["negative particle size"], ["positive particle size"]]:
if j.domain in [
["negative particle size"],
["positive particle size"],
["negative primary particle size"],
["positive primary particle size"],
["negative secondary particle size"],
["positive secondary particle size"],
]:
j = pybamm.size_average(j)
# Average, and broadcast if necessary
j_av = pybamm.x_average(j)
if j.domain == []:
j = pybamm.FullBroadcast(j, f"{domain} electrode", "current collector")
elif j.domain == ["current collector"]:
elif (
j.domain == ["current collector"]
and getattr(self, "reaction_loc", None) != "interface"
):
j = pybamm.PrimaryBroadcast(j, f"{domain} electrode")

variables = {
Expand All @@ -230,6 +244,13 @@
f"X-averaged {domain} electrode {reaction_name}"
"interfacial current density [A.m-2]": j_av,
}
if self.phase_name == reaction_name:
variables.update(
{
f"{Domain} electrode {reaction_name}interfacial current density [A.m-2]": j,
f"X-averaged {domain} electrode {reaction_name}interfacial current density [A.m-2]": j_av,
}
)

return variables

Expand Down Expand Up @@ -298,10 +319,18 @@
a = variables[
f"{Domain} electrode {phase_name}surface area to volume ratio [m-1]"
]

j = variables[
f"{Domain} electrode {reaction_name}interfacial current density [A.m-2]"
]
if "plating" in reaction_name:
j = variables[
f"{Domain} electrode {reaction_name}interfacial current density [A.m-2]"
]
elif phase_name in reaction_name:
j = variables[
f"{Domain} electrode {reaction_name}interfacial current density [A.m-2]"
]
else:
j = variables[

Check warning on line 331 in src/pybamm/models/submodels/interface/base_interface.py

View check run for this annotation

Codecov / codecov/patch

src/pybamm/models/submodels/interface/base_interface.py#L331

Added line #L331 was not covered by tests
f"{Domain} electrode {phase_name}{reaction_name}interfacial current density [A.m-2]"
]
a_j = a * j
a_j_av = pybamm.x_average(a_j)

Expand Down Expand Up @@ -410,9 +439,11 @@
if j.domains["secondary"] == [f"{domain} electrode"]:
# x-average
j_xav = pybamm.x_average(j)
else:
elif getattr(self, "reaction_loc", None) != "interface":
j_xav = j
j = pybamm.SecondaryBroadcast(j_xav, [f"{domain} electrode"])
else:
j_xav = j

variables = {
f"{Domain} electrode {reaction_name}"
Expand Down
Loading
Loading