From a4dc9966eda70f06933c541d08d9ef0bfbf3b6a2 Mon Sep 17 00:00:00 2001
From: Andrew Heather <>
Date: Fri, 7 Dec 2018 17:24:13 +0000
Subject: [PATCH] ENH: Added new AMIWeights function object

Reports the min|max|average AMI weights to text file and optionally
writes VTK surfaces of the sum of the weights, and mask field for
ACMI patches.

Example usage:

    AMIWeights
    {
        type            AMIWeights;
        libs            ("libfieldFunctionObjects.so");
        writeControl    writeTime;
        writeFields     yes;
    }
---
 .../postProcessing/fields/AMIWeights          |  20 +
 .../field/AMIWeights/AMIWeights.C             | 396 ++++++++++++++++++
 .../field/AMIWeights/AMIWeights.H             | 163 +++++++
 src/functionObjects/field/Make/files          |   2 +
 .../RAS/propeller/system/AMIWeights           |  17 +
 .../RAS/propeller/system/controlDict          |   1 +
 6 files changed, 599 insertions(+)
 create mode 100644 etc/caseDicts/postProcessing/fields/AMIWeights
 create mode 100644 src/functionObjects/field/AMIWeights/AMIWeights.C
 create mode 100644 src/functionObjects/field/AMIWeights/AMIWeights.H
 create mode 100644 tutorials/incompressible/pimpleFoam/RAS/propeller/system/AMIWeights

diff --git a/etc/caseDicts/postProcessing/fields/AMIWeights b/etc/caseDicts/postProcessing/fields/AMIWeights
new file mode 100644
index 00000000000..3bbda0fcc5d
--- /dev/null
+++ b/etc/caseDicts/postProcessing/fields/AMIWeights
@@ -0,0 +1,20 @@
+/*--------------------------------*- C++ -*----------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     | Version:  v1806
+    \\  /    A nd           | Web:      www.OpenFOAM.com
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+Description
+    Calculates and writes the second largest eigenvalue of the sum of the
+    square of the symmetrical and anti-symmetrical parts of the velocity
+    gradient tensor.
+
+\*---------------------------------------------------------------------------*/
+
+type            AMIWeights;
+libs            ("libfieldFunctionObjects.so");
+writeFields     yes;
+writeControl    writeTime;
+
+// ************************************************************************* //
diff --git a/src/functionObjects/field/AMIWeights/AMIWeights.C b/src/functionObjects/field/AMIWeights/AMIWeights.C
new file mode 100644
index 00000000000..4f0952442cb
--- /dev/null
+++ b/src/functionObjects/field/AMIWeights/AMIWeights.C
@@ -0,0 +1,396 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  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 "AMIWeights.H"
+#include "fvMesh.H"
+#include "vtkSurfaceWriter.H"
+#include "PatchTools.H"
+#include "cyclicACMIPolyPatch.H"
+#include "addToRunTimeSelectionTable.H"
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+namespace Foam
+{
+namespace functionObjects
+{
+    defineTypeNameAndDebug(AMIWeights, 0);
+    addToRunTimeSelectionTable(functionObject, AMIWeights, dictionary);
+}
+}
+
+// * * * * * * * * * * * * Protected Member Functions  * * * * * * * * * * * //
+
+void Foam::functionObjects::AMIWeights::writeFileHeader(Ostream& os)
+{
+    writeHeader(os, "AMI");
+
+    writeCommented(os, "Time");
+    forAll(patchIDs_, patchi)
+    {
+        writeTabbed(os, "Patch");
+        writeTabbed(os, "nbr_patch");
+
+        if (Pstream::parRun())
+        {
+            writeTabbed(os, "distributed");
+        }
+
+        writeTabbed(os, "src_min_weight");
+        writeTabbed(os, "src_max_weight");
+        writeTabbed(os, "src_average_weight");
+        writeTabbed(os, "src_min_neighbours");
+        writeTabbed(os, "src_max_neighbours");
+        writeTabbed(os, "src_average_neighbours");
+        writeTabbed(os, "tgt_min_weight");
+        writeTabbed(os, "tgt_max_weight");
+        writeTabbed(os, "tgt_average_weight");
+        writeTabbed(os, "tgt_min_neighbours");
+        writeTabbed(os, "tgt_max_neighbours");
+        writeTabbed(os, "tgt_average_neighbours");
+    }
+
+    os  << endl;
+}
+
+
+void Foam::functionObjects::AMIWeights::reportPatch
+(
+    const cyclicAMIPolyPatch& pp
+)
+{
+    const word& nbrPatchName = pp.neighbPatchName();
+
+    const Switch distributed = pp.AMI().singlePatchProc() == -1;
+
+    const scalarField& srcWeightsSum = pp.AMI().srcWeightsSum();
+    const scalar srcMinWeight = gMin(srcWeightsSum);
+    const scalar srcMaxWeight = gMax(srcWeightsSum);
+    const scalar srcAveWeight = gAverage(srcWeightsSum);
+
+    const labelListList& srcAddress = pp.AMI().srcAddress();
+    label srcMinNbr = labelMax;
+    label srcMaxNbr = labelMin;
+    scalar srcAveNbr = 0;
+    for (const labelList& srcFace : srcAddress)
+    {
+        const label n = srcFace.size();
+        srcAveNbr += n;
+        srcMinNbr = min(srcMinNbr, n);
+        srcMaxNbr = max(srcMaxNbr, n);
+    }
+
+    reduce(srcMinNbr, minOp<label>());
+    reduce(srcMaxNbr, maxOp<label>());
+
+    srcAveNbr =
+        returnReduce(srcAveNbr, sumOp<scalar>())
+       /(returnReduce(srcAddress.size(), sumOp<scalar>()) + ROOTVSMALL);
+
+    const scalarField& tgtWeightsSum = pp.AMI().tgtWeightsSum();
+    const scalar tgtMinWeight = gMin(tgtWeightsSum);
+    const scalar tgtMaxWeight = gMax(tgtWeightsSum);
+    const scalar tgtAveWeight = gAverage(tgtWeightsSum);
+
+    const labelListList& tgtAddress = pp.AMI().tgtAddress();
+    label tgtMinNbr = labelMax;
+    label tgtMaxNbr = labelMin;
+    scalar tgtAveNbr = 0;
+    for (const labelList& tgtFace : tgtAddress)
+    {
+        const label n = tgtFace.size();
+        tgtAveNbr += n;
+        tgtMinNbr = min(tgtMinNbr, n);
+        tgtMaxNbr = max(tgtMaxNbr, n);
+    }
+
+    reduce(tgtMinNbr, minOp<label>());
+    reduce(tgtMaxNbr, maxOp<label>());
+
+    tgtAveNbr =
+        returnReduce(tgtAveNbr, sumOp<scalar>())
+       /(returnReduce(tgtAddress.size(), sumOp<scalar>()) + ROOTVSMALL);
+
+    file()
+        << mesh_.time().timeName() << tab
+        << pp.name() << tab
+        << nbrPatchName << tab;
+
+
+    if (Pstream::parRun())
+    {
+        file() << distributed << tab;
+    }
+
+    file()
+        << srcMinWeight << tab
+        << srcMaxWeight << tab
+        << srcAveWeight << tab
+        << srcMinNbr << tab
+        << srcMaxNbr << tab
+        << srcAveNbr << tab
+        << tgtMinWeight << tab
+        << tgtMaxWeight << tab
+        << tgtAveWeight << tab
+        << tgtMinNbr << tab
+        << tgtMaxNbr << tab
+        << tgtAveNbr << tab
+        << endl;
+
+    Log << "    Patches: " << nl
+        << "        Source: " << pp.name() << nl
+        << "        Target: " << nbrPatchName << nl;
+
+    if (Pstream::parRun())
+    {
+        Log << "        Parallel distributed: " << distributed << nl;
+    }
+
+    Log << nl;
+
+    const label w = IOstream::defaultPrecision() + 8;
+
+    Log << "                     | " << setw(w) << pp.name()
+        << " | " << setw(w) << nbrPatchName << " | " << nl
+        << "        min(weight)  | " << setw(w) << srcMinWeight
+        << " | " << setw(w) << tgtMinWeight << " | " << nl
+        << "        max(weight)  | " << setw(w) << srcMaxWeight
+        << " | " << setw(w) << tgtMaxWeight << " | " << nl
+        << "        ave(weight)  | " << setw(w) << srcAveWeight
+        << " | " << setw(w) << tgtAveWeight << " | " << nl
+        << "        min(address) | " << setw(w) << srcMinNbr
+        << " | " << setw(w) << tgtMinNbr << " | " << nl
+        << "        max(address) | " << setw(w) << srcMaxNbr
+        << " | " << setw(w) << tgtMaxNbr << " | " << nl
+        << "        ave(address) | " << setw(w) << srcAveNbr
+        << " | " << setw(w) << tgtAveNbr << " | " << nl
+        << endl;
+
+    setResult(pp.name() + ":src", pp.name());
+    setResult(pp.name() + ":tgt", nbrPatchName);
+    setResult(pp.name() + ":src:min(weight)", srcMinWeight);
+    setResult(pp.name() + ":src:max(weight)", srcMaxWeight);
+    setResult(pp.name() + ":src:ave(weight)", srcAveWeight);
+    setResult(pp.name() + ":src:min(address)", srcMinNbr);
+    setResult(pp.name() + ":src:max(address)", srcMaxNbr);
+    setResult(pp.name() + ":src:ave(address)", srcAveNbr);
+    setResult(pp.name() + ":tgt:min(weight)", tgtMinWeight);
+    setResult(pp.name() + ":tgt:max(weight)", tgtMaxWeight);
+    setResult(pp.name() + ":tgt:ave(weight)", tgtAveWeight);
+    setResult(pp.name() + ":tgt:min(address)", tgtMinNbr);
+    setResult(pp.name() + ":tgt:max(address)", tgtMaxNbr);
+    setResult(pp.name() + ":tgt:ave(address)", tgtAveNbr);
+}
+
+
+void Foam::functionObjects::AMIWeights::writeWeightField
+(
+    const cyclicAMIPolyPatch& cpp,
+    const scalarField& weightSum,
+    const word& side
+) const
+{
+    vtkSurfaceWriter writer;
+
+    // Collect geometry
+    labelList pointToGlobal;
+    labelList uniqueMeshPointLabels;
+    autoPtr<globalIndex> globalPoints;
+    autoPtr<globalIndex> globalFaces;
+    faceList mergedFaces;
+    pointField mergedPoints;
+    Foam::PatchTools::gatherAndMerge
+    (
+        mesh_,
+        cpp.localFaces(),
+        cpp.meshPoints(),
+        cpp.meshPointMap(),
+
+        pointToGlobal,
+        uniqueMeshPointLabels,
+        globalPoints,
+        globalFaces,
+
+        mergedFaces,
+        mergedPoints
+    );
+
+    // Collect field
+    scalarField mergedWeights;
+    globalFaces().gather
+    (
+        UPstream::worldComm,
+        ListOps::create<label>
+        (
+            UPstream::procID(UPstream::worldComm),
+            labelOp<int>()  // int -> label
+        ),
+        weightSum,
+        mergedWeights
+    );
+
+    if (Pstream::master())
+    {
+        writer.write
+        (
+            baseTimeDir(),
+            cpp.name() + "_" + side,
+            meshedSurfRef(mergedPoints, mergedFaces),
+            "weightsSum",
+            mergedWeights,
+            false
+        );
+    }
+
+    if (isA<cyclicACMIPolyPatch>(cpp))
+    {
+        const cyclicACMIPolyPatch& pp = refCast<const cyclicACMIPolyPatch>(cpp);
+        scalarField mergedMask;
+        globalFaces().gather
+        (
+            UPstream::worldComm,
+            ListOps::create<label>
+            (
+                UPstream::procID(UPstream::worldComm),
+                labelOp<int>()  // int -> label
+            ),
+            pp.mask(),
+            mergedMask
+        );
+
+        if (Pstream::master())
+        {
+            writer.write
+            (
+                baseTimeDir(),
+                cpp.name() + "_" + side,
+                meshedSurfRef(mergedPoints, mergedFaces),
+                "mask",
+                mergedMask,
+                false
+            );
+        }
+    }
+}
+
+
+void Foam::functionObjects::AMIWeights::writeWeightFields
+(
+    const cyclicAMIPolyPatch& cpp
+) const
+{
+    if (cpp.owner())
+    {
+        writeWeightField(cpp, cpp.AMI().srcWeightsSum(), "src");
+        writeWeightField(cpp.neighbPatch(), cpp.AMI().tgtWeightsSum(), "tgt");
+    }
+}
+
+
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+Foam::functionObjects::AMIWeights::AMIWeights
+(
+    const word& name,
+    const Time& runTime,
+    const dictionary& dict
+)
+:
+    fvMeshFunctionObject(name, runTime, dict),
+    writeFile(mesh_, name, typeName, dict),
+    writeFields_(false),
+    patchIDs_()
+{
+    read(dict);
+}
+
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+bool Foam::functionObjects::AMIWeights::read(const dictionary& dict)
+{
+    if (fvMeshFunctionObject::read(dict) && writeFile::read(dict))
+    {
+        const polyBoundaryMesh& pbm = mesh_.boundaryMesh();
+
+        patchIDs_.clear();
+        labelHashSet ids;
+        forAll(pbm, patchi)
+        {
+            if (isA<cyclicAMIPolyPatch>(pbm[patchi]))
+            {
+                const auto& ami =
+                    static_cast<const cyclicAMIPolyPatch&>(pbm[patchi]);
+
+                if (ami.owner())
+                {
+                    ids.insert(patchi);
+                }
+            }
+        }
+
+        patchIDs_ = ids.sortedToc();
+
+        writeFileHeader(file());
+
+        writeFields_ = dict.get<bool>("writeFields");
+
+        return true;
+    }
+
+    return false;
+}
+
+
+bool Foam::functionObjects::AMIWeights::execute()
+{
+    return true;
+}
+
+
+bool Foam::functionObjects::AMIWeights::write()
+{
+    Log << type() << " " << name() <<  " write:" << nl;
+
+    const polyBoundaryMesh& pbm = mesh_.boundaryMesh();
+    for (const label patchi : patchIDs_)
+    {
+        const polyPatch& pp = pbm[patchi];
+        const auto& cpp = static_cast<const cyclicAMIPolyPatch&>(pp);
+
+        reportPatch(cpp);
+
+        if (writeFields_)
+        {
+            writeWeightFields(cpp);
+        }
+    }
+
+
+    return true;
+}
+
+
+// ************************************************************************* //
diff --git a/src/functionObjects/field/AMIWeights/AMIWeights.H b/src/functionObjects/field/AMIWeights/AMIWeights.H
new file mode 100644
index 00000000000..bebbd0dcc78
--- /dev/null
+++ b/src/functionObjects/field/AMIWeights/AMIWeights.H
@@ -0,0 +1,163 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  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::AMIWeights
+
+Group
+    grpFieldFunctionObjects
+
+Description
+
+Usage
+    Example of function object specification:
+    \verbatim
+    AMIWeights1
+    {
+        type        AMIWeights;
+        libs        ("libfieldFunctionObjects.so");
+        writeFields yes;
+    }
+    \endverbatim
+
+    Where the entries comprise:
+    \table
+        Property     | Description             | Required    | Default value
+        type         | type name: AMIWeights   | yes         |
+        writeFields  | write weights as VTK fields | yes     |
+    \endtable
+
+    Output data is written to the file \<timeDir\>/AMIWeights.dat
+
+See also
+    Foam::functionObjects::fvMeshFunctionObject
+    Foam::functionObjects::writeFile
+
+SourceFiles
+    AMIWeights.C
+    AMIWeightsTemplates.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef functionObjects_AMIWeights_H
+#define functionObjects_AMIWeights_H
+
+#include "fvMeshFunctionObject.H"
+#include "writeFile.H"
+#include "cyclicAMIPolyPatch.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+namespace functionObjects
+{
+
+/*---------------------------------------------------------------------------*\
+                         Class AMIWeights Declaration
+\*---------------------------------------------------------------------------*/
+
+class AMIWeights
+:
+    public fvMeshFunctionObject,
+    public writeFile
+{
+protected:
+
+    // Protected data
+
+        //- Flag to write AMI fields (as VTK files)
+        bool writeFields_;
+
+        //- List of AMI patch IDs
+        labelList patchIDs_;
+
+
+    // Protected Member Functions
+
+        //- Output file header information
+        virtual void writeFileHeader(Ostream& os);
+
+        //- Helper function to report patch information
+        virtual void reportPatch(const cyclicAMIPolyPatch& pp);
+
+        void writeWeightField
+        (
+            const cyclicAMIPolyPatch& cpp,
+            const scalarField& weightSum,
+            const word& side
+        ) const;
+
+        void writeWeightFields(const cyclicAMIPolyPatch& cpp) const;
+
+        //- No copy construct
+        AMIWeights(const AMIWeights&) = delete;
+
+        //- No copy assignment
+        void operator=(const AMIWeights&) = delete;
+
+
+public:
+
+    //- Runtime type information
+    TypeName("AMIWeights");
+
+
+    // Constructors
+
+        //- Construct from Time and dictionary
+        AMIWeights
+        (
+            const word& name,
+            const Time& runTime,
+            const dictionary& dict
+        );
+
+
+    //- Destructor
+    virtual ~AMIWeights() = default;
+
+
+    // Member Functions
+
+        //- Read the field min/max data
+        virtual bool read(const dictionary&);
+
+        //- Execute, currently does nothing
+        virtual bool execute();
+
+        //- Write the AMIWeights
+        virtual bool write();
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace functionObjects
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/functionObjects/field/Make/files b/src/functionObjects/field/Make/files
index d5aa38ee3dd..fbbac57cb6d 100644
--- a/src/functionObjects/field/Make/files
+++ b/src/functionObjects/field/Make/files
@@ -1,3 +1,5 @@
+AMIWeights/AMIWeights.C
+
 fieldAverage/fieldAverage.C
 fieldAverage/fieldAverageItem/fieldAverageItem.C
 fieldAverage/fieldAverageItem/fieldAverageItemIO.C
diff --git a/tutorials/incompressible/pimpleFoam/RAS/propeller/system/AMIWeights b/tutorials/incompressible/pimpleFoam/RAS/propeller/system/AMIWeights
new file mode 100644
index 00000000000..10a3a902c03
--- /dev/null
+++ b/tutorials/incompressible/pimpleFoam/RAS/propeller/system/AMIWeights
@@ -0,0 +1,17 @@
+/*--------------------------------*- C++ -*----------------------------------*\
+| =========                 |                                                 |
+| \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox           |
+|  \\    /   O peration     | Version:  v1806                                 |
+|   \\  /    A nd           | Web:      www.OpenFOAM.com                      |
+|    \\/     M anipulation  |                                                 |
+\*---------------------------------------------------------------------------*/
+
+AMIWeights
+{
+    type          AMIWeights;
+    libs          ("libfieldFunctionObjects.so");
+    writeControl  writeTime;
+    writeFields   no;
+}
+
+// ************************************************************************* //
diff --git a/tutorials/incompressible/pimpleFoam/RAS/propeller/system/controlDict b/tutorials/incompressible/pimpleFoam/RAS/propeller/system/controlDict
index 92a90406da2..507be631bb1 100644
--- a/tutorials/incompressible/pimpleFoam/RAS/propeller/system/controlDict
+++ b/tutorials/incompressible/pimpleFoam/RAS/propeller/system/controlDict
@@ -57,6 +57,7 @@ functions
     #includeFunc Q
     #include "surfaces"
     #include "forces"
+    #include "AMIWeights"
 }
 
 // ************************************************************************* //
-- 
GitLab