CFD Online Discussion Forums

CFD Online Discussion Forums (http://www.cfd-online.com/Forums/)
-   OpenFOAM Running, Solving & CFD (http://www.cfd-online.com/Forums/openfoam-solving/)
-   -   Easy lagrangian fluid particle tracking (http://www.cfd-online.com/Forums/openfoam-solving/96976-easy-lagrangian-fluid-particle-tracking.html)

michaelb February 6, 2012 12:49

Easy lagrangian fluid particle tracking
 
Hello Foamers,

I would like to know if there is a simple solver which could allow me to track a cloud of fluid particles ( size less : no drag & massless : no settling due to gravity) injected as a sphere of given position and diameter at t = 0 (only) with no initial velocity in an unsteady simulation.

Is there a way of doing such things with the icoLagrangianFoam solver in OpenFOAM-1.6-ext ? I couldn't figure out what would be the appropriate parameters in the kinematicCloudProperties dic file.

Actually, I need, for each saved timestep, the particles locations and their distances to wall patches.
If anybody could give me an hint on how to start here, I would be eternally thankful.

Best regards,

Michael

BernhardGrieser February 9, 2012 11:00

passiveParticle
 
Hi Michael,

I'm trying the same at the moment: Massless tracer particles. So far, I've only succeeded to load two particles in a cloud, and to write out their positions in an object file. Still fiddling around with the particle movement, though.
That's my code, so far:

Code:

vector position1(0.00005, 0.0001, 0.000995);
vector position2(0.00005, 0.0001, 0.001005);
label celli1 = mesh.findCell(position1);
label celli2 = mesh.findCell(position2);
Info<< "celli1 = mesh.findCell(position1) = " << celli1 << endl;
Info<< "celli2 = mesh.findCell(position2) = " << celli2 << endl;
const IDLList<passiveParticle>& particles();

passiveParticleCloud cloud(mesh, "cloud", particles);
cloud.clear();
passiveParticle* ptr1 = new passiveParticle(cloud, position1, celli1);
cloud.addParticle(ptr1);
passiveParticle* ptr2 = new passiveParticle(cloud, position2, celli2);
cloud.addParticle(ptr2);

cloud.writePositions();

Did you already solve the issue? Can somebody else help us?

Best Regards,
Bernhard

BernhardGrieser February 9, 2012 11:10

move particle tracer with the flow
 
one question would be, for example:

1.) How to address the cloud member function move()?

Compiling with
Code:

cloud.move();
returns the error message:
Code:

error: no matching function for call to 'Foam::passiveParticleCloud::move()'
2.) How to interpret the following warning message?

Code:

warning: the address of 'const Foam::IDLList<Foam::passiveParticle>& particles()' will always evaluate as 'true'
Cheers,
Bernhard

francesco_capuano February 9, 2012 12:55

One guess for question #1: move is a member function of the base cloud class Cloud<ParticleType>, which is templated on particle type, see:

http://foam.sourceforge.net/docs/cpp...ce.html#l00181

You might try to define cloud as an object of Cloud<passiveParticle> and then try to call the move function.

Hope it helps,
Francesco

BernhardGrieser February 10, 2012 04:03

template class TrackingData
 
Hi Francesco, thanks for the reply.

Quote:

Originally Posted by francesco_capuano (Post 343620)
You might try to define cloud as an object of Cloud<passiveParticle> and then try to call the move function.

My cloud was already defined as a passiveParticleCloud (see previous post). While instantiating, it calls the constructor of Cloud<passiveParticle>. But the member function move(TrackingData&) still cannot be called, probably because I don't know which argument it needs to be fed with. I probably address it the wrong way, since I'm not that familiar with template classes. Here's the move implementation:

Code:

template<class ParticleType>
template<class TrackingData>
void Foam::Cloud<ParticleType>::move(TrackingData& td)

{
    const globalMeshData& pData = polyMesh_.globalData();
    const labelList& processorPatches = pData.processorPatches();
    const labelList& processorPatchIndices = pData.processorPatchIndices();
    const labelList& processorPatchNeighbours =
        pData.processorPatchNeighbours();

    // Initialise the setpFraction moved for the particles
    forAllIter(typename Cloud<ParticleType>, *this, pIter)
    {
        pIter().stepFraction() = 0;
    }

    // Assume there will be particles to transfer
    bool transfered = true;

    // While there are particles to transfer
    while (transfered)
    {
        // List of lists of particles to be transfered for all the processor
        // patches
        List<IDLList<ParticleType> > transferList(processorPatches.size());

        // Loop over all particles
        forAllIter(typename Cloud<ParticleType>, *this, pIter)
        {
            ParticleType& p = pIter();

            // Move the particle
            bool keepParticle = p.move(td);

            // If the particle is to be kept
            // (i.e. it hasn't passed through an inlet or outlet)
            if (keepParticle)
            {
                // If we are running in parallel and the particle is on a
                // boundary face
                if (Pstream::parRun() && p.facei_ >= pMesh().nInternalFaces())
                {
                    label patchi = pMesh().boundaryMesh().whichPatch(p.facei_);
                    label n = processorPatchIndices[patchi];

                    // ... and the face is on a processor patch
                    // prepare it for transfer
                    if (n != -1)
                    {
                        p.prepareForParallelTransfer(patchi, td);
                        transferList[n].append(this->remove(&p));
                    }
                }
            }
            else
            {
                deleteParticle(p);
            }
        }

        if (Pstream::parRun())
        {
            // List of the numbers of particles to be transfered across the
            // processor patches
            labelList nsTransPs(transferList.size());

            forAll(transferList, i)
            {
                nsTransPs[i] = transferList[i].size();
            }

            // List of the numbers of particles to be transfered across the
            // processor patches for all the processors
            labelListList allNTrans(Pstream::nProcs());
            allNTrans[Pstream::myProcNo()] = nsTransPs;
            combineReduce(allNTrans, combineNsTransPs());

            transfered = false;

            forAll(allNTrans, i)
            {
                forAll(allNTrans[i], j)
                {
                    if (allNTrans[i][j])
                    {
                        transfered = true;
                        break;
                    }
                }
            }

            if (!transfered)
            {
                break;
            }

            forAll(transferList, i)
            {
                if (transferList[i].size())
                {
                    OPstream particleStream
                    (
                        Pstream::blocking,
                        refCast<const processorPolyPatch>
                        (
                            pMesh().boundaryMesh()[processorPatches[i]]
                        ).neighbProcNo()
                    );

                    particleStream << transferList[i];
                }
            }

            forAll(processorPatches, i)
            {
                label patchi = processorPatches[i];

                const processorPolyPatch& procPatch =
                    refCast<const processorPolyPatch>
                    (pMesh().boundaryMesh()[patchi]);

                label neighbProci =
                    procPatch.neighbProcNo() - Pstream::masterNo();

                label neighbProcPatchi = processorPatchNeighbours[patchi];

                label nRecPs = allNTrans[neighbProci][neighbProcPatchi];

                if (nRecPs)
                {
                    IPstream particleStream
                    (
                        Pstream::blocking,
                        procPatch.neighbProcNo()
                    );
                    IDLList<ParticleType> newParticles
                    (
                        particleStream,
                        typename ParticleType::iNew(*this)
                    );

                    forAllIter
                    (
                        typename Cloud<ParticleType>,
                        newParticles,
                        newpIter
                    )
                    {
                        ParticleType& newp = newpIter();
                        newp.correctAfterParallelTransfer(patchi, td);
                        addParticle(newParticles.remove(&newp));
                    }
                }
            }
        }
        else
        {
            transfered = false;
        }
    }
}

How can I interpret the TrackingData template? Since move expects its argument to be of Type TrackingData, which tracking data are valid, then?

Thanks :)

francesco_capuano February 10, 2012 09:21

Maybe you already know it, anyway the following reference could be useful:

http://www.tfd.chalmers.se/~hani/kur...LPT_120911.pdf

Cheers,
Francesco

michaelb February 10, 2012 10:50

Hi guys,

thanks for addressing the problem.
I've kept trying so far to use what is already implemented in the code and it really looks like that there are tons of material to deal with. ( Because I'm horrible at C++ coding... )
Unfortunately, I can't figure out what are meaning all the parameters available in a "kinematicCloudProperties" file.
I thought that I could specify a very low particle density leading me to a very small Stokes number therefore implying near to massless fluid particle tracking.
So until now, I've been playing around with the tutorials/lagrangian/icoLagrangianFoam/channelParticles of OpenFOAM-1.6-ext. Only three particles are injected at t=0, but are not moving unless I activate the gravity. Particles are then just settling downwards.

Here is my "kinematicCloudProperties" file :
Code:

/*--------------------------------*- C++ -*----------------------------------*\
| =========                |                                                |
| \\      /  F ield        | OpenFOAM Extend Project: Open Source CFD        |
|  \\    /  O peration    | Version:  1.6-ext                              |
|  \\  /    A nd          | Web:      www.extend-project.de                |
|    \\/    M anipulation  |                                                |
\*---------------------------------------------------------------------------*/
FoamFile
{
    version    2.0;
    format      ascii;
    class      dictionary;
    location    "constant";
    object      kinematicCloud1Properties;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

active          yes;

InjectionModel  ManualInjection;

DragModel      none;

DispersionModel none;

PatchInteractionModel StandardWallInteraction;

PostProcessingModel none;

coupled        true;

cellValueSourceCorrection on;

parcelTypeId    2;

constantProperties
{
    rhoMin          rhoMin [ 1 -3 0 0 0 ] 1e-15;
    minParticleMass minParticleMass [ 1 0 0 0 0 ] 1e-15;
    rho0            rho0 [ 1 -3 0 0 0 ] 5000;
}

interpolationSchemes
{
    rho            cell;
    U              cellPoint;
    mu              cell;
}

integrationSchemes
{
    U              Euler;
}

particleForces
{
    gravity        off;
    virtualMass    off;
    pressureGradient off;
}

ManualInjectionCoeffs
{
    massTotal      massTotal [ 1 0 0 0 0 ] 0.0002;
    parcelBasisType mass;
    SOI            0;
    positionsFile  "kinematicCloudPositions";
    U0              ( 0 0 0 );
    parcelPDF
    {
        pdfType        RosinRammler;
        RosinRammlerPDF
        {
            minValue        5e-05;
            maxValue        0.0001;
            d              ( 7.5e-05 );
            n              ( 0.5 );
        }
    }
}

ConeInjectionCoeffs
{
    SOI            0.001;
    duration        0.005;
    position        ( 0.25 0.25 0.05 );
    direction      ( 0 -1 0 );
    parcelsPerSecond 10000;
    volumeFlowRate  Constant 0.01;
    Umag            Constant 50;
    thetaInner      Constant 0;
    thetaOuter      Constant 30;

    parcelPDF
    {
        pdfType        RosinRammler;
        RosinRammlerPDF
        {
            minValue        5e-05;
            maxValue        0.0001;
            d              ( 7.5e-05 );
            n              ( 0.5 );
        }
    }
}

StandardWallInteractionCoeffs
{
    type            rebound;
}

and the kinematicCloudPosition

Code:

/*--------------------------------*- C++ -*----------------------------------*\
| =========                |                                                |
| \\      /  F ield        | OpenFOAM Extend Project: Open Source CFD        |
|  \\    /  O peration    | Version:  1.6-ext                              |
|  \\  /    A nd          | Web:      www.extend-project.de                |
|    \\/    M anipulation  |                                                |
\*---------------------------------------------------------------------------*/
FoamFile
{
    version    2.0;
    format      ascii;
    class      vectorField;
    object      kinematicCloud1Positions;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
(
(0.05 0.025 0.005)
(0.05 0.05 0.005)
(0.05 0.075 0.005)
)

If nobody can give me an hint here, I think I'll just follow your way Bernhard...

Best,

Michael

michaelb February 10, 2012 10:59

@Bernhard :
We might take advantage of the files associated with the pdf Francesco suggested :
http://www.tfd.chalmers.se/~hani/kur...olver_case.tgz

I'll have a look and keep you updated.

Best,

Michael

BernhardGrieser February 10, 2012 12:23

Quote:

Originally Posted by francesco_capuano (Post 343789)
Maybe you already know it, anyway the following reference could be useful:

http://www.tfd.chalmers.se/~hani/kurser/OS_CFD/OF_kurs_LPT_120911.pdf

Cheers,
Francesco


Nice :) Didn't know that one, thanks!

Still stuck with the problem, though... I'll dive back in the code, then.

Cheers, Bernhard

michaelb February 14, 2012 08:53

Hi Bernhard,

I managed to inject some particles with kinematicCloud classes in the pimpleFoam transient solver. Here's an example with around 5000 particles in a 3D lid-driven cavity. Works fine in parallel too. Tell me if you want me to share.

However, I'm still wondering about the kinematicCloudProperties file, and yet not sure whether I specified correct parameters for massless fluid particles.
The reason I'm doubting is that when I turn off the particleForces, particles aren't moving anymore. And much of the parameters are still very obscure to me.

I'm doubting whether I should start I new thread about the correct use of "kinematicCloudProperties". Would somebody be able to tell us a little bit more about it ?

Cheers,

Michael

http://img17.imageshack.us/img17/929...ew312064bi.png

BernhardGrieser February 14, 2012 11:56

passive particle tracking simplistic approach
 
Quote:

Originally Posted by michaelb (Post 344331)
Tell me if you want me to share.

Hi Michael,

looks very promising, I'd like to have a look at your implementation, yes.

Meanwhile I succeeded in implementing my own particle tracking in a very simplistic approach. For my part, I only have a very small amount of tracer particles, so I do not take advantage of the stepwise particle tracking (stepFraction to next face, etc.) which was intended to avoid calling the mesh.findCell(position) for every particle at every timestep, from what I've understood.

Maybe you want to have a look at it:

Code:

// my solver C-file:

#include "fvCFD.H"
#include "volMesh.H"
#include "interpolationCellPoint.H"
#include "IFstream.H"
#include "OFstream.H"

int main(int argc, char *argv[])
{
    #include "setRootCase.H"
    #include "createTime.H"
    #include "createMesh.H"
    #include "createFields.H"
    #include "initContinuityErrs.H"

    // Load particle positions from dictionary

    IOdictionary particlePositionsDict
    (
        IOobject
        (
            "particlePositionsDict",
            runTime.timeName(),
            mesh,
            IOobject::MUST_READ,
            IOobject::AUTO_WRITE
        )
    );

    List<vector> particlePositions
    (
        particlePositionsDict.lookup("particlePositions")
    );

    // Initialize List for the interpolated particle velocity and the motion vector deltaX
    labelList particleCell(particlePositions.size());
    List<vector> Uint(particlePositions.size());
    List<vector> deltaX(particlePositions.size());

    // Initialize particleTracks
    std::fstream particleTracks;
    particleTracks.open("particleTracks",std::ios::out | std::ios::app);
    particleTracks << "#Time[s]\t\t";
    forAll(particlePositions, posI)
    {
        Info<< "    Position of particle #" << posI+1 << ": " << particlePositions[posI] << endl;
        particleTracks << "particle" << posI+1 << ".x()\t"
              << "particle" << posI+1 << ".y()\t"
              << "particle" << posI+1 << ".z()\t\t";
    }
    particleTracks << std::endl;

// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

    Info<< "\nStarting time loop\n" << endl;

    while (runTime.loop())
    {
        Info<< "Time = " << runTime.timeName() << nl << endl;

        #include "readPISOControls.H"
        #include "CourantNo.H"
       
        scalar deltaT = runTime.deltaT().value();
        interpolationCellPoint<vector> UInt_(U);
        particleTracks << runTime.time().value() << "\t\t";

        forAll(particlePositions, posI)
        {
            particleCell[posI] = mesh.findCell(particlePositions[posI]);
            Uint[posI] = UInt_.interpolate(particlePositions[posI], particleCell[posI]);
            deltaX[posI] = Uint[posI]*deltaT;
            particlePositions[posI] += deltaX[posI];
            particleTracks << particlePositions[posI].x() << "\t"
                      << particlePositions[posI].y() << "\t"
                      << particlePositions[posI].z() << "\t\t";
        }

        particleTracks << std::endl;
...

Basically, I create a restartable log file for the particle positions at every timestep ("particleTracks") as well as a restartable dictionary in the respective time folder which contains the particle positions (particlePositionsDict).

The latter one looks like this:

Code:

/*--------------------------------*- C++ -*----------------------------------*\
| =========                |                                                |
| \\      /  F ield        | OpenFOAM: The Open Source CFD Toolbox          |
|  \\    /  O peration    | Version:  1.7.1                                |
|  \\  /    A nd          | Web:      www.OpenFOAM.com                      |
|    \\/    M anipulation  |                                                |
\*---------------------------------------------------------------------------*/
FoamFile
{
    version    2.0;
    format      ascii;
    class      dictionary;
    location    "0";
    object      particlePositionsDict;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

particlePositions
(
    (5e-05 0.0001 0.000993)
    (5e-05 0.0001 0.001013)
);

Best regards,
Bernhard

michaelb February 14, 2012 13:02

1 Attachment(s)
Hello,

attached is what I've done without any guarantee ;).
I compiled it under OpenFOAM-2.0.1 and for some reason, it required some libraries that were buried deep into the source code.

I like your implementation since you have a total control of what you're doing. How many particles do you think you could track ? Is there a limitation implied by the format ?
Do you have any idea if this is quicker and more/less accurate than what I've done?
I would be very interested in the results if you do some comparison.

cheers,

Michael

BernhardGrieser February 15, 2012 07:58

Quote:

Originally Posted by michaelb (Post 344394)
I like your implementation since you have a total control of what you're doing. How many particles do you think you could track ? Is there a limitation implied by the format ?

Hi Michael, the flaws of the simplistic approach are: nonparallel simulations only, particleTracks have to be transformed in order to be able to view them in paraview.

Thanks for your files, I'll check them.
Bernhard

CFDUser_ April 6, 2014 06:41

Quote:

Originally Posted by francesco_capuano (Post 343789)
Maybe you already know it, anyway the following reference could be useful:

http://www.tfd.chalmers.se/~hani/kurser/OS_CFD/OF_kurs_LPT_120911.pdf

Cheers,
Francesco


Dear Francesco,

This link is not working. May be it will be useful for me as well. Can you please re-upload.

Thanks

Regards
CFDUser_

wyldckat April 6, 2014 07:24

Quick answer - The current link is this one: http://www.tfd.chalmers.se/~hani/kur...LPT_120911.pdf

CFDUser_ April 6, 2014 07:29

Quote:

Originally Posted by wyldckat (Post 484138)

Thanks for very quick and kind reply. Seems it will be very useful for me.

Regards
CFDUser_

CFDUser_ December 7, 2014 05:35

Dear All,

Can somebody post LPTVOF case files Michaelb said about?

Quote:

Originally Posted by michaelb (Post 343807)
@Bernhard :
We might take advantage of the files associated with the pdf Francesco suggested :
http://www.tfd.chalmers.se/~hani/kurser/OS_CFD/LPTVOF_solver_case.tgz

I'll have a look and keep you updated.

Best,

Michael

Thanks & Regards,
CFDUser_

wyldckat December 8, 2014 14:09

Quote:

Originally Posted by CFDUser_ (Post 522774)
Can somebody post LPTVOF case files Michaelb said about?

Quick answer: http://www.tfd.chalmers.se/~hani/kur...olver_case.tgz

PS: The course changes links with each new year: http://openfoamwiki.net/index.php/Ha...ource_software

camzis June 15, 2015 06:19

different numbers of particle injection?
 
Dear foamers,
I'm currently working with the solver "iconUncoupledKinematicParcelFoam (lagrangian)



I need to inject a different number of particles at each time step.
I'm currently using the model "patchInjection" in the file kinematicCloudProperties.
The problem is, I dont know how to make the number of particels vary.


Is there a way to force the number of particles to vary by wether providing a table (for number of particles vs. time) or as a function?


Any suggestions, ideas are very welcome.

Please, my master thesis depends on this.


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