CFD Online Discussion Forums

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

marupio January 19, 2010 14: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 06: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 10: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 10: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 17: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.


All times are GMT -4. The time now is 17:25.