最近在整理相变蓄能模块的仿真文档,顺手把相变潜热处理的一整套算法流程用伪代码固定了下来。这个动作看着不起眼,但你要真去做过传热数值计算就会知道,相变潜热处理是储能材料、蓄冷蓄热装置、电池热管理甚至建筑围护结构仿真里最绕的一环:光有能量方程不够,里面那部分“藏起来的潜热”不会老老实实地按显热升温来走。伪代码的作用,就是把这个非线性过程从“心里明白”变成“纸上能交流、代码能落地”的东西。这篇博文就围绕“伪代码示意相变潜热处理”展开,写给正在做相变仿真、需要复现算法或刚接触这块内容的工程师和研究生。
我自己日常做相变传热仿真,经常要在论文、技术报告、评审材料里向别人解释:相变潜热到底是怎么被数值算法“吃”进去的?与其丢一段几千行的Fortran或者Python源码,不如先丢一段伪代码。伪代码没有语法负担,又能把时间推进、相变区间判断、焓场修正这些核心逻辑讲得明明白白。下面我就从物理背景、方案选型、设计思路、完整伪代码示例和踩坑经验这几个方面,把这些年总结的东西一次说透。
1. 相变潜热处理到底在算什么
1.1 相变过程的主要热学特征
相变潜热处理,处理的对象本质上是“材料在熔化或凝固过程中吸收或放出的那一大块热量”。拿大家都熟悉的冰水混合物来说:0℃的冰吸收热量融化时,温度长时间停在0℃附近,直到全部冰变成水,温度才继续往上升。这块让冰融化、却不表现为温度升高的热量,就是潜热。
数值模拟里遇到相变材料,最常见的麻烦是:材料的热物性在相变点附近发生了很剧烈的变化。固态时是某个比热容和导热系数,液态时是另一个,两者可能差几倍甚至一个量级;再加上相变界面又会移动,如果把材料当成普通固体来算,算出来的温度曲线往往和实验对不上。更麻烦的是,相变不是在一个严格的单点温度发生的,实际材料通常有一个相变温度区间,比如石蜡类相变材料可能在 28℃ 到 32℃ 之间逐步完成熔化。区间内既存在固态部分,也存在液态部分,两部分的体积分数还在不断变化。
所以,相变潜热处理处理的核心问题是:在一个既有显热(温度升高带走的热量)又有潜热(相变界面移动带走或释放的热量)的系统里,如何用一个统一的能量方程,把这两部分热量都算对,还要求数值过程稳定、不振荡、不焓发散。
1.2 处理潜热的难点与算法目标
如果只看经典的导热方程:
ρ c_p (∂T / ∂t) = ∇·(k ∇T)
这个方程只能描述显热过程。要描述相变潜热,需要在方程右边或者物性项里额外加入潜热贡献。假如一个单元正在发生相变,每秒钟吸收的热量里只有一部分让温度升高,剩下一部分在“制造液态部分”。如果你的算法没有把这个过程显式表达出来,很容易出现两种典型故障:温度卡在相变温度附近振荡,或者相变界面推进速度明显偏快偏慢。
从算法目标来说,相变潜热处理至少要满足三点:
- 能量守恒要尽量好。整个计算域内的总能量增量应该等于边界流入的热量,不能让潜热“凭空消失”。
- 相变界面位置和温度场的演化要合理。界面推进速度、温度分布要和解析解或者实验数据对得上。
- 数值过程要稳定。特别是在相变温度区间较窄时,温度迭代不能出现非物理振荡。
这三条说起来简单,实际做的时候反反复复出问题。我自己的体会是,先把物理图像搞清楚,再谈算法怎么写。
1.3 为什么伪代码能帮上忙
很多新手拿到相变仿真代码,习惯直接去读现成开源程序或商业软件的帮助文档,结果看半天也不知道某个模块到底在干什么。伪代码最大的价值不是给机器看,而是给人看。它把物理场更新、物性判断、潜热修正这些步骤,按照人脑容易理解的方式重新组织一遍。
我写这篇“伪代码示意相变潜热处理”,本质上就是把工程里已经验证过的一种通用处理框架拿出来,去掉具体编程语言的干扰,让读者先建立算法骨架。等你真的要把这段伪代码翻译成 Python、MATLAB 或 C++ 时,只需要把每个框里的步骤映射到对应的数组操作和循环结构就行了。伪代码的另一个好处在于:它能清楚地区分“物理判断”和“数值细节”,比如相变区间判断、液相分数更新这类物理逻辑,应该放在最显眼的位置;而网格编号、索引进位这些琐碎内容,可以后置处理。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 主流处理方案与伪代码选型
2.1 等效比热容法
等效比热容法,也叫显热容法,是最容易理解的一类方案。它的核心思路是:既然相变潜热会让材料在相变区间内表现出“大得离谱的比热容”,那我干脆把潜热折算到比热容里,让比热容在相变区间内变大。
公式上通常写成:
c_p,eff(T) = c_p,s + L / (T_l - T_s) ,当 T_s ≤ T ≤ T_l
c_p,eff(T) = c_p,s 或 c_p,l ,当 T 在相变区间外
其中 T_s 是固相线温度,T_l 是液相线温度,L 是单位质量潜热。等效比热容法的优点是编程简单,只用普通导热方程,把物性函数替换成温度相关的等效比热,再搭配迭代算法就能算。我自己早期做一维融化问题时就先用这个方法练手。
它的麻烦在于,如果相变温度区间很小(比如纯物质相变接近单点温度),等效比热容会变得非常大,时间步长必须取得很小,否则温度会跳过相变区间,潜热根本没被吸收。伪代码里体现这个方法时,重点要放在“物性查表”和“比热容如何随温度更新”上。
2.2 焓法
焓法是目前处理相变潜热的主流方案之一,也是我在伪代码里最常选择的方法。所谓焓,就是把显热和潜热打包成一个状态量:
H = ∫ c_p dT + L · f_l
其中 f_l 是液相分数。液固两相区里,f_l 在 0 和 1 之间变化;固态区 f_l = 0,液态区 f_l = 1。相变潜热处理在这里就变成了:先计算每个单元焓的演化,再从焓反推温度或液相分数。
焓法的好处是能量守恒形式很直接,不需要人为去制造一个“等效比热”,而且处理有相变温度区间的材料时稳定性比等效比热容法好。缺点是要多维护一个焓场,并且在从焓反推温度时要做判断和插值。但从伪代码的组织来看,焓法反而逻辑更顺:先更新焓,再更新液相分数,最后更新温度,每一步都很清晰。
2.3 温度回升法与源项法
温度回升法常见于凝固类问题的早期处理。它的思路是:在一个时间步内先按纯显热来算温度,如果算出来的温度越过了相变点,就把多出来的热量换算成潜热增量,再把温度拉回到相变温度,这就是“回升”的含义。这个方法概念直观,适合相变温度是单点的情况,但用到相变温度区间时要额外修正,麻烦程度不低。
源项法则是在能量方程里显式加一个源项,把相变潜热当作单位时间内吸收或释放的额外热量。这种方法在商业 CFD 软件里用得很多,因为它可以把相变模型嵌进通用求解器里,但需要小心控制源项的线性化,否则迭代容易发散。
几种方案对比如下:
| 方法 | 核心思路 | 优点 | 缺点 | 适合场景 |
|---|---|---|---|---|
| 等效比热容法 | 把潜热折算进比热容 | 实现简单,和普通导热代码兼容度好 | 窄相变区间时步长限制大 | 相变温度区间较宽、精度要求不极端的工程估算 |
| 焓法 | 用焓同时表示显热和潜热 | 能量守恒好,稳定,适合区间相变 | 需要维护焓场,反推温度有分支判断 | 数值研究、论文复现、通用性较强的仿真 |
| 温度回升法 | 先按显热算,再把超出的热量折算回潜热 | 概念直观,适合单点相变 | 处理温度区间时要额外修补 | 凝固、铸造类单点相变问题 |
| 源项法 | 在能量方程中显式加入潜热源项 | 能嵌入通用 CFD 框架 | 源项线性化不当易发散 | 商业软件、流固耦合复杂问题 |
考虑到伪代码示意要让人容易看懂且便于延伸,我下面的完整示例采用焓法。它也是我实际项目里用得最多的方案。
3. 伪代码整体设计思路:先物理后算法
3.1 控制方程与离散骨架
相变潜热处理的主题始终落在能量方程上。以一维平板导热为例,忽略对流以后,控制方程为:
ρ (∂H / ∂t) = ∂/∂x (k ∂T / ∂x)
这里我用焓 H 作为主变量,温度 T 是从焓反推出来的从变量。把这个方程按空间离散成 N 个控制体后,每个控制体的焓更新时间步可以写成:
H_i^{n+1} = H_i^n + (Δt / (ρ Δx²)) × [ k_{i+1/2} (T_{i+1}^n - T_i^n) - k_{i-1/2} (T_i^n - T_{i-1}^n) ]
其中 k_{i+1/2} 是界面处的导热系数,一般取相邻两个控制体导热系数的调和平均或算术平均。如果只看伪代码,这一步对应的就是“遍历所有网格点,用上一时刻的温度场算热通量,再更新当前焓场”。物理上等价于:先把所有热量“灌”到焓里,再由焓去判断温度该升多少、相变产生了多少液相。
之所以要强调先更新焓,是因为焓是守恒量。直接对温度方程做显式处理,潜热释放时机容易出偏差;转而更新焓,则每个时间步内到底吸收了多少热量是清清楚楚的,不容易丢能量。
3.2 时间推进策略
时间推进策略直接影响伪代码的写法。如果是显式格式,焓的更新只依赖上一时刻的温度场,实现简单,但有稳定性限制,时间步长 Δt 不能超过扩散稳定条件:
Δt ≤ ρ c_p Δx² / (2k)
这个条件在相变区间里要特别小心,因为等效比热容可能大幅度变化,实际对应的扩散系数也会变化。稳妥的做法是在伪代码里预留一个“自动判断时间步长”的注释位。如果是隐式格式,则每个时间步内要做迭代,伪代码里就要写“内层迭代”或“收敛判断”这些流程。我常见用于说明的版本是显式,因为骨架更清晰;但实际工程里经典做法往往用隐式或全隐式,好处是可以用更大的时间步长。
伪代码中,时间推进可以用这样的方式表达:
- 外层循环:遍历时间步 n = 1 到 N_total
- 中层循环:如果是隐式求解,则迭代到残差小于容差
- 内层循环:遍历空间网格点 i = 1 到 N_grid
这种嵌套结构必须写清楚,否则后来的人看伪代码会分不清“哪些是空间循环,哪些是时间循环”。
3.3 伪代码书写的通用约定
伪代码不是某一门语言,但它仍然要有自己的表达约定。我的习惯是顶层描述用中文或英文短语都行,但必须保证:每个变量都附一句注释,每种判断条件写成可读性强的比较式,所有物理量的单位在伪代码开头列明白。比如:
- T_solidus:固相线温度,单位 ℃
- T_liquidus:液相线温度,单位 ℃
- L:相变潜热,单位 J/kg
- H_s:固相线对应的焓
- H_l:液相线对应的焓
- f_l:液相分数,0~1
写伪代码时不需要把指针、数组索引表达式这些语言细节都写上,但循环边界和判断条件是骨架,少了这些就容易产生歧义。尤其是相变区间判断,伪代码里必须写清楚“当 H 小于 H_s 时,材料完全固态;当 H 大于 H_l 时,材料完全液态;当 H_s ≤ H ≤ H_l 时,材料处于两相区”。
这段伪代码的约定,是我指导新人时最常强调的。很多初学者喜欢直接把 Python 语法当伪代码用,结果讲到一半全在讨论 list 和 numpy 数组的区别,反而把物理逻辑丢了。
4. 完整伪代码示例与逐段解读
4.1 一维相变储能板融化问题定义
为了把伪代码落到具体场景,我假设要计算一堵厚度为 d 的相变储能板。左侧壁面温度突然升高到 T_hot,右侧壁面保持绝热或者恒定低温。储能板初始为固态,温度 T_0 低于固相线温度 T_solidus。材料在 T_solidus 到 T_liquidus 之间存在两相区,潜热为 L。希望通过计算得到不同时刻板内温度场和液相分数分布。
这个问题的解析解在纯单点相变、半无限大区域时可以退化成 Neumann 解或 Stefan 问题解析解,因此非常适合用来验证伪代码对应程序的正确性。实际工程里,这类问题对应太阳能相变蓄热器、电加热相变地板、电池冷板里的相变材料融化过程。
4.2 完整伪代码
下面就是通常软件伪代码表达方式,我按“初始化 → 时间推进 → 焓更新 → 温度与液相分数反算 → 输出”的顺序写:
text复制BEGIN
// ===== 参数与网格定义 =====
设置 Lx, Nx, dx = Lx / Nx
设置 T_initial, T_hot_left
设置 rho, k_solid, k_liquid
设置 cp_solid, cp_liquid
设置 T_solidus, T_liquidus, L_latent
设置 dt, N_time, n_out
// ===== 数组分配 =====
定义数组 T[0..Nx], H[0..Nx], f_l[0..Nx]
定义数组 k_face[0..Nx+1] // 界面导热系数
定义数组 cp_eff[0..Nx]
// ===== 初始条件 =====
FOR i = 0 TO Nx
T[i] = T_initial
IF T[i] <= T_solidus THEN
f_l[i] = 0.0
H[i] = cp_solid * T[i]
ELSE IF T[i] >= T_liquidus THEN
f_l[i] = 1.0
H[i] = cp_solid * T_solidus + L_latent + cp_liquid * (T[i] - T_liquidus)
ELSE
f_l[i] = (T[i] - T_solidus) / (T_liquidus - T_solidus)
H[i] = cp_solid * T_solidus + L_latent * f_l[i]
END IF
END FOR
// ===== 设置边界条件 =====
T[0] = T_hot_left // 左边界定温
T[Nx] = T_initial // 右边界,先按初始温度处理
// ===== 时间步循环 =====
FOR n = 1 TO N_time
// 1. 界面导热系数插值
FOR i = 1 TO Nx-1
k_face[i+1/2] = 2.0 * k[i] * k[i+1] / (k[i] + k[i+1])
END FOR
// 2. 用上一时刻温度更新每个网格的焓
FOR i = 1 TO Nx-1
flux_right = k_face[i+1/2] * (T[i+1] - T[i]) / dx
flux_left = k_face[i-1/2] * (T[i] - T[i-1]) / dx
H_new[i] = H[i] + dt / (rho * dx) * ( flux_right - flux_left )
END FOR
// 3. 从新的焓反算温度和液相分数
FOR i = 1 TO Nx-1
H[i] = H_new[i]
// 固态区
IF H[i] < H_s(i) THEN
T[i] = H[i] / cp_solid
f_l[i] = 0.0
// 两相区
ELSE IF H[i] >= H_s(i) AND H[i] <= H_s(i) + L_latent THEN
f_l[i] = (H[i] - H_s(i)) / L_latent
// 温度等于两相区内对应液相分数的温度
// 如果在区间内做线性映射
T[i] = T_solidus + f_l[i] * (T_liquidus - T_solidus)
// 液态区
ELSE
T[i] = T_liquidus + (H[i] - H_s(i) - L_latent) / cp_liquid
f_l[i] = 1.0
END IF
END FOR
// 4. 边界处理及输出
T[0] = T_hot_left
T[Nx] = T_initial(或按绝热边界更新)
IF mod(n, n_out) == 0 THEN
输出 T、 f_l、 H 到结果文件
END IF
END FOR
END
这段伪代码是一维显式焓法的基础框架。注意我在代码里用了 H_s(i) 这个记号,它表示第 i 个网格在当前温度下对应的固态显热基准焓。严格来说,如果 cp 随温度变化,H_s(i) 不能简单写成 cp_solid * T_solidus 一个定值,否则会带来焓基准不一致的问题;在初版伪代码里先按定比热容处理,先把逻辑跑通,再看要不要扩展成变比热。
4.3 关键片段解读
伪代码里最关键的就是“从焓反算温度”这一段。它本质上是一个分段线性映射:焓小于固相线焓时,材料还是固态,温度线性升高;焓落在固相线焓和固相线焓加潜热之间时,材料处于两相区,液相分数线性增加,温度在 T_solidus 和 T_liquidus 之间移动;焓再大就进入液态,温度继续升高。
有一个细节经常坑人:焓的零点基准。由于相变潜热的存在,焓不能随便定义成 cp*T。你必须先约定一个参考点,比如把 T_solidus、固态时的焓作为零点,然后潜热作为一个偏移量加到液相段里。否则,不同网格之间焓差会带上一个“人为的常数偏移”,使热通量计算出现假热流。我在伪代码中实际上没有显式写“H_s[i] = cp_solid * T_solidus”,但在实际映射时要特别注意:固态焓曲线和液态焓曲线之间不能直接连成一条线,中间要跨过一大段潜热偏移。
第二个关键片段是界面导热系数的插值。相变材料固态和液态导热系数往往不同,两相区内界面处于不同状态时,等效导热系数最好是相邻两个控制体导热系数的调和平均。谐平均的好处是能把“串联热阻”的思想带进来,界面两侧物性差别大时,计算结果比算术平均更接近物理实际。伪代码里写 2*k[i]*k[i+1]/(k[i]+k[i+1]),翻译成实际代码时这一行很容易写错,常见错法是写成 (k[i]+k[i+1])/2,对于固态和液态导热系数相差很大的相变材料,误差会被放大。
第三个关键片段是时间步长与稳定性。由于焓法在相变区间内让等效比热容变得很大,显式时间推进的稳定步长会受到明显限制。伪代码没有显式写自动调整步长,但实际使用时必须在 dt 的选择上留出安全裕度。我习惯在正式程序里先按最大等效比热容估算一个参考步长,再把 dt 设为它的 0.5 倍以下,这样能显著减小相变界面附近的振荡概率。
伪代码里还有一个偏工程化的处理:右边界到底用定温还是绝热。初始示例里我写的是右边界保持初始温度,这其实等价于把储能板另一侧看作无限大热沉,解析解当然就不再是“板上表面加热,下表面绝热”的经典模型了。因此,你在根据伪代码写正式算例时,要花一分钟确认边界条件的物理含义,再决定是 T[Nx] = ... 还是 q[Nx] = 0。许多“仿真相变结果不对”的问题,追根溯源是边界条件描述与伪代码不一致。
5. 常见问题与排查技巧实录
5.1 温度振荡和“锯齿”问题
焓法做相变温度区间较窄的算例时,最容易出现温度在 T_solidus 附近反复穿越的振荡。比如某个网格理论温度应该稳定在 30.1℃ 等潜热吸收完,实际计算却在 29.8℃ 和 30.5℃ 来回跳。原因一般是时间步长过大,导致一个时间步内焓的变化量超过了“相变区间对应焓范围”。
排查节奏可以先这样走:
- 检查 dt 是否满足显式稳定性限制,必要时缩小到原来的 1/5 到 1/10。
- 检查液相分数 f_l 是否被限制在 0 到 1 之间。有些实现里当 H 刚越过 H_s+L 时,f_l 可能算出 1.002,如果不夹紧,温度反算会出现非物理跳变。
- 检查相变温度区间的宽度。如果 T_solidus 和 T_liquidus 差得特别小,比如 0.1℃,那么该区间的“焓容量”很小,数值上非常灵敏。可以考虑做相变区间的光滑化处理,也就是人为将相变区间扩到 1~2℃,然后再对结果做校正,很多商业软件也有类似处理。
这些问题的共同点是:物理量本身是连续的,但离散算法在分段映射处不够光滑。伪代码里如果增加一个 f_l = max(0.0, min(1.0, f_l)) 的夹逼操作,能挡住大量奇奇怪怪的异常值。
5.2 单位、量纲和热物性设置的坑
相变潜热处理失败最冤的情况是单位混用。温度用摄氏度,但焓的计算里却混入开尔文温差;潜热用 kJ/kg,比热容却用 J/(kg·K),最后得出的液相分数完全离谱。
排查速查表我整理在下面:
| 检查项 | 常见错误 | 正确做法 |
|---|---|---|
| 潜热单位 | L 写成 kJ/kg 而其它公式用 J | 统一为 J/kg |
| 温度零点 | 直接用 ℃ 代入热力学公式 | 温差用 K,绝对温度看情况使用 |
| 比热容 | 固液两相比热容相差大,却取定值 | 分段设置 cp_solid、cp_liquid |
| 导热系数插值 | 算术平均 | 串联热阻场景用调和平均 |
| 焓基准 | 不同网格的焓零点不一致 | 统一选取 T_solidus 固态为基准点 |
伪代码在参数定义处,最好带上单位注释,比如“L_latent // J/kg”。别小看这一点,实际代码三个月后你自己回来看,都能被单位坑一次。
还有一个容易被忽略的地方:相变材料宏观上通常不是纯物质,很多商用相变材料的固相线和液相线差好几摄氏度。如果伪代码里把 T_solidus 和 T_liquidus 都设成同一个数,相当于假设材料在单点相变,那么焓法里的两相区宽度变成 0,分段映射退化成阶跃,数值处理难度会急剧上升。所以,我建议即使目标是单点相变材料,也在伪代码里取一个很小的相变区间,比如 0.5℃,给算法留出有限宽度的过渡带。
5.3 相变点附近的“相界面模糊”问题
和移动边界法(front tracking)不同,焓法是一种固定网格方法,相变界面不是在网格间移动的一条明确边界,而是被表示成一个存在液相分数介于 0 和 1 之间的两相区。这样做的好处是实现简单,缺点是相界面变得模糊。网格越粗,相界面所占的厚度越大,算出来的相变界面位置天然带误差。
想要改善界面分辨率,可以从以下两个方向入手:
- 加密相变界面可能经过区域的网格,或使用自适应网格。工程上可以先粗算一遍,看液相分数 0 到 1 过渡带覆盖了几个网格,再针对过渡带局部加密。
- 使用比焓更精细的反算映射。比如在固态和液态比热容差异明显时,两相区内的焓和液相分数曲线不再是简单的线性关系。伪代码里用线性映射只是第一步,真实材料数据给到后,要替换成一个分段线性查表函数。
伪代码阶段你可以先不写查表函数,但应该预留一个“cal_T_from_H(H)”的模块,让后续用查表或牛顿迭代替换内部实现。这样整套伪代码不但能说明问题,还能平滑升级成工程代码。
6. 从伪代码到正式代码:三件需要注意的事
6.1 伪代码到具体语言的翻译清单
把这段伪代码翻译成 Python、MATLAB、C++ 或 Fortran,并不是机械的逐行替换。你至少要处理下面几类差距:
- 数组下标起始不同。Python 从 0 开始,Fortran 从 1 开始,C++ 也从 0 开始;如果网格编号用的是 Nx+1 个点,边界节点的下标要仔细对齐。
- 循环是并行还是串行。焓更新阶段如果用了 OpenMP 或 GPU 加速,新焓 H_new 必须独立于旧焓 H 存放,不能在同一个数组里原地更新,否则某个网格的新值会被相邻网格马上用到,破坏显式格式的时间逻辑。伪代码里我已经写成
H_new[i] = H[i] + ...,正是为了强调这一点。 - 物性函数的处理。如果材料固相和液相的 cp、k 不同,需要在每个时间步内根据当前温度和液相分数重新计算
k[i]和cp_eff[i],并放到循环外或循环内合适的位置。
顺手给一段 Python 风格的翻译示例片段:
python复制for n in range(1, N_time + 1):
# 计算界面导热系数
for i in range(1, Nx):
k_face[i] = 2.0 * k[i - 1] * k[i] / (k[i - 1] + k[i])
# 更新焓
for i in range(1, Nx):
flux_right = k_face[i + 1] * (T[i + 1] - T[i]) / dx
flux_left = k_face[i] * (T[i] - T[i - 1]) / dx
H_new[i] = H[i] + dt / (rho * dx) * (flux_right - flux_left)
# 从焓反算温度
for i in range(1, Nx):
H[i] = H_new[i]
T[i], f_l[i] = cal_T_f_from_H(H[i], T_solidus, T_liquidus, cp_solid, cp_liquid, L_latent)
6.2 参考解验证:一维斯蒂芬问题与材料手册数据
从伪代码到正式代码,如果不做验证就上算例,风险极大。我最常推荐的第一个验证算例是经典的一维斯蒂芬问题:半无限大区域,初始为固态且温度均匀,边界突变为高于相变点的恒定温度,材料在一个单点相变温度 T_m 发生相变。这个问题的解析解是存在且可查的,可以给出不同时刻相变界面的位置。
验证时具体操作可以这样做:
- 把伪代码中的 T_liquidus 取得很接近 T_solidus,模拟单点相变。
- 在边界施加 T_hot,设置足够长的计算域,使右边界在计算时间内不受影响。
- 输出界面前沿位置和温度分布,和 Neumann 解析解对比。
界面位置的相对误差通常在几个百分点以内,只要网格和时间步长足够细。如果有偏差,先检查热物性取值和界面导热系数插值是否符合解析解模型假设。另一个验证思路是用材料手册上的 DSC 实验数据,把焓随温度的变化曲线改成查表形式,然后算一个简单一维融化/凝固过程的温度历程,跟实验热电偶曲线对比。这个做法的好处是覆盖实际材料在相变区间内的非线性焓变化,比单纯线性的两相区模型更贴近工程现实。
6.3 扩展到二维/三维与耦合场景的改动
伪代码的一维版本看起来很简单,但往二维或三维扩展时,核心相变潜热处理逻辑不变,变的只是空间循环维度和界面导热系数的写法。以二维为例,每个网格要计算 x 方向和 y 方向各两个面的热通量,焓更新式变成:
H_{i,j}^{n+1} = H_{i,j}^n + Δt / ρ × [ (q_x_right - q_x_left)/Δx + (q_y_top - q_y_bottom)/Δy ]
这段伪代码里唯一需要小心的是:循环遍历二维网格时,边界外侧的虚拟节点要单独处理;界面导热系数仍按相邻网格取调和平均。材料若是各向异性,特别是层状相变复合材料,还需要用张量形式处理导热系数。
如果还要考虑自然对流液态区的影响,相变问题就从纯导热扩展成“固液相变+对流传热”的复杂问题。这时焓法负责处理潜热,但液态区的动量方程需要和能量方程耦合,情况会复杂很多。伪代码里通常要加一步“判断该网格是否为液态,如果是液态,则参与流场求解,否则速度置零”。这已经是 CFX、Fluent 里常见凝固/融化模型的做法了,但基础的潜热处理思路还是那套:更新焓、反算液相分数、再反算温度。
6.4 我个人的实操习惯
最后聊一点我自己的习惯。每次拿到新的相变材料参数,我并不会直接去跑二维全模型,而是先建一个一维 20 到 50 个网格的小算例,用伪代码写成注释,放到正式代码最前面。跑通了,再往二维、三维或者耦合系统里加。这个习惯帮我省了大量排查时间,因为相变潜热处理一旦出错,往往在复杂模型里会被各种流动、固耦合现象掩盖,根本定位不到根因。而在极小的一维模型里,温度、焓、液相分数三个场都可以直接打印出来,拿张纸逐行核对就能发现问题。
伪代码写出来,表面上是给别人讲解算法用,实际上它更像是给自己留的一份“算法思维导图”。当代码写到三个月后,你会发现当初那些“理所当然”的分段映射、焓基准、界面导热系数插值,都有可能记不清楚。而一份连同注释都很完整的伪代码,就成了一切后来修改的基础。你后面不管换编程语言、换材料、加边界条件,都是从这段骨架上去添砖加瓦,最底层的相变潜热处理逻辑反而不会再去动它。所以,扎实地把伪代码这一层想明白、写清楚,后面所有工作都会顺利不少。
