04 二维脉动泊肃叶流动案例注解
在test_2d_pulsatile_poiseuille_flow案例中,我们将学到
双向buffer(
BidirectionalBuffer)的使用压力边界的指定
指定压力边界时如何进行density summation
Riemann solver的取舍
注意区分本文的两个概念:buffer区域的粒子和buffer particle。前者指的是位于buffer区域(某个aligned box)的粒子,属于real particle。后者指的是内存空间中预留的空位置,属性没有任何意义,不属于real particle。
TargetPressure
用于指定压力。这个类是一个函数对象,具有如下签名:
Real TargetPressure::operator()(Real p, Real current_time);在本例中,作者在管道左端和右端分别设置了一个TargetPressure:
struct LeftInflowPressure
{
template <class BoundaryConditionType>
LeftInflowPressure(BoundaryConditionType &boundary_condition) {}
Real operator()(Real p, Real physical_time)
{
/*pulsatile pressure*/
Real pressure = Inlet_pressure * cos(physical_time);
/*constant pressure*/
// Real pressure = Inlet_pressure;
return pressure;
}
};
struct RightInflowPressure
{
template <class BoundaryConditionType>
RightInflowPressure(BoundaryConditionType &boundary_condition) {}
Real operator()(Real p, Real physical_time)
{
/*constant pressure*/
Real pressure = Outlet_pressure;
return pressure;
}
};左端是,右端是。
流入/流出双向buffer
因为流体在压力边界上可能流入也可能流出,因此这里需要一个双向的buffer(而非仅仅emitter或仅仅disposer)。BidirectionalBuffer实现了这一功能。它一共做三件事情:
每一步对buffer区域的粒子进行动态标记
流体从buffer的lower bound流出时,删除之(real particle转为buffer particle)
流体从buffer的upper bound流出时,注入之(buffer particle转为real particle)
对于底层是如何构造和更新的,见fluid_boundary。
定义
当我们定义一个BidirectionalBuffer时,需要提供三个东西:
TargetPressure类,用于在注入时将periodic bounding回lower bound的粒子压力设为指定压力。这里TargetPressure是LeftInflowPressure。AlignedBoxByCell对象,用于确定buffer区域。ParticleBuffer<Base>(或其派生类)对象,用于在流体注入时checkEnoughBuffer。
注意,这个类还会用到previous_surface_indicator_,因此它必须在定义FreeSurfaceIndication<Inner<SpatialTemporal>>或*SpatialTemporalFreeSurfaceIndicationComplex之后方能定义。
对于出口附近的双向buffer,需要特别注意:要将aligned box翻转180度(Pi)。这是因为出口处的正向速度代表流出(删除),反向速度代表流入(注入),与入口处的刚好相反。
执行
初始化邻居表后先标记一次buffer区域的粒子;
在中层循环中进行粒子注入;
在中层循环中进行粒子删除;
在中层循环中、更新邻居表之后,进行buffer区域粒子的重新标记。
压力边界
底层的具体实现见fluid_boundary。用户只需要知道压力边界主要做的事是在动量方程中加入一项,由此我们能看出需要提供三个东西:
目标压力,由
TargetPressure作为模板参数提供。这里是LeftInflowPressure和RightInflowPressure。邻居粒子的核梯度之和,
PressureCondition会在底层调用之。这里由kernel_summation提供。因此kernel_summation.exec()必须放在...inflow_pressure_condition.exec(dt)之前。这一项是加到动量方程中的,因此
pressure_relaxation.exec(dt)必须放在...inflow_pressure_condition.exec(dt)之前。
density summation
buffer区域的粒子已经在BidirectionalBuffer中通过给定的压力指定了密度。就不用对这些粒子再做density summation了(何况本来支撑域也不完整)。只需要对内部流动区域的粒子做density summation。通过DensitySummationPressureComplex实现这一点。
Riemann solver
在之前的案例中,density relaxation都没有采用Riemann solver。但是本例中density relaxation采用了Riemann solver。
在 SPHinXsys 这套弱可压(WCSPH)框架里,“要不要用 Riemann solver”本质上是在取舍两件事:
稳定性 / 抑制数值噪声(尤其是边界附近、强瞬变、开口边界)
数值耗散(Riemann会更“粘”,更耗散,速度/剪切层会更容易被抹平)
Riemann VS NoRiemann大概在做什么
Integration...WithWallRiemann(你提到的AcousticRiemannSolver)用“声学近似的 Riemann 解算器”在粒子对(含壁面接触)上构造界面状态(压力/速度等),相当于给通量加了更强的上风/耗散机制。常见收益:
更稳:密度/压力振荡更不容易炸
对开口边界(inlet/outlet、buffer、pressure BC)通常更友好,反射/噪声更小
对瞬态强加速、局部强压缩(虽然 WCSPH 理论上低马赫)更鲁棒
Integration...WithWallNoRiemann不走 Riemann 通量构造,通常更接近“传统 SPH 压力梯度/连续方程离散”的那种风格,耗散更低、结果更“锐”,但也更吃参数(时间步长、人工粘性/修正、壁面处理质量)。
一句话:Riemann 更稳更耗散;NoRiemann 更不耗散但更容易起噪/不稳。
为什么不同例子会选不同的2nd half
pulsatile_poiseuille_flow 用
Integration2ndHalfWithWallRiemann这个案例里有明显的 压力边界 + 双向 buffer(injection/deletion),而且是脉动工况(时间变化边界驱动)。这类“开口边界 + 非定常驱动”特别容易在边界/缓冲区附近出现密度/压力的数值波反射和噪声;用 Riemann(声学)通常能更稳、更不容易炸,所以会倾向选...Riemann。poiseuille_flow / channel_flow_shell 用
Integration2ndHalfWithWallNoRiemann这俩属于典型“粘性主导/低马赫”的内部流或周期/封闭性质更强的设置(尤其 channel_flow_shell 还是周期 x)。这类工况更在意速度剖面、剪切层的准确性,不希望额外耗散把剖面抹平,所以常见选择是NoRiemann,前提是整体数值足够稳定。
选择准则
目标是“更稳先跑通/边界更干净”:选
...Riemann目标是“粘性流剖面/剪切细节更准、耗散更小”:选
...NoRiemann
Last updated
Was this helpful?