PyBaMM 18 个内置参数集实测:四种静默出错的方式
PyBaMM 18 个内置参数集实测:四种静默出错的方式
站内搜索
直接问 AI

PyBaMM 18 个内置参数集实测:四种静默出错的方式

PyBaMM 自带 18 个参数集,大多数教程随手挑一个——通常是 Chen2020——却不说为什么。我用 PyBaMM 26.5.0 把同一组实验在每个参数集上都跑了一遍。会报错的那些不是问题,KeyError 会准确告诉你缺哪个参数。真正的问题是四种情况:参数集对你的研究是错的,却能一路跑完,一条警告都没有。

PyBaMM 电池建模系列 · 静默错误实测(第 1 / 7 篇):下一篇 PyBaMM Experiment 跑完了却是错的:五个实测陷阱 · 专题页:数据管线 4 篇 + 静默错误实测 7 篇 · RSS 订阅。

一、盒子里到底有什么

18 个里只有 11 个能直接放进标准的 pybamm.lithium_ion.DFN()。其余的需要特定模型——半电池、等效电路、铅酸、钠离子、复合电极和一个 MSMR 示例——把它们塞进普通 DFN 会直接报错,这是好事。

参数集 电芯 / 体系 电压窗口 (V) 能否直接用 DFN
Marquis2019 Kokam SLPB78205130H(默认参数集) 3.105–4.1 能
Chen2020 LG M50,石墨 / NMC811 2.5–4.2 能
OKane2022 LG M50,基于 Chen2020,补齐了退化参数 2.5–4.2 能
ORegan2022 LG M50,带温度相关的热物性和电解液数据 2.5–4.4 能
Ecker2015 Kokam SLPB 75106100 2.5–4.2 能
Mohtat2020 石墨 / NMC532 软包 2.8–4.2 能
Ai2020 Enertech 软包,钴酸锂 3.0–4.2 能
NCA_Kim2011 石墨 / NCA “标称设计”软包 2.7–4.2 能
Prada2013 磷酸铁锂 2.0–3.6 能
Ramadass2004 石墨 / 钴酸锂,由多个来源拼凑 2.8–4.2 能
Chayambuka2022 钠离子 2.0–4.2 能跑(见下文)
Chen2020_composite 石墨 + 硅复合负极 2.5–4.2 不能,需要颗粒相选项
MSMR_Example 石墨 / NMC622,MSMR 热力学 2.8–4.2 不能,需要 MSMR 选项
Ecker2015_graphite_halfcell、OKane2022_graphite_SiOx_halfcell、Xu2019 半电池 — 不能,需要半电池选项
ECM_Example 等效电路,仅作演示 3.2–4.2 不能,需要 ECM 模型
Sulzer2019 铅酸 1.75–2.42 不能,需要铅酸模型

有两句话写在源码里,参数集列表上看不到。Ramadass2004 的文档字符串自称是”一个有点东拼西凑的参数集”,”应谨慎使用”。OKane2022 的则写着”本参数集不声称代表真实参数值”。这两句下文都会用到。

Chayambuka2022 是钠离子参数集,它能在 pybamm.lithium_ion.DFN() 里不报错地跑,因为多孔电极方程的形式相同。PyBaMM 另有 pybamm.sodium_ion 模块;如果研究的就是钠离子,用那个,别指望锂离子模型碰巧接受了这些数字。

二、静默错误之一:”1C” 不是一小时

PyBaMM 把倍率换算成电流,用的是 "Nominal cell capacity [A.h]"——参数集里手填的一个数。而电芯实际能放多少电,取决于完全不同的东西:电极载量和计量比窗口。没有任何机制保证两者一致。

我把每个参数集从满电放到它自己的下限电压,分别用 C/20(接近平衡)和 1C:

参数集 标称 (A·h) C/20 实放 (A·h) 偏差 “1C” 用时
Ramadass2004 1.000 1.775 +77.5% 106.0 分钟
Marquis2019(默认) 0.681 0.872 +28.1% 75.3 分钟
NCA_Kim2011 0.430 0.483 +12.3% 66.4 分钟
Ecker2015 0.156 0.171 +9.4% 65.3 分钟
Ai2020 2.280 2.463 +8.0% 63.5 分钟
Chen2020 5.000 5.144 +2.9% 59.9 分钟
OKane2022 5.000 5.100 +2.0% 59.5 分钟
ORegan2022 5.000 5.010 +0.2% 53.1 分钟
Mohtat2020 5.000 4.961 −0.8% 58.0 分钟
Prada2013 2.300 2.285 −0.7% 50.6 分钟

默认参数集——也就是 pybamm.lithium_ion.DFN().default_parameter_values 拿到的那个——实际能装的电比标称多 28%。用默认参数跑”1C”的人,实际跑的大约是 0.78C,一次”1C”放电要 75 分钟。倍率性能曲线、产热估算、任何按倍率归一化的结果都跟着偏了这个比例。

修法是测一次实际容量写回去,让倍率名副其实。对 Marquis2019,这样做之后 1C 放电从 75.3 分钟变成 58.5 分钟(剩下的差距是 1C 下的极化,正常):

pv = pybamm.ParameterValues("Marquis2019")
q_real = audit("Marquis2019")["actual_capacity_Ah"]   # 0.8717,见第六节
pv.update({"Nominal cell capacity [A.h]": q_real})

或者干脆在实验里用安培指定电流,不用倍率——但慢速步骤一定要写明时长上限,比如 "for 60 hours or until 2.5 V"。不写的话,安培步骤会在 24 小时处被截断,而且不抛异常,详见 PyBaMM Experiment 跑完了却是错的。

三、静默错误之二:Chen2020 里温度几乎不起作用

Chen2020 是最常被选用的参数集,它描述的是 LG M50 电芯——和 OKane2022、ORegan2022 是同一颗。我在三者上跑同样的 1C 放电,分别在 25°C 和 −10°C:

LG M50 参数集 25°C 1C −10°C 1C 变化
Chen2020 4.992 A·h 4.961 A·h −0.6%
OKane2022 4.955 A·h 3.191 A·h −35.6%
ORegan2022 4.422 A·h 1.922 A·h −56.5%

同一颗电芯、同样的温度、同样的电流:4.96、3.19、1.92 A·h。用 Chen2020 做低温研究,结论会是”低温影响不大”,而 PyBaMM 里没有任何东西会提醒你。

原因看得见:把每个能随温度变化的参数在 298.15 K 和 263.15 K 各求一次值,看谁变了:

参数 Chen2020 OKane2022
负极交换电流密度 −84.7% −84.7%
正极交换电流密度 −61.5% −61.5%
电解液扩散系数 不变 −59.8%
电解液电导率 不变 −59.8%
负极颗粒扩散系数 常数,不是函数 −80.3%
正极颗粒扩散系数 常数,不是函数 −73.9%

Chen2020 的电解液扩散系数和电导率函数,签名里接收了 T,函数体里却一次都没用。两个固相扩散系数干脆是常数(3.3e-14 和 4e-15)。只有反应动力学带阿伦尼乌斯项——而 1C 放电的容量受限于传输,不受限于动力学。唯一会变的那一项,恰好对这个测试最不重要。

Ai2020(−0.5%)和 Prada2013(−0.3%)也是这样。只要温度是研究变量,先查这一点;LG M50 的三个参数集里,真正带着温度依赖的是 OKane2022 和 ORegan2022。

电池自己发热时,同样的差距也会出现。打开集总热模型后,Chen2020 在 3C 放电中升温 58 K,容量只多 4%;同一颗电芯的 OKane2022 多出 114%。这一点和其他热模型陷阱的实测见 PyBaMM 热模型实测。

想靠”混搭参数集”来修,先看这个

ORegan2022 的电解液阳离子迁移数来自 Landesfeind & Gasteiger (2019) 的拟合式,它在低温下会变号。在 1 mol/L 时,25°C 是 0.221,10°C 附近过零,−10°C 时是 −0.383。Chen2020 和 OKane2022 用的是常数 0.2594。

在浓溶液理论里负迁移数并非不可能,所以这本身不能算错。为了看它对低温结果贡献多大,我在其他参数都保持低温的前提下单独动它:

ORegan2022,−10°C 1C 容量 相对 25°C 的损失
原样 1.922 A·h 56.5%
迁移数冻结为 25°C 的函数 2.665 A·h 39.7%
迁移数换成 Chen2020 的 0.2594 1.249 A·h 71.7%

迁移数的温度依赖解释了约 30% 的低温损失。但换成另一个参数集里”看起来合理”的常数,结果不是变好而是更糟——因为 ORegan2022 里的迁移数、热力学因子和电导率是一起拟合的。替换其中一个,就破坏了它们之间的一致性。Ramadass2004 自称”东拼西凑”时警告的,正是这件事。

四、静默错误之三:实验字符串会覆盖电芯自己的电压上下限

常见写法是写一套循环流程,然后在几个参数集上套用:

["Discharge at 1C until 2.5 V", "Rest for 1 hour",
 "Charge at C/2 until 4.2 V", "Hold at 4.2 V until C/50"]

它在全部 11 个参数集上都跑完了。包括 Prada2013——一颗上限 3.6 V 的磷酸铁锂电芯——被充到了 4.2 V,以及下限 3.105 V 的 Marquis2019 被放到了 2.5 V。我捕获了这两次运行期间发出的全部警告:零条。

实验字符串里的电压是直接替换参数集的截止电压,而不是和它比较。LFP 过充时,正极颗粒表面计量比降到了 0.0002——电极被推到了开路电位曲线的最边缘。求解器没意见;物理上早就没有意义了。

让流程从参数集里读上下限,而不是手敲数字:

def cycle_within_window(pv, discharge="1C", charge="C/2"):
    lo = pv["Lower voltage cut-off [V]"]
    hi = pv["Upper voltage cut-off [V]"]
    return pybamm.Experiment([
        f"Discharge at {discharge} until {lo} V",
        "Rest for 1 hour",
        f"Charge at {charge} until {hi} V",
        f"Hold at {hi} V until C/50",
    ])

这样同一个 Prada2013 的运行就保持在 2.000–3.600 V 之间。

五、静默错误之四:SEI 到处都能跑,但那不是你这颗电芯的 SEI

我在 11 个参数集上分别构建带常见退化和热模型选项的 DFN。缺参数时会抛出点名的 KeyError,这是好的那种失败:

参数集 SEI(溶剂扩散) SEI(EC 反应) 析锂 颗粒开裂 应力驱动 LAM 集总热模型
OKane2022 ✓ ✓ ✓ ✓ ✓ ✓
Ecker2015 ✓ ✓ ✓ ✗ ✗ ✓
Ai2020 ✓ ✓ ✗ ✓ ✓ ✓
Chen2020 ✓ ✓ ✗ ✗ ✗ ✓
Marquis2019 ✓ ✓ ✗ ✗ ✗ ✓
Mohtat2020 ✓ ✓ ✗ ✗ ✗ ✓
NCA_Kim2011 ✓ ✓ ✗ ✗ ✗ ✓
Ramadass2004 ✓ ✓ ✗ ✗ ✗ ✗
ORegan2022 ✗ ✗ ✗ ✗ ✗ ✓
Prada2013 ✗ ✗ ✗ ✗ ✗ ✗
Chayambuka2022 ✗ ✗ ✗ ✗ ✗ ✗

OKane2022 是唯一支持全部选项的参数集,这也是它常被当作退化研究起点的原因。缺的参数也都在意料之中:析锂缺 Exchange-current density for stripping [A.m-2],开裂缺 Negative electrode initial crack length [m],三个不支持 SEI 的参数集缺 Initial SEI thickness [m]。

安静的陷阱在 SEI 那一列。8 个参数集都能带 SEI 构建——但 Chen2020、Marquis2019、Mohtat2020 和 Ai2020 的文档字符串写的是同一句话:”SEI 参数是取自 Ramadass2004、Ploehn2004 等论文的 SEI 生长示例参数”。模型能跑,也能画出一条容量衰减曲线。那是一条用别的电芯借来的通用 SEI 参数算出的曲线,不是针对参数集里那颗电芯拟合的。如果你要拿衰减速率去和实验数据比,这个区别就是整个研究。

OKane2022 也是如此:它的 SEI 速率常数就是 Chen2020 的通用示例值,只有活化能和每个 SEI 分子对应的锂数不同。这对衰减曲线意味着什么,见 PyBaMM SEI 与析锂实测。

六、信任一个参数集之前可以跑的检查

下面覆盖了需要跑仿真才能发现的两种静默错误,每个参数集几秒钟:

import pybamm


def audit(name, T_cold=263.15):
    pv = pybamm.ParameterValues(name)
    q_nom = pv["Nominal cell capacity [A.h]"]
    v_lo = pv["Lower voltage cut-off [V]"]
    v_hi = pv["Upper voltage cut-off [V]"]

    def capacity(rate, T=None):
        p = pybamm.ParameterValues(name)
        if T is not None:
            p.update({"Ambient temperature [K]": T, "Initial temperature [K]": T})
        sim = pybamm.Simulation(
            pybamm.lithium_ion.DFN(),
            parameter_values=p,
            experiment=pybamm.Experiment([f"Discharge at {rate} until {v_lo} V"]),
        )
        return float(sim.solve(initial_soc=1.0)["Discharge capacity [A.h]"].entries[-1])

    q_slow = capacity("C/20")
    q_warm = capacity("1C")
    q_cold = capacity("1C", T=T_cold)
    return {
        "nominal_vs_actual_%": round(100 * (q_slow / q_nom - 1), 1),
        "capacity_change_at_T_cold_%": round(100 * (q_cold / q_warm - 1), 1),
        "voltage_window_V": (v_lo, v_hi),
        "actual_capacity_Ah": round(q_slow, 4),
    }
Marquis2019  {'nominal_vs_actual_%': 28.1, 'capacity_change_at_T_cold_%': -26.6, 'voltage_window_V': (3.105, 4.1), ...}
Chen2020     {'nominal_vs_actual_%': 2.9,  'capacity_change_at_T_cold_%': -0.6,  'voltage_window_V': (2.5, 4.2), ...}
OKane2022    {'nominal_vs_actual_%': 2.0,  'capacity_change_at_T_cold_%': -35.6, 'voltage_window_V': (2.5, 4.2), ...}

把它当成三个问题读:标称容量和实际容量差在几个百分点以内,还是倍率需要重新标定?结果到底随不随温度变?你的实验字符串必须遵守什么电压窗口?

至于退化,先读参数集的文档字符串——help(pybamm.parameter_sets["Chen2020"])——再读衰减曲线。”示例参数”这几个字,就是结果和示意图之间的区别。

七、测量方式

  • PyBaMM 26.5.0,Python 3.13,Apple M3。默认求解器(IDAKLUSolver),除特别说明外为默认选项的 pybamm.lithium_ion.DFN()。
  • 每次放电都从 initial_soc=1.0 开始,到参数集自己的 Lower voltage cut-off [V] 结束。”实际容量”取 C/20 放电容量。
  • 低温运行把 Ambient temperature [K] 和 Initial temperature [K] 都设为 263.15 K,模型为默认的等温模型。
  • 选项兼容性指 Simulation.build() 成功,不代表由此得到的退化速率在物理上经过校准。
  • 每个参数集在独立进程里带超时运行,一个卡住不会污染其他结果。实际没有一个卡住。

References

相关阅读:PyBaMM 参数拟合与可辨识性——内置参数集不够用时该怎么办;PyBaMM 求解器收敛失败排查——参数集能跑但求解器不收敛时;PyBaMM 架构——SPM、SPMe 和 DFN 怎么选;PyBaMM 热模型实测——这些参数集带的热参数靠不靠得住。

发表回复

向下探索