CFD Online Discussion Forums

CFD Online Discussion Forums (https://www.cfd-online.com/Forums/)
-   OpenFOAM Programming & Development (https://www.cfd-online.com/Forums/openfoam-programming-development/)
-   -   tmp - stands for 'true macro pain'? (https://www.cfd-online.com/Forums/openfoam-programming-development/71912-tmp-stands-true-macro-pain.html)

marupio January 19, 2010 13:25

tmp - stands for 'true macro pain'?
 
I've never seen such a chopped-up macro-heavy series of classes before. Just trying to figure out DimensionedScalarField is a nightmare for me (and very humbling).

What is tmp?

It looks like a wrapper class for a field, which redirects its pointer on destruction, provided refCount says it's okay. Is this some sort of sneaky way of ensuring local objects survive function exits to prevent copying on return?

Does OpenFOAM get much more complicated than this? I'm not the most experienced programmer out there...

-Dave

hjasak January 20, 2010 05:40

Yes - it does get much more complicated than this. :) But, you get used to it.

tmp is essential and extremely useful. This is a mechanism that avoids copying large chunks of data in memory when you do operations on them. Consider for example

scalarField a(1000000, 1.0);
scalarField b(1000000, 2.0);

scalarField c = a + b;

This calls operator+(const scalarField& a, const scalarField& b), and that needs a return type. If the return type is a scalarField, you will first create and then copy a field of length 1000000 floating point numbers - which hurts and takes time.

Therefore, you make a return type a tmp<scalarField>, where tmp holds a pointer to the actual array. Its (tmp's) constructor, copy constructor and destructor get called, but they do not involve allocation and copying of the real array of data - it just counts the number of references and manages storage.

Clear? Hope this helps...

Now to the real question: this is a piece of optimisation machinery in FOAM. Why do you need to know more (unless you are writing field and matrix operators)? It should all be transparent and automatic.

Good luck,

Hrv

marupio January 20, 2010 09:47

I figured I was going too deep into the primitives. It's just sometimes I was seeing tmp<> and sometimes I wasn't. For instance, in icoFoam we see:

fvVectorMatrix UEqn

whereas in simpleFoam (UEqn.H) we see:

tmp<fvVectorMatrix> UEqn

... and two days later I have a macro headache. Am I right in thinking the tmp<> is unnecessary in simpleFoam? Otherwise, if it's supposed to work in the background, why should it appear at the top level (in a solver)?

At the moment, I don't intend to write any matrix functions for this project, just a solver with a complex biochemistry model (called ADM1).

begin aside
But the whole reason I'm using OpenFOAM for this project is because my long term goal is to write a new kind of fluid solver that models a fluid as a sort of half-continuum, half-particle cloud. So every fluid quantity has a fully-specified statistical distribution at every point. I imagine that will involve adding new primitive classes and operators (e.g. DimensionedScalarDistributionField).
end aside

-Dave

edit:

BTW You cite operator+ for scalarField: operator+(const scalarField& a, const scalarField& b)... this was the exact function I was looking for, but never found it. I used gcc's preprocessor to assemble the macros, and for DimensionedScalarField, the operator+'s it found are listed below. Note, it always involves a Field and a single scalar. I'm sure it exists... but I couldn't find it. This just goes to show: the deeper you go, the foggier it gets.

template<class GeoMesh>
tmp<DimensionedField<scalar, GeoMesh> > operator +
(
const dimensioned<scalar>& dt1,
const DimensionedField<scalar, GeoMesh>& df2
);

template<class GeoMesh>
tmp<DimensionedField<scalar, GeoMesh> > operator +
(
const scalar& t1,
const DimensionedField<scalar, GeoMesh>& df2
);

template<class GeoMesh>
tmp<DimensionedField<scalar, GeoMesh> > operator +
(
const dimensioned<scalar>& dt1,
const tmp<DimensionedField<scalar, GeoMesh> >& tdf2
);

template<class GeoMesh>
tmp<DimensionedField<scalar, GeoMesh> > operator +
(
const scalar& t1,
const tmp<DimensionedField<scalar, GeoMesh> >& tdf2
);

template<class GeoMesh>
tmp<DimensionedField<scalar, GeoMesh> > operator +
(
const DimensionedField<scalar, GeoMesh>& df1,
const dimensioned<scalar>& dt2
);

template<class GeoMesh>
tmp<DimensionedField<scalar, GeoMesh> > operator +
(
const DimensionedField<scalar, GeoMesh>& df1,
const scalar& t2
);

template<class GeoMesh>
tmp<DimensionedField<scalar, GeoMesh> > operator +
(
const tmp<DimensionedField<scalar, GeoMesh> >& tdf1,
const dimensioned<scalar>& dt2
);

template<class GeoMesh>
tmp<DimensionedField<scalar, GeoMesh> > operator +
(
const tmp<DimensionedField<scalar, GeoMesh> >& tdf1,
const scalar& t2
);

hjasak January 20, 2010 09:56

Well, put on your 1970-s hat (or a shirt with huge collars!) and let's talk about memory peaks. For simpleFoam, the memory peak is at the point of creation of the pressure equation, because, unlike PISO, I am allowed to delete the momentum matrix before the creation of the pressure matrix. Therefore, I make the momentum matrix a tmp and call UEqn.clear(); at an opportune place in the code and drop the peak memory requirement by one complete assymetric matrix!

Guess what clear() does: it will delete the pointer to the actual momentum matrix (before reaching the destructor) and free up a lot of memory to be re-used for the pressure matrix.

This is how FOAM beats eg Fluent and STAR in memory usage per cell, but in the 21st century (and especially when you are writing new discretisation capability) you don't care that much. In PISO-based solvers you don't have this option, since you will call UEqn.H() later on in the algorithm, and this requires access to momentum matrix coefficients. Therefore, there's no need for the tmp<matrix> gymnastics...

Clear?

Hrv

marupio January 21, 2010 16:35

Got it. Thanks! I'm sure I'll be back...

cgoniva September 22, 2010 03:52

Dear All!

I'm trying to write a code which is similar to the
inline tmp <volVectorField> spray::momentumSource()

Inside that function a tmp <volVectorField> is built from a new volVectorField

So I am wondering what is the scope of this tmp object and whether I need to do some clean up using delete at some point? I could not find such a thing inside the spray class.

Thx for your advise,
Cheers Chris

gschaider September 22, 2010 04:31

Quote:

Originally Posted by cgoniva (Post 276095)
Dear All!

I'm trying to write a code which is similar to the
inline tmp <volVectorField> spray::momentumSource()

Inside that function a tmp <volVectorField> is built from a new volVectorField

So I am wondering what is the scope of this tmp object and whether I need to do some clean up using delete at some point? I could not find such a thing inside the spray class.

Thx for your advise,
Cheers Chris

The whole point of tmp is that you don't have to care about deleting too much. Have you read: http://openfoamwiki.net/index.php/OpenFOAM_guide/tmp
I think it is explained quite nicely there

Bernhard

cgoniva September 22, 2010 05:07

Thx for your quick reply Bernhard!

I'll have a look at that link!

Cheers
Chris

AlmostSurelyRob June 4, 2015 08:26

Sorry for necro-bumping this thread, but I would still like to ask for the clarification on tmp class. I understood the idea of returning a sort of smart pointer which will handle the memory allocation properly, but how is this related to mechanism of copy-elision* that already exists in C++?

I occasionally come across it when dealing with multiphase models as various interaction expressions return tmp.

* Copy-elision wikipedia:
http://en.wikipedia.org/wiki/Copy_elision
https://en.wikipedia.org/wiki/Return_value_optimization

Kojirion June 7, 2015 23:01

Quote:

Originally Posted by hjasak (Post 243143)
scalarField a(1000000, 1.0);
scalarField b(1000000, 2.0);

scalarField c = a + b;

This calls operator+(const scalarField& a, const scalarField& b), and that needs a return type. If the return type is a scalarField, you will first create and then copy a field of length 1000000 floating point numbers - which hurts and takes time.

I would expect that copy to be elided. RVO has been supported for a long time - apparently Scott Meyers recommended return by value in More Effective C++ back in 1996.
So while I do not dispute that tmp may have very good uses throughout OF code, this example - and the simllar reasoning answering 'Why is it needed?' in the guide is unconvincing.

Regarding early deletion, if the object is local it would be sufficient to create an arbitrary scope with a pair of braces; that makes it perfectly possible to destruct some locals in the middle of a function.

More interesting would be the problem of a+b+c and avoiding the temporary result of a+b, which expression templates would help with and tmp does not, as far as I can tell.

MadScientist October 2, 2017 08:50

Move Constructor?
 
Quote:

I would expect that copy to be elided.
I agree, but going further than that, today couldn't this problem be addressed by a simple move constructor? It doesn't even have to rely on a "smart" compiler.

In my opinion, the problem with the tmp fields is not even the gymnastics. The big pain is that, when something breaks, the error message is very cryptic!

shahidqa32 April 11, 2019 06:04

Help need in OpenFoam by BlueCFD terminal to study Gray Scott Model
 
I am posting my error. Your help will be highly appreciated.

perputation.H: In function 'int main(int, char**)':
perputation.H:3:42: error: binding reference of type 'Foam::scalarField& {aka Foam::Field<double>&}' to 'const Foam::Field<double>' discards qualifiers
scalarField& AField = A.internalField();

when I use command wmake
it shows error as mentioned above.

Thank you in advance to help.

raunakbardia May 24, 2019 16:41

Still facing issues with the use of tmp<>
 
3 Attachment(s)
Here is my problem:

1. I am trying to modify an existing class and am using the return types in the other functions that the class was originally designed with. My function returns a tmp<> type variable and looks something like this:

Code:

Foam::tmp<Foam::volScalarField>
Foam::temperaturePhaseChangeTwoPhaseMixtures::constant::mFlux() const
{
    const volScalarField massGenerationRate
    ( 
        IOobject
        ( 
            "mDot",
            mesh_.time().timeName(),
            mesh_
        ), 
        mesh_,
        mDotA_
    ); 
    return tmp<volScalarField>
    ( 
        massGenerationRate
    ); 
}

2. Now in the main program this function is called by the mixture object in the following way.

Code:

    tmp<volScalarField> mCheck = mixture->mFlux();
    const volScalarField& mDotAlpha = mCheck();    // Reference volScalarField
    Info << mDotAlpha[397] << '\n';      // This prints when I run the program
    const volScalarField mDotFinal(mDotAlpha); // ERROR
    Info << mDotAlpha[397] << '\n'; // This does not print out

3. The program compiles and then I run the solver from the run directory. At runtime, the code crashes at the line that is commented as an error in the above piece.

From what I understood tmp<> destroys the pointer to the object if it is not referenced immediately. But in the above example, I do reference it in the very next line and it works. only up to that point. The permanent creation of that object in my program fails. Here is the error:

Quote:

#0 Foam::error::printStack(Foam::Ostream&) in "/usr/local/openfoam/v1706/OpenFOAM-v1706/platforms/linux64GccDPInt32Opt/lib/libOpenFOAM.so"
#1 Foam::sigSegv::sigHandler(int) in "/usr/local/openfoam/v1706/OpenFOAM-v1706/platforms/linux64GccDPInt32Opt/lib/libOpenFOAM.so"
#2 ? in "/lib64/libc.so.6"
#3 std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(std::string const&) in "/usr/local/openfoam/v1706/ThirdParty-v1706/platforms/linux64/gcc-4.8.5/lib64/libstdc++.so.6"
#4 Foam::regIOobject::regIOobject(Foam::regIOobject const&) in "/usr/local/openfoam/v1706/OpenFOAM-v1706/platforms/linux64GccDPInt32Opt/lib/libOpenFOAM.so"
#5 Foam::DimensionedField<double, Foam::volMesh>::DimensionedField(Foam::Dimensioned Field<double, Foam::volMesh> const&)BFD: Dwarf Error: found dwarf version '4', this reader only handles version 2 information.
in "/home/rbardia/OpenFOAM/rbardia-v1706/platforms/linux64GccDPInt32Opt/bin/phaseChangeInterIsoFoam"
#6 Foam::GeometricField<double, Foam::fvPatchField, Foam::volMesh>::GeometricField(Foam::GeometricFiel d<double, Foam::fvPatchField, Foam::volMesh> const&)BFD: Dwarf Error: found dwarf version '4', this reader only handles version 2 information.
in "/home/rbardia/OpenFOAM/rbardia-v1706/platforms/linux64GccDPInt32Opt/bin/phaseChangeInterIsoFoam"
#7 ?BFD: Dwarf Error: found dwarf version '4', this reader only handles version 2 information.
in "/home/rbardia/OpenFOAM/rbardia-v1706/platforms/linux64GccDPInt32Opt/bin/phaseChangeInterIsoFoam"
#8 __libc_start_main in "/lib64/libc.so.6"
#9 Foam::UOPstream::write(float)BFD: Dwarf Error: found dwarf version '4', this reader only handles version 2 information.
in "/home/rbardia/OpenFOAM/rbardia-v1706/platforms/linux64GccDPInt32Opt/bin/phaseChangeInterIsoFoam"
Segmentation fault
What is the possible reason behind this? Similar, pre-defined functions that are part of the same class work fine. I have spent a lot of time but am not able to resolve this issue.

I have attached the header and implementation files of the function: constant.H & constant.C and the file that calls that function in the main folder: alphaEqn.H

Zeppo May 25, 2019 09:28

In your code an object massGenerationRate is created on stack and so is destroyed whenever the execution flow leaves function mFlux. Storing a reference to it in tmp doesn't help because returning a tmp you rerurn a reference to nothnig as the object was wiped out and doesn't exist anymore. All you have to do is to create massGenerationRate in virtual (dynamic) memory, store the pointer to it in smart pointer (tmp) object and you are pretty safe to return it from function. Please try this
Code:

Foam::tmp<Foam::volScalarField>
Foam::temperaturePhaseChangeTwoPhaseMixtures::constant::mFlux() const
{
    tmp<volScalarField> massGenerationRate
    ( 
        new volScalarField
        ( 
            IOobject
            ( 
                "mDot",
                mesh_.time().timeName(),
                mesh_
            ), 
            mesh_,
            mDotA_
        ); 
    );

    return massGenerationRate;
}


raunakbardia June 2, 2019 15:31

Thanks. I realized that. :)


All times are GMT -4. The time now is 07:54.