From ed62ed1b19b01a3df7d159b59087d6ef5074c623 Mon Sep 17 00:00:00 2001
From: Mark Olesen <Mark.Olesen@esi-group.com>
Date: Thu, 27 Sep 2018 13:38:31 +0200
Subject: [PATCH] ENH: parallel and xml output for surface vector fields (issue
 #926)

-  implemented as vtk::surfaceFieldWriter, which replaces
   the vtk::writeSurfFields function.
---
 src/conversion/Make/files                     |   2 +-
 .../vtk/output/foamVtkSurfaceFieldWriter.C    | 300 ++++++++++++++++++
 .../vtk/output/foamVtkSurfaceFieldWriter.H    | 186 +++++++++++
 .../vtk/output/foamVtkWriteSurfFields.C       | 171 ----------
 .../vtk/output/foamVtkWriteSurfFields.H       |  67 ----
 5 files changed, 487 insertions(+), 239 deletions(-)
 create mode 100644 src/conversion/vtk/output/foamVtkSurfaceFieldWriter.C
 create mode 100644 src/conversion/vtk/output/foamVtkSurfaceFieldWriter.H
 delete mode 100644 src/conversion/vtk/output/foamVtkWriteSurfFields.C
 delete mode 100644 src/conversion/vtk/output/foamVtkWriteSurfFields.H

diff --git a/src/conversion/Make/files b/src/conversion/Make/files
index 5d6233a1c61..eb9ac5e43c1 100644
--- a/src/conversion/Make/files
+++ b/src/conversion/Make/files
@@ -26,6 +26,6 @@ polyDualMesh/polyDualMesh.C
 
 vtk/output/foamVtkInternalWriter.H
 vtk/output/foamVtkPatchWriter.H
-vtk/output/foamVtkWriteSurfFields.C
+vtk/output/foamVtkSurfaceFieldWriter.C
 
 LIB = $(FOAM_LIBBIN)/libconversion
diff --git a/src/conversion/vtk/output/foamVtkSurfaceFieldWriter.C b/src/conversion/vtk/output/foamVtkSurfaceFieldWriter.C
new file mode 100644
index 00000000000..7b439e8c5f8
--- /dev/null
+++ b/src/conversion/vtk/output/foamVtkSurfaceFieldWriter.C
@@ -0,0 +1,300 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2016-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 "foamVtkSurfaceFieldWriter.H"
+#include "emptyFvsPatchFields.H"
+#include "fvsPatchFields.H"
+#include "surfaceFields.H"
+
+// * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
+
+Foam::List<Foam::vector> Foam::vtk::surfaceFieldWriter::flattenBoundary
+(
+    const surfaceVectorField& field
+) const
+{
+    // Boundary field - flatten
+
+    List<vector> flat(mesh_.nBoundaryFaces(), Zero);
+
+    forAll(field.boundaryField(), patchi)
+    {
+        const polyPatch& pp = mesh_.boundaryMesh()[patchi];
+        const auto& pfld = field.boundaryField()[patchi];
+
+        if (!isA<emptyFvsPatchVectorField>(pfld))
+        {
+            SubList<vector>(flat, pp.size(), pp.offset()) = pfld;
+        }
+    }
+
+    return flat;
+}
+
+
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+Foam::vtk::surfaceFieldWriter::surfaceFieldWriter
+(
+    const fvMesh& mesh,
+    const vtk::outputOptions opts
+)
+:
+    vtk::fileWriter(vtk::fileTag::POLY_DATA, opts),
+    mesh_(mesh),
+    numberOfPoints_(0)
+{
+    opts_.append(false); // No append mode (horrible for streaming)
+    opts_.legacy(false); // Disallow legacy (inconvenient)
+}
+
+
+Foam::vtk::surfaceFieldWriter::surfaceFieldWriter
+(
+    const fvMesh& mesh,
+    const fileName& file,
+    bool parallel
+)
+:
+    surfaceFieldWriter(mesh)
+{
+    open(file, parallel);
+}
+
+
+Foam::vtk::surfaceFieldWriter::surfaceFieldWriter
+(
+    const fvMesh& mesh,
+    const vtk::outputOptions opts,
+    const fileName& file,
+    bool parallel
+)
+:
+    surfaceFieldWriter(mesh, opts)
+{
+    open(file, parallel);
+}
+
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+bool Foam::vtk::surfaceFieldWriter::beginFile(std::string title)
+{
+    if (title.size())
+    {
+        return vtk::fileWriter::beginFile(title);
+    }
+
+
+    // Provide Default title
+
+    if (legacy())
+    {
+        return vtk::fileWriter::beginFile("surfaceFields");
+    }
+
+
+    // XML (inline)
+
+    return vtk::fileWriter::beginFile
+    (
+        "surfaceFields "
+        "case='" + mesh_.time().globalCaseName()
+      + "' region='" + mesh_.name()
+      + "' time='" + mesh_.time().timeName()
+      + "' index='" + Foam::name(mesh_.time().timeIndex())
+      + "'"
+    );
+}
+
+
+bool Foam::vtk::surfaceFieldWriter::writeGeometry()
+{
+    enter_Piece();
+
+    // Output
+
+    const pointField& centres = mesh_.faceCentres();
+
+    // PointData for each face.
+    numberOfPoints_ = centres.size();
+
+    if (parallel_)
+    {
+        reduce(numberOfPoints_, sumOp<label>());
+    }
+
+    // <Piece>
+    if (format_)
+    {
+        format()
+            .tag
+            (
+                vtk::fileTag::PIECE,
+                fileAttr::NUMBER_OF_POINTS, numberOfPoints_
+            );
+    }
+
+
+    // <Point>
+    if (format_)
+    {
+        const uint64_t payLoad =
+            vtk::sizeofData<float,3>(numberOfPoints_);
+
+        format().tag(vtk::fileTag::POINTS)
+            .beginDataArray<float,3>(vtk::dataArrayAttr::POINTS);
+
+        format().writeSize(payLoad);
+    }
+
+    if (parallel_)
+    {
+        // Internal faces
+        vtk::writeListParallel
+        (
+            format_.ref(),
+            SubList<point>(centres, mesh_.nInternalFaces())
+        );
+
+        // Boundary faces
+        vtk::writeListParallel
+        (
+            format_.ref(),
+            SubList<point>(centres, mesh_.boundaryMesh().range())
+        );
+    }
+    else
+    {
+        // Non-parallel: use a normal write
+
+        vtk::writeList(format(), centres);
+    }
+
+    if (format_)
+    {
+        format().flush();
+
+        // Non-legacy
+        format()
+            .endDataArray()
+            .endTag(vtk::fileTag::POINTS);
+    }
+
+    return true;
+}
+
+
+bool Foam::vtk::surfaceFieldWriter::beginCellData(label nFields)
+{
+    // No legacy, no CellData
+    return enter_CellData(0, 0);
+}
+
+
+bool Foam::vtk::surfaceFieldWriter::beginPointData(label nFields)
+{
+    // No legacy
+    return enter_PointData(numberOfPoints_, 0);
+}
+
+
+void Foam::vtk::surfaceFieldWriter::write(const surfaceVectorField& field)
+{
+    if (isState(outputState::POINT_DATA))
+    {
+        ++nPointData_;
+    }
+    else
+    {
+        FatalErrorInFunction
+            << "Bad writer state (" << stateNames[state_]
+            << ") - should be (" << stateNames[outputState::POINT_DATA]
+            << ") for field " << field.name() << nl << endl
+            << exit(FatalError);
+    }
+
+    label nFaces = field.mesh().nFaces();
+
+    if (parallel_)
+    {
+        reduce(nFaces, sumOp<label>());
+    }
+
+    if (nFaces != numberOfPoints_)
+    {
+        FatalErrorInFunction
+            << "Expecting " << numberOfPoints_
+            << " faces, but found " << nFaces
+            << exit(FatalError);
+    }
+
+    if (format_)
+    {
+        // Non-legacy
+        const uint64_t payLoad =
+            vtk::sizeofData<float, 3>(nFaces);
+
+        format().beginDataArray<float, 3>(field.name());
+        format().writeSize(payLoad);
+    }
+
+
+    // Internal field
+    const SubList<vector> internal(field, mesh_.nInternalFaces());
+
+    // Boundary field (flattened)
+    auto boundary(flattenBoundary(field));
+
+
+    if (parallel_)
+    {
+        // Internal field
+        vtk::writeListParallel(format_.ref(), internal);
+
+        // Boundary field
+        vtk::writeListParallel(format_.ref(), boundary);
+    }
+    else
+    {
+        // Non-parallel
+
+        // Internal field
+        vtk::writeList(format_.ref(), internal);
+
+        // Boundary field
+        vtk::writeList(format_.ref(), boundary);
+    }
+
+
+    if (format_)
+    {
+        format().flush();
+        format().endDataArray();
+    }
+}
+
+
+// ************************************************************************* //
diff --git a/src/conversion/vtk/output/foamVtkSurfaceFieldWriter.H b/src/conversion/vtk/output/foamVtkSurfaceFieldWriter.H
new file mode 100644
index 00000000000..4238ec2a555
--- /dev/null
+++ b/src/conversion/vtk/output/foamVtkSurfaceFieldWriter.H
@@ -0,0 +1,186 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2011-2016 OpenFOAM Foundation
+     \\/     M anipulation  | Copyright (C) 2016-2018 OpenCFD Ltd.
+-------------------------------------------------------------------------------
+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::vtk::surfaceFieldWriter
+
+Description
+    Write surfaces fields (as PointData) in VTP format.
+    Legacy VTK format is intentionally not supported.
+
+    The file output is structured as HEAD, FIELD_DATA, PIECE followed by any
+    CELL_DATA or POINT_DATA. These states are tracked internally to help
+    detect logic errors.
+
+    The FieldData element is to be placed prior to writing any geometry
+    Piece. This moves the information to the front of the output file
+    for visibility and simplifies the logic.
+
+Note
+    Parallel output is combined into a single Piece without point merging,
+    which is similar to using multi-piece data sets, but allows more
+    convenient creation as a streaming process.
+    In the future, the duplicate points at processor connections
+    may be addressed using ghost points.
+
+SourceFiles
+    surfaceFieldWriter.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef foamVtkSurfaceFieldWriter_H
+#define foamVtkSurfaceFieldWriter_H
+
+#include "foamVtkFileWriter.H"
+#include "fvMesh.H"
+#include "pointField.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+namespace vtk
+{
+
+/*---------------------------------------------------------------------------*\
+                    Class vtk::surfaceFieldWriter Declaration
+\*---------------------------------------------------------------------------*/
+
+class surfaceFieldWriter
+:
+    public vtk::fileWriter
+{
+    // Private Member Data
+
+        //- Reference to the OpenFOAM mesh (or subset)
+        const fvMesh& mesh_;
+
+        //- The numer of field points for the current Piece
+        label numberOfPoints_;
+
+
+    // Private Member Functions
+
+        //- Flatten boundary field values into a contiguous list
+        List<vector> flattenBoundary(const surfaceVectorField& field) const;
+
+
+        //- No copy construct
+        surfaceFieldWriter(const surfaceFieldWriter&) = delete;
+
+        //- No copy assignment
+        void operator=(const surfaceFieldWriter&) = delete;
+
+
+public:
+
+    // Constructors
+
+        //- Construct from mesh (default format INLINE_BASE64)
+        surfaceFieldWriter
+        (
+            const fvMesh& mesh,
+            const vtk::outputOptions opts = vtk::formatType::INLINE_BASE64
+        );
+
+        //- Construct from mesh (default format INLINE_BASE64),
+        //- and open the file for writing.
+        //  The file name is with/without an extension.
+        surfaceFieldWriter
+        (
+            const fvMesh& mesh,
+            const fileName& file,
+            bool parallel = Pstream::parRun()
+        );
+
+        //- Construct from mesh and open the file for writing.
+        //  The file name is with/without an extension.
+        surfaceFieldWriter
+        (
+            const fvMesh& mesh,
+            const vtk::outputOptions opts,
+            const fileName& file,
+            bool parallel = Pstream::parRun()
+        );
+
+
+    //- Destructor
+    virtual ~surfaceFieldWriter() = default;
+
+
+    // Member Functions
+
+        //- File extension for current format type.
+        using vtk::fileWriter::ext;
+
+        //- File extension for given output type
+        inline static word ext(vtk::outputOptions)
+        {
+            // No legacy
+            return vtk::fileExtension[vtk::fileTag::POLY_DATA];
+        }
+
+
+        //- Write file header (non-collective)
+        //  \note Expected calling states: (OPENED).
+        virtual bool beginFile(std::string title = "");
+
+        //- Write cloud positions
+        //  Also writes the file header if not previously written.
+        //  \note Must be called prior to writing CellData or PointData
+        virtual bool writeGeometry();
+
+        //- Begin CellData output section for specified number of fields.
+        //  Must be called prior to writing any cell data fields.
+        //  \param nFields is the number of fields, which is required for
+        //      legacy format.
+        //  \note Expected calling states: (PIECE | POINT_DATA).
+        //
+        //  \return True if the state changed
+        virtual bool beginCellData(label nFields=0);
+
+        //- Begin PointData output section
+        //  Must be called prior to writing data fields.
+        //  \note Expected calling states: (PIECE).
+        //
+        //  \return True if the state changed
+        virtual bool beginPointData(label nFields=0);
+
+
+    // Write
+
+        //- Write field
+        void write(const surfaceVectorField& field);
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace vtk
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/conversion/vtk/output/foamVtkWriteSurfFields.C b/src/conversion/vtk/output/foamVtkWriteSurfFields.C
deleted file mode 100644
index 1742470dbf8..00000000000
--- a/src/conversion/vtk/output/foamVtkWriteSurfFields.C
+++ /dev/null
@@ -1,171 +0,0 @@
-/*---------------------------------------------------------------------------*\
-  =========                 |
-  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
-   \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2011-2016 OpenFOAM Foundation
-     \\/     M anipulation  | Copyright (C) 2016-2017 OpenCFD Ltd.
--------------------------------------------------------------------------------
-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 "foamVtkWriteSurfFields.H"
-#include "OFstream.H"
-#include "emptyFvsPatchFields.H"
-#include "fvsPatchFields.H"
-#include "surfaceFields.H"
-#include "foamVtkOutput.H"
-
-// * * * * * * * * * * * * * * * Global Functions  * * * * * * * * * * * * * //
-
-void Foam::vtk::writeSurfFields
-(
-    const fvMesh& mesh,
-    const fileName& baseName,
-    const vtk::outputOptions outOpts,
-    const UPtrList<const surfaceVectorField>& surfVectorFields
-)
-{
-    outputOptions opts(outOpts);
-    opts.append(false);  // No append supported
-
-    const bool legacy_(opts.legacy());
-
-    std::ofstream os(baseName + (legacy_ ? ".vtk" : ".vtp"));
-    autoPtr<vtk::formatter> format = opts.newFormatter(os);
-
-    // Same payload size for points and vector fields!
-    const int nCmpt(3);  // vector
-    const uint64_t payLoad(mesh.nFaces() * 3 * sizeof(float));
-
-    if (legacy_)
-    {
-        legacy::fileHeader(format(), "surfaceFields", vtk::fileTag::POLY_DATA);
-        legacy::beginPoints(os, mesh.nFaces());
-    }
-    else
-    {
-        // XML (inline)
-
-        format()
-            .xmlHeader()
-            .xmlComment("surfaceFields")
-            .beginVTKFile(vtk::fileTag::POLY_DATA, "0.1");
-
-        // Tricky - hide in beginPiece()
-        format()
-            .openTag(vtk::fileTag::PIECE)
-            .xmlAttr(vtk::fileAttr::NUMBER_OF_POINTS, mesh.nFaces())
-            .closeTag();
-
-        format().tag(vtk::fileTag::POINTS)
-            .openDataArray<float,3>(vtk::dataArrayAttr::POINTS)
-            .closeTag();
-    }
-
-    const pointField& fc = mesh.faceCentres();
-
-    format().writeSize(payLoad);
-    vtk::writeList(format(), fc);
-    format().flush();
-
-    if (!legacy_)
-    {
-        format()
-            .endDataArray()
-            .endTag(vtk::fileTag::POINTS);
-    }
-
-
-    // Fields
-    if (legacy_)
-    {
-        legacy::beginPointData
-        (
-            format(),
-            mesh.nFaces(),
-            surfVectorFields.size()
-        );
-    }
-    else
-    {
-        format().tag(vtk::fileTag::POINT_DATA);
-    }
-
-    // surfVectorFields
-    forAll(surfVectorFields, fieldi)
-    {
-        const auto& fld = surfVectorFields[fieldi];
-
-        if (legacy_)
-        {
-            legacy::floatField<nCmpt>(format(), fld.name(), mesh.nFaces());
-        }
-        else
-        {
-            format().openDataArray<float, nCmpt>(fld.name())
-                .closeTag();
-        }
-
-        format().writeSize(payLoad);
-
-        for (label facei=0; facei < mesh.nInternalFaces(); ++facei)
-        {
-            vtk::write(format(), fld[facei]);
-        }
-
-        forAll(fld.boundaryField(), patchi)
-        {
-            const fvPatch& pp = mesh.boundary()[patchi];
-            const auto& pf = fld.boundaryField()[patchi];
-
-            if (isA<emptyFvsPatchVectorField>(pf))
-            {
-                // Note: loop over polypatch size, not fvpatch size.
-                forAll(pp.patch(), i)
-                {
-                    vtk::write(format(), vector::zero);
-                }
-            }
-            else
-            {
-                vtk::writeList(format(), pf);
-            }
-        }
-
-        format().flush();
-
-        if (!legacy_)
-        {
-            format().endDataArray();
-        }
-    }
-
-    if (!legacy_)
-    {
-        format().endTag(vtk::fileTag::POINT_DATA);
-
-        // slight cheat. </Piece> too
-        format().endTag(vtk::fileTag::PIECE);
-
-        format().endTag(vtk::fileTag::POLY_DATA)
-            .endVTKFile();
-    }
-}
-
-
-// ************************************************************************* //
diff --git a/src/conversion/vtk/output/foamVtkWriteSurfFields.H b/src/conversion/vtk/output/foamVtkWriteSurfFields.H
deleted file mode 100644
index ff7e77b00fd..00000000000
--- a/src/conversion/vtk/output/foamVtkWriteSurfFields.H
+++ /dev/null
@@ -1,67 +0,0 @@
-/*---------------------------------------------------------------------------*\
-  =========                 |
-  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
-   \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2011-2016 OpenFOAM Foundation
-     \\/     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/>.
-
-InNamespace
-    Foam::vtk
-
-Description
-    Write surface fields as vectors
-
-SourceFiles
-    foamVtkWriteSurfFields.C
-
-\*---------------------------------------------------------------------------*/
-
-#ifndef foamVtkWriteSurfFields_H
-#define foamVtkWriteSurfFields_H
-
-#include "fvMesh.H"
-#include "surfaceMesh.H"
-#include "surfaceFieldsFwd.H"
-#include "foamVtkOutputOptions.H"
-
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
-
-namespace Foam
-{
-namespace vtk
-{
-
-//- Write surface vector fields
-void writeSurfFields
-(
-    const fvMesh& mesh,
-    const fileName& baseName,
-    const vtk::outputOptions outOpts,
-    const UPtrList<const surfaceVectorField>& surfVectorFields
-);
-
-} // End namespace vtk
-} // End namespace Foam
-
-
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
-
-#endif
-
-// ************************************************************************* //
-- 
GitLab