From 14a39405c22873036e5c11c8028c9d1a03173666 Mon Sep 17 00:00:00 2001
From: Mark Olesen <Mark.Olesen@esi-group.com>
Date: Wed, 17 Oct 2018 17:37:23 +0200
Subject: [PATCH] ENH: simple dataCloud function object (issue #1044)

- writes positions and a single field (eg, diameter) in plain ASCII files,
  suitable for importing in a spreadsheet or manipulation with
  scripting tools.

- code integrated from
  https://develop.openfoam.com/Community/OpenFOAM-addOns
---
 src/functionObjects/lagrangian/Make/files     |   1 +
 .../lagrangian/dataCloud/dataCloud.C          | 227 ++++++++++++++++++
 .../lagrangian/dataCloud/dataCloud.H          | 180 ++++++++++++++
 .../lagrangian/dataCloud/dataCloudTemplates.C | 130 ++++++++++
 .../simplifiedSiwek/system/controlDict        |   1 +
 .../simplifiedSiwek/system/dataCloud          |  13 +
 6 files changed, 552 insertions(+)
 create mode 100644 src/functionObjects/lagrangian/dataCloud/dataCloud.C
 create mode 100644 src/functionObjects/lagrangian/dataCloud/dataCloud.H
 create mode 100644 src/functionObjects/lagrangian/dataCloud/dataCloudTemplates.C
 create mode 100644 tutorials/lagrangian/coalChemistryFoam/simplifiedSiwek/system/dataCloud

diff --git a/src/functionObjects/lagrangian/Make/files b/src/functionObjects/lagrangian/Make/files
index 921ca2d707f..c7feb02e33f 100644
--- a/src/functionObjects/lagrangian/Make/files
+++ b/src/functionObjects/lagrangian/Make/files
@@ -1,3 +1,4 @@
+dataCloud/dataCloud.C
 cloudInfo/cloudInfo.C
 icoUncoupledKinematicCloud/icoUncoupledKinematicCloud.C
 dsmcFields/dsmcFields.C
diff --git a/src/functionObjects/lagrangian/dataCloud/dataCloud.C b/src/functionObjects/lagrangian/dataCloud/dataCloud.C
new file mode 100644
index 00000000000..53267ab0251
--- /dev/null
+++ b/src/functionObjects/lagrangian/dataCloud/dataCloud.C
@@ -0,0 +1,227 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2018 OpenCFD Ltd.
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+License
+    This file is part of OpenFOAM.
+
+    OpenFOAM is free software: you can redistribute it and/or modify it
+    under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
+
+\*---------------------------------------------------------------------------*/
+
+#include "dataCloud.H"
+#include "Cloud.H"
+#include "dictionary.H"
+#include "fvMesh.H"
+#include "pointList.H"
+#include "OFstream.H"
+#include "addToRunTimeSelectionTable.H"
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+namespace Foam
+{
+namespace functionObjects
+{
+    defineTypeNameAndDebug(dataCloud, 0);
+    addToRunTimeSelectionTable(functionObject, dataCloud, dictionary);
+}
+}
+
+// * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
+
+bool Foam::functionObjects::dataCloud::writeCloud
+(
+    const fileName& outputName,
+    const word& cloudName
+)
+{
+    const auto* objPtr = mesh_.findObject<cloud>(cloudName);
+    if (!objPtr)
+    {
+        return false;
+    }
+
+    objectRegistry obrTmp
+    (
+        IOobject
+        (
+            "tmp::dataCloud::" + cloudName,
+            mesh_.time().constant(),
+            mesh_,
+            IOobject::NO_READ,
+            IOobject::NO_WRITE,
+            false
+        )
+    );
+
+    objPtr->writeObjects(obrTmp);
+
+    const auto* pointsPtr = obrTmp.findObject<vectorField>("position");
+
+    if (!pointsPtr)
+    {
+        // This should be impossible
+        return false;
+    }
+
+    // Total number of parcels on all processes
+    label nTotParcels = pointsPtr->size();
+    reduce(nTotParcels, sumOp<label>());
+
+    if (!nTotParcels)
+    {
+        return false;
+    }
+
+
+    if (Pstream::master())
+    {
+        mkDir(outputName.path());
+    }
+
+    return
+    (
+        writeField<label>(outputName, obrTmp)
+     || writeField<scalar>(outputName, obrTmp)
+     || writeField<vector>(outputName, obrTmp)
+    );
+}
+
+
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+Foam::functionObjects::dataCloud::dataCloud
+(
+    const word& name,
+    const Time& runTime,
+    const dictionary& dict
+)
+:
+    fvMeshFunctionObject(name, runTime, dict),
+    selectClouds_(),
+    fieldName_(),
+    directory_()
+{
+    read(dict);
+}
+
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+bool Foam::functionObjects::dataCloud::read(const dictionary& dict)
+{
+    fvMeshFunctionObject::read(dict);
+
+    const int padWidth = dict.lookupOrDefault<int>("width", 8);
+
+    // Appropriate printf format - Enforce min/max sanity limits
+    if (padWidth < 1 || padWidth > 31)
+    {
+        printf_.clear();
+    }
+    else
+    {
+        printf_ = "%0" + std::to_string(padWidth) + "d";
+    }
+
+
+    selectClouds_.clear();
+    dict.readIfPresent("clouds", selectClouds_);
+
+    if (selectClouds_.empty())
+    {
+        selectClouds_.resize(1);
+        selectClouds_.first() =
+            dict.lookupOrDefault<word>("cloud", cloud::defaultName);
+    }
+
+    dict.readEntry("field", fieldName_);
+
+    // Output directory
+
+    directory_.clear();
+    dict.readIfPresent("directory", directory_);
+
+    if (directory_.size())
+    {
+        // User-defined output directory
+        directory_.expand();
+        if (!directory_.isAbsolute())
+        {
+            directory_ = time_.globalPath()/directory_;
+        }
+    }
+    else
+    {
+        // Standard postProcessing/ naming
+        directory_ = time_.globalPath()/functionObject::outputPrefix/name();
+    }
+    directory_.clean();
+
+    return true;
+}
+
+
+bool Foam::functionObjects::dataCloud::execute()
+{
+    return true;
+}
+
+
+bool Foam::functionObjects::dataCloud::write()
+{
+    const wordList cloudNames(mesh_.sortedNames<cloud>(selectClouds_));
+
+    if (cloudNames.empty())
+    {
+        return true;  // skip - not available
+    }
+
+    const word timeDesc = "_" +
+    (
+        printf_.empty()
+      ? Foam::name(time_.timeIndex())
+      : word::printf(printf_, time_.timeIndex())
+    );
+
+    Log << name() << " output Time: " << time_.timeName() << nl;
+
+    // Each cloud separately
+    for (const word& cloudName : cloudNames)
+    {
+        // Legacy is not to be supported
+
+        const fileName outputName
+        (
+            directory_/cloudName + timeDesc + ".dat"
+        );
+
+        // writeCloud() includes mkDir (on master)
+
+        if (writeCloud(outputName, cloudName))
+        {
+            Log << "    cloud  : "
+                << outputName.relative(time_.globalPath()) << endl;
+        }
+    }
+
+    return true;
+}
+
+
+// ************************************************************************* //
diff --git a/src/functionObjects/lagrangian/dataCloud/dataCloud.H b/src/functionObjects/lagrangian/dataCloud/dataCloud.H
new file mode 100644
index 00000000000..a1ef28b587f
--- /dev/null
+++ b/src/functionObjects/lagrangian/dataCloud/dataCloud.H
@@ -0,0 +1,180 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2018 OpenCFD Ltd.
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+License
+    This file is part of OpenFOAM.
+
+    OpenFOAM is free software: you can redistribute it and/or modify it
+    under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
+
+Class
+    Foam::functionObjects::dataCloud
+
+Group
+    grpLagrangianFunctionObjects
+
+Description
+    This functionObject writes a cloud position and in ASCII.
+
+    Example of function object specification:
+    \verbatim
+    cloudWrite1
+    {
+        type            dataCloud;
+        libs            ("liblagrangianFunctionObjects.so");
+        writeControl    writeTime;
+        writeInterval   1;
+        cloud           myCloud;
+        field           d;
+    }
+    \endverbatim
+
+Usage
+    \table
+        Property     | Description                      | Required | Default
+        type         | Type name: dataCloud             | yes   |
+        cloud        |                                  | no    | defaultCloud
+        clouds       | wordRe list of clouds            | no    |
+        field        | Name of the field                | yes   |
+        directory    | The output directory name     | no | postProcessing/NAME
+        width        | Padding width for file name      | no    | 8
+    \endtable
+
+See also
+    Foam::functionObjects::vtkCloud
+
+SourceFiles
+    dataCloud.C
+    dataCloudTemplates.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef functionObjects_dataCloud_H
+#define functionObjects_dataCloud_H
+
+#include "fvMeshFunctionObject.H"
+#include "vectorField.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+namespace functionObjects
+{
+
+/*---------------------------------------------------------------------------*\
+                          Class dataCloud Declaration
+\*---------------------------------------------------------------------------*/
+
+class dataCloud
+:
+    public fvMeshFunctionObject
+{
+    // Private data
+
+        //- The printf format for zero-padding names
+        string printf_;
+
+        //- Requested names of clouds to process
+        wordRes selectClouds_;
+
+        //- Subset of cloud fields to process
+        word fieldName_;
+
+        //- Output directory
+        fileName directory_;
+
+
+    // Private Member Functions
+
+        template<class Type>
+        static void writeField
+        (
+            Ostream& os,
+            const vectorField& points,
+            const Field<Type>& field
+        );
+
+        //- Write to disk
+        bool writeCloud(const fileName& outputName, const word& cloudName);
+
+        //- Write from objectRegistry entry
+        template<class Type>
+        bool writeField
+        (
+            const fileName& outputName,
+            const objectRegistry& obrTmp
+        ) const;
+
+
+        //- No copy construct
+        dataCloud(const dataCloud&) = delete;
+
+        //- No copy assignment
+        void operator=(const dataCloud&) = delete;
+
+
+public:
+
+    //- Runtime type information
+    TypeName("dataCloud");
+
+
+    // Constructors
+
+        //- Construct from Time and dictionary
+        dataCloud
+        (
+            const word& name,
+            const Time& runTime,
+            const dictionary& dict
+        );
+
+
+    //- Destructor
+    virtual ~dataCloud() = default;
+
+
+    // Member Functions
+
+        //- Read the dataCloud specification
+        virtual bool read(const dictionary& dict);
+
+        //- Execute, currently does nothing
+        virtual bool execute();
+
+        //- Write fields
+        virtual bool write();
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace functionObjects
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#ifdef NoRepository
+    #include "dataCloudTemplates.C"
+#endif
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/functionObjects/lagrangian/dataCloud/dataCloudTemplates.C b/src/functionObjects/lagrangian/dataCloud/dataCloudTemplates.C
new file mode 100644
index 00000000000..c51033e9e56
--- /dev/null
+++ b/src/functionObjects/lagrangian/dataCloud/dataCloudTemplates.C
@@ -0,0 +1,130 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2018 OpenCFD Ltd.
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+License
+    This file is part of OpenFOAM.
+
+    OpenFOAM is free software: you can redistribute it and/or modify it
+    under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
+
+\*---------------------------------------------------------------------------*/
+
+#include "IOField.H"
+#include "OFstream.H"
+#include "pointField.H"
+#include "vectorField.H"
+
+// * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
+
+template<class Type>
+void Foam::functionObjects::dataCloud::writeField
+(
+    Ostream& os,
+    const vectorField& points,
+    const Field<Type>& field
+)
+{
+    const label len = field.size();
+
+    for (label pointi=0; pointi<len; ++pointi)
+    {
+        const point& pt = points[pointi];
+        const Type& val = field[pointi];
+
+        os << pt.x() << ' ' << pt.y() << ' ' << pt.z();
+
+        for (direction cmpt=0; cmpt < pTraits<Type>::nComponents; ++cmpt)
+        {
+            os << ' ' << component(val, cmpt);
+        }
+        os << nl;
+    }
+}
+
+
+template<class Type>
+bool Foam::functionObjects::dataCloud::writeField
+(
+    const fileName& outputName,
+    const objectRegistry& obrTmp
+) const
+{
+    // Fields are not always on all processors (eg, multi-component parcels).
+    // Thus need to resolve between all processors.
+
+    const auto* fldPtr = obrTmp.findObject<IOField<Type>>(fieldName_);
+
+    if (!returnReduce((fldPtr != nullptr), orOp<bool>()))
+    {
+        return false;
+    }
+
+    const auto* pointsPtr = obrTmp.findObject<vectorField>("position");
+
+    if (!pointsPtr)
+    {
+        // This should be impossible
+        return false;
+    }
+
+    if (Pstream::master())
+    {
+        OFstream os(outputName);
+
+        os << "# x y z " << fieldName_ << nl;
+
+        // Master
+        if (fldPtr)
+        {
+            writeField(os, *pointsPtr, *fldPtr);
+        }
+
+        // Slaves - recv
+        for (int slave=1; slave<Pstream::nProcs(); ++slave)
+        {
+            IPstream fromSlave(Pstream::commsTypes::blocking, slave);
+            vectorField points(fromSlave);
+            Field<Type> fld(fromSlave);
+
+            writeField(os, points, fld);
+        }
+    }
+    else
+    {
+        // Slaves
+
+        OPstream toMaster(Pstream::commsTypes::blocking, Pstream::masterNo());
+
+        if (fldPtr)
+        {
+            toMaster
+                << *pointsPtr
+                << *fldPtr;
+        }
+        else
+        {
+            toMaster
+                << vectorField()
+                << Field<Type>();
+        }
+    }
+
+    return true;
+}
+
+
+// ************************************************************************* //
diff --git a/tutorials/lagrangian/coalChemistryFoam/simplifiedSiwek/system/controlDict b/tutorials/lagrangian/coalChemistryFoam/simplifiedSiwek/system/controlDict
index 20916ea29a8..a2f5fcfc88e 100644
--- a/tutorials/lagrangian/coalChemistryFoam/simplifiedSiwek/system/controlDict
+++ b/tutorials/lagrangian/coalChemistryFoam/simplifiedSiwek/system/controlDict
@@ -54,6 +54,7 @@ maxDeltaT       1;
 functions
 {
     #include "vtkCloud"
+    // #include "dataCloud"
     #include "runTimePostProcessing"
 }
 
diff --git a/tutorials/lagrangian/coalChemistryFoam/simplifiedSiwek/system/dataCloud b/tutorials/lagrangian/coalChemistryFoam/simplifiedSiwek/system/dataCloud
new file mode 100644
index 00000000000..127d1ccf071
--- /dev/null
+++ b/tutorials/lagrangian/coalChemistryFoam/simplifiedSiwek/system/dataCloud
@@ -0,0 +1,13 @@
+// -*- C++ -*-
+// Minimal example of using the dataCloud function object.
+dataCloud
+{
+    type    dataCloud;
+    libs    ("liblagrangianFunctionObjects.so");
+    log     true;
+
+    cloud   coalCloud1;
+    field   d;
+}
+
+// ************************************************************************* //
-- 
GitLab