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

Call of probes::write() function

Register Blogs Community New Posts Updated Threads Search

Like Tree3Likes
  • 3 Post By floquation

Reply
 
LinkBack Thread Tools Search this Thread Display Modes
Old   December 16, 2016, 10:24
Default Call of probes::write() function
  #1
Super Moderator
 
Tobi's Avatar
 
Tobias Holzmann
Join Date: Oct 2010
Location: Bad Wörishofen
Posts: 2,711
Blog Entries: 6
Rep Power: 52
Tobi has a spectacular aura aboutTobi has a spectacular aura aboutTobi has a spectacular aura about
Send a message via ICQ to Tobi Send a message via Skype™ to Tobi
Dear all,

I am struggling to figure out how the probes::write() function is called or which class is executing this method. Up to know, it should be clear but I get unexpected results; there should be a class that I miss. However, in order to provide all information I start from the beginning. The function objects are called within the Time::run() function:
Code:
bool Foam::Time::run() const
{
    bool running = value() < (endTime_ - 0.5*deltaT_);

    if (!subCycling_)
    {
        // only execute when the condition is no longer true
        // ie, when exiting the control loop
        if (!running && timeIndex_ != startTimeIndex_)
        {
            // Note, end() also calls an indirect start() as required
            Info<< "run() Call end" << endl;
            functionObjects_.end();
        }
    }

    if (running)
    {
        if (!subCycling_)
        {
            const_cast<Time&>(*this).readModifiedObjects();

            if (timeIndex_ == startTimeIndex_)
            {
                Info<< "run() Call start" << endl;
                functionObjects_.start();
            }
            else
            {
                Info<< "run() Call execute" << endl;
                functionObjects_.execute();
            }
        }

        // Update the "running" status following the
        // possible side-effects from functionObjects
        running = value() < (endTime_ - 0.5*deltaT_);
    }

    return running;
}
As we can see, the execute() function from the functionObjects_ object is called. This object is related to the class named functionObjectsList. In this class we will find the following code for the functionObjectsList::execution() function (I added some additional comments):

Code:
bool Foam::functionObjectList::execute()
{
    Info<< "In functionObjectList::execute()" << endl;
    bool ok = true;

    if (execution_)
    {
        if (!updated_)
        {
            read();
        }

        forAll(*this, objectI)
        {
            Info<< "Call operator[](objectI).execute()" << endl;
            ok = operator[](objectI).execute() && ok;
            Info<< "Call operator[](objectI).write()" << endl;
            ok = operator[](objectI).write() && ok;
        }
    }

    return ok;
}
In the forAll loop, we loop over each object that is in the list and call the execute() and write() function respectively. the operator[](objectI) gives us the object that is derived from the class functionObjects. Here we have both functions defined as pure virtual and therefore not declared. Based on the fact that I am using the probes function, the write and execute function have to be declared there. We will find the following in the probes class:

Code:
bool Foam::probes::execute()
{
    Info<< "    probes::execute()" << endl;
    return true;
}


bool Foam::probes::write()
{
    Info<< "    probes::write()" << endl;
    if (size() && prepare())
    {
        sampleAndWrite(scalarFields_);
        sampleAndWrite(vectorFields_);
        sampleAndWrite(sphericalTensorFields_);
        sampleAndWrite(symmTensorFields_);
        sampleAndWrite(tensorFields_);

        sampleAndWriteSurfaceFields(surfaceScalarFields_);
        sampleAndWriteSurfaceFields(surfaceVectorFields_);
        sampleAndWriteSurfaceFields(surfaceSphericalTensorFields_);
        sampleAndWriteSurfaceFields(surfaceSymmTensorFields_);
        sampleAndWriteSurfaceFields(surfaceTensorFields_);
    }

    return true;
}
So far so good. Now if I run a simulation, we call the Time::run() function which will call the related functionObject_.execute() function. This function will call the above given stuff and finally I should get the output of "probes::execute()" and "probes::write()". However, running a simulation will output the first one (execute()) but not the write(). The latter is only called if the write interval of the function is correct.

To sum up:
For each time step I know that the functions:
  • operator[](objectI).execute()
  • operator[](objectI).write()
are called which should give the following output on my screen:

probes::execute()
probes::write()


However, the output looks different:
Code:
run() Call start
Time = 0.005

DICPCG:  Solving for T, Initial residual = 1, Final residual = 1.65094e-07, No Iterations 5
DICPCG:  Solving for T, Initial residual = 0.00446913, Final residual = 2.58563e-07, No Iterations 3
DICPCG:  Solving for T, Initial residual = 0.000148071, Final residual = 1.6611e-07, No Iterations 2
ExecutionTime = 0.13 s  ClockTime = 0 s

run() Call execute
In functionObjectList::execute() = execution_ 1
Call operator[](objectI).execute()
    probes::execute()
Call operator[](objectI).write()
Time = 0.01

DICPCG:  Solving for T, Initial residual = 0.203755, Final residual = 3.79207e-07, No Iterations 4
DICPCG:  Solving for T, Initial residual = 0.00184335, Final residual = 1.15759e-07, No Iterations 3
DICPCG:  Solving for T, Initial residual = 5.70567e-05, Final residual = 6.6188e-08, No Iterations 2
ExecutionTime = 0.14 s  ClockTime = 0 s

run() Call execute
In functionObjectList::execute() = execution_ 1
Call operator[](objectI).execute()
    probes::execute()
Call operator[](objectI).write()
    probes::write()
Time = 0.015

DICPCG:  Solving for T, Initial residual = 0.109922, Final residual = 2.11614e-07, No Iterations 4
DICPCG:  Solving for T, Initial residual = 0.00104612, Final residual = 6.94234e-08, No Iterations 3
DICPCG:  Solving for T, Initial residual = 3.13333e-05, Final residual = 7.19276e-07, No Iterations 1
ExecutionTime = 0.14 s  ClockTime = 0 s

run() Call execute
In functionObjectList::execute() = execution_ 1
Call operator[](objectI).execute()
    probes::execute()
Call operator[](objectI).write()
Time = 0.02

DICPCG:  Solving for T, Initial residual = 0.0728088, Final residual = 1.56726e-07, No Iterations 4
DICPCG:  Solving for T, Initial residual = 0.000680395, Final residual = 6.78655e-07, No Iterations 2
DICPCG:  Solving for T, Initial residual = 2.06035e-05, Final residual = 4.78523e-07, No Iterations 1
ExecutionTime = 0.14 s  ClockTime = 0 s

run() Call end
    probes::execute()
    probes::write()
End
After the first calculation, we call the execute() function which outputs probes::execute() but we are not entering the probes::write() function and I do not know why. Maybe and it should be the reason, I miss one class in between which is called from the functionObject,checks if the appropriate time is reached to save the data and then call the probes::write() function. However, I got stuck here and any help is appreciated.
__________________
Keep foaming,
Tobias Holzmann
Tobi is offline   Reply With Quote

Old   December 16, 2016, 12:32
Default
  #2
Senior Member
 
floquation's Avatar
 
Kevin van As
Join Date: Sep 2014
Location: TU Delft, The Netherlands
Posts: 252
Rep Power: 21
floquation will become famous soon enough
I think I found it... The reasoning was quite simple in hindsight:
If there is a "class in between", then that class must necessarily extend "probes", as probes in the direct child of functionObject (probes extends functionObject). Then probes' child's write() would be called before probes' write().
However, this is not the case and it would be nonsensical design, as every FO would then require a child to do the time-stuff. Those are a lot of different children - all educated to do exactly the same thing!
Therefore, the FO you call "execute()" and "write()" on simply cannot be your probes FO. (Or there is some magic C++ going on that I do not understand as a Java programmer.) This brought me to the runtime selection mechanism: where are the FOs constructed? This made me find your problem. Let me explain:

---

Let's do it in the chronological calling order.

First, a functionObjectList is constructed and its read() method is called.
This method reads the "functions" entity of controlDict. Then it constructs the appropriate functionObject using OF's runtime selection mechanism and it adds it to the list (= to itself).
Inside the read() method you may find:

Code:
(...)
                try
                {
                    if
                    (
                        dict.found("writeControl")
                     || dict.found("outputControl")
                    )
                    {
                        foPtr.set
                        (
                            new functionObjects::timeControl(key, time_, dict)
                        );
                    }
                    else
                    {
                        foPtr = functionObject::New(key, time_, dict);
                    }
                }
(...)
                if (foPtr.valid())
                {
                    objPtr = foPtr.ptr();
                }
                else
                {
                    ok = false;
                }
(...)
            // Insert active functionObjects into the list
            if (objPtr)
            {
                newPtrs.set(nFunc, objPtr);
                newIndices.insert(key, nFunc);
                nFunc++;
            }
The important thing here is that it does not construct a functionObject of type "probes". Rather, it constructs a functionObject of type "timeControl": ".../functionObjects/timeControl/timeControlFunctionObject.H"

Its constructor looks as follows:
Code:
Foam::functionObjects::timeControl::timeControl
(
    const word& name,
    const Time& t,
    const dictionary& dict
)
:
    functionObject(name),
    time_(t),
    dict_(dict),
    timeStart_(-VGREAT),
    timeEnd_(VGREAT),
    nStepsToStartTimeChange_
    (
        dict.lookupOrDefault("nStepsToStartTimeChange", 3)
    ),
    executeControl_(t, dict, "execute"),
    writeControl_(t, dict, "write"),
    foPtr_(functionObject::New(name, t, dict_))
{
    readControls();
}
It has a pointer (foPtr_) to a function object, which is constructed based on "name" and "dict", the first of which was called "key" in functionObjectList::read(). This will be your "probes" FO.
This makes sense based on the comment in timeControlFunctionObject.H:
Code:
        //- The functionObject to execute
        autoPtr<functionObject> foPtr_;
Then, what you are calling (in functionObjectList::execute()) is its (timeControlFO) write method, which does the following:

Code:
bool Foam::functionObjects::timeControl::write()
{
    if (active() && (postProcess || writeControl_.execute()))
    {
        foPtr_->write();
    }

    return true;
}
This, in turn, calls probes' write method, but only if it feels like it.

So you are right. "probes.write()" will only be called if it is the time to execute.

I can speculate why OF implemented it in this "pointer sequence" way, rather than with inheritance (randomFO -> timeControlFO -> FO)...
With inheritance, it would be the user's responsibility to call the parent write() method before writing, which is "unsafe".
Or, timeControlFO must ensure that "write" cannot be overwritten by randomFO and it should define a new interface for a new method that should do the writing. However, I don't think it is possible to prevent a child from overwriting, is it? Either way, even if it is, this would make the name of the "write()" function inconsistent between FOs that are not timeControlFOs.


That was a tough nut to crack (2 hours later...), but quite educative.
wyldckat, Tobi and mostanad like this.
floquation is offline   Reply With Quote

Old   December 16, 2016, 13:07
Default
  #3
Super Moderator
 
Tobi's Avatar
 
Tobias Holzmann
Join Date: Oct 2010
Location: Bad Wörishofen
Posts: 2,711
Blog Entries: 6
Rep Power: 52
Tobi has a spectacular aura aboutTobi has a spectacular aura aboutTobi has a spectacular aura about
Send a message via ICQ to Tobi Send a message via Skype™ to Tobi
Dear Kevin,

thank you very much for your clear explanation. I was so focused on my interpretation that I forgot that there could be other ways like, taking a pointer of the object to the time selector class which will then call the function.

Great explanation and thanks for your time. I highly appreciate that.
Tobi
__________________
Keep foaming,
Tobias Holzmann
Tobi is offline   Reply With Quote

Old   November 29, 2021, 03:43
Default
  #4
Senior Member
 
mohammad
Join Date: Sep 2015
Posts: 281
Rep Power: 12
mostanad is on a distinguished road
Quote:
Originally Posted by Tobi View Post
Dear Kevin,

thank you very much for your clear explanation. I was so focused on my interpretation that I forgot that there could be other ways like, taking a pointer of the object to the time selector class which will then call the function.

Great explanation and thanks for your time. I highly appreciate that.
Tobi

Hi Tobi,
Thank you for posting this fantastic question. You have looked at probes function object and more specifically, the write and execute functions in both functionObjectList.C and probes.C. However, I need to look at another function object, forces, for force calculation over a patch.



This functionObject includes a function called forceEff(). This only returns the calculated forces vector over the patch. However, I don't how I can call it inside my solver. I have tested this:


Code:
word patchName = "forcesP"; \\patchName
label id = runTime.functionObjects().findObjectID(patchName); \\finding object id

runTime.functionObjects()[id].execute(); 

runTime.functionObjects()[id].write();

To check my access to execute and write functions within the solver, and it works. But actually, using this command:


Code:
runTime.functionObjects()[id].forceEff();

leads to the following error:


Code:
nonNewtonianIcoFoamModified.C: In function ‘int main(int, char**)’:
nonNewtonianIcoFoamModified.C:124:39: error: ‘class Foam::functionObject’ has no member named ‘forceEff’
         runTime.functionObjects()[id].forceEff();

I know functionObjects() in the above comes from functionObjectList class, but don't know why it leads to an error for an existing function. So your help is this regard is much appreciated.


Cheers,
Mohammad
mostanad is offline   Reply With Quote

Old   December 3, 2021, 07:42
Default
  #5
Senior Member
 
Mark Olesen
Join Date: Mar 2009
Location: https://olesenm.github.io/
Posts: 1,715
Rep Power: 40
olesen has a spectacular aura aboutolesen has a spectacular aura about
Quote:
Originally Posted by mostanad View Post
...
To check my access to execute and write functions within the solver, and it works. But actually, using this command:

Code:
runTime.functionObjects()[id].forceEff();
leads to the following error:
...
What you are attempting cannot work. Sure forces derives from a functionObject, which means that its virtual methods execute() and write() work as expected. However, functionObject itself obvious does not have a forceEff() method (virtual or otherwise). You will need to cast it. For example,
Code:
dynamicCast<const functionObjects::forces>(runTime.functionObjects()[id])
    .forceEff();
For good programming style, you should actually verify the type as well. Starting with OpenFOAM-v1812 you can conveniently combine them with the isA function. For example,
Code:
{
    const auto* forcesFo = isA<functionObjects::forces>(runTime.functionObjects()[id]);
    if (forcesFo)
    {
        forcesFo->forceEff();
    }
}
olesen is offline   Reply With Quote

Old   December 4, 2021, 20:36
Default
  #6
Senior Member
 
mohammad
Join Date: Sep 2015
Posts: 281
Rep Power: 12
mostanad is on a distinguished road
Quote:
Originally Posted by olesen View Post
What you are attempting cannot work. Sure forces derives from a functionObject, which means that its virtual methods execute() and write() work as expected. However, functionObject itself obvious does not have a forceEff() method (virtual or otherwise). You will need to cast it. For example,
Code:
dynamicCast<const functionObjects::forces>(runTime.functionObjects()[id])
    .forceEff();
For good programming style, you should actually verify the type as well. Starting with OpenFOAM-v1812 you can conveniently combine them with the isA function. For example,
Code:
{
    const auto* forcesFo = isA<functionObjects::forces>(runTime.functionObjects()[id]);
    if (forcesFo)
    {
        forcesFo->forceEff();
    }
}

Thanks Mark foryour nice comment.
So you think this form of cast function can be added to a point of solver for calling the forces value? Am I right?
mostanad is offline   Reply With Quote

Reply

Tags
coding


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
foamToTecplot360 thomasduerr OpenFOAM Post-Processing 121 June 11, 2021 11:05
[blockMesh] Errors during blockMesh meshing Madeleine P. Vincent OpenFOAM Meshing & Mesh Conversion 51 May 30, 2016 11:51
ParaView for OF-1.6-ext Chrisi1984 OpenFOAM Installation 0 December 31, 2010 07:42
latest OpenFOAM-1.6.x from git failed to compile phsieh2005 OpenFOAM Bugs 25 February 9, 2010 05:37
Version 15 on Mac OS X gschaider OpenFOAM Installation 113 December 2, 2009 11:23


All times are GMT -4. The time now is 14:42.