본문 바로가기

OpenFOAM/소스코드 파해치기

[OpenFOAM 소스코드 파해치기]입출력오브젝트

일본어 원문링크


작성일 : 2012년  3월  20일

번역일 : 2016년  3월   29일


크롬 브라우저로 보시는 것을 권장해 드립니다.


 ------ OpenFOAM 소스코드 파해치기 시리즈 ------

OpenFOAM 소스코드 파해치기 목차로 이동

 ------------------------------------------------------------

 ----- OpenFOAM 소스코드를 다루는 문법 기본 -----

OpenFOAM 소스코드를 다루는 문법 기본으로 이동

 ------------------------------------------------------------



 

  들어가기 


필드장 변수의 입출력에 대해 알아보자.



  사용 버전 


OpenFOAM 2.1.0

 


   Time


솔버에 있어 필드장 변수의 입출력은 아래의 코드로 수행된다.

runTime.write();


입출력을 매번 수행하지 않고, controlDict의 writeControl/writeInterval 에서 지정된 타이밍에 수행하므로, 내부에서 어떠한 조작이 수행되고 있음을 상상할 수 있다. 또한, 필드의 변수를 불러들일때는 createField.H 에의해 다음과 같이 수행된다.

volVectorField U ( IOobject ( "U", runTime.timeName(), mesh, IOobject::MUST_READ, IOobject::AUTO_WRITE ), mesh );


"IOobject::AUTO_WRITE"라는 출력에 관계되는 문자열이 보인다. 여기에는 이 외에 "IOobject::NO_WRITE"라는 옵션도 가능하므로, 여기서 필드장 변수를 출력할지의 여부를 결정한다는 것을 알 수 있다. 따라서, 솔버는 controlDict 와 필드장 변수 별 출력설정을 확인해, 변수를 출력할지 안할지를 판단하고 있음을 알 수 있다.


따라서, runTime 클래스인 Time이 필드장 변수의 출력에 관계하고 있음을 알 수 있다. Time이 계승받는 형태를 보면 다음과 같다.

Time - objectRegistry - regIOobject - IOobject


이름에서 알 수 있듯, 분명히 입출력에 관계되는 클래스들에 해당한다. 아마도 Time 자신이 아닌, 계승 받는 상위 클래스의 어느곳에서 입출력을 수행하고 있다고 생각할 수 있다(항상 이렇다고는 말할 수 없지만, 정상적인 프로그램에서는 그렇다).


어쨋든 runTIme의 정의를 살펴보자 (createTime.H)

Foam::Time runTime(Foam::Time::controlDictName, args);


contorlDict를 읽어들이는 작업만 수행하고 있다. 









  fvMesh

 

runTime이 U 등의 입출력을 수행한다고 한다면, 어딘가에서 U 를 건드린다는 것을 의미하지만, runTime과 U는 직접적으로 관련이 되는 코드는 보이지 않는다. U 는 mesh와 관련이 있으므로, mesh가 runTime과 관련이 있음을 생각할 수 있다. mesh의 정의를 살펴보자(createMesh.H)

    Foam::fvMesh mesh
    (
        Foam::IOobject
        (
            Foam::fvMesh::defaultRegion,
            runTime.timeName(),
            runTime,
            Foam::IOobject::MUST_READ
        )
    );

runTime 이 주어지고 있으므로, mesh를 통해 runTime과 U 가 접촉하게 된다. mesh 의 클래스인 fvMesh가 계승받는 클래스는 다음과 같다.

fvMesh - polyMesh - objectRegistry - regIOobject - IOobject


Time의 친척이다









  GeometricField

 

U 가 runTime 에서 입출력된다고 판정한다면, U 가 mesh 를 통해 runTime에 전달되던지 다른 방법이 있던지 한다는 것을 알 수 있다.. Time/fvMesh 의 선조같은 클래스인 objectRegistry가 이름을 보면 수상하지만, 그 부분은 나중에 다루기로 하고 먼저 U의 클래스인 volVectorField의 실체인 GeoemtricField의 혈통을 살펴보자.

GeometricField - DimensionedField - regIOobject - IOobject

GeometricField는 Time 이나 fvMesh 와는 먼 친척이다. IOobject는 아마도 입출력 오브젝트일지 몰라도, regIOobject는 "등록된 IOobject"를 의미하고 있다. 이것이 objectRegistry에 등록되거나 하는 것일까?









  관계

 

관계자들 각각의 특징들을 한번 훑어보았으니, 그들간의 관계를 살펴보자. runTime, mesh, U 를 서로 연결하고 있는 것은 생성자 밖에 없다. 특히, IOobject의 생성자의 세번째 인수가 열쇠다. IOobject의 생성자는 다음과 같이 만들어져있다.

Foam::IOobject::IOobject ( const word& name, const fileName& instance, const objectRegistry& registry, readOption ro, writeOption wo, bool registerObject ) : name_(name), headerClassName_(typeName), note_(), instance_(instance), local_(), db_(registry), rOpt_(ro), wOpt_(wo), registerObject_(registerObject), objState_(GOOD) { ...

3번째 인수는 objectRegistry 의 형태로 입력되며, 멤버함수인 db_ 로 등록되고 있다. 그렇다면 IOobject의 오브젝트 자신은 어떤 운명인 것인가?


fvMesh의 경우, 생성자를 통해 IOobject io 는 그대로 fvMesh의 상위클래스인 polyMesh 로 전달되어 polyMesh 에서는 그 상위 클래스인 objectRegistry로 전달한다. objectRegistry 에서도 역시 상위클래스인 regIOobject로 그대로 보낸다. regIOobject에서 드디어 IOobject로 전달되므로, 즉, fvMesh에 전달된 IOobject는 그대로 IOobject로서 fvMesh 자신을 만드는데 사용되고 있음을 알 수 있다. 그렇다는 것은, IOobject 의 db_ 는 그대로 fvMesh의 db_ 라는 것을 의미한다.


결국 runTime은 mesh의 db_ 에 등록되어, mesh는 U의 db_ 에 등록된다는 것이다.


여기서, regIOobject의 생성자에는 다음과 같은 글귀가 있다.

// Register with objectRegistry if requested

if (registerObject())

{

checkIn();

}

아무래도 여기서는 등록을 수행하지 않는것 같다. 이부분을 알아보기 전에, 출력을 살펴보자.









  출력

 

출력은 AUTO_WRITE 또는 NO_WRITE로 판정되므로 이 명령어가 사용되는 근처에 출력처리가 있을 것이다. objectRegistry의 writeObject가 그 함수에 해당한다는것을 알 수 있다.

bool Foam::objectRegistry::writeObject ( IOstream::streamFormat fmt, IOstream::versionNumber ver, IOstream::compressionType cmp ) const { bool ok = true; forAllConstIter(HashTable<regIOobject*>, *this, iter) { ... if (iter()->writeOpt() != NO_WRITE) { ok = iter()->writeObject(fmt, ver, cmp) && ok; } } return ok; }

함수를 보면, iterator를 사용해 HashTable을 돌려 각 요소(regIOobject)에 출력옵션이 "NO_WRITE"가 아니면 각각의 writeObject 함수를 불러들인다. 이때 HashTable은 자기자신(*this)을 지정하고 있다. objectRegistry 자신이 출력오브젝트를 관리하는 테이블임을 의미하며, 실제로 objectRegistry는 HashTable 클래스를 계승하고 있다.


따라서, objectRegistry 내부의 오브젝트의 등록처리를 어떻게 수행하는지 찾으면 된다는 것을 의미한다. HashTable에 요소를 추가하는것은 insert 함수이지만, 그것을 사용하는 것은 checkIn 함수이다.

bool Foam::objectRegistry::checkIn(regIOobject& io) const { ... return const_cast(*this).insert(io.name(), &io); }

이 checkIn 함수에 의해 등록된 오브젝트가 입출력된다는 것을 의미한다?









  관계(계속)

 

그리하야 regIOobject의 생성자에서 호출되는 checkIn 함수가 범인이지만 인수를 받지 않으므로 objectRegistry 것이 아닌 regIOobject의 멤버함수에 해당한다. 그것을 확인할수 있는 부분은 아래의 코드 한줄 이다.

registered_ = db().checkIn(*this);

db 함수가 출력하는 값은 db_ 이므로, db_ 에 자기자신을 등록한다는 것을 의미한다.


정리하면, mesh, U 는 IOobject의 세번째 인수 objectRegistry (각각, runTime, mesh)를 db_ 로 하여, 자기자신을 regIOobject로 그 db_ 에 등록하고 있다. runTime이 mesh를 가지고 있고, mesh 가 U 를 가지고 있는 구조이다. 참고로 runTime도 regIOobject이지만, Time은 별도로 처리되고 있으며, 등록되지 않는다(IOobject의 생성자의 제일 마지막 인수가 이 부분에 해당한다).









  출력의 매커니즘

 

이상으로, 필드변수의 출력에 대해 아래와 같은 매커니즘이 수행되고 있다.


1. runTime.write() 가 호출된다. 이것은 regIOobject의 함수로, writeObject 함수(regIOobjectWrite.C)를 호출한다.

2. objectRegistry의 writeObject가 호출되어 runTime에 등록되어있는 mesh의 writeObject가 호출된다.

3. mesh도 objectRegistry이므로, objectRegistry의 writeObject가 호출되고, mesh에 등록되어있는 필드장 변수의 writeObject가 각각 호출된다.

4. regIOobject의 writeObject가 호출되어 writeData가 호출된다(이것은 단순히 가상함수로, 실상은 각각의 것)

 

그리하여 필드장 변수는 runTime이 아닌, mesh에 등록되어있는것이 판명되었다.

 



   정리


이번에 출력한 클래스


  • IOobject : 입출력 오브젝트
  • regIOobject : objectRegistry 에 등록되는 입출력오브젝트
  • objectRegistry : regIOobject를 관리하는 클래스

     




   추가정보


  • 솔버에서 "objectRegistry::debug = true" 를 사용하면 디버깅 정보가 출력, 위의 출력구조의 작동을 확인할 수 있다.
  • 필드장 변수를 정의할 때 IOobject에 이름을 등록하나, 같은 이름을 등록하면 먼저 정의된 것이 우선순위를 가진다. 그러나, mesh에 등록되어있는것과 runTime에 등록되어있는것은 별도로 다루므로 기존의 이름과 같은 이름을 꼭 사용하고 싶다면 runTime의 checkIn 함수에서 runTime으로 동록하면된다.
  • OpenFOAM은 C++ 처럼 구조화가 잘 되어있으므로, 변수에 마음대로 접근할 수 없도록 되어 있으나, 사실 mesh는 모든 필드장 변수를 알고 있으므로, mesh 하나로 모든 필드장 변수에도 접근은 이론적으로 가능하다.