经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » 编程经验 » 查看文章
Openfoam Pstream类探索
来源:cnblogs  作者:TJUHE  时间:2023/2/24 9:07:54  对本文有异议

对于数值仿真而言,无论是商软或者开源软件,并行计算都是非常重要的,
作为一名仿真工程师,如果想把自身数值仿真能力提升一个层次,需要对并行计算有很好的理解与应用


openfoam并行通信主要通过Pstream类完成

Pstream类,类如其名,parallel_stream,并行计算时使用的信息流
Openfoam对其的介绍是:

Inter-processor communications stream.

处理器间交换信息流

类似的命名方法我们在c++文件读取时说过,std有fstream类读取写入文件/二进制文件,比如说我们要读取文件,会把读取内容放入缓存区内进行操作

  1. #include <iostream>
  2. #include <fstream> // ifstream类需要包含的头文件。
  3. #include <string> // getline()函数需要包含的头文件。
  4. using namespace std;
  5. int main()
  6. {
  7. string filename = R"(./test.txt)";
  8. //ifstream fin(filename, ios::in);
  9. ifstream fin;
  10. fin.open(filename , ios::in);
  11. // 判断打开文件是否成功。
  12. // 失败的原因主要有:1)目录不存在;2)文件不存在;3)没有权限,Linux平台下很常见。
  13. if (fin.is_open() == false)
  14. {
  15. cout << "打开文件" << filename << "失败。\n"; return 0;
  16. }
  17. string buffer;
  18. while (fin >> buffer)
  19. {
  20. cout << buffer << endl;
  21. }
  22. fin.close(); // 关闭文件,fin对象失效前会自动调用close()。
  23. cout << "操作文件完成。\n";
  24. }

类似的openfoam也有PstreamBuffers类进行并行通信缓冲
可以这样使用:

  1. PstreamBuffers pBuffers(Pstream::commsTypes::nonBlocking);
  2. for (label proci = 0; proci < Pstream::nProcs(); proci++)
  3. {
  4. if (proci != Pstream::myProcNo())
  5. {
  6. someObject vals;
  7. UOPstream str(proci, pBuffers);
  8. str << vals;
  9. }
  10. }
  11. pBuffers.finishedSends(); // no-op for blocking
  12. for (label proci = 0; proci < Pstream::nProcs(); proci++)
  13. {
  14. if (proci != Pstream::myProcNo())
  15. {
  16. UIPstream str(proci, pBuffers);
  17. someObject vals(str);
  18. }
  19. }

上面这个程序可以看到,先后使用UOPstream与UIPstream进行缓冲区的文件输出与读取,这就很像ofstream类与ifstream类,甚至命名方式上都有几分相似,我们打开相应的继承关系图

image
image

二者分别服务于IPstream类与OPstream类,我们再打开今天文章的主角,Pstream类继承关系图
image
发现IPstream类与OPstream类是Pstream类的衍生类,Pstream类是其基础
打开Pstream类的源码:

点击查看代码
  1. namespace Foam
  2. {
  3. /*---------------------------------------------------------------------------* Class Pstream Declaration
  4. \*---------------------------------------------------------------------------*/
  5. class Pstream
  6. :
  7. public UPstream
  8. {
  9. protected:
  10. // Protected data
  11. //- Transfer buffer
  12. DynamicList<char> buf_;
  13. public:
  14. // Declare name of the class and its debug switch
  15. ClassName("Pstream");
  16. // Constructors
  17. //- Construct given optional buffer size
  18. Pstream
  19. (
  20. const commsTypes commsType,
  21. const label bufSize = 0
  22. )
  23. :
  24. UPstream(commsType),
  25. buf_(0)
  26. {
  27. if (bufSize)
  28. {
  29. buf_.setCapacity(bufSize + 2*sizeof(scalar) + 1);
  30. }
  31. }
  32. // Gather and scatter
  33. //- Gather data. Apply bop to combine Value
  34. // from different processors
  35. template<class T, class BinaryOp>
  36. static void gather
  37. (
  38. const List<commsStruct>& comms,
  39. T& Value,
  40. const BinaryOp& bop,
  41. const int tag,
  42. const label comm
  43. );
  44. //- Like above but switches between linear/tree communication
  45. template<class T, class BinaryOp>
  46. static void gather
  47. (
  48. T& Value,
  49. const BinaryOp& bop,
  50. const int tag = Pstream::msgType(),
  51. const label comm = Pstream::worldComm
  52. );
  53. //- Scatter data. Distribute without modification. Reverse of gather
  54. template<class T>
  55. static void scatter
  56. (
  57. const List<commsStruct>& comms,
  58. T& Value,
  59. const int tag,
  60. const label comm
  61. );
  62. //- Like above but switches between linear/tree communication
  63. template<class T>
  64. static void scatter
  65. (
  66. T& Value,
  67. const int tag = Pstream::msgType(),
  68. const label comm = Pstream::worldComm
  69. );
  70. // Combine variants. Inplace combine values from processors.
  71. // (Uses construct from Istream instead of <<)
  72. template<class T, class CombineOp>
  73. static void combineGather
  74. (
  75. const List<commsStruct>& comms,
  76. T& Value,
  77. const CombineOp& cop,
  78. const int tag,
  79. const label comm
  80. );
  81. //- Like above but switches between linear/tree communication
  82. template<class T, class CombineOp>
  83. static void combineGather
  84. (
  85. T& Value,
  86. const CombineOp& cop,
  87. const int tag = Pstream::msgType(),
  88. const label comm = Pstream::worldComm
  89. );
  90. //- Scatter data. Reverse of combineGather
  91. template<class T>
  92. static void combineScatter
  93. (
  94. const List<commsStruct>& comms,
  95. T& Value,
  96. const int tag,
  97. const label comm
  98. );
  99. //- Like above but switches between linear/tree communication
  100. template<class T>
  101. static void combineScatter
  102. (
  103. T& Value,
  104. const int tag = Pstream::msgType(),
  105. const label comm = Pstream::worldComm
  106. );
  107. // Combine variants working on whole List at a time.
  108. template<class T, class CombineOp>
  109. static void listCombineGather
  110. (
  111. const List<commsStruct>& comms,
  112. List<T>& Value,
  113. const CombineOp& cop,
  114. const int tag,
  115. const label comm
  116. );
  117. //- Like above but switches between linear/tree communication
  118. template<class T, class CombineOp>
  119. static void listCombineGather
  120. (
  121. List<T>& Value,
  122. const CombineOp& cop,
  123. const int tag = Pstream::msgType(),
  124. const label comm = Pstream::worldComm
  125. );
  126. //- Scatter data. Reverse of combineGather
  127. template<class T>
  128. static void listCombineScatter
  129. (
  130. const List<commsStruct>& comms,
  131. List<T>& Value,
  132. const int tag,
  133. const label comm
  134. );
  135. //- Like above but switches between linear/tree communication
  136. template<class T>
  137. static void listCombineScatter
  138. (
  139. List<T>& Value,
  140. const int tag = Pstream::msgType(),
  141. const label comm = Pstream::worldComm
  142. );
  143. // Combine variants working on whole map at a time. Container needs to
  144. // have iterators and find() defined.
  145. template<class Container, class CombineOp>
  146. static void mapCombineGather
  147. (
  148. const List<commsStruct>& comms,
  149. Container& Values,
  150. const CombineOp& cop,
  151. const int tag,
  152. const label comm
  153. );
  154. //- Like above but switches between linear/tree communication
  155. template<class Container, class CombineOp>
  156. static void mapCombineGather
  157. (
  158. Container& Values,
  159. const CombineOp& cop,
  160. const int tag = Pstream::msgType(),
  161. const label comm = UPstream::worldComm
  162. );
  163. //- Scatter data. Reverse of combineGather
  164. template<class Container>
  165. static void mapCombineScatter
  166. (
  167. const List<commsStruct>& comms,
  168. Container& Values,
  169. const int tag,
  170. const label comm
  171. );
  172. //- Like above but switches between linear/tree communication
  173. template<class Container>
  174. static void mapCombineScatter
  175. (
  176. Container& Values,
  177. const int tag = Pstream::msgType(),
  178. const label comm = UPstream::worldComm
  179. );
  180. // Gather/scatter keeping the individual processor data separate.
  181. // Values is a List of size UPstream::nProcs() where
  182. // Values[UPstream::myProcNo()] is the data for the current processor.
  183. //- Gather data but keep individual values separate
  184. template<class T>
  185. static void gatherList
  186. (
  187. const List<commsStruct>& comms,
  188. List<T>& Values,
  189. const int tag,
  190. const label comm
  191. );
  192. //- Like above but switches between linear/tree communication
  193. template<class T>
  194. static void gatherList
  195. (
  196. List<T>& Values,
  197. const int tag = Pstream::msgType(),
  198. const label comm = UPstream::worldComm
  199. );
  200. //- Scatter data. Reverse of gatherList
  201. template<class T>
  202. static void scatterList
  203. (
  204. const List<commsStruct>& comms,
  205. List<T>& Values,
  206. const int tag,
  207. const label comm
  208. );
  209. //- Like above but switches between linear/tree communication
  210. template<class T>
  211. static void scatterList
  212. (
  213. List<T>& Values,
  214. const int tag = Pstream::msgType(),
  215. const label comm = UPstream::worldComm
  216. );
  217. // Exchange
  218. //- Helper: exchange contiguous data. Sends sendData, receives into
  219. // recvData. If block=true will wait for all transfers to finish.
  220. template<class Container, class T>
  221. static void exchange
  222. (
  223. const UList<Container>& sendData,
  224. const labelUList& recvSizes,
  225. List<Container>& recvData,
  226. const int tag = UPstream::msgType(),
  227. const label comm = UPstream::worldComm,
  228. const bool block = true
  229. );
  230. //- Helper: exchange sizes of sendData. sendData is the data per
  231. // processor (in the communicator). Returns sizes of sendData
  232. // on the sending processor.
  233. template<class Container>
  234. static void exchangeSizes
  235. (
  236. const Container& sendData,
  237. labelList& sizes,
  238. const label comm = UPstream::worldComm
  239. );
  240. //- Exchange contiguous data. Sends sendData, receives into
  241. // recvData. Determines sizes to receive.
  242. // If block=true will wait for all transfers to finish.
  243. template<class Container, class T>
  244. static void exchange
  245. (
  246. const UList<Container>& sendData,
  247. List<Container>& recvData,
  248. const int tag = UPstream::msgType(),
  249. const label comm = UPstream::worldComm,
  250. const bool block = true
  251. );
  252. };

我们看到Pstream类有一个构造函数,剩下的都是静态成员函数,而这些成员函数就是并行通讯的工具箱
这里多问一句,为什么工具箱的函数都是静态成员函数


为什么这里用静态成员函数呢

用静态成员可以变量实现多个对象间的数据共享,比全局变量更安全
这里我详细说下,举个例子

  1. Time mytime1;
  2. mytime1.hour=2;
  3. Time mytime2;
  4. mytime2.hour=4;

这段程序中成员变量是跟着对象走的,他们的对象各自占用不同的内存地址,彼此互不影响
那我们想做类内的全局变量满足相互通信需求,在不同对象mytime1和mytime2中共享一个副本,怎么办
这时static关键字就派上用场了,增加了static关键字或成员函数不隶属整个对象,而隶属于整个类
因为这个变量跟着类走,所以调用时用“类名::成员变量名”或“类名::成员变量函数”进行调用(当然也可用“对象名.静态函数名”),表示明确的隶属关系,不创建对象也可进行访问编辑
在Pstream类调用工具箱中函数时,我们常见到这样的调用方式,而且不创建Pstream对象也可进行调用

  1. // 在head节点收集信息
  2. Pstream::gatherList(nInternalFaces);
  3. Pstream::gatherList(nBoundaries);

因为类的静态成员脱离了与对象的关系,普通成员变量的内存分配是在对象初始化时完成的,对于静态成员必须在程序的全局区进行清晰的初始化
全局区的初始化过程可由某个.cpp源文件的开头的静态成员函数完成,如下所示:

  1. void Time::func(int testValue)
  2. {
  3. mystatic = testValue ;
  4. }

或者在全局区这样写:

  1. int Time::mystatic=10;

这样能保证这个静态成员变量能够被正常使用。
此外静态成员函数只能调用静态成员变量,也没有this指针可以使用
这里上一张图可能更方便理解
image

C++程序运行时,静态变量和全局变量存储在数据段,所以需要在全局区通过直接分配内存或者静态函数进行分配内存
因而静态成员的生命周期与程序运行周期相同,在程序中只有一份,无论创建对象与否,或者创建多少对象
说到这里可能大家对Openfoam的并行通信多了一些理解,只要开始了并行计算那么就可以通过Pstream类内的成员函数进行通信调用,在同样的数据段副本上进行信息流沟通


接下来依次说下类中各个工具的使用

收发数据
Pstream::gather()与Pstream::scatter()分别有两个重载,分别是收集以及散布数据,不如后面Pstream::gatherList()与Pstream::scatterList()常用,这里不细说了
Pstream::combineGather()、Pstream::combineScatter()重载情况与上同,用于就地集中收集或散布的数据,不太常用

Pstream::listCombineGather()、Pstream::listCombineScatter()重载情况与上同,用于一次整合list容器中的变量
Pstream::mapCombineGather()、Pstream::mapCombineScatter()重载情况与上同,用于一次整合整个map容器中的变量

Pstream::gatherList()以及Pstream::scatterList()的第二个重载比较常用,

  1. template<class T>
  2. static void gatherList
  3. (
  4. List<T>& Values,
  5. const int tag = Pstream::msgType(),
  6. const label comm = UPstream::worldComm
  7. );
  8. template<class T>
  9. static void scatterList
  10. (
  11. List<T>& Values,
  12. const int tag = Pstream::msgType(),
  13. const label comm = UPstream::worldComm
  14. );

Pstream::gatherList()以及Pstream::scatterList()的输入第一个参数是Values
这个Values需要自己整合下,Values是UPstream::nProcs()数量大小的List,比如说我要收集内部面可以这样创建需要收集的List

  1. List<label> nIternalFaces(Pstream::nProcs());
  2. nIternalFaces[Pstream::myProcNo()] = mesh.Cf().size();//比如说看看每个节点分到了多少网格
  3. Pstream::gatherList(nIternalFaces);//在头结点收集数据

Pstream::scatterList()与之类似
Pstream::gatherList()以及Pstream::scatterList()的输入第二个参数是Pstream::msgType(),默认为1,可以不输入

  1. int Foam::UPstream::msgType_(1);

Pstream::gatherList()以及Pstream::scatterList()的输入第三个参数是Pstream::msgType(),默认为0,可以不输入

  1. Foam::label Foam::UPstream::worldComm(0);

交换数据
Pstream::exchange()有两个重载,用于交换连续的数据,一般情况下等待其他所有传输完成再传输,可通过默认参数block()修改优先权
Pstream::exchangeSizes()用于交换数据的大小


下面是Pstream类函数相互关系
image


结语

并行开发远不止收发数据这么简单,还有很多类可说的,后续会一一进行介绍,并对openfoam并行计算进行优化

一起探索openfoam也是相当有趣的一件事,非常欢迎私信讨论
指正的价值要比打赏更重要,下面是个人联系方式,能结交到志同道合的朋友是我的荣幸
image

原文链接:https://www.cnblogs.com/heloveHe/p/17148332.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号