简介:一份基于MATLAB的共晶凝固相场法模拟程序,面向材料成型、计算材料学方向的科研人员及相关专业高年级本科生。程序围绕二元共晶合金凝固过程中的两相竞争生长场景,将相场变量演化与溶质扩散方程耦合,通过自由能驱动、相判断、过冷度计算等模块实现凝固组织演化的数值求解。压缩包共7个文件,全部为.m脚本,压缩后仅6KB,包含相判断、液相线斜率计算、驱动力求解以及主控流程等子程序,各脚本功能划分清晰,便于逐段阅读、调试与修改。目前已有447人学习下载。程序体量虽小,但完整覆盖了相场法模拟共晶凝固的核心流程,适合初学者快速搭建相场模型框架,理解界面动力学、溶质再分配机制以及凝固组织的演化规律;也可在此基础上扩展多相场、各向异性或噪声扰动等功能,作为进一步深入研究的起点。
1. 把凝固相场跑起来,MATLAB 才是最快的那条路
相场法(Phase Field Method, PFM)这几年从材料热力学教科书里的偏微分方程,变成了凝固组织模拟的标配工具。它的核心思路很直接:不显式追踪固液界面,而是用一个在 0 到 1 之间连续变化的序参量来描述相状态,界面天然带有厚度,演化方程直接来自自由能泛函的变分。相比界面追踪类方法,相场法处理枝晶分叉、共晶耦合生长、奥斯特瓦尔德熟化这些拓扑变化剧烈的场景时,几乎不需要额外判据。但代价是计算量不小,而且方程里的参数——界面宽度、各向异性强度、耦合系数——每一个都对最终形貌有决定性影响。我在实际处理pfm_gongjing.zip这类凝固相场代码包时最深的感受是:用 C++ 或 Fortran 写一套能跑的相场框架,光是把非线性项、拉普拉斯算子和时间推进调通就要一到两周;而 MATLAB 凭借矩阵原生操作和内置的稀疏线性代数,把同样的问题压缩到一天内出第一张彩色形貌图。这不是说 MATLAB 适合做超大规模生产级模拟,而是说在参数探索、批量试算、教学验证这个层面,MATLAB 的迭代效率没有对手。这篇文章就围绕相场法在凝固模拟中的落地展开,从方程到代码再到参数调优,给出一条能直接照做的路径。
2. 相场法的控制方程与凝固模型的数学骨架
2.1 序参量与自由能泛函的基本设定
相场法的基础是选一个序参量,通常记作 \phi,代表局部区域的相状态。\phi = 1 表示完全固相,\phi = 0 表示完全液相,界面处 \phi 从 0 平滑过渡到 1。这个过渡层的厚度 W 是人为设定的参数,在真实物理中不存在,但数值上必须存在,否则界面没有驱动力。自由能泛函的一般形式为:
F = \int \left[ \frac{1}{2} W^2 |\nabla \phi|^2 + f(\phi, c, T) \right] dV
梯度项 W^2|\nabla \phi|^2 为界面提供能量惩罚,f 是局域自由能密度,可以耦合浓度场 c 和温度场 T。共晶凝固模型比纯物质凝固多一个浓度场,因此需要同时求解相场方程和溶质扩散方程,两者的耦合通过自由能密度中的混合项实现。
我做过的共晶模型里,自由能密度通常写成双阱形式:
f(\phi, c) = \frac{1}{4} \phi^4 - \frac{1}{2} \phi^2 + \lambda (c - c_L - h(\phi)(c_S - c_L))^2
其中 c_S 和 c_L 分别是固相和液相平衡浓度,h(\phi) 是插值函数,常见取 h(\phi) = \phi^2(3 - 2\phi),保证在 \phi = 0 和 \phi = 1 处满足边界条件。\lambda 是耦合强度,值越大,浓度偏差对相变驱动力的影响越强。
2.2 Allen-Cahn 方程与溶质扩散方程的耦合形式
相场的演化遵循 Ginzburg-Landau 型的 Allen-Cahn 方程,形式为:
[ \frac{\partial \phi}{\partial t} = -M \frac{\delta F}{\delta \phi} ]
其中 M 是界面迁移率,\frac{\delta F}{\delta \phi} 是自由能泛函对序参量的变分导数。展开变分导数后,方程变为:
[ \frac{\partial \phi}{\partial t} = M \left[ W^2 \nabla^2 \phi - f'(\phi, c, T) \right] ]
这里的 f'(\phi, c, T) 是自由能密度对 \phi 的偏导。浓度场的演化使用 Cahn-Hilliard 型扩散方程:
[ \frac{\partial c}{\partial t} = \nabla \cdot \left[ D(\phi) \nabla \frac{\delta F}{\delta c} \right] ]
D(\phi) 是浓度依赖的扩散系数,固相扩散系数通常比液相低几个数量级,因此要在插值函数中体现。实际实现里,这个方程可以简化为标准的扩散方程加源项,因为化学势对浓度的偏导在等温等压条件下近似为常数。
2.2.1 无量纲化处理与特征参数选择
数值实现前必须做无量纲化,否则方程中各项量级差异太大,MATLAB 的数值求解器会直接失效。常见的无量纲化策略是选定界面宽度 W 作为长度基准,扩散时间 W^2/D_L 作为时间基准。无量纲化后,Allen-Cahn 方程变为:
[ \frac{\partial \phi}{\partial \tau} = \nabla^{2} \phi - f^{}(\phi, c, T) ]
其中 \nabla^* 表示对无量纲坐标求梯度,f^* 是无量纲自由能密度。这样做的好处是,数值计算中的网格尺寸 dx 只需满足 dx < W,就能保证界面有足够多的网格点。经验值是 W 至少要覆盖 5 个网格点,即 W/dx >= 5,否则界面会出现 pinning 现象,枝晶尖端速度被严重低估。
2.3 各向异性与晶体生长方向的数学表达
凝固模拟不能忽略各向异性,否则长出的枝晶是圆形,没有任何晶面偏好。各向异性通常施加在界面宽度 W 或迁移率 M 上,最常见的是四重对称各向异性:
[ W(\theta) = W_0 \left( 1 + \epsilon_4 \cos(4\theta) \right) ]
其中 \theta 是界面法向与参考晶轴之间的夹角,计算方法是 \theta = \arctan(\phi_y / \phi_x),\phi_x 和 \phi_y 是序参量的空间偏导。\epsilon_4 是各向异性强度,典型值在 0.01 到 0.05 之间,太大会导致数值不稳定,太小则枝晶形貌不显著。
各向异性引入后,方程中原本简单的拉普拉斯算子 W^2 \nabla^2 \phi 变为一个更复杂的形式,包含角度偏导数项。近似处理时常用以下形式:
[ W(\theta)^2 \nabla^2 \phi + \partial_x\left( W(\theta) W'(\theta) \partial_y \phi \right) - \partial_y\left( W(\theta) W'(\theta) \partial_x \phi \right) ]
这一项在用 MATLAB 实现时,用梯度算子计算 \theta,再计算 W(\theta) 及其对 \theta 的导数 W'(\theta),最终组装成离散矩阵。注意这个公式中 W'(\theta) 是 W 对 \theta 的导数,不是对坐标的导数。
3. MATLAB 实现相场模型的离散化与矢量化代码
3.1 有限差分格式与边界条件设定
MATLAB 实现相场法最常用的空间离散是有限差分。二阶中心差分是最低要求,因为 Allen-Cahn 方程中曲率项需要二阶精度。拉普拉斯算子的离散模板为:
[ \nabla^2 \phi_{i,j} = \frac{\phi_{i+1,j} + \phi_{i-1,j} + \phi_{i,j+1} + \phi_{i,j-1} - 4\phi_{i,j}}{dx^2} ]
这对应一个 5 点模板。用 MATLAB 实现时,常见的错误是写嵌套 for 循环遍历每个网格点,这在网格数超过 200×200 时运行速度极慢。正确的做法是利用矩阵的circshift函数或索引偏移来实现矢量化。
边界条件的选择非常影响枝晶形貌。我一般用周期性边界条件做无边界干扰的单枝晶生长,计算区域四边的溶质浓度不受容器壁影响;做多晶粒竞争生长时,用零诺伊曼边界条件更接近实际凝固,热流不能穿过边界。周期性边界的实现很简单,索引取模就行;零诺伊曼边界的做法是设置虚拟网格点,让边界上的梯度方向导数为零。
3.2 矢量化组装离散方程的完整代码框架
下面给出一段可以直接运行的 MATLAB 代码核心框架,模拟一个单晶枝晶的凝固过程。这个代码片段不是完整工程文件,但包含了所有关键步骤:初始化、演化主循环、可视化。
% 相场法凝固模拟 - 单晶枝晶生长示例 % 参数无量纲化,界面宽度W0=1, 网格dx=0.4 clear; clc; close all; % 网格设置 N = 400; % 网格点数 NxN dx = 0.4; % 网格间距(无量纲) W0 = 1.0; % 界面宽度 eps4 = 0.04; % 四重各向异性强度 M = 1.0; % 迁移率 lambda = 30; % 耦合系数 Teq = -0.35; % 无量纲过冷度 T = Teq * ones(N, N); % 均匀过冷 % 初始化数组 phi = -ones(N, N); % -1 为液相, +1 为固相 phi(200:202, 200:202) = 1; % 中心放置一个方形晶核 % 时间步长与总步数 dt = 0.02; nsteps = 2000; % 主循环 for step = 1:nsteps % 计算梯度分量 phi_x, phi_y(中心差分) phi_x = (circshift(phi, [0 -1]) - circshift(phi, [0 1])) / (2*dx); phi_y = (circshift(phi, [-1 0]) - circshift(phi, [1 0])) / (2*dx); % 界面法向角 theta theta = atan2(phi_y, phi_x); % 各向异性界面宽度 W = W0 * (1 + eps4 * cos(4*theta)); % 各向异性拉普拉斯项 lap = (circshift(phi, [0 -1]) + circshift(phi, [0 1]) + ... circshift(phi, [-1 0]) + circshift(phi, [1 0]) - 4*phi) / dx^2; % 自由能密度偏导数 df/dphi(双势阱) dfdphi = phi.^3 - phi + lambda * Teq * (1 - phi.^2); % Allen-Cahn 时间推进(显式Euler) phi = phi + dt * M * (W.^2 .* lap - dfdphi); % 每100步画一次图 if mod(step, 100) == 0 imagesc(phi); axis equal; colorbar; title(sprintf('Step %d', step)); drawnow; end end这段代码的关键逻辑说明:circshift函数负责实现空间平移,相邻网格点的访问靠它完成,四个方向的平移组合出二阶中心差分的拉普拉斯算子。atan2(phi_y, phi_x)计算每个网格点的法向角,注意这里需要先计算梯度分量,不能用gradient函数的默认输出,因为默认的梯度计算在边界处精度会降阶。
显式 Euler 时间推进的稳定性限制是 dt < dx^2/(4D),其中 D 是扩散系数的最大值。这里没有显式的浓度场,所以稳定性条件主要由拉普拉斯项的系数决定,取 dt = 0.02 在 dx = 0.4 时是安全的。各向异性项包含 cos(4\theta),在 \epsilon_4 较大时容易产生数值振荡,需要缩小 dt。
3.2.1 参数的含义与调整方向
代码中的 lambda 控制相变驱动力的大小,lambda 越大固液界面越尖锐,但过大会导致浓度场在界面处出现数值振荡。Teq 是无量纲过冷度,过冷度越大枝晶尖端推进越快,但同时也越容易出现侧枝失稳。eps4 直接决定枝晶的四个主轴方向的生长优势,如果想模拟六重对称形貌,把 4 改成 6 即可。
3.3 二维五对角线性方程组的稀疏矩阵组装
显式时间推进虽然实现简单,但步长受限严重。如果网格加密到 1000×1000,显式方案的迭代步数要上万才能看到清晰枝晶,这时候隐式方法反而更划算。Crank-Nicolson 格式对 Allen-Cahn 方程做时间离散,会得到一个五对角稀疏线性方程组。
MATLAB 中组装五对角矩阵的标准做法是使用spdiags。拉普拉斯算子的五对角结构,配合边界条件的修正,可以一次构造完成:
% 组装拉普拉斯算子的稀疏矩阵(周期性边界) e = ones(N^2, 1); A_center = -4 * e; A_xp = e; A_xm = e; A_yp = e; A_ym = e; % 周期性边界修正 A_xp(N:N:N^2) = 0; A_xm(1:N:N^2) = 0; A = spdiags([A_ym, A_xm, A_center, A_xp, A_yp], ... [-N, -1, 0, 1, N], N^2, N^2); % 隐式求解 (I - dt*M*W^2*A/dx^2) * phi_new = phi_old LHS = speye(N^2) - (dt * M / dx^2) * A; phi_vec = phi(:); phi_vec = LHS \ phi_vec; phi = reshape(phi_vec, N, N);这里spdiags的第 4 个参数是五个对角线相对于主对角线的偏移量,-N 对应上方第 N 行,N 对应下方第 N 行,这就是二维问题中"上邻居"和"下邻居"在向量化表示中的位置。注意周期性边界条件要求 \phi_{1,j} 的左边邻居是 \phi_{N,j},所以在 x 方向的偏移修正处把越界位置清零,再通过spdiags的 wrap-around 特性自动完成。
用\求解这个稀疏系统,在 400×400 网格上单次求解大约耗时 1 到 3 毫秒,相比显式迭代每步的浮点运算量相差不多,但稳定性允许 dt 放大 5 到 10 倍,总计算时间减少一半以上。
4. pfm_gongjing.zip 代码包的结构拆解与移植适配
4.1 压缩包的内容组织和核心文件的职责划分
pfm_gongjing.zip这个包名里的 "gongjing" 对应共晶(eutectic)的拼音,所以这个代码包的主体是一个二维共晶生长模型。完整代码包通常包含这几类文件:主入口脚本(main 或 run 开头)、初始化函数(设置网格、参数表、初始条件)、相场演化函数(Allen-Cahn 方程求解)、浓度场演化函数(扩散方程求解)、后处理脚本(计算液相分数、绘制形貌图)。
建议拿到压缩包后先做依赖分析。用 MATLAB 编辑器打开主脚本,逐个 Ctrl+F 查找调用的函数名,列出函数依赖树。常见的坑是addpath写的是相对路径,解压到新位置后路径失效。解决方法是把整个文件夹加入路径,或者用cd切换到代码目录。另一种常见问题是用户报告"运行很慢",打开脚本后发现主循环里有一堆未预分配的数组在动态增长,MATLAB 会在每次扩展时重新复制数组,性能断崖下降。
4.2 把通用相场核适配到共晶双相凝固场景
共晶模型与纯物质模型的最大差异在于序参量从单相变成了双相:\phi_1 和 \phi_2 分别代表两个固相(比如 \alpha 相和 \beta 相),液相由 \phi_1 = 0 且 \phi_2 = 0 表示。界面能各向异性要分别施加到两个相上,而且两相之间的界面能通常不同于相与液体的界面能。
耦合浓度场的方程也要扩展为三个方程联立:两个 Allen-Cahn 方程加一个浓度扩散方程。浓度场在 \alpha 相中的平衡浓度 c_\alpha,在 \beta 相中为 c_\beta,在液相中为 c_liquid。三个值之间满足杠杆定律关系。实现时,插值函数要从单相 h(\phi) 扩展到双相权重:
[ c(\phi_1, \phi_2) = c_L + h_1(\phi_1)(c_\alpha - c_L) + h_2(\phi_2)(c_\beta - c_L) ]
代码包中通常有两种写法:一种是直接在演化函数内使用全局变量传递参数,另一种是通过 struct 变量打包。后者更推荐,因为在 MATLAB 中用 struct 字段名比全局变量更清晰,且避免 Base workspace 被污染。
下面是共晶浓度场更新的核心代码:
% 共晶凝固的浓度场演化(显式扩散 + 源项) D_L = 2.0; D_S = 0.01; % 液相和固相扩散系数 Dphi = D_L + (D_S - D_L) * (phi1.^2 + phi2.^2) * (3 - 2*phi1 - 2*phi2); % 浓度场拉普拉斯 lap_c = (circshift(c, [0 -1]) + circshift(c, [0 1]) + ... circshift(c, [-1 0]) + circshift(c, [1 0]) - 4*c) / dx^2; % 扩散项 + 排溶质源项 source = (phi1 + phi2) * (c_S - c_L) * 0.5; c = c + dt * (Dphi .* lap_c + source);4.2.1 相场与浓度场时间步长的解耦策略
共晶凝固中,浓度扩散的特征时间远长于界面驰豫时间,即两者的刚性比很高。如果统一用同一套 dt,要么因为界面快速演化而被迫缩小步长,浪费大量计算时间在扩散项的缓慢变化上;要么为了扩散稳定性而缩小步长,界面演化迟迟推不动。
常见做法是将两个场用不同时间步长推进。具体方案为:定义界面演化步 dt_phi,浓度场演化步 dt_c = k * dt_phi,k 取 5 到 10 的整数。每推进 k 步相场,同时推进 1 步浓度场,或者反过来浓度场用更大的步长和更少的步数。用 MATLAB 实现时,外层循环控制浓度场步数,内层循环跑 k 步相场。
% 时间尺度分离:内循环跑相场,外循环跑浓度场 for nc = 1:1000 for nphi = 1:5 % 相场演化(步长 dt_phi) phi1 = phi1 + dt_phi * eq1(phi1, phi2, c); phi2 = phi2 + dt_phi * eq2(phi1, phi2, c); end % 浓度场演化(步长 dt_c = 5*dt_phi) c = c + dt_c * diff_eq(c, phi1, phi2); end这样做的实质是将显式格式的 CFL 条件分别施加到两套方程上,互不拖后腿。注意内循环的相场方程如果依赖浓度场变量 c,需要在每步读取当前 c 值,c 在每 5 步内保持不变——这近似等价于对 c 做时间粗化,只要 k 不超过稳定性上限,误差在工程可接受范围。
4.3 从pfm_gongjing到自定义材料的改造步骤
拿到代码包后,把模型移植到自己研究的合金体系,需要修改的位置集中在参数初始化文件中。列出必调的参数并解释含义:
| 参数名 | 含义 | 典型取值范围 | 调整依据 |
|---|---|---|---|
| eps4 | 四重各向异性强度 | 0.01-0.05 | 大于 0.05 易产生尖端分裂数值伪影 |
| lambda | 耦合强度 | 20-50 | 越小界面越宽,越大越容易出现溶质截留 |
| D_L / D_S | 液相/固相扩散系数 | 10^3 量级差 | 决定枝晶间溶质偏析程度 |
| W0 | 界面宽度 | 3-5 倍网格间距 | 必须保证至少 5 个网格点覆盖界面 |
| c_inf | 初始溶质浓度 | 共晶点附近 | 决定初生相还是共晶组织 |
| Teq | 无量纲过冷度 | -0.1 到 -0.5 | 过冷度越大枝晶尖端速度越快 |
材料参数通常要从热力学数据库(如 CALPHAD 方法)或者文献中查平衡浓度和扩散系数,然后换算成无量纲形式。换算公式在代码包的 README 或注释中一般都有说明,务必先确认无量纲基准再改参数,否则整个模拟结果的数值含义全错。
5. 用 MATLAB 验证相场模拟结果的三个高价值技巧
第一个技巧是校验质量守恒。相场法数值实现中,两个常见错误会导致质量不守恒:界面太薄导致溶质在界面处"漏"掉;或者浓度场与相场的时间步长解耦后,每轮浓度场更新前没有同步最新的界面位置。在 MATLAB 中,只需在演化过程中周期性统计总溶质质量,画出时间曲线:
% 质量守恒检测:总溶质含量随时间变化 mass = sum(c(:)) * dx^2; if mod(step, 20) == 0 fprintf('Step %d, total mass = %.6f\n', step, mass); end质量下降超过 1% 就必须缩小 dt 或调整插值函数 h(\phi)。质量守恒的检查能筛掉绝大多数数值实现错误。
第二个技巧是界面曲率与 Gibbs-Thomson 关系的定量验证。在平衡条件下,界面曲率 \kappa 与过冷度的关系满足 Gibbs-Thomson 效应。用 MATLAB 从序参量场提取界面,计算曲率分布,再做线性拟合,斜率就是毛细长度。方法是将 \phi = 0 的等值线视为界面,用contourc提取等值线的坐标点,然后对坐标点做样条插值并计算曲率。实现如下:
% 从phi场提取零等值线坐标 C = contourc(phi, [0 0]); % C的第一行是等值线等级,第二行是点数,之后是坐标点序列 idx = 1; coords = []; while idx < size(C, 2) npts = C(2, idx); coords = [coords; C(1, idx+1:idx+npts)', C(2, idx+1:idx+npts)']; idx = idx + npts + 1; end % 对等值线做平滑,计算局部曲率 % 曲率公式: kappa = (x'y'' - y'x'') / (x'^2 + y'^2)^(3/2)这段代码提取的等值线坐标直接喂给gradient函数得到一阶导,再做一次梯度得到二阶导,套入曲率公式即可。
第三个技巧是枝晶尖端速度的稳态判定。模拟刚开始时,从晶核到稳态生长要经过一段过渡期,直接读取平均速度会导致结果偏小。正确做法是记录尖端位置随时间变化的曲线,取线性段的斜率作为稳态速度。用 MATLAB 找到尖端的简单方法是将 \phi 场的最大值所在位置视为尖端,因为固相尖端处的 \phi 最接近 1:
% 追踪固相尖端位置 [~, max_idx] = max(phi(:)); [tip_y, tip_x] = ind2sub(size(phi), max_idx); tip_history(step) = tip_x * dx; % 物理坐标取稳定段的线性回归斜率:
% 线性段拟合尖端速度 t_fit = t(500:end); x_fit = tip_history(500:end); p = polyfit(t_fit, x_fit, 1); tip_velocity = p(1); % 无量纲尖端速度这个稳态速度与理论解析解的对比,是检验整个模型是否成立的金标准。如果偏差超过 15%,通常要检查各向异性强度是否过大、网格分辨率是否不足、时间步长是否导致数值耗散过大。
本文还有配套的精品资源,点击获取