PyBaMM 热模型实测:产热为零、缺失的熵热与从 25 °C 开始的低温仿真
PyBaMM 热模型实测:产热为零、缺失的熵热与从 25 °C 开始的低温仿真
站内搜索
直接问 AI

PyBaMM 热模型实测:产热为零、缺失的熵热与从 25 °C 开始的低温仿真

在 PyBaMM 里打开热模型只需要一个选项:DFN({"thermal": "lumped"})。仿真照常跑完,出来一条温度曲线,看上去也合情合理。下面这几个问题的共同点是看起来都不像错:一个按构造恒为零的产热变量;四个参数集直接省掉的一整项热源;一个因为几何写法而根本热不起来的电池;以及一个悄悄从 25 °C 开始的”低温”仿真。文中所有数字都是用 PyBaMM 26.5.0,在带热参数的 8 个内置锂离子参数集上实测得到的。

PyBaMM 电池建模系列 · 静默错误实测(第 3 / 7 篇):上一篇 PyBaMM Experiment 跑完了却是错的:五个实测陷阱 · 下一篇 PyBaMM 的容量衰减到底在量什么:80% capacity、Capacity [A.h] 与 Loss of capacity 实测 · 专题页:数据管线 4 篇 + 静默错误实测 7 篇 · RSS 订阅。

一、默认模型是等温的,而且它的产热变量全是零

pybamm.lithium_ion.DFN() 默认 "thermal": "isothermal":电池温度在每个位置、每个时刻都等于环境温度,不解能量方程。这个默认值本身没问题。不那么显然的是产热变量里装的是什么:

Chen2020,1C 放电 "Total heating [W]" 峰值
DFN() 每个时间点都是 0.0 W
DFN({"calculate heat source for isothermal models": "true"}) 1.04 W

等温子模型(pybamm/models/submodels/thermal/isothermal.py)里,只要没打开这个选项,欧姆热、不可逆热、可逆热、总产热和表面散热全部被赋成标量 0。变量是存在的,sol["Total heating [W]"] 也返回一个长度正确的数组——只是里面全是零。用默认模型做的”产热分布图”或者”这个工况产多少热”的表,报告的是一个完全不发热的电池。

第二个后果:等温模型完全忽略 "Initial temperature [K]"。它的温度就是环境温度。只把初始温度设成 263.15 K,8 个参数集的 1C 容量和 25 °C 时逐位相同(保留三位小数);只设环境温度,才得到完整的低温结果。

二、等温和集总热模型之间不是”小修正”

很容易把集总(lumped)模型当成把答案轻轻推一下的精化。下面是两种模型下的 1C 和 3C 放电:

参数集 1C:等温 → 集总 温升 3C:等温 → 集总 温升
Chen2020 4.992 → 5.001 A·h(+0.2%) 13.8 K 2.326 → 2.418 A·h(+4.0%) 58.0 K
OKane2022 4.955 → 5.005 A·h(+1.0%) 12.9 K 2.333 → 5.000 A·h(+114%) 49.6 K
ORegan2022 4.422 → 4.736 A·h(+7.1%) 15.6 K 1.110 → 3.506 A·h(+216%) 43.3 K
Ai2020 2.413 → 2.414 A·h(0.0%) 3.6 K 2.308 → 2.319 A·h(+0.5%) 13.8 K
Mohtat2020 4.836 → 4.852 A·h(+0.3%) 3.0 K 4.589 → 4.713 A·h(+2.7%) 9.3 K
Marquis2019 0.854 → 0.854 A·h(0.0%) 0.4 K 0.821 → 0.823 A·h(+0.2%) 1.2 K
Ecker2015 0.170 → 0.170 A·h(0.0%) 0.1 K 0.168 → 0.168 A·h(+0.1%) 0.6 K
NCA_Kim2011 0.476 → 0.476 A·h(0.0%) 0.0 K 0.461 → 0.461 A·h(0.0%) 0.1 K

这张表里其实混着三件事,下面各用一节展开。

  • 三个 LG M50 参数集在 3C 下被集总模型加热了 43–58 K,但这件事有没有影响取决于电化学部分。OKane2022 和 ORegan2022 随着升温分别多放出 114% 和 216%。Chen2020 升温 58 K,容量只多 4%,因为它的电解液和颗粒输运参数都不随温度变(见参数集那篇里的温度表)。热模型在跑,电化学几乎不理它。
  • 温升有多大,由一个没人为你的测试台测过的换热系数决定(第三节)。
  • 有四个参数集几乎完全热不起来,原因和化学体系毫无关系(第四节)。

两个模型都谈不上”正确”。等温等于完美散热——电池夹在一块很大的热沉上;默认换热系数下的集总模型大致相当于电池放在静止空气里。选跟你要复现的测试条件一致的那个,并且知道在高倍率下两者可以差三倍。

三、一个没测过的数决定了高倍率的答案

集总模型通过 "Total heat transfer coefficient [W.m-2.K-1]" 乘以 "Cell cooling surface area [m2]" 散热。内置参数集的默认值是 10(Chen2020、OKane2022、ORegan2022、Marquis2019、Ecker2015)、5(Mohtat2020)、25(NCA_Kim2011)或 35(Ai2020)。在 OKane2022 上以 3C 扫这个系数:

换热系数 温升 3C 容量
等温(无穷大) 0 K 2.333 A·h
1000 W/m²·K 1.9 K 2.598 A·h
100 W/m²·K 15.1 K 4.647 A·h
25 W/m²·K 31.8 K 4.934 A·h
10 W/m²·K(默认) 49.6 K 5.000 A·h
5 W/m²·K 62.3 K 5.025 A·h
1 W/m²·K 78.2 K 5.047 A·h

仅仅一个边界条件,就让 3C 容量在 2.3 到 5.0 A·h 之间变动。再看默认那一行实际在说什么:电池在 3C 下放出满容量,是因为它的温度峰值接近 75 °C。用这个模型得出的”3C 没问题”,说的是一颗很烫的电池——连同它对老化和安全意味着的一切——而不是室温下的电池。

换热系数不是电芯的属性,它描述的是夹具、气流和相邻电芯。要和实测对比,这个数就必须来自你的测试条件:在壳体上贴一个热电偶,记录一次已知热脉冲后的冷却曲线,就足以拟合出来。不能直接用参数集里的值。

四、八个参数集里有四个描述的是”一张两面散热的极片”

集总电池能不能热起来,由热时间常数决定:τ = 热容 ÷(h × 散热面积)。逐个参数集算出来:

参数集 散热面积 ÷ 体积 热容 h τ 3C 温升
NCA_Kim2011 12,143 m⁻¹ 9.4 J/K 25 7 s 0.1 K
Ecker2015 11,316 m⁻¹ 4.2 J/K 10 24 s 0.6 K
Marquis2019 7,295 m⁻¹ 14.1 J/K 10 25 s 1.2 K
Mohtat2020 10,459 m⁻¹ 121.1 J/K 5 59 s 9.3 K
Ai2020 394 m⁻¹ 41.3 J/K 35 195 s 13.8 K
Chen2020、OKane2022 219 m⁻¹ 42.8 J/K 10 806 s 58.0 / 49.6 K
ORegan2022 219 m⁻¹ 60.6 J/K 10 1141 s 43.3 K

前四个很显眼。它们的 "Cell cooling surface area [m2]" 都等于电极高度 × 电极宽度 × 2(误差 0.5% 以内),"Cell volume [m3]" 等于建模的那一层电极叠层的体积。换句话说,这里的”电池”就是一张极片,两个面都在散热。这种几何散热快到集总温度在几秒到一分钟内就跟上环境温度。把这四个参数集切到 "lumped" 几乎什么都不变——不是热模型没生效,而是它描述的这个”电池”根本存不住热。Mohtat2020 在 3C 下还能升 9.3 K,只是因为它的可逆热异常大(下一节)。

相比之下,LG M50 的几个参数集描述的是一个真实的 21700 钢壳:0.00531 m² 正好是直径 21 mm、高 70 mm 圆柱的侧面加两个端面,电极叠层占壳体体积的 85%。如果你用前四个参数集之一去模拟一颗封装好的电池,在相信任何温度之前,先把几何换成真实的壳体或软包尺寸。

五、四个参数集没有可逆热

电池产热主要有三部分:电解液和固相里的欧姆热、反应过电位带来的不可逆热,以及可逆热(熵热)。可逆热正比于开路电位对温度的导数(Bernardi、Pawlikowski、Newman,1985)。PyBaMM 里这个导数是 "… electrode OCP entropic change [V.K-1]"。Chen2020、OKane2022、Ecker2015 和 NCA_Kim2011 里,它对正负两极都恰好是 0.0。仿真在整整一项热源被关掉的情况下照常运行,没有任何提示。

在确实包含这项的四个参数集里,它有多重要(集总模型,可逆热净值占总产热的比例):

参数集 C/5 1C 3C
Mohtat2020 96.4% 85.7% 73.6%
Ai2020 78.5% 46.8% 30.7%
ORegan2022 28.9% 19.3% 26.8%
Marquis2019 20.8% 5.5% 2.8%
Chen2020、OKane2022、Ecker2015、NCA_Kim2011 按构造为 0%

在同样这四个参数集里,把它去掉对预测温升的影响:

参数集 C/5 温升:原始 → 熵系数置零 1C 温升:原始 → 熵系数置零
ORegan2022 2.46 → 0.80 K(−68%) 15.6 → 11.0 K(−30%)
Ai2020 0.53 → 0.13 K(−75%) 3.58 → 1.70 K(−53%)
Mohtat2020 0.63 → 0.02 K(−97%) 3.04 → 0.25 K(−92%)
Marquis2019 0.069 → 0.011 K(−85%) 0.37 → 0.16 K(−56%)

规律和预期一致:可逆热随电流线性增长,不可逆热和欧姆热大致随电流平方增长,所以缺掉的这一项在低倍率下影响最大——恰恰是慢速循环、工况平均和日历+循环老化研究所在的区间。参数集之间的差距(1C 下从 6% 到 86%)本身也是一个警告:熵系数随化学体系和荷电状态而变,从别的参数集借一个过来,并不比留成零好多少。

LG M50 是唯一能直接对照的电芯。Chen2020 和 OKane2022 描述它时用的是零熵系数,正负极和隔膜的比热都是 700 J/kg·K;ORegan2022 则带着同一颗电芯的实测热参数(O’Regan 等,2022),包括熵系数和大 42% 的热容(60.6 对 42.8 J/K)。如果你需要 LG M50 的温度,应该从 ORegan2022 开始。

六、从 25 °C 开始的”低温启动”

这是代价最高的一个。要模拟低温放电,最自然的写法是:

pv.update({"Ambient temperature [K]": 253.15})

在等温模型里这是对的。在集总模型里不对:电池温度从 "Initial temperature [K]" 出发——所有内置参数集里都是 298.15 K——然后按第四节的时间常数向寒冷的环境温度弛豫。对 LG M50 来说这需要 13 到 19 分钟,差不多就是一次 3C 放电的时长。于是仿真里的电池大部分时间是在温热状态下干活的。

集总模型,放电到截止电压 只设环境温度(电池从 25 °C 开始) 环境温度和初始温度都设
OKane2022,−20 °C,3C 19.7 分钟放出 4.932 A·h 1.1 分钟放出 0.275 A·h
ORegan2022,−10 °C,3C 12.7 分钟放出 3.170 A·h 0.9 分钟放出 0.232 A·h
ORegan2022,−20 °C,2C 13.2 分钟放出 2.207 A·h 1.3 分钟放出 0.212 A·h
ORegan2022,−20 °C,3C 12.3 分钟放出 3.082 A·h 0.5 分钟放出 0.125 A·h

真正冷的电池一分钟之内就碰到截止电压,自发热还来不及救它。同一个仿真只少设一个参数,报告的放电量就多出 10 到 25 倍,得出一颗”−20 °C 下扛得住 3C”的电池。

如果电池熬过了第一分钟,两种写法的最终容量几乎一样——到放电末期自发热已经占主导,容量是在那时决定的。误差转移到了功率预测上:

集总模型,1C 10 s 时电压(从 25 °C 开始) 10 s 时电压(从低温开始) 容量(两种)
OKane2022,−10 °C 4.006 V 3.886 V 4.868 A·h
Mohtat2020,−20 °C 4.105 V 3.917 V 2.909 / 2.906 A·h
ORegan2022,−20 °C 4.108 V 3.888 V 3.414 / 3.384 A·h

头几秒电压偏乐观 0.12–0.22 V,而这恰恰是低温启动或功率能力研究要的那个量。

如果按 Experiment 接口的本意去用,它处理得比直接改参数好。在 26.5 里,第一个步骤的温度会被复制进 "Initial temperature [K]"(见 simulation.py 的 _set_up_and_parameterise_experiment)。所以下面两种写法都会让电池从低温开始,而且都复现了上面 0.275 A·h 的结果:

step = pybamm.step.string("Discharge at 3C until 2.5 V", temperature="-20oC")
experiment = pybamm.Experiment([step])

experiment = pybamm.Experiment(["Discharge at 3C until 2.5 V"], temperature=253.15)

把温度写进字符串里——"Discharge at 3C until 2.5 V at -20oC"——会抛出 ValueError: Temperature must be specified as a keyword argument instead of in the string,至少是大声地失败。静默的是前面那种:在 ParameterValues 里改了环境温度,却没动初始温度。

七、每次热仿真之后跑一遍的检查

下面这个函数接收一个已经求解过的 pybamm.Simulation,对上面每一种”能跑完但会误导”的设置给出警告:

import numpy as np
import pybamm


def thermal_audit(sim):
    """Warnings for a solved pybamm.Simulation whose thermal set-up runs but misleads."""
    opts, pv, sol = sim.model.options, sim.parameter_values, sim.solution
    out = []
    if opts["thermal"] == "isothermal":
        if opts["calculate heat source for isothermal models"] == "false":
            out.append("isothermal: every heating variable is 0 unless "
                       "'calculate heat source for isothermal models' is 'true'")
        return out

    def first(name):
        return float(np.ravel(sol[name].entries)[0])

    T0 = first("Volume-averaged cell temperature [K]")
    T_amb = first("Volume-averaged ambient temperature [K]")
    if abs(T0 - T_amb) > 0.5:
        out.append(f"cell starts at {T0 - 273.15:.1f} °C but ambient is "
                   f"{T_amb - 273.15:.1f} °C: set 'Initial temperature [K]' too")

    for electrode in ("Negative", "Positive"):
        dUdT = pv[f"{electrode} electrode OCP entropic change [V.K-1]"]
        if isinstance(dUdT, (int, float)) and dUdT == 0:
            out.append(f"{electrode.lower()} electrode entropic change is 0: "
                       "no reversible heat from it")

    heat_capacity = (first("Volume-averaged effective heat capacity [J.K-1.m-3]")
                     * pv["Cell volume [m3]"])
    tau = heat_capacity / (pv["Total heat transfer coefficient [W.m-2.K-1]"]
                           * pv["Cell cooling surface area [m2]"])
    if tau < 120:
        out.append(f"thermal time constant is {tau:.0f} s: "
                   "this lumped cell behaves almost isothermally")
    return out

在各种配置下跑一次 1C 放电的输出:

Chen2020, default DFN
    isothermal: every heating variable is 0 unless 'calculate heat source for isothermal models' is 'true'
Chen2020, isothermal + heat source
    (no warnings)
Chen2020, lumped
    negative electrode entropic change is 0: no reversible heat from it
    positive electrode entropic change is 0: no reversible heat from it
OKane2022, lumped, only ambient -20 C
    cell starts at 25.0 °C but ambient is -20.0 °C: set 'Initial temperature [K]' too
    negative electrode entropic change is 0: no reversible heat from it
    positive electrode entropic change is 0: no reversible heat from it
OKane2022, lumped, step temperature -20 C
    negative electrode entropic change is 0: no reversible heat from it
    positive electrode entropic change is 0: no reversible heat from it
Marquis2019, lumped
    thermal time constant is 25 s: this lumped cell behaves almost isothermally
ORegan2022, lumped, both -10 C
    (no warnings)
Ai2020, lumped
    (no warnings)

它查不了的:换热系数是否符合你的测试台(第三节),以及电化学部分是否响应给它的温度(第二节的 Chen2020)。这两件都需要和实测对比,代码检查做不到。熵系数检查只标出常数零;如果参数集给的是一个函数,就默认它是认真给的。

八、测量方法

  • PyBaMM 26.5.0,Python 3.13,Apple M3,默认 IDAKLUSolver。模型为 pybamm.lithium_ion.DFN,选项 {"thermal": "isothermal"} 或 {"thermal": "lumped"}。
  • 每次放电从 initial_soc=1.0 开始,通过 Experiment 放到参数集自己的 Lower voltage cut-off [V]。
  • 温升取 "Volume-averaged cell temperature [K]" 的最大值减去初值。产热量是 "Total heating [W]" 及其可逆、不可逆、欧姆分量在解的时间点上用梯形法做的时间积分。
  • τ 用 t = 0 时的 "Volume-averaged effective heat capacity [J.K-1.m-3]" 乘以 "Cell volume [m3]",除以 h 乘散热面积——和集总子模型用的是同一组量(lumped.py、base_thermal.py)。
  • “熵系数置零”只把两个 entropic change 参数设为 0,其余不动。
  • 这 8 个参数集是定义了集总热参数的全部内置锂离子参数集。Prada2013 和 Ramadass2004 在集总模式下会抛 KeyError(分别缺集流体厚度和电池体积);那是大声的失败,本文不涉及。
  • 每个参数集在独立进程里带超时运行,没有一个卡住。

References

相关阅读:PyBaMM 18 个内置参数集实测——为什么 Chen2020 几乎不响应温度;PyBaMM Experiment 常见陷阱——实验正常跑完却交回错误结果的其他方式;PyBaMM 求解器收敛失败排查——低温高倍率跑不完的时候;温度如何进入 SEI 增长和析锂,见 PyBaMM SEI 与析锂实测。

发表回复

向下探索