Skip to content

Commit 728eda4

Browse files
authored
Merge pull request #2672 from pybamm-team/#2669-particle-tuple
Allow particle size to be a tuple and fix MPM half-cell
2 parents 77b6eac + 3c30f50 commit 728eda4

File tree

21 files changed

+390
-187
lines changed

21 files changed

+390
-187
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# [Unreleased](https://github.com/pybamm-team/PyBaMM/)
22

3+
## Features
4+
5+
- The "particle size" option can now be a tuple to allow different behaviour in each electrode([#2672](https://github.com/pybamm-team/PyBaMM/pull/2672)).
6+
37
# [v23.1](https://github.com/pybamm-team/PyBaMM/tree/v23.1) - 2023-01-31
48

59
## Features

examples/notebooks/models/using-submodels.ipynb

Lines changed: 88 additions & 88 deletions
Large diffs are not rendered by default.

pybamm/geometry/battery_geometry.py

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -44,36 +44,56 @@ def battery_geometry(
4444
"separator": {"x_s": {"min": l_n, "max": l_n_l_s}},
4545
"positive electrode": {"x_p": {"min": l_n_l_s, "max": 1}},
4646
}
47+
4748
# Add particle domains
4849
if include_particles is True:
4950
zero_one = {"min": 0, "max": 1}
50-
geometry.update(
51-
{
52-
"negative particle": {"r_n": zero_one},
53-
"positive particle": {"r_p": zero_one},
54-
}
55-
)
5651
for domain in ["negative", "positive"]:
57-
phases = int(getattr(options, domain)["particle phases"])
58-
if phases >= 2:
52+
if options.electrode_types[domain] == "porous":
5953
geometry.update(
6054
{
61-
f"{domain} primary particle": {"r_n_prim": zero_one},
62-
f"{domain} secondary particle": {"r_n_sec": zero_one},
55+
f"{domain} particle": {f"r_{domain[0]}": zero_one},
6356
}
6457
)
58+
phases = int(getattr(options, domain)["particle phases"])
59+
if phases >= 2:
60+
geometry.update(
61+
{
62+
f"{domain} primary particle": {
63+
f"r_{domain[0]}_prim": zero_one
64+
},
65+
f"{domain} secondary particle": {
66+
f"r_{domain[0]}_sec": zero_one
67+
},
68+
}
69+
)
70+
6571
# Add particle size domains
66-
if options is not None and options["particle size"] == "distribution":
72+
if (
73+
options is not None
74+
and options.negative["particle size"] == "distribution"
75+
and options.electrode_types["negative"] == "porous"
76+
):
6777
R_min_n = geo.n.prim.R_min
68-
R_min_p = geo.p.prim.R_min
6978
R_max_n = geo.n.prim.R_max
70-
R_max_p = geo.p.prim.R_max
7179
geometry.update(
7280
{
7381
"negative particle size": {"R_n": {"min": R_min_n, "max": R_max_n}},
82+
}
83+
)
84+
if (
85+
options is not None
86+
and options.positive["particle size"] == "distribution"
87+
and options.electrode_types["positive"] == "porous"
88+
):
89+
R_min_p = geo.p.prim.R_min
90+
R_max_p = geo.p.prim.R_max
91+
geometry.update(
92+
{
7493
"positive particle size": {"R_p": {"min": R_min_p, "max": R_max_p}},
7594
}
7695
)
96+
7797
# Add current collector domains
7898
current_collector_dimension = options["dimensionality"]
7999
if form_factor == "pouch":

pybamm/models/full_battery_models/base_battery_model.py

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -73,11 +73,6 @@ class BatteryModelOptions(pybamm.FuzzyDict):
7373
"stress-driven", "reaction-driven", or "stress and reaction-driven".
7474
A 2-tuple can be provided for different behaviour in negative and
7575
positive electrodes.
76-
* "particle phases": str
77-
Number of phases present in the electrode. A 2-tuple can be provided for
78-
different behaviour in negative and positive electrodes.
79-
For example, set to ("2", "1") for a negative electrode with 2 phases,
80-
e.g. graphite and silicon.
8176
* "operating mode" : str
8277
Sets the operating mode for the model. This determines how the current
8378
is set. Can be:
@@ -97,7 +92,19 @@ class BatteryModelOptions(pybamm.FuzzyDict):
9792
* "particle" : str
9893
Sets the submodel to use to describe behaviour within the particle.
9994
Can be "Fickian diffusion" (default), "uniform profile",
100-
"quadratic profile", or "quartic profile".
95+
"quadratic profile", or "quartic profile". A 2-tuple can be provided
96+
for different behaviour in negative and positive electrodes.
97+
* "particle mechanics" : str
98+
Sets the model to account for mechanical effects such as particle
99+
swelling and cracking. Can be "none" (default), "swelling only",
100+
or "swelling and cracking".
101+
A 2-tuple can be provided for different behaviour in negative and
102+
positive electrodes.
103+
* "particle phases": str
104+
Number of phases present in the electrode. A 2-tuple can be provided for
105+
different behaviour in negative and positive electrodes.
106+
For example, set to ("2", "1") for a negative electrode with 2 phases,
107+
e.g. graphite and silicon.
101108
* "particle shape" : str
102109
Sets the model shape of the electrode particles. This is used to
103110
calculate the surface area to volume ratio. Can be "spherical"
@@ -106,12 +113,6 @@ class BatteryModelOptions(pybamm.FuzzyDict):
106113
Sets the model to include a single active particle size or a
107114
distribution of sizes at any macroscale location. Can be "single"
108115
(default) or "distribution". Option applies to both electrodes.
109-
* "particle mechanics" : str
110-
Sets the model to account for mechanical effects such as particle
111-
swelling and cracking. Can be "none" (default), "swelling only",
112-
or "swelling and cracking".
113-
A 2-tuple can be provided for different behaviour in negative and
114-
positive electrodes.
115116
* "SEI" : str
116117
Set the SEI submodel to be used. Options are:
117118
@@ -528,7 +529,7 @@ def __init__(self, extra_options):
528529
):
529530
raise pybamm.OptionError(
530531
"If there are multiple particle phases: 'surface form' cannot be "
531-
"'false', 'particle size' must be 'false', 'particle' must be "
532+
"'false', 'particle size' must be 'single', 'particle' must be "
532533
"'Fickian diffusion'. Also the following must "
533534
"be 'none': 'particle mechanics', "
534535
"'loss of active material', 'lithium plating'"
@@ -554,9 +555,10 @@ def __init__(self, extra_options):
554555
"interface utilisation",
555556
"loss of active material",
556557
"open circuit potential",
557-
"particle mechanics",
558558
"particle",
559+
"particle mechanics",
559560
"particle phases",
561+
"particle size",
560562
"stress-induced diffusion",
561563
]
562564
and isinstance(value, tuple)
@@ -845,12 +847,14 @@ def options(self, extra_options):
845847
raise pybamm.OptionError(
846848
"x-average side reactions cannot be 'false' for SPM models"
847849
)
848-
if isinstance(self, pybamm.lithium_ion.SPM) and not isinstance(
849-
self, pybamm.lithium_ion.MPM
850-
):
851-
if options["particle size"] == "distribution":
850+
if isinstance(self, pybamm.lithium_ion.SPM):
851+
if (
852+
"distribution" in options["particle size"]
853+
and options["surface form"] == "false"
854+
):
852855
raise pybamm.OptionError(
853-
"'particle size' should be 'single' for SPM and SPMe models"
856+
"surface form must be 'algebraic' or 'differential' if "
857+
" 'particle size' contains a 'distribution'"
854858
)
855859
if isinstance(self, pybamm.lead_acid.BaseModel):
856860
if options["thermal"] != "isothermal" and options["dimensionality"] != 0:

pybamm/models/full_battery_models/lithium_ion/mpm.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,5 +54,7 @@ def __init__(self, options=None, name="Many-Particle Model", build=True):
5454
@property
5555
def default_parameter_values(self):
5656
default_params = super().default_parameter_values
57-
default_params = pybamm.get_size_distribution_parameters(default_params)
57+
default_params = pybamm.get_size_distribution_parameters(
58+
default_params, electrode=self.options["working electrode"]
59+
)
5860
return default_params

pybamm/models/submodels/active_material/base_active_material.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,10 +83,11 @@ def _get_standard_active_material_variables(self, eps_solid):
8383
# R_n, R_p. For a size distribution, calculate the area-weighted
8484
# mean using the distribution instead. Then the surface area is
8585
# calculated the same way
86-
if self.options["particle size"] == "single":
86+
domain_options = getattr(self.options, domain)
87+
if domain_options["particle size"] == "single":
8788
R = self.phase_param.R
8889
R_dim = self.phase_param.R_dimensional
89-
elif self.options["particle size"] == "distribution":
90+
elif domain_options["particle size"] == "distribution":
9091
if self.domain == "negative":
9192
R_ = pybamm.standard_spatial_vars.R_n
9293
elif self.domain == "positive":

pybamm/models/submodels/interface/base_interface.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,8 @@ def _get_exchange_current_density(self, variables):
7373
if self.reaction == "lithium-ion main":
7474
# For "particle-size distribution" submodels, take distribution version
7575
# of c_s_surf that depends on particle size.
76-
if self.options["particle size"] == "distribution":
76+
domain_options = getattr(self.options, domain)
77+
if domain_options["particle size"] == "distribution":
7778
c_s_surf = variables[
7879
f"{Domain} {phase_name}particle surface concentration distribution"
7980
]

pybamm/models/submodels/interface/kinetics/base_kinetics.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,16 +72,20 @@ def get_coupled_variables(self, variables):
7272
delta_phi = delta_phi.orphans[0]
7373
# For "particle-size distribution" models, delta_phi must then be
7474
# broadcast to "particle size" domain
75+
domain_options = getattr(self.options, domain)
7576
if (
7677
self.reaction == "lithium-ion main"
77-
and self.options["particle size"] == "distribution"
78+
and domain_options["particle size"] == "distribution"
7879
):
7980
delta_phi = pybamm.PrimaryBroadcast(delta_phi, [f"{domain} particle size"])
8081

8182
# Get exchange-current density
8283
j0 = self._get_exchange_current_density(variables)
8384
# Get open-circuit potential variables and reaction overpotential
84-
if self.options["particle size"] == "distribution":
85+
if (
86+
domain_options["particle size"] == "distribution"
87+
and self.options.electrode_types[domain] == "porous"
88+
):
8589
ocp = variables[
8690
f"{Domain} electrode {reaction_name}open circuit potential distribution"
8791
]

pybamm/models/submodels/interface/open_circuit_potential/current_sigmoid_ocp.py

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,26 @@ def get_coupled_variables(self, variables):
1212
m_lith = pybamm.sigmoid(current, 0, k) # for lithation (current < 0)
1313
m_delith = 1 - m_lith # for delithiation (current > 0)
1414

15-
Domain = self.domain.capitalize()
15+
domain, Domain = self.domain_Domain
1616
phase_name = self.phase_name
1717

1818
if self.reaction == "lithium-ion main":
1919
T = variables[f"{Domain} electrode temperature"]
20-
# Particle size distribution is not yet implemented
21-
if self.options["particle size"] != "distribution":
20+
# For "particle-size distribution" models, take distribution version
21+
# of c_s_surf that depends on particle size.
22+
domain_options = getattr(self.options, domain)
23+
if domain_options["particle size"] == "distribution":
24+
c_s_surf = variables[
25+
f"{Domain} {phase_name}particle surface concentration distribution"
26+
]
27+
# If variable was broadcast, take only the orphan
28+
if isinstance(c_s_surf, pybamm.Broadcast) and isinstance(
29+
T, pybamm.Broadcast
30+
):
31+
c_s_surf = c_s_surf.orphans[0]
32+
T = T.orphans[0]
33+
T = pybamm.PrimaryBroadcast(T, [f"{domain} particle size"])
34+
else:
2235
c_s_surf = variables[
2336
f"{Domain} {phase_name}particle surface concentration"
2437
]

pybamm/models/submodels/interface/open_circuit_potential/single_ocp.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ def get_coupled_variables(self, variables):
1414
T = variables[f"{Domain} electrode temperature"]
1515
# For "particle-size distribution" models, take distribution version
1616
# of c_s_surf that depends on particle size.
17-
if self.options["particle size"] == "distribution":
17+
domain_options = getattr(self.options, domain)
18+
if domain_options["particle size"] == "distribution":
1819
c_s_surf = variables[
1920
f"{Domain} {phase_name}particle surface concentration distribution"
2021
]

0 commit comments

Comments
 (0)