CFD Online Logo CFD Online URL
www.cfd-online.com
[Sponsors]
Home > Forums > Software User Forums > OpenFOAM > OpenFOAM Programming & Development

Trying to understand OpenFOAM source code (pTraits.h)

Register Blogs Community New Posts Updated Threads Search

Like Tree8Likes
  • 8 Post By t.teschner

Reply
 
LinkBack Thread Tools Search this Thread Display Modes
Old   April 21, 2019, 22:45
Cool Trying to understand OpenFOAM source code (pTraits.h)
  #1
Senior Member
 
Lee Strobel
Join Date: Jun 2016
Posts: 133
Rep Power: 9
Time4Tea is on a distinguished road
I'm trying to read and understand some of the OpenFOAM source code (specifcally, version 6 from openfoam.org). I've chosen to start at pTraits.h, as it seems quite foundational. I was trying to understand some of the classes relating to vector fields and point fields, but they seem to be using this header as a base.


Anyway, I have some basic knowledge of C/C++; however, I am confused by some of the syntax that is being used:


Firstly, at line 44:


Code:
class Istream;

The Istream class clearly isn't being defined here; however, there are no other header file being included. Where does this Istream come from and what does it do? I can see that it is being used in one of the constructors further down.


Secondly, lines 52-53:


Code:
:
     public PrimitiveType

I don't understand what the colon means in the class template declaration, or what the 'public PrimitiveType' is for.



Also, to summarize what this header is doing: it is providing a basic template class, with a couple of constructors for primitive objects - is that correct?



In general, I'm not sure if this particular file is a good place to start delving into the OF codebase - any advice on that would be much appreciated.
Time4Tea is offline   Reply With Quote

Old   April 22, 2019, 08:09
Default
  #2
Senior Member
 
Tom-Robin Teschner
Join Date: Dec 2011
Location: Cranfield, UK
Posts: 204
Rep Power: 16
t.teschner is on a distinguished road
these are just some c++ specific ways of doing things.

The "class Istream" is a forward declaration of the class Istream and is just here so that your linker doesn't complain. You could remove this and include a header file to the actual implementation of the class, i.e. put #include "IStream.H" at the top, but that includes the whole header. I guess it comes down to your personal preference and coding style, but generally speaking, for large projects such as openfoam you should try to avoid including header files and use forward declaration whenever possible.
The forward declared class just tells the linker not to complain and to assume that such a class exist. Of course, this class needs then to be declared somewhere else which the compiler / linker can find.

Regarding your second question, the construct

Code:
template<class A>
class B : public A
is used for static polymorphism. I am not sure how much you know about polymorphism in general (virtual functions, templates) which would also determine how much sense an explanation would make at this point, but basically, you can achieve static polymorphism through templates and dynamic polymorphism through the use of virtual functions.
The above construct allows you to use methods from class A in class B but you don't need to know what A is, you just need to make sure that it will have the required method that you want to call. The advantage here is, that class A can have many different versions while class B doesn't care what A is, it just makes use of its interface (data abstraction, another important concept in C++).

Now this all sounds very complicated and dry without an example, so lets say we want to write a CFD solver and we want to have different reconstruction / interpolation scheme, say a first and a second order accurate one. One way of implementing that with the structure given in the pTraits class could look like this:

Code:
#include <iostream>

class firstOrderReconstruction
{
  public:
    void reconstruct()
    {
      std::cout << "Implement a first order reconstruction here" << std::endl;
    }
};

class secondOrderReconstruction
{
  public:
    void reconstruct()
    {
      std::cout << "Implement a second order reconstruction here" << std::endl;
    }
};

template<class scheme>
class reconstructor : public scheme
{
  using scheme::reconstruct;
  public:
    void applyReconstruction()
    {
      reconstruct();
    }
};

int main()
{
  reconstructor<firstOrderReconstruction > firstOrder;
  reconstructor<secondOrderReconstruction> secondOrder;

  firstOrder.applyReconstruction(); // will print "Implement a first order reconstruction here"
  secondOrder.applyReconstruction(); // will print "Implement a second order reconstruction here"
  return 0;
}
so basically we provide two implementations for the reconstruction class (a first and second order one) and just by specifying it as the template argument we now have a first or second order accurate reconstruction scheme. If you look at the class "reconstructor", it inherits from its template argument but it doesn't know if it is first or second order. This makes the code less readable but more performance optimised because templates are resolved at compile time, meaning that each possible combination (here we only have two, the first and second order reconstruction class as a template argument) will be compiled and this at run time the correct version will be selected. this is performance optimised because we decide at compile time which version we use, the more "C" style way of doing things here would be to use an if clause, or a switch / case statement, but that would incur a runtime overhead and thus would be slower.
As a side note, you could have achieved the same behaviour above with dynamic runtime polymorphism, i.e. using virtual functions, that would have looked like this

Code:
#include <iostream>

class reconstructor
{
  public:
    // we could also drop the = 0 at the end, but in this way we force 
    // all classes that derive from this to implement this virtual function
    // (now called a pure virtual function)  
    virtual void applyReconstruction() = 0;
};

class firstOrderReconstruction : public reconstructor
{
  public:
    void applyReconstruction() final override
    {
      std::cout << "Implement a first order reconstruction here" << std::endl;
    }
};

class secondOrderReconstruction : public reconstructor
{
  public:
    void applyReconstruction() final override
    {
      std::cout << "Implement a second order reconstruction here" << std::endl;
    }
};

int main()
{
  firstOrderReconstruction  firstOrder;
  secondOrderReconstruction secondOrder;

  firstOrder.applyReconstruction(); // will print "Implement a first order reconstruction here"
  secondOrder.applyReconstruction(); // will print "Implement a second order reconstruction here"
  return 0;
}
Similar, but there is a difference in the execution again. Virtual functions get resolved at runtime, i.e. when we call firstOrder.applyReconstruction() for example, only then do we lookup which version we actually want to call (i.e. which class is overriding the virtual function in the base class reconstructor). Templates do that at compile time, not at runtime, thus we say virtual functions follow dynamic polymorphism while templates can be used (and abused) to give you static polymorphism.

Fairly exhaustive, but I feel that c++ question are rarely answered in just two sentences. Hope that that gives you a better idea.
nipinl, Time4Tea, vivek05 and 5 others like this.
t.teschner is offline   Reply With Quote

Old   April 22, 2019, 21:42
Default
  #3
Senior Member
 
Lee Strobel
Join Date: Jun 2016
Posts: 133
Rep Power: 9
Time4Tea is on a distinguished road
Hi t.teschner, thank you very much for the great explanation, with the examples. This is very helpful, thank you.


I think I have a basic understanding of Polymorphism and Inheritance, but I am reading some more into them and how they are implemented in C++. I'm getting the impression that OF makes quite extensive use of these concepts, so I will need to understand them more deeply.


Your explanation and examples of the polymorphism are very helpful. So, the pTraits class can inherit from a range of different classes and 'PrimitiveType' is the template 'placeholder' for the type of class it inherits from?



One thing I am still a bit confused about though is what the pTraits class is doing. It seems to be inheriting these two constructors from whichever class PrimitiveType is referring to (the first one looks like a copy constructor - is that correct?). However, as far as I can see, it doesn't seem to be modifying them in any way. So, what is the function of the pTraits class?
Time4Tea is offline   Reply With Quote

Old   April 23, 2019, 01:22
Default
  #4
Senior Member
 
Tom-Robin Teschner
Join Date: Dec 2011
Location: Cranfield, UK
Posts: 204
Rep Power: 16
t.teschner is on a distinguished road
Yes, openfoam and makes great use of C++'s object orientated features (which allows us to type our equations in a pain-free manner ...). And yes, you can see the template parameter as a placeholder, that is exactly what they are.

I don't have the code currently in front of me but I would guess that this class just provides an interface for other classes which derive from this class (so that any derived class has a constructor that accepts the template argument's parameter or an Istream object). So its use would have to be evaluate in the context of a derived class. The term copy constructor is not correct in this context, for that the constructor would accept an argument of the class itself, i.e. the copy constructor would look something like pTraits(pTraits& p).

Hope this helps.

Last edited by t.teschner; April 25, 2019 at 14:59.
t.teschner is offline   Reply With Quote

Reply

Tags
c++, code, openfoam 6


Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are Off
Pingbacks are On
Refbacks are On


Similar Threads
Thread Thread Starter Forum Replies Last Post
Custom Thermophysical Properties wsmith02 OpenFOAM 4 June 1, 2023 14:30
polynomial BC srv537 OpenFOAM Pre-Processing 4 December 3, 2016 09:07
OpenFOAM without MPI kokizzu OpenFOAM Installation 4 May 26, 2014 09:17
OpenFOAM on MinGW crosscompiler hosted on Linux allenzhao OpenFOAM Installation 127 January 30, 2009 19:08
DecomposePar links against liblamso0 with OpenMPI jens_klostermann OpenFOAM Bugs 11 June 28, 2007 17:51


All times are GMT -4. The time now is 11:31.