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

Turbulence models and run time selection tables

Register Blogs Community New Posts Updated Threads Search

Like Tree26Likes
  • 15 Post By wyldckat
  • 5 Post By tomislav_maric
  • 2 Post By santiagomarquezd
  • 1 Post By tomislav_maric
  • 1 Post By tomislav_maric
  • 2 Post By alexeym

Reply
 
LinkBack Thread Tools Search this Thread Display Modes
Old   September 13, 2014, 15:47
Default Turbulence models and run time selection tables
  #1
Senior Member
 
santiagomarquezd's Avatar
 
Santiago Marquez Damian
Join Date: Aug 2009
Location: Santa Fe, Santa Fe, Argentina
Posts: 452
Rep Power: 23
santiagomarquezd will become famous soon enough
Hi all, I spent some hours overnight to understand how turbulence models are selected by the run time selection mechanism, the best commentary I've read was by marupio:

"4.3.5 When does this all happen?

The "virtual constructor" table is fully built and loaded into memory when the include list is fully read, and before the first line of code is executed. This is achieved using the static keyword. All static members of a class exist prior to any instance of that class."

OpenFOAM guide/runTimeSelection mechanism

For example en turbulenceModel.C

Code:
   75 
   76 autoPtr<turbulenceModel> turbulenceModel::New
   77 (
   78     const volVectorField& U,
   79     const surfaceScalarField& phi,
   80     transportModel& transport,
   81     const word& turbulenceModelName
   82 )
   83 {
   84     // get model name, but do not register the dictionary
   85     // otherwise it is registered in the database twice
   86     const word modelType
   87     (
   88         IOdictionary
   89         (
   90             IOobject
   91             (
   92                 "turbulenceProperties",
   93                 U.time().constant(),
   94                 U.db(),
   95                 IOobject::MUST_READ_IF_MODIFIED,
   96                 IOobject::NO_WRITE,
   97                 false
   98             )
   99         ).lookup("simulationType")
  100     );
  101 
  102     Info<< "Selecting turbulence model type " << modelType << endl;
  103 
  104     turbulenceModelConstructorTable::iterator cstrIter =
  105         turbulenceModelConstructorTablePtr_->find(modelType);
  106 
  107     if (cstrIter == turbulenceModelConstructorTablePtr_->end())
  108     {
  109         FatalErrorIn
  110         (
  111             "turbulenceModel::New(const volVectorField&, "
  112             "const surfaceScalarField&, transportModel&, const word&)"
  113         )   << "Unknown turbulenceModel type "
  114             << modelType << nl << nl
  115             << "Valid turbulenceModel types:" << endl
  116             << turbulenceModelConstructorTablePtr_->sortedToc()
  117             << exit(FatalError);
  118     }
  119 
  120     return autoPtr<turbulenceModel>
  121     (
  122         cstrIter()(U, phi, transport, turbulenceModelName)
  123     );
The general turbulence model indicated in constant/turbulenceProperties, for example "RASModel", is searched in the turbulenceModelConstructorTable (lines 104-105). Next if it is found the proper constructor is called, if not, an error is presented in stdout. I've walked through the code using gdb until these lines and checked the Hash Table. I wasn't be able to see the contents of the table entries, but they are three, I guess they're RASModel, LESModel and laminar. Clearly this table was created and filled before any turbulence model is instantiated. I found the macros which define the method to create the table and add the entries but I couldn't find the very moment when they're are called such as marupio wrote. They are static so they are available before the objects are instantiated, but they need to be invoked in some code line. This was also a question of Jaswinder,

http://www.cfd-online.com/Forums/ope...nderstand.html

I've read other material about this topic, such as:

Run-time type selection in OpenFOAM – the type name system

but the point of when these tables are created and how isn't completely clear for me yet.

All commentaries are appreciated!

Santiago.
__________________
Santiago MÁRQUEZ DAMIÁN, Ph.D.
Research Scientist
Research Center for Computational Methods (CIMEC) - CONICET/UNL
Tel: 54-342-4511594 Int. 7032
Colectora Ruta Nac. 168 / Paraje El Pozo
(3000) Santa Fe - Argentina.
http://www.cimec.org.ar
santiagomarquezd is offline   Reply With Quote

Old   September 14, 2014, 04:38
Default
  #2
Retired Super Moderator
 
Bruno Santos
Join Date: Mar 2009
Location: Lisbon, Portugal
Posts: 10,975
Blog Entries: 45
Rep Power: 128
wyldckat is a name known to allwyldckat is a name known to allwyldckat is a name known to allwyldckat is a name known to allwyldckat is a name known to allwyldckat is a name known to all
Greetings Santiago,

OK, this is a bit strenuous to detail every single step with showing the actual code (and it's already done in the other pages), so I'll have to describe this in a more conceptual way.

First of all, let's approach a few initialization programming paradigms in a not very technical way, while keeping in mind that the technical terms and examples can be found here: http://www.cplusplus.com/doc/tutorial/
  1. Classes have constructors, which can initialize internal variables in two major ways:
    1. Define it in the initialization section, for example:
      Code:
      Rectangle::Rectangle (int a, int b) :
        width(a),
        height(b)
      {
        //Nothing else needed here, at least not in this case
      }
    2. Define it in the method's code section,
      Code:
      Rectangle::Rectangle (int a, int b)
      {
        width = a;
        height = b;
      }
  2. The difference between the two is that:
    1. The first way does the initialization in the first stages when the memory is being allocated for an instance of this class, e.g.:
      Code:
      Rectangle box(1,2);
    2. The second way does the initialization as if it were calling a method. For example, it would be as if you did this:
      Code:
      Rectangle box;
      box.set(1,2);
  3. Now, if you define the variable with the initial values in the global memory area of the program, e.g.:
    Code:
    Rectangle box(1,2);
    
    int main()
    {
      cout << box;
      return 0;
    }
    This means that the variable "box" is initialized even before the "main" function is called for execution.
  4. When libraries are involved, it's almost the same thing, if you define variables in the global memory region. Worse even is that it's very hard to have control over which is loaded first into memory... but that's a topic for a whole other discussion.


Now, with these concepts in mind, we can easily describe how the "RunTimeSelectionTable" mechanism works in OpenFOAM:
  1. If we look at the file "src/turbulenceModels/incompressible/turbulenceModel/turbulenceModel.C", we can find this line:
    Code:
    defineRunTimeSelectionTable(turbulenceModel, turbulenceModel);
    • This is the equivalent to the other example:
      Code:
      Rectangle box(1,2);
      albeit with a lot more macro-voodoo-magic in the middle.
  2. This line will essentially create a new global variable "turbulenceModelConstructorTablePtr_", which can later be used by the method "turbulenceModel::New()" a few lines below in the same file. Keep in mind that at first this variable is not populated with the whole list of turbulence models, because that's the next step.
  3. Since "turbulenceModelConstructorTablePtr_" is a global+shared variable, this means that it can be populated by any other class or library. That's where this line:
    Code:
    addToRunTimeSelectionTable(turbulenceModel, laminar, turbulenceModel);
    comes into play. This is in the file "src/turbulenceModels/incompressible/turbulenceModel/laminar/laminar.C".
  4. The line "addToRunTimeSelectionTable" will create a new global variable, but which isn't directly used. Weird, isn't it?
    But the trick is that this line:
    Code:
    addToRunTimeSelectionTable(turbulenceModel, laminar, turbulenceModel);
    does something similar to this example:
    Code:
    ElementRect cube(1);
    where the class "ElementRect" has this constructor:
    Code:
    ElementRect::ElementRect(int edgeLength)
    {
      box.set(edgeLength, edgeLength);
    }
  5. Therefore, the example will roll out to something like this:
    Code:
    Rectangle box(1,2);
    ElementRect cube(1);
    
    int main()
    {
      cout << box;
      return 0;
    }
    where the "box" is first defined to have dimensions of "1x2", but then the "cube" variable overrides the dimensions of the initial "box" settings.
  6. For our turbulence code, it conceptually becomes something like this:
    Code:
    //global pointer in "turbulenceModel.C" variable which will have the table
    static turbulenceModelConstructorTable* turbulenceModelConstructorTablePtr_;
    
    //laminar model adds itself to the global table, in "laminar.C"
    turbulenceModel::addlaminarConstructorToTable<turbulenceModel>        addlaminarConstructorToturbulenceModelTable_();
    Confusing? Well, the actual step of creating the instance of the object for "turbulenceModelConstructorTablePtr_" to point to, is done from within the constructor of the variable "addlaminarConstructorToturbulenceModelTable_" .
  7. In our example, it would be something like this:
    Code:
    ElementRect::ElementRect(int edgeLength)
    {
      box = new Rectangle(edgeLength, edgeLength);
    }
    
    //snip snip...
    
    Rectangle* box;
    ElementRect cube(1);


So, to answer your question, have a look at the file "src/OpenFOAM/db/runTimeSelection/construction/runTimeSelectionTables.H":
  1. This block of code:
    Code:
        void baseType::construct##argNames##ConstructorTables()                   \
        {                                                                         \
            static bool constructed = false;                                      \
            if (!constructed)                                                     \
            {                                                                     \
                constructed = true;                                               \
                baseType::argNames##ConstructorTablePtr_                          \
                    = new baseType::argNames##ConstructorTable;                   \
            }                                                                     \
        }
    Does the same as our example:
    Code:
    box = new Rectangle(edgeLength, edgeLength);
  2. This block of code:
    Code:
            add##argNames##ConstructorToTable                                     \
            (                                                                     \
                const word& lookup = baseType##Type::typeName                     \
            )                                                                     \
            {                                                                     \
                construct##argNames##ConstructorTables();                         \
                if (!argNames##ConstructorTablePtr_->insert(lookup, New))         \
                {                                                                 \
                    std::cerr<< "Duplicate entry " << lookup                      \
                        << " in runtime selection table " << #baseType            \
                        << std::endl;                                             \
                    error::safePrintStack(std::cerr);                             \
                }                                                                 \
            }                                                                     \
    1. Uses this line:
      Code:
      construct##argNames##ConstructorTables();
      to call the function defined on the previous point.
    2. Then uses this code:
      Code:
      argNames##ConstructorTablePtr_->insert(lookup, New)
      to add itself to the list. Therefore, this is where the global table is populated.
As to when: it's done while the libraries are still being loaded up and initialized into the application's memory regions, even before the "main" method is called for the application itself. This means that it's considerably hard to use any debugger to pinpoint this moment of construction and population, since it occurs before the application starts processing the "main" function. I say this because AFAIK, few debuggers support this feature... worse even if libraries are involved.

Best regards,
Bruno
snak, marupio, akidess and 12 others like this.
__________________
wyldckat is offline   Reply With Quote

Old   September 14, 2014, 07:45
Default
  #3
Senior Member
 
Tomislav Maric
Join Date: Mar 2009
Location: Darmstadt, Germany
Posts: 284
Blog Entries: 5
Rep Power: 21
tomislav_maric is on a distinguished road
Hi Santiago,

have you seen my blog post on the RTS implementation? If you go through the steps described there very carefully, you'll see that the table initialization is encapsulated in the nested class template generated by the macro.

" That is it, the initialization of the addAlgorithmBaseWordConstructorToAlgorithmBaseTabl e_ object takes care of adding the “constructor” of the class to the run-time type selection table."

This constructor calls the class static function that creates the table, in case it has not yet been created:

Code:
addWordConstructorToTable ( const word& lookup = AlgorithmBaseType::typeName )
{
    constructWordConstructorTables();
So, to see when the tables are created, you have to see when the object of the type addWordConstructorToTable is initialized. It is a global object...

The constructor of this object calls the static function responsible for creating the table. The function is static, because the table is a class-static object: there are as many tables as there are RTS enabled classes in OpenFOAM. If the RTS tables would be initialized as objects, the order of their initialization would be undefined, because that's just how C++ works. Therfore, the creation is packed in the static class function. Having them initialized in an undefined order might result in the attempt of the derived RTS class to add its constructor to the yet uninitialized RTS table of the base class. Boom.


I had a lot of difficulties in writing that post, because the system is a bit complex: it uses nested class templates, static as well as global objects, etc...

I hope this helps somewhat..
__________________
When asking a question, prepare a SSCCE.
tomislav_maric is offline   Reply With Quote

Old   September 18, 2014, 14:52
Default
  #4
Senior Member
 
santiagomarquezd's Avatar
 
Santiago Marquez Damian
Join Date: Aug 2009
Location: Santa Fe, Santa Fe, Argentina
Posts: 452
Rep Power: 23
santiagomarquezd will become famous soon enough
Thanks Bruno for the complete explanation! Respect to the Tomislav question, yes I've read your material but still couldn't find the moment when tables are created. I've prepared some slides explaining the RTS with the macros and the calling sequence for a real case (extracted by debugging). The topic of when the tables are created is not explained but the mechanisms are shown. I've took some concepts and paragraphs of you and the credits are added,

https://drive.google.com/file/d/0B2l...it?usp=sharing

I hope more people got involved and add their knowledge here!.

Regards.
tomislav_maric and fumiya like this.
__________________
Santiago MÁRQUEZ DAMIÁN, Ph.D.
Research Scientist
Research Center for Computational Methods (CIMEC) - CONICET/UNL
Tel: 54-342-4511594 Int. 7032
Colectora Ruta Nac. 168 / Paraje El Pozo
(3000) Santa Fe - Argentina.
http://www.cimec.org.ar
santiagomarquezd is offline   Reply With Quote

Old   September 28, 2014, 14:54
Default
  #5
Retired Super Moderator
 
Bruno Santos
Join Date: Mar 2009
Location: Lisbon, Portugal
Posts: 10,975
Blog Entries: 45
Rep Power: 128
wyldckat is a name known to allwyldckat is a name known to allwyldckat is a name known to allwyldckat is a name known to allwyldckat is a name known to allwyldckat is a name known to all
Greetings to all!

@Santiago: Many thanks for compiling+sharing that document!
I gave a quick read to it and didn't find anything wrong with it, but I didn't double-check every single detail either .


Honestly, there is already tons of knowledge here on the forums, on the conferences/workshops, open+public+online courses and so on.
The problem is that there are very few people that are compiling this information into more convenient/consolidated formats/locations. The best couple of places where I know this can be consolidated is at http://openfoamwiki.net and at the Cocoons Project, but people are usually more keen on using the formats/locations they're more familiar with ... hence information gets dispersed and only those who know what to look for will find the answers... oh well.

Best regards,
Bruno
__________________
wyldckat is offline   Reply With Quote

Old   October 10, 2014, 06:23
Default
  #6
Senior Member
 
Tomislav Maric
Join Date: Mar 2009
Location: Darmstadt, Germany
Posts: 284
Blog Entries: 5
Rep Power: 21
tomislav_maric is on a distinguished road
Quote:
Originally Posted by santiagomarquezd View Post
Thanks Bruno for the complete explanation! Respect to the Tomislav question, yes I've read your material but still couldn't find the moment when tables are created. I've prepared some slides explaining the RTS with the macros and the calling sequence for a real case (extracted by debugging). The topic of when the tables are created is not explained but the mechanisms are shown. I've took some concepts and paragraphs of you and the credits are added,

https://drive.google.com/file/d/0B2l...it?usp=sharing

I hope more people got involved and add their knowledge here!.

Regards.

Cool that you assembled the document! I find that describing things like this often leads to new ways of understanding the problem. Also, thanks for adding credits, it would be great if you would add a link to that blog post in the future because cross-linking is what keeps the documentation alive and searchable...

As for the RTS table initialization, if you still don't understand when this happens, I would propose you try something out.

  1. Get the application that selects objects either from the blog post, or from bitbucket.org/sourceflux.
  2. Expand all the macros in that file using the macro expansion script.
  3. Insert Info statements in interesting places: inside RTS table constructor functions, nested class template constructors, etc.
  4. Save this file as the application C file.
  5. Compile the application and run it.
Now you will be able to read the execution semantics of the RTS system in the console standard output. This should help you see that the object initialization (last line in the expanded macro) calls the creation static class function, which generates the class static HashTable object.

Hope this helps...

Tomislav
arvindpj likes this.
__________________
When asking a question, prepare a SSCCE.
tomislav_maric is offline   Reply With Quote

Old   October 10, 2014, 06:36
Default
  #7
Senior Member
 
Tomislav Maric
Join Date: Mar 2009
Location: Darmstadt, Germany
Posts: 284
Blog Entries: 5
Rep Power: 21
tomislav_maric is on a distinguished road
Quote:
Originally Posted by wyldckat View Post
Greetings to all!

Honestly, there is already tons of knowledge here on the forums, on the conferences/workshops, open+public+online courses and so on.
The problem is that there are very few people that are compiling this information into more convenient/consolidated formats/locations. The best couple of places where I know this can be consolidated is at http://openfoamwiki.net and at the Cocoons Project, but people are usually more keen on using the formats/locations they're more familiar with ... hence information gets dispersed and only those who know what to look for will find the answers... oh well.

Best regards,
Bruno
I believe that any kind of documentation that is searchable on the internet is a benefit to the community. I personally really love the blog form of documenting things for many reasons. Others may like PDF presentations like santiagomarquezd, some will edit the Wiki, some will contribute their part to the CocoonsProject. In the end, it's about taking spare time to provide help to others. Of course people will do that in the form that they like most - it's their time...

Yes I perfectly agree that the information is dispersed, but better to have people actually contributing to the community and having fun while doing so, then forcing people to just use wiki, or just use CocoonsProject, or just post on the forum, or to use any other "established" system(s)...

Variety is a sign evolution....
wyldckat likes this.
__________________
When asking a question, prepare a SSCCE.
tomislav_maric is offline   Reply With Quote

Old   December 21, 2015, 03:08
Default
  #8
Member
 
Pekka Pasanen
Join Date: Feb 2012
Location: Finland
Posts: 87
Rep Power: 14
zordiack is on a distinguished road
I feel like this is totally related to my problem with duplicate entry errors. Can someone help me how to implement own turbulence model properly in -dev or 3.0.x? I'm guessing the use of makeRASModel(kOmegaSSTSASnew), which uses makeTurbulenceModel.H, also creates entries for base classes laminar, RASModel and LESModel in the custom library. Solvers then complain about those duplicate entries.

More details here:

http://www.cfd-online.com/Forums/ope...tml#post578060
zordiack is offline   Reply With Quote

Old   December 21, 2015, 03:46
Default
  #9
Senior Member
 
Alexey Matveichev
Join Date: Aug 2011
Location: Nancy, France
Posts: 1,930
Rep Power: 38
alexeym has a spectacular aura aboutalexeym has a spectacular aura about
Send a message via Skype™ to alexeym
@zordiack,

Do you have test case? On my laptop all I have got is sigSEGV if I try to use the library.
alexeym is offline   Reply With Quote

Old   December 21, 2015, 05:12
Default
  #10
Member
 
Pekka Pasanen
Join Date: Feb 2012
Location: Finland
Posts: 87
Rep Power: 14
zordiack is on a distinguished road
Here is one, modified pitzDaily.

Compile the new libs (kOmegaSSTSASnew) from the github link, run blockMesh, run simpleFoam.
Attached Files
File Type: gz pitzdaily_sstsas.tar.gz (3.6 KB, 43 views)
zordiack is offline   Reply With Quote

Old   December 21, 2015, 10:28
Default
  #11
Senior Member
 
Alexey Matveichev
Join Date: Aug 2011
Location: Nancy, France
Posts: 1,930
Rep Power: 38
alexeym has a spectacular aura aboutalexeym has a spectacular aura about
Send a message via Skype™ to alexeym
Hi,

When you do

Code:
makeBaseTurbulenceModel
(
    geometricOneField,
    volScalarField,
    compressibleTurbulenceModel,
    CompressibleTurbulenceModel,
    ThermalDiffusivity,
    fluidThermo
);
in myturbulentFluidThermoModels.C (the same thing with incompressible models) you readd lots of things runtime selection table (laminar, RAS, and LES in particular, yet you can check makeBaseTurbulenceModel macro for more details).

See pull request on Github with "corrected" variant. I am not sure that it is canonical way of defining new models, yet it compiles and solver does not complain about duplicate entries.
zordiack and lifeinhand like this.
alexeym is offline   Reply With Quote

Old   December 21, 2015, 11:31
Default
  #12
Member
 
Pekka Pasanen
Join Date: Feb 2012
Location: Finland
Posts: 87
Rep Power: 14
zordiack is on a distinguished road
Quote:
Originally Posted by alexeym View Post
Hi,

When you do

Code:
makeBaseTurbulenceModel
(
    geometricOneField,
    volScalarField,
    compressibleTurbulenceModel,
    CompressibleTurbulenceModel,
    ThermalDiffusivity,
    fluidThermo
);
in myturbulentFluidThermoModels.C (the same thing with incompressible models) you readd lots of things runtime selection table (laminar, RAS, and LES in particular, yet you can check makeBaseTurbulenceModel macro for more details).

See pull request on Github with "corrected" variant. I am not sure that it is canonical way of defining new models, yet it compiles and solver does not complain about duplicate entries.
This is great, the code is much cleaner now and it works Thank you so much for your help. The current implementation is something I tried to do, but I just went kind of one level too high.
zordiack is offline   Reply With Quote

Reply


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
Wind turbine simulation Saturn CFX 58 July 3, 2020 01:13
An error has occurred in cfx5solve: volo87 CFX 5 June 14, 2013 17:44
Low-Re turbulence models grtabor OpenFOAM Running, Solving & CFD 4 February 15, 2010 10:25
RPM in Wind Turbine Pankaj CFX 9 November 23, 2009 04:05
air bubble is disappear increasing time using vof xujjun CFX 9 June 9, 2009 07:59


All times are GMT -4. The time now is 15:26.