ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

PISO与SIMPLEC算法深度解析:原理、选型与OpenFOAM调试

PISO与SIMPLEC算法深度解析:原理、选型与OpenFOAM调试 做CFD的兄弟应该都有过这种纠结同样是处理压力-速度耦合为什么有的算例用PISO一切正常换个工况就振荡到飞起为什么隔壁组说SIMPLEC收敛快你换上之后反而残差卡住不动了这俩算法看着差不多实际用起来却经常一个天一个地。这篇就专门掰扯PISO和SIMPLEC。我会从算法最底层的迭代结构讲起区分它们的适用边界然后落到OpenFOAM里具体怎么配、怎么调、怎么排查发散。内容会偏工程向适合正在跑算例、被收敛性搞得头疼的CFDer也适合刚接触压力-速度耦合算法、想系统搞懂原理的研究生。先说个结论放在前面PISO更擅长瞬态计算中保持时间精度SIMPLEC则在稳态问题里能用更大的松弛因子实现更快的收敛。但这只是粗略印象具体为什么、什么时候选谁、怎么调参数下面详细展开。1. 两种算法的设计初衷与核心差异1.1 从SIMPLE说起压力修正的基本逻辑要理解PISO和SIMPLEC必须先搞清楚SIMPLESemi-Implicit Method for Pressure Linked Equations算法到底做了什么。SIMPLE算法的核心思路是预测-修正两步走先用当前压力场求解动量方程得到一个不满足连续方程的预测速度场然后构造压力修正方程通过修正压力来修正速度使得修正后的速度尽量满足连续方程。这个过程听起来很顺但里面有一个关键假设在修正压力的时候忽略了邻近单元速度修正对压力修正方程的影响。换句话说SIMPLE默认“邻居单元的速度修正量之和为零”。这个假设在稳态计算中是可以接受的因为反正要迭代很多次误差会在外迭代中被慢慢磨掉。但在瞬态计算里每个时间步只推进一次误差没法充分消除就会导致压力场和速度场不匹配时间精度明显下降。这一点特别重要后面理解PISO为什么要分成两三次修正就会容易很多。SIMPLE的创始人Patankar和Spalding当年设计它的时候本来就是冲着稳态问题去的所以这个假设在那个场景下完全合理。但后来大家发现它不适合瞬态才慢慢衍生出PISO和SIMPLEC这两种改进版本。1.2 PISO的两次修正到底在修什么PISOPressure Implicit with Splitting of Operators是Issa在1986年提出的它的核心思想是在SIMPLE的基础上多做一次或多次压力修正。具体流程是这样的第一步和SIMPLE一样用预测的压力场求解动量方程得到预测速度第二步求解第一个压力修正方程得到压力修正量更新压力和速度第三步再次求解压力修正方程但这次方程右侧的源项不再包含邻居速度修正的近似而是直接用上一步修正后的速度场重新计算质量通量残差。关键在于第二次修正。第一次修正之后压力场和速度场其实已经基本满足连续方程了但压力方程推导时省略的邻居速度修正项造成了压力场本身的误差。PISO通过第二次修正把这个误差显式地补偿回来所以它的压力场更准确、速度场更自洽而且不需要像SIMPLE那样反复外迭代。PISO这个名字里的“Splitting of Operators”指的就是这种算子分裂的思路把压力修正方程和动量方程解耦各自独立求解而不是耦合在一起迭代。这种分裂方式在瞬态计算中很有效因为它让每个时间步内的变量更新都保持一致的精度不会因为多次外迭代而引入额外的时间误差。1.3 SIMPLEC修正了什么SIMPLECSIMPLE-Consistent是Van Doormaal和Raithby在1984年提出的比PISO还早两年。它的出发点很简单SIMPLE在压力修正方程中忽略邻居速度修正的时候这个忽略并不总是合适。尤其是在网格长宽比很大的边界层区域邻居修正的影响往往不能忽略导致压力修正方程被低估收敛速度变慢。SIMPLEC的做法是在压力修正方程的推导中不再完全忽略邻居速度修正而是用一个近似方式把它的一部分贡献纳入主对角系数中。具体来说SIMPLEC把“邻居速度修正量的和”近似表示为“修正压力的加权和”这样得到的压力修正方程比SIMPLE的更接近完整形式因此压力修正量更准确修正之后的速度场更接近真实解。这意味着什么意味着在同样的松弛因子设置下SIMPLEC可以承受比SIMPLE更大的压力松弛因子甚至可以不松弛压力pressure relaxation factor设为1也能稳定迭代。这一点在稳态计算中非常有用因为SIMPLE每轮迭代的压力修正量是保守的SIMPLEC每轮能走更大的一步收敛自然更快。不过SIMPLEC也有它的代价它本质上还是迭代法需要在每个迭代步中逐步逼近解所以不适合做瞬态计算的时间推进。如果非要用它来跑瞬态每个时间步内就需要多次外循环来达到收敛计算量会显著增加。2. 选型判断什么场景用什么算法2.1 瞬态vs稳态最核心的决策维度选PISO还是SIMPLEC第一个要问的问题是你的算例是瞬态还是稳态瞬态算例首选PISO。原因我前面提到了PISO通过每个时间步内的多次修正让压力场和速度场在当前时刻保持自洽避免时间精度被破坏。在LES、DES这类涡分辨模拟中时间步长通常取得很小每个时间步内的非线性变化不大PISO的两三次修正足够把压力-速度耦合处理到位。如果在这个场景下用SIMPLEC为了保证每个时间步的压力-速度耦合精度就必须在每个时间步内套多个外迭代循环而且每步的迭代次数还不容易预先估计整体计算效率会明显下降。稳态算例就反过来。稳态计算的目的只有一个尽快收敛到最终解中间过程怎么走的不重要。SIMPLEC因为可以用更大的松弛因子每轮迭代推进更多所以在稳态计算中通常比SIMPLE收敛更快。如果你用的是基于SIMPLE家族的稳态求解器比如simpleFoam把松弛因子调大一点配合SIMPLEC稳态收敛速度的提升会比较明显。2.2 网格质量与压力修正的敏感度除了瞬态和稳态网格质量也是一个重要的判断维度。PISO的压力修正方程对网格质量比较敏感尤其是高度倾斜或拉伸的非正交网格PISO在压力修正步骤中容易出现压力场振荡。这不是PISO本身的问题而是任何基于投影法的压力修正算法都会遇到的共性——压力方程离散时对非正交项的显式处理会引入误差源。我的经验是在四面体网格或质量较差的非结构网格上如果算瞬态PISO往往是唯一的实用选择SIMPLEC在瞬态上本来就不合适但对非正交修正nonOrthogonalCorrectors要特别上心不能直接沿用正交网格的参数。这部分的处理细节后面在OpenFOAM配置部分专门讲。在结构化的高质量六面体网格上跑稳态问题SIMPLEC的优势会更突出。因为网格正交性好压力修正方程离散误差小SIMPLEC大步长修正的优势能充分发挥收敛曲线会很光滑。2.3 计算开销的理性比较很多资料会说PISO比SIMPLEC贵但其实这种说法太笼统。PISO每个时间步内要解多次压力泊松方程通常2到3次但这只是瞬态计算一个时间步的开销。如果你的瞬态算例时间步数很大比如LES跑了数十万步那PISO多出来的压力修正开销是可控的因为每步只需要3次压力修正不会随计算进程增长。SIMPLEC在稳态计算中每轮迭代只需要解一次压力方程但每轮之外还要多做一些邻居修正相关的系数计算。在OpenFOAM的实现里SIMPLEC实际上会修改动量方程的离散系数具体是调整主对角元和源项这部分额外操作在每轮迭代中都有固定开销。总体来看SIMPLEC单轮迭代的计算量比SIMPLE稍大但因为它需要的迭代轮数更少总计算量通常反而更低。真正要小心的是用SIMPLEC跑瞬态。如果非要在瞬态中用SIMPLEC每个时间步内可能需要5到10次甚至更多外循环才能让压力-速度耦合充分收敛每次外循环都要重算动量方程和压力方程这个开销比PISO的固定3次修正大得多而且很难提前设置正确的循环次数。下面用一张表把这几个关键对比维度理清楚对比维度PISOSIMPLEC适用场景瞬态计算为主稳态计算为主每步/每轮压力方程求解次数2~3次修正1次修正系数调整邻居速度修正处理通过多次修正显式补偿纳入压力修正方程推导压力松弛因子要求通常需要0.3~0.7可以设得很大甚至不松弛对非正交网格的敏感度较敏感需配合非正交修正相对不敏感时间精度高适合LES/DES/URANS低主要用于收敛终值迭代法本质非迭代型每步推进迭代型需逐步逼近3. 在OpenFOAM里配置PISO与SIMPLEC3.1 fvSolution文件中的关键参数不管是PISO还是SIMPLEC在OpenFOAM里都通过fvSolution文件控制。很多刚上手的人会在system目录下的fvSolution里看到一堆参数不知道怎么对应到算法。这里逐个讲清楚先看一个典型的瞬态PISO配置solvers { p { solver PCG; preconditioner DIC; tolerance 1e-06; relTol 0.01; } pFinal { solver PCG; preconditioner DIC; tolerance 1e-06; relTol 0; } U { solver PBiCGStab; preconditioner DILU; tolerance 1e-06; relTol 0.01; } } PISO { momentumPredictor yes; nOuterCorrectors 1; nCorrectors 2; nNonOrthogonalCorrectors 0; }这里的重点在PISO子字典的四个参数momentumPredictor控制每个时间步开始前是否先用已知压力场求解一次动量方程。建议设为yes尤其是在高雷诺数流动中因为这个预测步给后面压力修正一个合理的速度初值能显著提高稳定性。nCorrectors压力修正次数。瞬态计算的常用值是2即做两次压力修正。如果算例比较简单、网格质量好1次也能勉强跑但压力场时间精度会下降如果流场很复杂或者时间步长偏大可以设到3。nOuterCorrectors外循环次数。PISO在标准形式下这个值应该为1因为在每个时间步内PISO不依赖外循环迭代收敛而是靠内修正保证耦合。如果设大于1实际上就变成了PIMPLE算法。nNonOrthogonalCorrectors每次压力修正时对非正交项的显式修正次数。网格正交性好的时候设0非正交网格上需要设1到2。再看一个典型的稳态SIMPLEC配置SIMPLEC { momentumPredictor yes; nNonOrthogonalCorrectors 1; rhoMin 0.01; rhoMax 1.0; consistent yes; }注意这里用的是SIMPLEC关键字而不是SIMPLE。consistent yes就是告诉求解器采用SIMPLEC的一致性近似这会让代码在离散动量方程的时候调整系数矩阵容忍更大的松弛因子。3.2 fvSolution里松弛因子的设置逻辑很多人对relaxationFactors的设置比较随意但这其实直接影响算法成败。先看PISO场景relaxationFactors { equations { U 0.5; UFinal 0.5; k 0.5; kFinal 0.5; epsilon 0.5; epsilonFinal 0.5; } }PISO作为瞬态算法理论上不需要松弛。但在实际URANS计算中尤其是k-epsilon这类双方程湍流模型如果不加一点松弛湍流量的时间推进很容易出现负值或振荡。我的习惯是对湍流量方程加0.5左右的线性松弛而U和p不加松弛。压力方程在OpenFOAM中没有显式松弛一说它是通过nCorrectors和nNonOrthogonalCorrectors隐式控制的。稳态SIMPLEC的松弛因子设置跟PISO差别很大。典型的设置是relaxationFactors { fields { p 0.7; U 0.7; k 0.7; epsilon 0.7; } equations { U 0.7; k 0.7; epsilon 0.7; } }在SIMPLEC中压力场p的松弛因子可以设得比较大甚至可以设成1.0。这是因为SIMPLEC的一致性修正已经补偿了压力修正方程中被忽略的邻居项所以压力修正量本身已经足够准确不需要再用松弛来限制步长。如果你在SIMPLEC中把p的松弛因子设得很小比如0.3反而会抵消SIMPLEC的收敛加速优势让迭代变慢。U的松弛因子在SIMPLEC中也可以比SIMPLE设得更大。SIMPLE中U的松弛通常只有0.5到0.7SIMPLEC可以放到0.8甚至0.9。但这里有个度的问题如果算例里有强分离流或者激波等强非线性现象U松弛因子太大照样会发散该保守的时候还是要保守。3.3 时间步长选择与库朗数的配合PISO在瞬态计算中能不能稳定运行很大程度上取决于时间步长是否合适。很多人对库朗数Courant number没有概念直接拍脑袋给时间步长结果一跑就不稳定还以为是算法有问题。其实PISO对库朗数的容忍度是有限度的这一点要讲清楚。标准PISO算法在小库朗数下表现良好建议最大库朗数控制在1以下最好在0.5左右。库朗数的定义是Co |U| * dt / dx也就是一个时间步内流体穿越的网格数。Co小于1意味着流体在一个时间步内最多走一个网格这样压力信息传播的时间尺度不会超过网格尺度压力修正才能在每个时间步内有效。如果你的算例平均流速是10 m/s最小网格尺寸是1 mm那么库朗数0.5对应的最大时间步长大约就是0.00005秒5e-5 s也就是dt Co * dx / |U| 0.5 * 0.001 / 10 5e-5 s这个公式很简单但很多人就是不愿意算这一步。我见过大量PISO发散案例归根结底就是时间步长太大库朗数跑到5以上PISO的两次压力修正根本不足以让压力波在步内传播完毕压力场自然就炸了。如果你确实需要大时间步长应该考虑PIMPLE而不是PISO。PIMPLE允许在每个时间步内做多个外循环本质上就是PISO加上SIMPLE式的外迭代通过增大nOuterCorrectors来让每个时间步内的求解完全收敛从而突破库朗数限制。这是另一个话题这里就不展开了。4. 实操中常见的收敛性问题与排查方法4.1 压力场振荡从参数到网格的系统排查压力场振荡可以说是PISO用户遇到最多的问题。现象很典型速度残差一路下降看起来一切正常但压力残差在某个数量级上卡住不动或者压力场在局部出现棋盘式的正负交替分布。这种情况我在实际算例中遇到过很多次排查路径基本是固定的。先查nNonOrthogonalCorrectors。如果你用的是四面体网格或者高度非正交的六面体网格而nNonOrthogonalCorrectors却设成0那压力方程离散时非正交项没有被显式修正产生的误差就会表现为压力场振荡。把nNonOrthogonalCorrectors改成1或2通常能立刻看到压力残差下降。但要注意这个参数每增加1每一步就要多解一次压力方程计算开销直接增加所以不能盲目加大够用就行。第二个要查的是压力方程的求解精度。OpenFOAM中pFinal的tolerance一般要设得比p更严格。如果pFinal的relTol设成0.01意味着压力残差只需要降到初始残差的1%就算收敛这个精度在PISO的第二次修正中可能不够。我的习惯是pFinal的relTol设0用绝对tolerance控制在1e-6这样每个时间步内最后一步压力求解是真正收敛的对后面速度场的连续性有很大帮助。第三查网格质量。如果你划分的网格中存在大量非正交角超过70度的单元光靠nNonOrthogonalCorrectors可能压不住振荡。这时候要么重新做网格要么使用更高精度的离散格式比如在压力方程中用修正后的面通量插值格式。网格质量差的问题不能靠算法参数完全弥补这句话在CFD里是真理。4.2 SIMPLEC收敛停滞与发散原因SIMPLEC稳态计算常见的问题是算了几百步之后残差就卡在一个平台上不再下降。很多人这个时候会去调松弛因子但效果不大因为问题往往不在松弛因子而在速度-压力耦合之外的环节。最常见的原因是动量方程的对流项离散格式太耗散。如果你用的是first-order upwind格式数值粘性会非常大流场中的细小结构被抹平表现为残差台阶。解决方法是换成second-order的线性迎风格式linearUpwind或者Gamma格式。但注意二阶格式在高分辨率网格上没问题在粗网格上却可能因为过冲引起振荡需要配合限制器使用。另一个容易被忽略的原因是计算域出口边界条件设置不当。SIMPLEC在稳态计算中出口的压力和速度边界条件必须协调。如果出口用固定压力而入口用固定速度那么质量和动量必须通过整个计算域收敛来匹配收敛过程会非常慢。这种情况下即使算法和离散都没有问题残差也会长时间停留在某个水平。我处理这类问题的方法是把入口边界条件改成totalPressure或者检查出口的质量流量是否与入口匹配。还有一个比较少见的陷阱在SIMPLEC中同时启用了consistent yes但场松弛因子仍然按照SIMPLE的惯例来设置尤其是p的松弛因子设成0.3。前面说过SIMPLEC的优势就是能吃大松弛因子你把它当成SIMPLE用等于主动放弃了优势收敛自然快不起来。这种情况如果你发现残差降得特别慢不妨试试把p的松弛因子直接设成1.0U设成0.8看残差曲线有什么变化。4.3 发散时的应急处理步骤如果计算已经在跑了突然看到残差爆涨或者在某个时间步上NaN了第一反应先别急着改算法或松弛因子按照下面的顺序来查先看最后一个成功时间步的流场输出定位发散是从哪个区域开始的。如果发散点集中在某个局部区域优先检查那个区域的网格质量和边界条件检查是否发生了库朗数过大。在瞬态计算中这几乎是发散的最常见原因。把时间步长缩小一半重新跑如果残差稳定下来说明问题就出在时间步长上检查湍流量的边界是否出现了负值或非物理值。如果epsilon或k出现了负值尝试降低湍流量方程的松弛因子或者开启limitTemperature/limitVelocity这类辅助限制如果以上都查过还是发散考虑是不是压力修正次数不够增加nCorrectors到3试试最后再考虑调整离散格式把动量方程从二阶格式降为一阶格式看看是否稳定。如果降格式后稳定说明问题在于网格不足以支持二阶格式需要对网格加密或者调整网格质量。这几个步骤按顺序排查基本能覆盖绝大多数发散场景。切忌一上来就大刀阔斧地同时改多个参数那样即使算定了也不知道是哪个修改起的作用。5. OpenFOAM中的算法代码路径与补充配置5.1 pimpleFoam与simpleFoam的内部逻辑如果要对这两个算法有更深的理解最好对求解器内部代码有一定了解。OpenFOAM中PISO和SIMPLEC的主要实现藏在pimpleFoam和simpleFoam的源码里。两者共享一套压力修正框架但循环结构不同。pimpleFoam中的核心循环可以理解为while (runTime.run()) { runTime; // --- Pressure-velocity PIMPLE corrector loop for (int oCorr 0; oCorr nOuterCorrectors; oCorr) { // --- Momentum predictor if (momentumPredictor) { solve(UEqn -fvc::grad(p)); } // --- Pressure corrector loop for (int corr 0; corr nCorr; corr) { // 更新压力方程源项并求解 } } }注意这里的nOuterCorrectors如果等于1那这个循环就是纯PISO或PIMPLE的简化形式如果大于1就有多个外循环每个外循环内还会做若干次压力修正这种形式可以理解为PISO与SIMPLE外迭代的结合来自OpenFOAM官方文档的标准说法是PIMPLE算法。simpleFoam中的核心逻辑如下while (simple.loop(runTime)) { // --- Pressure-velocity SIMPLE corrector { // 动量预测 if (momentumPredictor) { solve(UEqn -fvc::grad(p)); } // 压力方程求解 // 速度修正 // 通量修正 } // 湍流方程求解 runTime.write(); }这个循环的每次迭代对应一个SIMPLE或SIMPLEC迭代步。如果你在fvSolution里写的是SIMPLEC并且consistent yes那UEqn的系数矩阵在求解前会经过一次一致性处理算法就会走SIMPLEC的路径。5.2 湍流模型与算法的适配问题压力-速度耦合算法的选择还会跟湍流模型产生交互作用这一点常被忽略。以k-omega SST模型为例这个模型在近壁面区域有较强的非线性源项对速度和压力场的瞬态变化非常敏感。在PISO的瞬态计算中如果湍流量的松弛因子设得太大比如0.9SST模型的压力应变项很容易产生数值振荡反过来污染主流动的收敛性。我的经验是k和omega的松弛因子在PISO中保持在0.4到0.6之间比较稳。在SIMPLEC稳态计算中湍流模型的迭代与主流动迭代之间的耦合方式也很关键。simpleFoam中这个顺序是先更新流场、再解湍流方程每个外迭代走一遍。如果你把湍流方程的求解精度设得过高relTol太低代价很大但收敛加速并不明显。反而是在湍流模型迭代还不充分的时候盲目减小主流动的残差控制阈值意义不大。另外提一个实际的建议无论是PISO还是SIMPLEC在刚开始计算的前几百步或前几十个时间步可以先把动量方程的对流项用一阶迎风跑起来等流场初值建立之后再切换为二阶格式。这种策略能显著提高初始阶段的稳定性。在OpenFOAM中可以用potentialInitialization或设置分段式的fvSchemes来控制虽然麻烦一点但对于复杂外形的算例非常值得做。6. 工程实战中的选型建议与经验速查6.1 一个实际选型决策表前前后后讲了这么多最终还是要落到选型这个动作上。如果你现在拿到一个新算例不知道怎么选可以参考下面这个决策流程如果算例是瞬态的直接选PISO或PIMPLE不用犹豫如果算例是稳态的网格质量较好优先选SIMPLEC如果算例是稳态的但网格质量一般先尝试SIMPLEC如果出现压力振荡或收敛停滞再回到SIMPLE如果算例是稳态的而且有强分离流、强旋流等复杂流动结构SIMPLE配合适度的松弛因子可能比SIMPLEC更稳如果是动网格或流固耦合问题优先PIMPLE因为它有更灵活的外循环控制。这个决策表不是死规则但它覆盖了大部分实际工程场景的初始选择。真正跑起来之后还是要盯着残差曲线和关键监测点比如某个位置的升力系数、监测点的速度灵活调整。6.2 我踩过的一些坑最后分享几个我个人的实操经验都是拿代价换来的。第一PISO的nCorrectors不是越大越好。有人觉得压力修正多做几次肯定更准于是设了5次。但实际上当压力修正超过3次后继续增加修正次数带来的精度提升微乎其微计算开销却线性增长而且在高库朗数下还可能因为每次都基于同一个预测速度场而引入伪振荡。我在自己的算例中通常用2次复杂工况最多3次。第二SIMPLEC的consistent yes不是所有情况都该开。在低雷诺数、层流、旋转机械这类流动较温和的算例中开consistent能明显加速收敛。但在高雷诺数、强激波、强分离这类非线性很强的算例中consistent修正反而可能让压力修正量过大导致稳定性下降。遇到这类算例如果SIMPLEC发散先把consistent改成no试试。第三不要迷信残差曲线的绝对值。OpenFOAM的残差定义是归一化的不同算例之间没有可比性。我见过有人死磕把残差压到1e-8跑了上万个迭代步其实算出来的结果跟1e-4时几乎一样。稳态计算的经验是关注点应该放在关键物理量气动系数、监测点压力等是否稳定而不仅是残差数量级。瞬态计算则应该关注每个时间步内的压力修正收敛情况而不是只看全局残差。第四备份每个能稳定运行的配置。我自己的习惯是每次调参算出一个稳定、收敛良好的配置就把fvSolution、fvSchemes和边界条件文件打包备份备注好工况参数。这比任何文档都管用。换网格、换流速、换介质之后前一个稳定配置往往是新算例最好的起点。CFD调参赛场里一个好的初始参数组合能省下大量试错时间。另外想强调的是算法选型和参数调试只是CFD工作的一部分。网格质量、边界条件设定、物理模型选择这些共同决定了最终仿真结果的可信度。算法选得再对网格拉胯、湍流模型不合适结果一样没法看。所以在纠结PISO还是SIMPLEC之前先确认你的网格和物理模型没有硬伤这样调算法才有意义。根据我自己的经验把基础的算法原理彻底搞明白比记一堆经验参数要有用得多。理解了PISO为什么需要两次修正、SIMPLEC为什么能吃大松弛因子你在新算例面前就能自主判断怎么调参数而不是翻旧算例照着抄也不知道为什么那样设置能work。这才是写这篇长文真正的出发点。
返回列表