Openfoam UPstream類探索

TJUHE發表於2023-02-24

前言

上篇文章我們大體捋順了Pstream類,但上篇沒有講到的是Pstream類很多東西是從UPstream類繼承來的
這次我們大體看一下UPstream類,以避免Pstream類內很多繼承來的東西不會用


簡述幾個常用的函式如下:

Pstream::myProcNo()

//- Number of this process (starting from masterNo() = 0)
static int myProcNo(const label communicator = 0)
{
    return myProcNo_[communicator];
}

就像我們提到的,靜態成員變數一般有兩種初始化方式,一種是全域性區直接定義,另一種是利用靜態成員函式進行值的返回,Pstream::myProcNo()是後者
輸入引數預設從零開始計算程式數量,返回值型別為static DynamicList < int >,容器初始大小是10

//- My processor number
        static DynamicList<int> myProcNo_;
Foam::DynamicList<int> Foam::UPstream::myProcNo_(10);

這裡我覺得有必要擴充套件解釋下Foam::DynamicList,Foam內非常常見的一種資料型別


Foam::DynamicList,公有繼承於Foam::List的vector容器

image
openfoam對其的描述為:

A 1D vector of objects of type < T > that resizes itself as necessary to accept the new objects.
Internal storage is a compact array and the list can be shrunk to compact storage. The increase of list size is controlled by three template parameters, which allows the list storage to either increase by the given increment or by the given multiplier and divider (allowing non-integer multiples).

Foam::DynamicList的介紹裡說是一個可以根據需要調整自身大小以接受新物件的一維容器,內部儲存是一個緊湊的陣列
講實話看解釋並沒有怎麼理解,直接閱讀相關實現程式

#include "DynamicList.H"
 
 // * * * * * * * * * * * * * * * IOstream Operators  * * * * * * * * * * * * //
 
 
 template<class T, unsigned SizeInc, unsigned SizeMult, unsigned SizeDiv>
 Foam::DynamicList<T, SizeInc, SizeMult, SizeDiv>::DynamicList(Istream& is)
 :
     List<T>(is),
     capacity_(List<T>::size())
 {}
 
 
 template<class T, unsigned SizeInc, unsigned SizeMult, unsigned SizeDiv>
 Foam::Ostream& Foam::operator<<
 (
     Ostream& os,
     const DynamicList<T, SizeInc, SizeMult, SizeDiv>& lst
 )
 {
     os << static_cast<const List<T>&>(lst);
     return os;
 }
 
 
 template<class T, unsigned SizeInc, unsigned SizeMult, unsigned SizeDiv>
 Foam::Istream& Foam::operator>>
 (
     Istream& is,
     DynamicList<T, SizeInc, SizeMult, SizeDiv>& lst
 )
 {
     is >> static_cast<List<T>&>(lst);
     lst.capacity_ = lst.List<T>::size();
 
     return is;
 }

首先看Foam::DynamicList的建構函式,利用初始化列表建立了大小為輸入引數的List< T >,隨後又把這個大小給了相關的成員變數,後續是兩個過載,其他構造大同小異
那這就很清晰了,Foam::DynamicList本質上還是List,
但又有新的問題了,那為什麼在openfoam釋義中提及vector或者array呢
能看到Foam::DynamicList並沒有過載[],寫到這裡我原以為只是一個類似陣列一樣可以擴充記憶體的List時,無法像vector一樣隨機儲存,但無意間發現Foam::DynamicList過載了(),我把過載那部分給大家看一下哈

template<class T, unsigned SizeInc, unsigned SizeMult, unsigned SizeDiv>
inline T& Foam::DynamicList<T, SizeInc, SizeMult, SizeDiv>::operator()
(
    const label elemI
)
{
    if (elemI >= List<T>::size())
    {
        setSize(elemI + 1);
    }

    return this->operator[](elemI);
}

能看到是可以隨機儲存的,並且呼叫的this指標,說明內部是List< T > ,但是外殼是陣列
此刻我們就明白了Foam::DynamicList為什麼說自己是一維容器,有兩種情況:

  1. 如果不超過陣列大小,在插入和讀取時,使用的List進行插入,複雜度僅是O(1)
  2. 但是超過陣列大小,進行插入操作,需要類似vector一樣重新分配空間,但又好在是List,重新分配空間無需複製貼上折騰,只需要傳地址即可

並且在以上基礎上可以類似vector進行隨機訪問元素,兼顧了兩者優點

寫到這裡不由得感慨,openfoam作為數值計算程式,在資料型別選擇上還是有自己獨到之處的,不僅要考慮安全性還要考慮效率
如果我需要插入或彈出一個資料,傳list地址即可,但是我還有隨機儲存需求,那就外邊套一層陣列的殼吧,擴容也方便

講到這裡多提一嘴
類似的還有Foam::DLList,

 namespace Foam
 {
     template<class T>
     using DLList = LList<DLListBase, T>;
 }

大概是包著List外殼的List容器,如果是這樣,插入複雜度是O(1),讀取複雜度是O(n)

那我們再回到問題的開始,為什麼的型別是Foam::DynamicList
因為myProcNo_需要動態變化,如果出現程式的增加和刪減,可以類似陣列進行擴容,
所以在平行計算中,Foam::DynamicList使用的非常頻繁,後續會發現經常出現在其他並行函式的返回值中


後續還有
Pstream::nProcs()
Pstream::myProcNo()
Pstream::parRun()
UPstream::exit(0)
這些函式需要去說下,今天有點累了,下期再聊
後續探索一定會有更多驚喜等待著我們


結語

一起探索openfoam也是相當有趣的一件事,非常歡迎私信討論
指正的價值要比打賞更重要,下面是個人聯絡方式,能結交到志同道合的朋友是我的榮幸
image

相關文章