-
-
Notifications
You must be signed in to change notification settings - Fork 553
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
I2376 bpx #2555
Merged
I2376 bpx #2555
Changes from 25 commits
Commits
Show all changes
27 commits
Select commit
Hold shift + click to select a range
326d5cf
#2376 added a draft bpx -> pybamm converter
martinjrobins 7feb92e
#2376 add a bpx test
martinjrobins f36845d
#2376, added bpx conversions and some defaults, remove events in simu…
martinjrobins 4e8d34c
#2376 match test to parameterised battery, add diffusivity and conduc…
martinjrobins c682227
#2375 turn events back on
martinjrobins b38f3a4
#2376 debug function parameters
rtimms 47f8ad2
#2376 debug function parameters
rtimms 4f84c2a
Merge branch 'develop' into i2376-bpx
rtimms da98f9a
#2376 remove some print stmts
martinjrobins d149c7a
#2376 update to new schema
rtimms 32a7a3f
#2376 add sto windows
rtimms ee1944d
#2376 entropic change
rtimms bf0d676
#2376 debug init events
rtimms 16902f2
#2376 debug init events
rtimms a1d2e55
Merge branch 'develop' into i2376-bpx
rtimms 80e26e1
#2376 update bpx version
rtimms 6528ea9
#2376 changelog
rtimms 64c01e9
#2376 changelog
rtimms 4149def
style: pre-commit fixes
pre-commit-ci[bot] e5874bb
#2376 update test
rtimms c1f9560
Merge branch 'i2376-bpx' of https://github.com/pybamm-team/PyBaMM int…
rtimms 5325def
style: pre-commit fixes
pre-commit-ci[bot] 3166814
#2376 coverage
rtimms 30ae779
Merge branch 'i2376-bpx' of https://github.com/pybamm-team/PyBaMM int…
rtimms 0084c7a
Merge branch 'develop' into i2376-bpx
rtimms 43e2bb6
Merge branch 'develop' into i2376-bpx
rtimms dcd39ab
#2376 make private
rtimms File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,377 @@ | ||
from bpx import BPX, Function, InterpolatedTable | ||
import pybamm | ||
import math | ||
from dataclasses import dataclass | ||
import numpy as np | ||
from pybamm import constants | ||
from pybamm import exp | ||
import copy | ||
|
||
|
||
import types | ||
import functools | ||
|
||
|
||
def copy_func(f): | ||
"""Based on http://stackoverflow.com/a/6528148/190597 (Glenn Maynard)""" | ||
g = types.FunctionType( | ||
f.__code__, | ||
f.__globals__, | ||
name=f.__name__, | ||
argdefs=f.__defaults__, | ||
closure=f.__closure__, | ||
) | ||
g = functools.update_wrapper(g, f) | ||
g.__kwdefaults__ = f.__kwdefaults__ | ||
return g | ||
|
||
|
||
@dataclass | ||
class Domain: | ||
name: str | ||
pre_name: str | ||
short_pre_name: str | ||
|
||
|
||
cell = Domain(name="cell", pre_name="", short_pre_name="") | ||
negative_electrode = Domain( | ||
name="negative electrode", | ||
pre_name="Negative electrode ", | ||
short_pre_name="Negative ", | ||
) | ||
positive_electrode = Domain( | ||
name="positive electrode", | ||
pre_name="Positive electrode ", | ||
short_pre_name="Positive ", | ||
) | ||
electrolyte = Domain(name="electrolyte", pre_name="Electrolyte ", short_pre_name="") | ||
separator = Domain(name="separator", pre_name="Separator ", short_pre_name="") | ||
experiment = Domain(name="experiment", pre_name="", short_pre_name="") | ||
|
||
|
||
def bpx_to_param_dict(bpx: BPX) -> dict: | ||
pybamm_dict = {} | ||
pybamm_dict = _bpx_to_param_dict(bpx.parameterisation.cell, pybamm_dict, cell) | ||
pybamm_dict = _bpx_to_param_dict( | ||
bpx.parameterisation.negative_electrode, pybamm_dict, negative_electrode | ||
) | ||
pybamm_dict = _bpx_to_param_dict( | ||
bpx.parameterisation.positive_electrode, pybamm_dict, positive_electrode | ||
) | ||
pybamm_dict = _bpx_to_param_dict( | ||
bpx.parameterisation.electrolyte, pybamm_dict, electrolyte | ||
) | ||
pybamm_dict = _bpx_to_param_dict( | ||
bpx.parameterisation.separator, pybamm_dict, separator | ||
) | ||
pybamm_dict = _bpx_to_param_dict( | ||
bpx.parameterisation.separator, pybamm_dict, experiment | ||
) | ||
|
||
# set a default current function and typical current based on the nominal capacity | ||
# i.e. a default C-rate of 1 | ||
pybamm_dict["Current function [A]"] = pybamm_dict["Nominal cell capacity [A.h]"] | ||
pybamm_dict["Typical current [A]"] = pybamm_dict["Nominal cell capacity [A.h]"] | ||
|
||
# number of electrons in reaction (1 for li-ion) | ||
for domain in [negative_electrode, positive_electrode]: | ||
pybamm_dict[domain.pre_name + "electrons in reaction"] = 1.0 | ||
|
||
# activity | ||
pybamm_dict["1 + dlnf/dlnc"] = 1.0 | ||
|
||
# typical electrolyte concentration | ||
pybamm_dict["Typical electrolyte concentration [mol.m-3]"] = pybamm_dict[ | ||
"Initial concentration in electrolyte [mol.m-3]" | ||
] | ||
|
||
# assume Bruggeman relation | ||
for domain in [negative_electrode, separator, positive_electrode]: | ||
pybamm_dict[domain.pre_name + "Bruggeman coefficient (electrolyte)"] = 1.5 | ||
pybamm_dict[domain.pre_name + "Bruggeman coefficient (electrode)"] = 1.5 | ||
|
||
# BPX is for single cell in series, user can change this later | ||
pybamm_dict["Number of cells connected in series to make a battery"] = 1 | ||
pybamm_dict[ | ||
"Number of electrodes connected in parallel to make a cell" | ||
] = pybamm_dict["Number of electrode pairs connected in parallel to make a cell"] | ||
|
||
# electrode area | ||
equal_len_width = math.sqrt(pybamm_dict["Electrode area [m2]"]) | ||
pybamm_dict["Electrode width [m]"] = equal_len_width | ||
pybamm_dict["Electrode height [m]"] = equal_len_width | ||
|
||
# surface area | ||
pybamm_dict["Cell cooling surface area [m2]"] = pybamm_dict[ | ||
"External surface area [m2]" | ||
] | ||
|
||
# volume | ||
pybamm_dict["Cell volume [m3]"] = pybamm_dict["Volume [m3]"] | ||
|
||
# reference temperature | ||
T_ref = pybamm_dict["Reference temperature [K]"] | ||
|
||
# lumped parameters | ||
for name in [ | ||
"Specific heat capacity [J.K-1.kg-1]", | ||
"Density [kg.m-3]", | ||
"Thermal conductivity [W.m-1.K-1]", | ||
]: | ||
for domain in [negative_electrode, positive_electrode, separator]: | ||
pybamm_name = domain.pre_name + name[:1].lower() + name[1:] | ||
if name in pybamm_dict: | ||
pybamm_dict[pybamm_name] = pybamm_dict[name] | ||
|
||
# BET surface area | ||
for domain in [negative_electrode, positive_electrode]: | ||
pybamm_dict[domain.pre_name + "active material volume fraction"] = ( | ||
pybamm_dict[domain.pre_name + "surface area per unit volume [m-1]"] | ||
* pybamm_dict[domain.short_pre_name + "particle radius [m]"] | ||
) / 3.0 | ||
Comment on lines
+130
to
+133
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. doing it this way round is very strange |
||
|
||
# transport efficiency | ||
for domain in [negative_electrode, separator, positive_electrode]: | ||
pybamm_dict[domain.pre_name + "porosity"] = pybamm_dict[ | ||
domain.pre_name + "transport efficiency" | ||
] ** (1.0 / 1.5) | ||
|
||
# TODO: allow setting function parameters in a loop over domains | ||
|
||
# entropic change | ||
|
||
# negative electrode | ||
U_n = pybamm_dict[ | ||
negative_electrode.pre_name + "entropic change coefficient [V.K-1]" | ||
] | ||
if callable(U_n): | ||
|
||
def negative_electrode_entropic_change(sto, c_s_max): | ||
return U_n(sto) | ||
|
||
else: | ||
|
||
def negative_electrode_entropic_change(sto, c_s_max): | ||
return U_n | ||
|
||
pybamm_dict[ | ||
negative_electrode.pre_name + "OCP entropic change [V.K-1]" | ||
] = negative_electrode_entropic_change | ||
|
||
# positive electrode | ||
U_p = pybamm_dict[ | ||
positive_electrode.pre_name + "entropic change coefficient [V.K-1]" | ||
] | ||
if callable(U_p): | ||
|
||
def positive_electrode_entropic_change(sto, c_s_max): | ||
return U_p(sto) | ||
|
||
else: | ||
|
||
def positive_electrode_entropic_change(sto, c_s_max): | ||
return U_p | ||
|
||
pybamm_dict[ | ||
positive_electrode.pre_name + "OCP entropic change [V.K-1]" | ||
] = positive_electrode_entropic_change | ||
|
||
# reaction rates in pybamm exchange current is defined j0 = k * sqrt(ce * cs * | ||
# (cs-cs_max)) in BPX exchange current is defined j0 = F * k_norm * sqrt((ce/ce0) * | ||
# (cs/cs_max) * (1-cs/cs_max)) | ||
c_e = pybamm_dict["Typical electrolyte concentration [mol.m-3]"] | ||
F = 96485 | ||
|
||
# negative electrode | ||
c_n_max = pybamm_dict[ | ||
"Maximum concentration in " + negative_electrode.pre_name.lower() + "[mol.m-3]" | ||
] | ||
k_n_norm = pybamm_dict[ | ||
negative_electrode.pre_name + "reaction rate constant [mol.m-2.s-1]" | ||
] | ||
E_a_n = pybamm_dict.get( | ||
negative_electrode.pre_name + "reaction rate activation energy [J.mol-1]", 0.0 | ||
) | ||
k_n = k_n_norm * F / (c_n_max * c_e**0.5) | ||
|
||
def negative_electrode_exchange_current_density(c_e, c_s_surf, c_s_max, T): | ||
k_ref = k_n # (A/m2)(m3/mol)**1.5 - includes ref concentrations | ||
|
||
arrhenius = exp(E_a_n / constants.R * (1 / T_ref - 1 / T)) | ||
return ( | ||
k_ref | ||
* arrhenius | ||
* c_e**0.5 | ||
* c_s_surf**0.5 | ||
* (c_s_max - c_s_surf) ** 0.5 | ||
) | ||
|
||
pybamm_dict[ | ||
negative_electrode.pre_name + "exchange-current density [A.m-2]" | ||
] = copy_func(negative_electrode_exchange_current_density) | ||
|
||
# positive electrode | ||
c_p_max = pybamm_dict[ | ||
"Maximum concentration in " + positive_electrode.pre_name.lower() + "[mol.m-3]" | ||
] | ||
k_p_norm = pybamm_dict[ | ||
positive_electrode.pre_name + "reaction rate constant [mol.m-2.s-1]" | ||
] | ||
E_a_p = pybamm_dict.get( | ||
positive_electrode.pre_name + "reaction rate activation energy [J.mol-1]", 0.0 | ||
) | ||
k_p = k_p_norm * F / (c_p_max * c_e**0.5) | ||
|
||
def positive_electrode_exchange_current_density(c_e, c_s_surf, c_s_max, T): | ||
k_ref = k_p # (A/m2)(m3/mol)**1.5 - includes ref concentrations | ||
|
||
arrhenius = exp(E_a_p / constants.R * (1 / T_ref - 1 / T)) | ||
return ( | ||
k_ref | ||
* arrhenius | ||
* c_e**0.5 | ||
* c_s_surf**0.5 | ||
* (c_s_max - c_s_surf) ** 0.5 | ||
) | ||
|
||
pybamm_dict[domain.pre_name + "exchange-current density [A.m-2]"] = copy_func( | ||
positive_electrode_exchange_current_density | ||
) | ||
|
||
# diffusivity | ||
|
||
# negative electrode | ||
E_a = pybamm_dict.get( | ||
negative_electrode.pre_name + "diffusivity activation energy [J.mol-1]", 0.0 | ||
) | ||
D_n_ref = pybamm_dict[negative_electrode.pre_name + "diffusivity [m2.s-1]"] | ||
|
||
if callable(D_n_ref): | ||
|
||
def negative_electrode_diffusivity(sto, T): | ||
arrhenius = exp(E_a / constants.R * (1 / T_ref - 1 / T)) | ||
return arrhenius * D_n_ref(sto) | ||
|
||
else: | ||
|
||
def negative_electrode_diffusivity(sto, T): | ||
arrhenius = exp(E_a / constants.R * (1 / T_ref - 1 / T)) | ||
return arrhenius * D_n_ref | ||
|
||
pybamm_dict[negative_electrode.pre_name + "diffusivity [m2.s-1]"] = copy_func( | ||
negative_electrode_diffusivity | ||
) | ||
|
||
# positive electrode | ||
E_a = pybamm_dict.get( | ||
positive_electrode.pre_name + "diffusivity activation energy [J.mol-1]", 0.0 | ||
) | ||
D_p_ref = pybamm_dict[positive_electrode.pre_name + "diffusivity [m2.s-1]"] | ||
|
||
if callable(D_p_ref): | ||
|
||
def positive_electrode_diffusivity(sto, T): | ||
arrhenius = exp(E_a / constants.R * (1 / T_ref - 1 / T)) | ||
return arrhenius * D_p_ref(sto) | ||
|
||
else: | ||
|
||
def positive_electrode_diffusivity(sto, T): | ||
arrhenius = exp(E_a / constants.R * (1 / T_ref - 1 / T)) | ||
return arrhenius * D_p_ref | ||
|
||
pybamm_dict[positive_electrode.pre_name + "diffusivity [m2.s-1]"] = copy_func( | ||
positive_electrode_diffusivity | ||
) | ||
|
||
# electrolyte | ||
E_a = pybamm_dict.get( | ||
electrolyte.pre_name + "diffusivity activation energy [J.mol-1]", 0.0 | ||
) | ||
D_e_ref = pybamm_dict[electrolyte.pre_name + "diffusivity [m2.s-1]"] | ||
|
||
if callable(D_e_ref): | ||
|
||
def electrolyte_diffusivity(sto, T): | ||
arrhenius = exp(E_a / constants.R * (1 / T_ref - 1 / T)) | ||
return arrhenius * D_e_ref(sto) | ||
|
||
else: | ||
|
||
def electrolyte_diffusivity(sto, T): | ||
arrhenius = exp(E_a / constants.R * (1 / T_ref - 1 / T)) | ||
return arrhenius * D_e_ref | ||
|
||
pybamm_dict[electrolyte.pre_name + "diffusivity [m2.s-1]"] = copy_func( | ||
electrolyte_diffusivity | ||
) | ||
|
||
# conductivity | ||
E_a = pybamm_dict.get( | ||
electrolyte.pre_name + "conductivity activation energy [J.mol-1]", 0.0 | ||
) | ||
C_ref_value = pybamm_dict[electrolyte.pre_name + "conductivity [S.m-1]"] | ||
|
||
if callable(C_ref_value): | ||
C_ref_fun = copy.copy(C_ref_value) | ||
|
||
def conductivity(c_e, T): | ||
C_ref = C_ref_fun(c_e) | ||
arrhenius = exp(E_a / constants.R * (1 / T_ref - 1 / T)) | ||
return arrhenius * C_ref | ||
|
||
else: | ||
C_ref_number = C_ref_value | ||
|
||
def conductivity(c_e, T): | ||
C_ref = C_ref_number | ||
arrhenius = exp(E_a / constants.R * (1 / T_ref - 1 / T)) | ||
return arrhenius * C_ref | ||
|
||
pybamm_dict[electrolyte.pre_name + "conductivity [S.m-1]"] = copy_func(conductivity) | ||
|
||
return pybamm_dict | ||
|
||
|
||
preamble = "from pybamm import exp, tanh, cosh\n\n" | ||
|
||
|
||
def _bpx_to_param_dict(instance: BPX, pybamm_dict: dict, domain: Domain) -> dict: | ||
for name, field in instance.__fields__.items(): | ||
value = getattr(instance, name) | ||
if value is None: | ||
continue | ||
elif isinstance(value, Function): | ||
value = value.to_python_function(preamble=preamble) | ||
elif isinstance(value, InterpolatedTable): | ||
timescale = 1 | ||
x = np.array(value.x) | ||
y = np.array(value.y) | ||
interpolator = "linear" | ||
value = pybamm.Interpolant( | ||
[x], y, pybamm.t * timescale, name=name, interpolator=interpolator | ||
) | ||
|
||
pybamm_name = field.field_info.alias | ||
pybamm_name_lower = pybamm_name[:1].lower() + pybamm_name[1:] | ||
if pybamm_name.startswith("Initial concentration") or pybamm_name.startswith( | ||
"Maximum concentration" | ||
): | ||
init_len = len("Initial concentration ") | ||
pybamm_name = ( | ||
pybamm_name[:init_len] | ||
+ "in " | ||
+ domain.pre_name.lower() | ||
+ pybamm_name[init_len:] | ||
) | ||
elif pybamm_name.startswith("Particle radius"): | ||
pybamm_name = domain.short_pre_name + pybamm_name_lower | ||
elif pybamm_name.startswith("OCP"): | ||
pybamm_name = domain.pre_name + pybamm_name | ||
elif pybamm_name.startswith("Cation transference number"): | ||
pybamm_name = pybamm_name | ||
elif domain.pre_name != "": | ||
pybamm_name = domain.pre_name + pybamm_name_lower | ||
|
||
pybamm_dict[pybamm_name] = value | ||
return pybamm_dict |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this should be a private file with private function names (all start with underscore). I know we haven't been good about this in the past but better late than never (see #2427 )