我对在基于非结构化单元的有限体积 CFD 中进行单元浏览的有效数据结构的建议感兴趣。
我遇到的一个例子(在dolfyn cfd 代码中)是这样的(我将展示相关部分)
该代码是基于人脸的,因此有一个人脸数据类型,它存储位于 Face(k)%cell1 和 Face(k)%cell2 之间的两个单元格的序列号。
欢迎对此提出任何意见或对替代方法的建议。
我对在基于非结构化单元的有限体积 CFD 中进行单元浏览的有效数据结构的建议感兴趣。
我遇到的一个例子(在dolfyn cfd 代码中)是这样的(我将展示相关部分)
该代码是基于人脸的,因此有一个人脸数据类型,它存储位于 Face(k)%cell1 和 Face(k)%cell2 之间的两个单元格的序列号。
欢迎对此提出任何意见或对替代方法的建议。
您展示的结构是一种常见的选择,相当于以 CSR 矩阵格式存储单元面邻接,而边界重影单元位于特殊位置。但是,请注意,FV 方法也可以制定为完全或几乎完全由面遍历组成,其中每个面只被访问一次(从两侧重构为面质心/正交点,解决黎曼问题,将通量分配回单元格上的残差)。您可以通过使用基于单元格的遍历并跳过稀疏矩阵中“对角线”下方的任何两个单元格来“伪造”它,但一种流行的替代方法是存储(leftCell, rightCell) = support(face)
,在这种情况下,人脸成为一等实体。这很有用,因为您通常需要一个地方来存储面正交点(质心)、面法线。您还可以将重建部分(如最小二乘)放入基于面部的数据结构中。面遍历似乎对矢量化友好,因为所有大小都是规则的,但另一方面,有重叠的输出,因此您需要组织遍历以避免将其置于内部循环中。使用这种更面向人脸的数据结构,对人脸编号进行排序是很自然的,这样每个边界条件类型都可以通过人脸的连续遍历来应用(也是矢量化友好的)。
如果选择这种数据结构,请记住对面进行排序,以便遍历尽可能重用缓存中的单元格数据。有关人脸排序和相关优化的性能分析,请参阅任何 PETSc-FUN3D 论文。
我知道这个问题已经得到解答,但这里有一个类似的基于单面的循环存储,它在 OpenFOAM C++ 库中实现:
每个单元格在 cellList 中都有一个索引 (ID)。为所有面孔定义了两个列表:“面孔内部所有者”和“面孔邻居”。两个面列表的长度对应于网格中的内部面数。人脸所有者将是 cellList 中 ID 较低的单元格(与人脸邻居相反)。最后写入边界面,它们具有向外的法线(来自解域),当然,只有一个所有者单元。面部区域法线的方向是使其从所有者单元格向外看到相邻单元格。
这适用于例如通量计算。每个面计算一次通量,并将其添加到所有者单元格的总面数中,并从相邻单元格中扣除(总和/扣除额根据面区域法线的方向决定)。边界面被排序并存储在面列表的底部,允许将边界条件定义为面列表的切片(边界块的开始标签,结束标签),从而简化边界条件的实现,以及作为边界条件更新过程的提高效率,因为它依赖于内部面操作提供的解决方案。
由于边界面被聚集成补丁,进程间通信是为耦合(处理器)补丁定义的,并且是预定义的。这意味着一旦边界网格上出现循环,顶层访问函数就会调用封装的 MPI 调用,从而使此类代码“自动”并行化,前提是它依赖于上述基于人脸的连接。