diff --git a/applications/utilities/mesh/conversion/Optional/Allwmake b/applications/utilities/mesh/conversion/Optional/Allwmake
index 19654fcf899028dc1f2e9b4f3e027c16e28d5082..a7a614f5cad3ab3c3253a0a6a5a0bc04433708fe 100755
--- a/applications/utilities/mesh/conversion/Optional/Allwmake
+++ b/applications/utilities/mesh/conversion/Optional/Allwmake
@@ -6,6 +6,7 @@ cd ${0%/*} || exit 1    # Run from this directory
 
 # Parse arguments for compilation (at least for error catching)
 . $WM_PROJECT_DIR/wmake/scripts/AllwmakeParseArguments
+
 # Get version info and arch-path
 . $WM_PROJECT_DIR/etc/config.sh/functions
 _foamSource $($WM_PROJECT_DIR/bin/foamEtcFile config.sh/ccmio)
diff --git a/applications/utilities/mesh/conversion/ccm/Allwmake b/applications/utilities/mesh/conversion/ccm/Allwmake
new file mode 100755
index 0000000000000000000000000000000000000000..7f59349f938d1226dedd2f21374253faba698a27
--- /dev/null
+++ b/applications/utilities/mesh/conversion/ccm/Allwmake
@@ -0,0 +1,17 @@
+#!/bin/sh
+cd ${0%/*} || exit 1    # Run from this directory
+
+# Parse arguments for compilation (at least for error catching)
+. $WM_PROJECT_DIR/wmake/scripts/AllwmakeParseArguments
+
+# Only build if libraries already exist
+if [ -e $FOAM_LIBBIN/libccm.so ]
+then
+    echo "Building optional ccm conversion components."
+    wmake $targetType ccmToFoam
+    wmake $targetType foamToCcm
+else
+    echo "Skipping optional ccm conversion components (no libccm.so)."
+fi
+
+#------------------------------------------------------------------------------
diff --git a/applications/utilities/mesh/conversion/ccm/ccmToFoam/Make/files b/applications/utilities/mesh/conversion/ccm/ccmToFoam/Make/files
new file mode 100644
index 0000000000000000000000000000000000000000..f23232f5e96fec5b699265e74e94cd2d964ea87b
--- /dev/null
+++ b/applications/utilities/mesh/conversion/ccm/ccmToFoam/Make/files
@@ -0,0 +1,3 @@
+ccmToFoam.C
+
+EXE = $(FOAM_APPBIN)/ccmToFoam
diff --git a/applications/utilities/mesh/conversion/ccm/ccmToFoam/Make/options b/applications/utilities/mesh/conversion/ccm/ccmToFoam/Make/options
new file mode 100644
index 0000000000000000000000000000000000000000..8b1f005a7833c6c646d3983fc9d54014120a747e
--- /dev/null
+++ b/applications/utilities/mesh/conversion/ccm/ccmToFoam/Make/options
@@ -0,0 +1,12 @@
+EXE_INC = \
+    -I$(LIB_SRC)/finiteVolume/lnInclude \
+    -I$(LIB_SRC)/meshTools/lnInclude \
+    -I$(LIB_SRC)/conversion/lnInclude \
+    -I$(LIB_SRC)/conversion/ccm/lnInclude \
+    -I$(LIB_SRC)/fileFormats/lnInclude
+
+EXE_LIBS = \
+    -lfiniteVolume \
+    -lgenericPatchFields \
+    -lmeshTools \
+    -lconversion -lccm
diff --git a/applications/utilities/mesh/conversion/ccm/ccmToFoam/ccmToFoam.C b/applications/utilities/mesh/conversion/ccm/ccmToFoam/ccmToFoam.C
new file mode 100644
index 0000000000000000000000000000000000000000..5b0a4cf660521e2a28e9449e7c0954331e83d244
--- /dev/null
+++ b/applications/utilities/mesh/conversion/ccm/ccmToFoam/ccmToFoam.C
@@ -0,0 +1,314 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2016 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/>.
+
+Application
+    ccmToFoam
+
+Group
+    grpMeshConversionUtilities
+
+Description
+    Reads CCM files as written by PROSTAR/STARCCM and writes an
+    OPENFOAM polyMesh.
+
+Usage
+    \b ccmToFoam [OPTION] ccmMesh
+
+    Options:
+      - \par -ascii
+        Write in ASCII format instead of binary
+
+      - \par -export
+        re-export mesh in CCM format for post-processing
+
+      - \par -list
+        List some information about the geometry
+
+      - \par -name \<name\>
+        Provide alternative base name for export. Default is <tt>meshExport</tt>.
+
+      - \par -combine
+        Combine identically named patches
+
+      - \par -noBaffles
+        Remove any baffles by merging the faces.
+
+      - \par -merge
+        Merge in-place interfaces
+
+      - \par -numbered
+        Use numbered patch/zone (not names) directly from ccm ids.
+
+      - \par -remap \<name\>
+        use specified remapping dictionary instead of <tt>constant/remapping</tt>
+
+      - \par -scale \<factor\>
+        Specify an alternative geometry scaling factor.
+        The default is \b 1 (no scaling).
+
+      - \par -solids
+        Treat any solid cells present just like fluid cells.
+        The default is to remove them.
+
+Note
+    - sub-domains (fluid | solid | porosity) are stored as separate domains
+      within the CCM file. These are merged together to form a single mesh.
+    - baffles are written as interfaces for later use
+
+See also
+    Foam::ccm::reader for more information about the File Locations
+
+\*---------------------------------------------------------------------------*/
+
+#include "argList.H"
+#include "Time.H"
+#include "ccm.H"
+#include "regionSplit.H"
+
+using namespace Foam;
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+int main(int argc, char *argv[])
+{
+    argList::addNote
+    (
+        "Reads CCM files as written by PROSTAR/STARCCM and writes an"
+        " OPENFOAM polyMesh."
+    );
+
+    argList::noParallel();
+    argList::validArgs.append("ccmMesh");
+    argList::addBoolOption
+    (
+        "ascii",
+        "write in ASCII format instead of binary"
+    );
+    argList::addBoolOption
+    (
+        "export",
+        "re-export mesh in CCM format for post-processing"
+    );
+    argList::addBoolOption
+    (
+        "list",
+        "list some information about the geometry"
+    );
+    argList::addOption
+    (
+        "remap",
+        "name",
+        "use specified remapping dictionary instead of <constant/remapping>"
+    );
+    argList::addOption
+    (
+        "name",
+        "name",
+        "provide alternative base name when re-exporting (implies -export). "
+        "Default is <meshExport>."
+    );
+    argList::addBoolOption
+    (
+        "combine",
+        "combine identically named patches"
+    );
+    argList::addBoolOption
+    (
+        "noBaffles",
+        "remove any baffles by merging the faces"
+    );
+    argList::addBoolOption
+    (
+        "merge",
+        "merge in-place interfaces"
+    );
+    argList::addBoolOption
+    (
+        "numbered",
+        "use numbered names (eg, patch_0, zone_0) only"
+    );
+    argList::addOption
+    (
+        "scale",
+        "scale",
+        "geometry scaling factor - default is 1 (ie, no scaling)"
+    );
+    argList::addBoolOption
+    (
+        "withSolid",
+        "treat any solid cells present just like fluid cells. "
+        "the default is to remove them."
+    );
+
+    argList args(argc, argv);
+    Time runTime(args.rootPath(), args.caseName());
+    runTime.functionObjects().off();
+
+    const bool optList = args.optionFound("list");
+
+    // exportName only has a size when export is in effect
+    fileName exportName;
+    if (args.optionReadIfPresent("name", exportName))
+    {
+        const word ext = exportName.ext();
+        // strip erroneous extension (.ccm, .ccmg, .ccmp)
+        if (ext == "ccm" || ext == "ccmg" || ext == "ccmp")
+        {
+            exportName = exportName.lessExt();
+        }
+    }
+    else if (args.optionFound("export"))
+    {
+        exportName = ccm::writer::defaultMeshName;
+        if (args.optionFound("case"))
+        {
+            exportName += '-' + args.globalCaseName();
+        }
+    }
+
+    // By default, no scaling
+    const scalar scaleFactor = args.optionLookupOrDefault("scale", 1.0);
+
+    // Default to binary output, unless otherwise specified
+    const IOstream::streamFormat format =
+    (
+        args.optionFound("ascii")
+      ? IOstream::ASCII
+      : IOstream::BINARY
+    );
+
+    // Increase the precision of the points data
+    IOstream::defaultPrecision(max(10u, IOstream::defaultPrecision()));
+
+
+    // Read control options
+    // ~~~~~~~~~~~~~~~~~~~~
+
+    ccm::reader::options rOpts;
+    rOpts.removeBaffles(args.optionFound("noBaffles"));
+    rOpts.mergeInterfaces(args.optionFound("merge"));
+
+    if (args.optionFound("numbered"))
+    {
+        rOpts.useNumberedNames(true);
+    }
+    else if (args.optionFound("combine"))
+    {
+        rOpts.combineBoundaries(true);
+    }
+
+    if (args.optionFound("solids"))
+    {
+        Info<< "treating solids like fluids" << endl;
+        rOpts.keepSolid(true);
+    }
+    else
+    {
+        rOpts.keepSolid(false);
+    }
+
+    // CCM reader for reading geometry/solution
+    ccm::reader reader(args[1], rOpts);
+
+    // list the geometry information
+    if (optList)
+    {
+        Info<< "mesh geometry information:" << endl;
+        if (reader.hasGeometry())
+        {
+            Info<< nl << "cellTable:" << reader.cellTableInfo()
+                << nl << "boundaryRegion:" << reader.boundaryTableInfo()
+                << nl << "interfaces:" << reader.interfaceDefinitionsInfo()
+                << endl;
+
+            if
+            (
+                args.optionFound("remap")
+              ? reader.remapMeshInfo(runTime, args["remap"])
+              : reader.remapMeshInfo(runTime)
+            )
+            {
+                Info<< nl
+                    << "Remapped cellTable:" << reader.cellTableInfo() << nl
+                    << "Remapped boundaryRegion:" << reader.boundaryTableInfo()
+                    << endl;
+            }
+        }
+        else
+        {
+            Info<< "NONE" << endl;
+        }
+
+        return 0;
+    }
+    else if (reader.readGeometry(scaleFactor))
+    {
+        autoPtr<polyMesh> mesh =
+        (
+            args.optionFound("remap")
+          ? reader.mesh(runTime, args["remap"])
+          : reader.mesh(runTime)
+        );
+
+        // report mesh bounding box information
+        Info<< nl << "Bounding box size: " << mesh().bounds().span() << nl;
+
+        // check number of regions
+        regionSplit rs(mesh);
+
+        Info<< "Number of regions: " << rs.nRegions();
+        if (rs.nRegions() == 1)
+        {
+            Info<< " (OK)." << nl;
+        }
+        else
+        {
+            Info<< nl << nl
+                << "**************************************************" << nl
+                << "**  WARNING: the mesh has disconnected regions  **" << nl
+                << "**************************************************" << nl;
+        }
+        Info<< endl;
+        reader.writeMesh(mesh, format);
+
+        // exportName only has a size when export is in effect
+        if (exportName.size())
+        {
+            const fileName geomName = exportName + ".ccmg";
+            Info<< nl << "Re-exporting geometry as " << geomName << nl;
+            ccm::writer(geomName, mesh).writeGeometry();
+        }
+    }
+    else
+    {
+        FatalErrorIn("ccmToFoam")
+            << "could not read geometry"
+            << exit(FatalError);
+    }
+
+    Info<< "\nEnd\n" << endl;
+
+    return 0;
+}
+
+// ************************************************************************* //
diff --git a/applications/utilities/mesh/conversion/ccm/foamToCcm/Make/files b/applications/utilities/mesh/conversion/ccm/foamToCcm/Make/files
new file mode 100644
index 0000000000000000000000000000000000000000..dd5d32f698998c0c1b955605c15096a6deb47c1b
--- /dev/null
+++ b/applications/utilities/mesh/conversion/ccm/foamToCcm/Make/files
@@ -0,0 +1,3 @@
+foamToCcm.C
+
+EXE = $(FOAM_APPBIN)/foamToCcm
diff --git a/applications/utilities/mesh/conversion/ccm/foamToCcm/Make/options b/applications/utilities/mesh/conversion/ccm/foamToCcm/Make/options
new file mode 100644
index 0000000000000000000000000000000000000000..df1a41bde0ac5a0fa70a0ee9372ab01cdd4c9b9d
--- /dev/null
+++ b/applications/utilities/mesh/conversion/ccm/foamToCcm/Make/options
@@ -0,0 +1,10 @@
+EXE_INC = \
+    -I$(LIB_SRC)/finiteVolume/lnInclude \
+    -I$(LIB_SRC)/conversion/lnInclude \
+    -I$(LIB_SRC)/conversion/ccm/lnInclude \
+    -I$(LIB_SRC)/fileFormats/lnInclude
+
+EXE_LIBS = \
+    -lfiniteVolume \
+    -lgenericPatchFields \
+    -lconversion -lccm
diff --git a/applications/utilities/mesh/conversion/ccm/foamToCcm/foamToCcm.C b/applications/utilities/mesh/conversion/ccm/foamToCcm/foamToCcm.C
new file mode 100644
index 0000000000000000000000000000000000000000..9d64cc09b063abf3e6fb862e797fff8d021402a6
--- /dev/null
+++ b/applications/utilities/mesh/conversion/ccm/foamToCcm/foamToCcm.C
@@ -0,0 +1,272 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2016 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/>.
+
+Application
+    foamToCcm
+
+Group
+    grpMeshConversionUtilities
+
+Description
+    Translates OPENFOAM mesh and/or results to CCM format
+
+Usage
+    \b foamToCcm [OPTION]
+
+    Options:
+      - \par -mesh
+        convert mesh only to CCM format
+
+      - \par -name \<name\>
+        Provide alternative base name. Default is <tt>meshExport</tt>.
+
+      - \par -overwrite
+        No backup of existing output files.
+
+      - \par -remap \<name\>
+        use specified remapping dictionary instead of <tt>constant/remapping</tt>
+
+      - \par -results
+        convert results only to CCM format
+
+Note
+    - No parallel data
+    - No Lagrangian elements
+    - the -noZero time option can be useful to avoid the often incomplete
+      initial conditions (missing useful calculated values)
+
+See also
+    Foam::ccm::writer for information about the
+    <tt>constant/remapping</tt> file.
+
+\*---------------------------------------------------------------------------*/
+
+#include "argList.H"
+#include "timeSelector.H"
+
+#include "volFields.H"
+#include "OFstream.H"
+#include "IOobjectList.H"
+#include "scalarIOField.H"
+#include "tensorIOField.H"
+
+#include "ccm.H"
+
+using namespace Foam;
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+// Main program:
+
+int main(int argc, char *argv[])
+{
+    argList::addNote
+    (
+        "Translate OPENFOAM data to CCM format"
+    );
+
+    Foam::timeSelector::addOptions();
+    argList::noParallel();
+    argList::addBoolOption
+    (
+        "mesh",
+        "convert mesh only"
+    );
+    argList::addOption
+    (
+        "name",
+        "name",
+        "provide alternative base name. Default is <meshExport>."
+    );
+    argList::addBoolOption
+    (
+        "overwrite",
+        "no backup of existing output files"
+    );
+    argList::addOption
+    (
+        "remap",
+        "name",
+        "use specified remapping dictionary instead of <constant/remapping>"
+    );
+    argList::addBoolOption
+    (
+        "results",
+        "convert results only"
+    );
+
+    #include "setRootCase.H"
+    #include "createTime.H"
+    runTime.functionObjects().off();
+
+    // get times list
+    instantList timeDirs = Foam::timeSelector::select0(runTime, args);
+
+    const bool optMesh      = args.optionFound("mesh");
+    const bool optResults   = args.optionFound("results");
+    const bool optOverwrite = args.optionFound("overwrite");
+
+    fileName exportName = ccm::writer::defaultMeshName;
+    if (args.optionReadIfPresent("name", exportName))
+    {
+        const word ext = exportName.ext();
+        // strip erroneous extension (.ccm, .ccmg, .ccmp)
+        if (ext == "ccm" || ext == "ccmg" || ext == "ccmp")
+        {
+            exportName = exportName.lessExt();
+        }
+    }
+    else if (args.optionFound("case"))
+    {
+        exportName += '-' + args.globalCaseName();
+    }
+
+    if (optMesh && optResults)
+    {
+        Warning
+            << "\n-mesh and -results options are mutually exclusive\n"
+            << endl;
+        args.printUsage();
+        FatalError.exit();
+    }
+
+//     // skip over time=0, unless some other time option has been specified
+//     if
+//     (
+//         !args.optionFound("zeroTime")
+//      && !args.optionFound("time")
+//      && !args.optionFound("latestTime")
+//      && Times.size() > 2
+//     )
+//     {
+//         startTime = 2;
+//     }
+//
+//    runTime.setTime(Times[startTime], startTime);
+
+    runTime.setTime(timeDirs[0], 0);
+    if (optMesh)
+    {
+        // convert mesh only
+        #include "createPolyMesh.H"
+
+        forAll(timeDirs, timeI)
+        {
+            runTime.setTime(timeDirs[timeI], timeI);
+
+            #include "getTimeIndex.H"
+
+            if (timeI == 0)
+            {
+                ccm::writer writer
+                (
+                    exportName + ".ccmg",
+                    mesh,
+                    !optOverwrite
+                );
+                writer.writeGeometry();
+            }
+            else if (mesh.moving())
+            {
+                ccm::writer writer
+                (
+                    exportName + ".ccmg_" + timeName,
+                    mesh,
+                    !optOverwrite
+                );
+                writer.writeGeometry();
+            }
+        }
+    }
+    else
+    {
+        // convert fields with or without converting mesh
+        #include "createMesh.H"
+
+        // #include "checkHasMovingMesh.H"
+        // #include "checkHasLagrangian.H"
+
+        IOobjectList objects(mesh, timeDirs[timeDirs.size()-1].name());
+        //  IOobjectList sprayObjects(mesh, Times[Times.size()-1].name(), "lagrangian");
+
+        forAll(timeDirs, timeI)
+        {
+            runTime.setTime(timeDirs[timeI], timeI);
+
+            #include "getTimeIndex.H"
+
+            Info<< "has "
+                << mesh.nCells() << " cells, "
+                << mesh.nPoints() << " points, "
+                << mesh.boundaryMesh().size() << " patches"
+                << endl;
+
+            if (!optResults)
+            {
+                if (timeI == 0)
+                {
+                    ccm::writer writer
+                    (
+                        exportName + ".ccmg",
+                        mesh,
+                        !optOverwrite
+                    );
+                    writer.writeGeometry();
+                }
+                else if (mesh.moving())
+                {
+                    ccm::writer writer
+                    (
+                        exportName + ".ccmg_" + timeName,
+                        mesh,
+                        !optOverwrite
+                    );
+                    writer.writeGeometry();
+                }
+            }
+
+            ccm::writer writer
+            (
+                exportName + ".ccmp_" + timeName,
+                mesh,
+                !optOverwrite
+            );
+            // writer.setTopologyFile(exportName + ".ccmg");
+            Info<< "writing solution:";
+            if (args.optionFound("remap"))
+            {
+                writer.writeSolution(objects, args["remap"]);
+            }
+            else
+            {
+                writer.writeSolution(objects);
+            }
+        }
+    }
+
+    Info<< "\nEnd\n" << endl;
+    return 0;
+}
+
+
+// ************************************************************************* //
diff --git a/applications/utilities/mesh/conversion/ccm/foamToCcm/getTimeIndex.H b/applications/utilities/mesh/conversion/ccm/foamToCcm/getTimeIndex.H
new file mode 100644
index 0000000000000000000000000000000000000000..4547e06457e9faf3f96bc5c0fc35bc28cdd7d1dc
--- /dev/null
+++ b/applications/utilities/mesh/conversion/ccm/foamToCcm/getTimeIndex.H
@@ -0,0 +1,49 @@
+// Read time index from */uniform/time, but treat 0 and constant specially
+
+    word timeName = "0";
+
+    if
+    (
+        runTime.timeName() != runTime.constant()
+     && runTime.timeName() != "0"
+    )
+    {
+        IOobject io
+        (
+            "time",
+            runTime.timeName(),
+            "uniform",
+            runTime,
+            IOobject::READ_IF_PRESENT,
+            IOobject::NO_WRITE,
+            false
+        );
+
+        if (io.typeHeaderOk<IOdictionary>(true))
+        {
+            IOdictionary timeObject
+            (
+                IOobject
+                (
+                    "time",
+                    runTime.timeName(),
+                    "uniform",
+                    runTime,
+                    IOobject::MUST_READ,
+                    IOobject::NO_WRITE,
+                    false
+                )
+            );
+
+            label index;
+            timeObject.lookup("index") >> index;
+            timeName = Foam::name(index);
+        }
+        else
+        {
+            timeName = runTime.timeName();
+        }
+    }
+
+    Info<< "\nTime [" << timeName << "] = " << runTime.timeName() << nl;
+
diff --git a/applications/utilities/mesh/conversion/fireToFoam/Make/files b/applications/utilities/mesh/conversion/fireToFoam/Make/files
new file mode 100644
index 0000000000000000000000000000000000000000..a8734855d05090b2b37c953497224a46ac205a5e
--- /dev/null
+++ b/applications/utilities/mesh/conversion/fireToFoam/Make/files
@@ -0,0 +1,3 @@
+fireToFoam.C
+
+EXE = $(FOAM_APPBIN)/fireToFoam
diff --git a/applications/utilities/mesh/conversion/fireToFoam/Make/options b/applications/utilities/mesh/conversion/fireToFoam/Make/options
new file mode 100644
index 0000000000000000000000000000000000000000..e3af2fe661b0e1a9cc1f77fbe4b08a9923d2bd35
--- /dev/null
+++ b/applications/utilities/mesh/conversion/fireToFoam/Make/options
@@ -0,0 +1,7 @@
+EXE_INC = \
+    -I$(LIB_SRC)/meshTools/lnInclude \
+    -I$(LIB_SRC)/conversion/lnInclude \
+    -I$(LIB_SRC)/fileFormats/lnInclude
+
+EXE_LIBS = \
+    -lconversion
diff --git a/applications/utilities/mesh/conversion/fireToFoam/fireToFoam.C b/applications/utilities/mesh/conversion/fireToFoam/fireToFoam.C
new file mode 100644
index 0000000000000000000000000000000000000000..0f7c32c701dea259c602298d4dc933c4c0eef449
--- /dev/null
+++ b/applications/utilities/mesh/conversion/fireToFoam/fireToFoam.C
@@ -0,0 +1,123 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2016 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/>.
+
+Application
+    fireToFoam
+
+Group
+    grpMeshConversionUtilities
+
+Description
+    Converts an AVL/FIRE polyhedral mesh to OPENFOAM
+
+Usage
+    \b fireToFoam [OPTION] firePolyMesh
+
+    Options:
+
+      - \param -ascii
+        Write in ASCII format instead of binary
+
+      - \par -check
+        Perform edge checking
+
+      - \par -scale \<factor\>
+        Specify an alternative geometry scaling factor.
+        The default is \b 1 (no scaling).
+
+\*---------------------------------------------------------------------------*/
+
+#include "argList.H"
+#include "Time.H"
+#include "FIREMeshReader.H"
+#include "checkFireEdges.H"
+
+using namespace Foam;
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+int main(int argc, char *argv[])
+{
+    argList::addNote
+    (
+        "Convert AVL/FIRE polyhedral mesh to OPENFOAM format"
+    );
+
+    argList::noParallel();
+    argList::validArgs.append("firePolyMesh");
+    argList::addBoolOption
+    (
+        "ascii",
+        "write in ASCII format instead of binary"
+    );
+    argList::addBoolOption
+    (
+        "check",
+        "perform edge checking as well"
+    );
+    argList::addOption
+    (
+        "scale",
+        "scale",
+        "geometry scaling factor - default is 1 (no scaling)"
+    );
+
+
+    argList args(argc, argv);
+    Time runTime(args.rootPath(), args.caseName());
+
+
+    // Binary output, unless otherwise specified
+    const IOstream::streamFormat format =
+    (
+        args.optionFound("ascii")
+      ? IOstream::ASCII
+      : IOstream::BINARY
+    );
+
+    // increase the precision of the points data
+    IOstream::defaultPrecision(max(10u, IOstream::defaultPrecision()));
+
+
+    fileFormats::FIREMeshReader reader
+    (
+        args[1],
+        // Default no scaling
+        args.optionLookupOrDefault("scale", 1.0)
+    );
+
+
+    autoPtr<polyMesh> mesh = reader.mesh(runTime);
+    reader.writeMesh(mesh(), format);
+
+
+    if (args.optionFound("check"))
+    {
+        checkFireEdges(mesh());
+    }
+
+    Info<< "\nEnd\n" << endl;
+    return 0;
+}
+
+// ************************************************************************* //
diff --git a/applications/utilities/mesh/conversion/foamToFireMesh/Make/files b/applications/utilities/mesh/conversion/foamToFireMesh/Make/files
new file mode 100644
index 0000000000000000000000000000000000000000..0cc67f4f557a5542705ef177178f54e44518a35e
--- /dev/null
+++ b/applications/utilities/mesh/conversion/foamToFireMesh/Make/files
@@ -0,0 +1,3 @@
+foamToFireMesh.C
+
+EXE = $(FOAM_APPBIN)/foamToFireMesh
diff --git a/applications/utilities/mesh/conversion/foamToFireMesh/Make/options b/applications/utilities/mesh/conversion/foamToFireMesh/Make/options
new file mode 100644
index 0000000000000000000000000000000000000000..e3af2fe661b0e1a9cc1f77fbe4b08a9923d2bd35
--- /dev/null
+++ b/applications/utilities/mesh/conversion/foamToFireMesh/Make/options
@@ -0,0 +1,7 @@
+EXE_INC = \
+    -I$(LIB_SRC)/meshTools/lnInclude \
+    -I$(LIB_SRC)/conversion/lnInclude \
+    -I$(LIB_SRC)/fileFormats/lnInclude
+
+EXE_LIBS = \
+    -lconversion
diff --git a/applications/utilities/mesh/conversion/foamToFireMesh/foamToFireMesh.C b/applications/utilities/mesh/conversion/foamToFireMesh/foamToFireMesh.C
new file mode 100644
index 0000000000000000000000000000000000000000..87ddd4dff938fdef1f9d626dbd180d92978db89d
--- /dev/null
+++ b/applications/utilities/mesh/conversion/foamToFireMesh/foamToFireMesh.C
@@ -0,0 +1,136 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2016 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/>.
+
+Application
+    foamToFireMesh
+
+Description
+    Reads an OpenFOAM mesh and writes an AVL/FIRE fpma format
+
+Usage
+    \b foamToFireMesh [OPTION]
+
+    Options:
+      - \par -ascii
+        Write in ASCII format instead of binary
+
+      - \par -scale \<factor\>
+        Specify an alternative geometry scaling factor.
+        The default is \b 1 (ie, no scaling).
+
+See also
+    Foam::cellTable, Foam::meshWriter and Foam::fileFormats::FIREMeshWriter
+
+\*---------------------------------------------------------------------------*/
+
+#include "argList.H"
+#include "timeSelector.H"
+#include "Time.H"
+#include "polyMesh.H"
+#include "FIREMeshWriter.H"
+
+using namespace Foam;
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+// Main program:
+
+int main(int argc, char *argv[])
+{
+    argList::addNote
+    (
+        "read OpenFOAM mesh and write an AVL/FIRE fpma format"
+    );
+    argList::noParallel();
+    timeSelector::addOptions();
+
+    argList::addBoolOption
+    (
+        "ascii",
+        "write in ASCII format instead of binary"
+    );
+    argList::addOption
+    (
+        "scale",
+        "factor",
+        "geometry scaling factor - default is 1 (none)"
+    );
+
+    #include "setRootCase.H"
+    #include "createTime.H"
+
+    instantList timeDirs = timeSelector::select0(runTime, args);
+
+    fileName exportName = meshWriter::defaultMeshName;
+    if (args.optionFound("case"))
+    {
+        exportName += '-' + args.globalCaseName();
+    }
+
+
+    // write control options
+    // ~~~~~~~~~~~~~~~~~~~~~
+    fileFormats::FIREMeshWriter::binary = !args.optionFound("ascii");
+
+    // default: rescale from [m] to [mm]
+    scalar scaleFactor = 1;
+    if (args.optionReadIfPresent("scale", scaleFactor))
+    {
+        if (scaleFactor <= 0)
+        {
+            scaleFactor = 1;
+        }
+    }
+
+    #include "createPolyMesh.H"
+
+    forAll(timeDirs, timeI)
+    {
+        runTime.setTime(timeDirs[timeI], timeI);
+
+        #include "getTimeIndex.H"
+
+        polyMesh::readUpdateState state = mesh.readUpdate();
+
+        if (!timeI || state != polyMesh::UNCHANGED)
+        {
+            fileFormats::FIREMeshWriter writer(mesh, scaleFactor);
+
+            fileName meshName(exportName);
+            if (state != polyMesh::UNCHANGED)
+            {
+                meshName += '_' + runTime.timeName();
+            }
+
+            writer.write(meshName);
+        }
+
+        Info<< nl << endl;
+    }
+
+    Info<< "End\n" << endl;
+
+    return 0;
+}
+
+
+// ************************************************************************* //
diff --git a/applications/utilities/mesh/conversion/foamToFireMesh/getTimeIndex.H b/applications/utilities/mesh/conversion/foamToFireMesh/getTimeIndex.H
new file mode 100644
index 0000000000000000000000000000000000000000..11b8993f28bd3efd8f8efd44d2880271d3a0419e
--- /dev/null
+++ b/applications/utilities/mesh/conversion/foamToFireMesh/getTimeIndex.H
@@ -0,0 +1,51 @@
+// Read time index from */uniform/time, but treat 0 and constant specially
+
+    word timeName = "0";
+
+    if
+    (
+        runTime.timeName() != runTime.constant()
+     && runTime.timeName() != "0"
+    )
+    {
+        IOobject io
+        (
+            "time",
+            runTime.timeName(),
+            "uniform",
+            runTime,
+            IOobject::READ_IF_PRESENT,
+            IOobject::NO_WRITE,
+            false
+        );
+
+        if (io.typeHeaderOk<IOdictionary>(true))
+        {
+            IOdictionary timeObject
+            (
+                IOobject
+                (
+                    "time",
+                    runTime.timeName(),
+                    "uniform",
+                    runTime,
+                    IOobject::MUST_READ_IF_MODIFIED,
+                    IOobject::NO_WRITE,
+                    false
+                )
+            );
+
+            label index;
+            timeObject.lookup("index") >> index;
+            timeName = Foam::name(index);
+        }
+        else
+        {
+            timeName = runTime.timeName();
+            // Info<< "skip ... missing entry " << io.objectPath() << endl;
+            // continue;
+        }
+    }
+
+    Info<< "\nTime [" << timeName << "] = " << runTime.timeName() << nl;
+
diff --git a/applications/utilities/mesh/conversion/foamToStarMesh/foamToStarMesh.C b/applications/utilities/mesh/conversion/foamToStarMesh/foamToStarMesh.C
index b4692d201ac15c2d2329fe8867de35766d1b7771..f8fe44f19fc05c81ff81eaa76bf2032e73a89d76 100644
--- a/applications/utilities/mesh/conversion/foamToStarMesh/foamToStarMesh.C
+++ b/applications/utilities/mesh/conversion/foamToStarMesh/foamToStarMesh.C
@@ -94,15 +94,9 @@ int main(int argc, char *argv[])
         exportName += '-' + args.globalCaseName();
     }
 
-    // default: rescale from [m] to [mm]
-    scalar scaleFactor = 1000;
-    if (args.optionReadIfPresent("scale", scaleFactor))
-    {
-        if (scaleFactor <= 0)
-        {
-            scaleFactor = 1;
-        }
-    }
+    // Default rescale from [m] to [mm]
+    const scalar scaleFactor = args.optionLookupOrDefault("scale", 1000.0);
+    const bool  writeBndFile = !args.optionFound("noBnd");
 
     #include "createPolyMesh.H"
 
@@ -116,12 +110,12 @@ int main(int argc, char *argv[])
 
         if (!timeI || state != polyMesh::UNCHANGED)
         {
-            fileFormats::STARCDMeshWriter writer(mesh, scaleFactor);
-
-            if (args.optionFound("noBnd"))
-            {
-                writer.noBoundary();
-            }
+            fileFormats::STARCDMeshWriter writer
+            (
+                mesh,
+                scaleFactor,
+                writeBndFile
+            );
 
             fileName meshName(exportName);
             if (state != polyMesh::UNCHANGED)
diff --git a/applications/utilities/mesh/conversion/star4ToFoam/star4ToFoam.C b/applications/utilities/mesh/conversion/star4ToFoam/star4ToFoam.C
index af57977d788beb9b155e978a82be2f61d8cb11c0..65261399d076de189a31b6c7fbb206ac1a6f36ca 100644
--- a/applications/utilities/mesh/conversion/star4ToFoam/star4ToFoam.C
+++ b/applications/utilities/mesh/conversion/star4ToFoam/star4ToFoam.C
@@ -48,7 +48,7 @@ Usage
 Note
     Baffles are written as interfaces for later use
 
-See Also
+See also
     Foam::cellTable, Foam::meshReader and Foam::fileFormats::STARCDMeshReader
 
 \*---------------------------------------------------------------------------*/
@@ -88,32 +88,35 @@ int main(int argc, char *argv[])
         "retain solid cells and treat them like fluid cells"
     );
 
+
     argList args(argc, argv);
     Time runTime(args.rootPath(), args.caseName());
 
-    // default rescale from [mm] to [m]
-    scalar scaleFactor = args.optionLookupOrDefault("scale", 0.001);
-    if (scaleFactor <= 0)
-    {
-        scaleFactor = 1;
-    }
-
-    fileFormats::STARCDMeshReader::keepSolids = args.optionFound("solids");
-
-    // default to binary output, unless otherwise specified
-    IOstream::streamFormat format = IOstream::BINARY;
-    if (args.optionFound("ascii"))
-    {
-        format = IOstream::ASCII;
-    }
+    // Binary output, unless otherwise specified
+    const IOstream::streamFormat format =
+    (
+        args.optionFound("ascii")
+      ? IOstream::ASCII
+      : IOstream::BINARY
+    );
 
-    // increase the precision of the points data
+    // Increase the precision of the points data
     IOstream::defaultPrecision(max(10u, IOstream::defaultPrecision()));
 
-    // remove extensions and/or trailing '.'
+
+    // Remove extensions and/or trailing '.'
     const fileName prefix = fileName(args[1]).lessExt();
 
-    fileFormats::STARCDMeshReader reader(prefix, runTime, scaleFactor);
+
+    fileFormats::STARCDMeshReader reader
+    (
+        prefix,
+        runTime,
+        // Default rescale from [mm] to [m]
+        args.optionLookupOrDefault("scale", 0.001),
+        args.optionFound("solids")
+    );
+
 
     autoPtr<polyMesh> mesh = reader.mesh(runTime);
     reader.writeMesh(mesh, format);
diff --git a/applications/utilities/postProcessing/dataConversion/foamToEnsight/Make/files b/applications/utilities/postProcessing/dataConversion/foamToEnsight/Make/files
index 044aa6ae43a0911e12dbc805adeeb202ad3ad864..a474a8fcfc5ca8a0593a612b2f5aceb2246ead9b 100644
--- a/applications/utilities/postProcessing/dataConversion/foamToEnsight/Make/files
+++ b/applications/utilities/postProcessing/dataConversion/foamToEnsight/Make/files
@@ -1,6 +1,4 @@
 ensightOutputCloud.C
-meshSubsetHelper.C
-
 foamToEnsight.C
 
 EXE = $(FOAM_APPBIN)/foamToEnsight
diff --git a/applications/utilities/postProcessing/dataConversion/foamToEnsight/foamToEnsight.C b/applications/utilities/postProcessing/dataConversion/foamToEnsight/foamToEnsight.C
index 1d0f6c3beb3d0e08ec4318c1af86605ad96ee29b..5080d840ebc1e3da0ab29f58a411f2c9a43da133 100644
--- a/applications/utilities/postProcessing/dataConversion/foamToEnsight/foamToEnsight.C
+++ b/applications/utilities/postProcessing/dataConversion/foamToEnsight/foamToEnsight.C
@@ -291,7 +291,7 @@ int main(int argc, char *argv[])
             << mesh.boundaryMesh()[0].name() << ")"
             << endl;
     }
-    meshSubsetHelper myMesh(mesh, cellZoneName);
+    meshSubsetHelper myMesh(mesh, meshSubsetHelper::ZONE, cellZoneName);
 
     //
     // Open new ensight case file, initialize header etc.
diff --git a/applications/utilities/postProcessing/dataConversion/foamToTecplot360/Make/files b/applications/utilities/postProcessing/dataConversion/foamToTecplot360/Make/files
index 270e84931b599af46bf3a9db82024e3202dd5006..3eb58f17730826790e60e9c50a6ecc12f9c81c13 100644
--- a/applications/utilities/postProcessing/dataConversion/foamToTecplot360/Make/files
+++ b/applications/utilities/postProcessing/dataConversion/foamToTecplot360/Make/files
@@ -1,5 +1,4 @@
 tecplotWriter.C
-vtkMesh.C
 foamToTecplot360.C
 
 EXE = $(FOAM_APPBIN)/foamToTecplot360
diff --git a/applications/utilities/postProcessing/dataConversion/foamToTecplot360/foamToTecplot360.C b/applications/utilities/postProcessing/dataConversion/foamToTecplot360/foamToTecplot360.C
index 6ec9aff2772060d06ea92c5c07e8859aa08428d1..5dbe1581cad2d7edc6860575c82858169bfbf7c6 100644
--- a/applications/utilities/postProcessing/dataConversion/foamToTecplot360/foamToTecplot360.C
+++ b/applications/utilities/postProcessing/dataConversion/foamToTecplot360/foamToTecplot360.C
@@ -3,7 +3,7 @@
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
     \\  /    A nd           | Copyright (C) 2011-2016 OpenFOAM Foundation
-     \\/     M anipulation  |
+     \\/     M anipulation  | Copyright (C) 2016 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -68,9 +68,6 @@ Usage
         information as a single argument. The double quotes denote a regular
         expression.
 
-      - \par -useTimeName
-        use the time index in the VTK file name instead of the time index
-
 \*---------------------------------------------------------------------------*/
 
 #include "pointMesh.H"
@@ -86,7 +83,7 @@ Usage
 #include "stringListOps.H"
 #include "wordRe.H"
 
-#include "vtkMesh.H"
+#include "meshSubsetHelper.H"
 #include "readFields.H"
 #include "tecplotWriter.H"
 
@@ -99,7 +96,7 @@ Usage
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
 template<class GeoField>
-void print(const char* msg, Ostream& os, const PtrList<GeoField>& flds)
+void print(const char* msg, Ostream& os, const PtrList<const GeoField>& flds)
 {
     if (flds.size())
     {
@@ -140,7 +137,7 @@ labelList getSelectedPatches
         if
         (
             isType<emptyPolyPatch>(pp)
-            || (Pstream::parRun() && isType<processorPolyPatch>(pp))
+         || (Pstream::parRun() && isType<processorPolyPatch>(pp))
         )
         {
             Info<< "    discarding empty/processor patch " << patchi
@@ -161,8 +158,7 @@ labelList getSelectedPatches
 }
 
 
-
-
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
 int main(int argc, char *argv[])
 {
@@ -170,10 +166,8 @@ int main(int argc, char *argv[])
     (
         "Tecplot binary file format writer"
     );
-
     timeSelector::addOptions();
     #include "addRegionOption.H"
-
     argList::addOption
     (
         "fields",
@@ -190,7 +184,7 @@ int main(int argc, char *argv[])
     (
         "faceSet",
         "name",
-        "restrict conversion to the specified cellSet"
+        "restrict conversion to the specified faceSet"
     );
     argList::addBoolOption
     (
@@ -248,7 +242,8 @@ int main(int argc, char *argv[])
     }
 
     word cellSetName;
-    string vtkName;
+    word faceSetName;
+    string vtkName = runTime.caseName();
 
     if (args.optionReadIfPresent("cellSet", cellSetName))
     {
@@ -266,11 +261,7 @@ int main(int argc, char *argv[])
             vtkName = vtkName.substr(i);
         }
     }
-    else
-    {
-        vtkName = runTime.caseName();
-    }
-
+    args.optionReadIfPresent("faceSet", faceSetName);
 
     instantList timeDirs = timeSelector::select0(runTime, args);
 
@@ -294,10 +285,11 @@ int main(int argc, char *argv[])
             args.optionFound("time")
          || args.optionFound("latestTime")
          || cellSetName.size()
+         || faceSetName.size()
          || regionName != polyMesh::defaultRegion
         )
         {
-            Info<< "Keeping old files in " << fvPath << nl << endl;
+            Info<< "Keeping old tecplot files in " << fvPath << nl << endl;
         }
         else
         {
@@ -309,9 +301,8 @@ int main(int argc, char *argv[])
 
     mkDir(fvPath);
 
-
-    // mesh wrapper; does subsetting and decomposition
-    vtkMesh vMesh(mesh, cellSetName);
+    // Mesh wrapper: does subsetting
+    meshSubsetHelper myMesh(mesh, meshSubsetHelper::SET, cellSetName);
 
     forAll(timeDirs, timeI)
     {
@@ -319,13 +310,12 @@ int main(int argc, char *argv[])
 
         Info<< "Time: " << runTime.timeName() << endl;
 
-        const word timeDesc = name(timeI);    //name(runTime.timeIndex());
+        const word timeDesc = name(timeI); // Foam::name(runTime.timeIndex());
 
         // Check for new polyMesh/ and update mesh, fvMeshSubset and cell
         // decomposition.
-        polyMesh::readUpdateState meshState = vMesh.readUpdate();
-
-        const fvMesh& mesh = vMesh.mesh();
+        polyMesh::readUpdateState meshState = myMesh.readUpdate();
+        const fvMesh& mesh = myMesh.mesh();
 
         INTEGER4 nFaceNodes = 0;
         forAll(mesh.faces(), facei)
@@ -348,24 +338,24 @@ int main(int argc, char *argv[])
 
         // Construct the vol fields (on the original mesh if subsetted)
 
-        PtrList<volScalarField> vsf;
-        readFields(vMesh, vMesh.baseMesh(), objects, selectedFields, vsf);
+        PtrList<const volScalarField> vsf;
+        readFields(myMesh, myMesh.baseMesh(), objects, selectedFields, vsf);
         print("    volScalarFields            :", Info, vsf);
 
-        PtrList<volVectorField> vvf;
-        readFields(vMesh, vMesh.baseMesh(), objects, selectedFields, vvf);
+        PtrList<const volVectorField> vvf;
+        readFields(myMesh, myMesh.baseMesh(), objects, selectedFields, vvf);
         print("    volVectorFields            :", Info, vvf);
 
-        PtrList<volSphericalTensorField> vSpheretf;
-        readFields(vMesh, vMesh.baseMesh(), objects, selectedFields, vSpheretf);
+        PtrList<const volSphericalTensorField> vSpheretf;
+        readFields(myMesh, myMesh.baseMesh(), objects, selectedFields, vSpheretf);
         print("    volSphericalTensorFields   :", Info, vSpheretf);
 
-        PtrList<volSymmTensorField> vSymmtf;
-        readFields(vMesh, vMesh.baseMesh(), objects, selectedFields, vSymmtf);
+        PtrList<const volSymmTensorField> vSymmtf;
+        readFields(myMesh, myMesh.baseMesh(), objects, selectedFields, vSymmtf);
         print("    volSymmTensorFields        :", Info, vSymmtf);
 
-        PtrList<volTensorField> vtf;
-        readFields(vMesh, vMesh.baseMesh(), objects, selectedFields, vtf);
+        PtrList<const volTensorField> vtf;
+        readFields(myMesh, myMesh.baseMesh(), objects, selectedFields, vtf);
         print("    volTensorFields            :", Info, vtf);
 
 
@@ -379,11 +369,11 @@ int main(int argc, char *argv[])
                 << " (\"-noPointValues\" (at your option)\n";
         }
 
-        PtrList<pointScalarField> psf;
-        PtrList<pointVectorField> pvf;
-        //PtrList<pointSphericalTensorField> pSpheretf;
-        //PtrList<pointSymmTensorField> pSymmtf;
-        //PtrList<pointTensorField> ptf;
+        PtrList<const pointScalarField> psf;
+        PtrList<const pointVectorField> pvf;
+        //PtrList<const pointSphericalTensorField> pSpheretf;
+        //PtrList<const pointSymmTensorField> pSymmtf;
+        //PtrList<const pointTensorField> ptf;
 
 
         if (!noPointValues)
@@ -418,8 +408,8 @@ int main(int argc, char *argv[])
 
             readFields
             (
-                vMesh,
-                pointMesh::New(vMesh.baseMesh()),
+                myMesh,
+                pointMesh::New(myMesh.baseMesh()),
                 objects,
                 selectedFields,
                 psf
@@ -428,8 +418,8 @@ int main(int argc, char *argv[])
 
             readFields
             (
-                vMesh,
-                pointMesh::New(vMesh.baseMesh()),
+                myMesh,
+                pointMesh::New(myMesh.baseMesh()),
                 objects,
                 selectedFields,
                 pvf
@@ -438,8 +428,8 @@ int main(int argc, char *argv[])
 
             //readFields
             //(
-            //    vMesh,
-            //    pointMesh::New(vMesh.baseMesh()),
+            //    myMesh,
+            //    pointMesh::New(myMesh.baseMesh()),
             //    objects,
             //    selectedFields,
             //    pSpheretf
@@ -448,8 +438,8 @@ int main(int argc, char *argv[])
             //
             //readFields
             //(
-            //    vMesh,
-            //    pointMesh::New(vMesh.baseMesh()),
+            //    myMesh,
+            //    pointMesh::New(myMesh.baseMesh()),
             //    objects,
             //    selectedFields,
             //    pSymmtf
@@ -458,8 +448,8 @@ int main(int argc, char *argv[])
             //
             //readFields
             //(
-            //    vMesh,
-            //    pointMesh::New(vMesh.baseMesh()),
+            //    myMesh,
+            //    pointMesh::New(myMesh.baseMesh()),
             //    objects,
             //    selectedFields,
             //    ptf
@@ -769,11 +759,10 @@ int main(int argc, char *argv[])
         //
         //---------------------------------------------------------------------
 
-        if (args.optionFound("faceSet"))
+        if (faceSetName.size())
         {
             // Load the faceSet
-            const word setName = args["faceSet"];
-            labelList faceLabels(faceSet(mesh, setName).toc());
+            labelList faceLabels(faceSet(mesh, faceSetName).toc());
 
             // Filename as if patch with same name.
             mkDir(fvPath/setName);
@@ -886,7 +875,6 @@ int main(int argc, char *argv[])
         }
 
 
-
         //---------------------------------------------------------------------
         //
         // Write patches as multi-zone file
@@ -901,7 +889,7 @@ int main(int argc, char *argv[])
 
         fileName patchFileName;
 
-        if (vMesh.useSubMesh())
+        if (myMesh.useSubMesh())
         {
             patchFileName =
                 fvPath/"boundaryMesh"/cellSetName
@@ -1072,7 +1060,7 @@ int main(int argc, char *argv[])
 
             fileName patchFileName;
 
-            if (vMesh.useSubMesh())
+            if (myMesh.useSubMesh())
             {
                 patchFileName =
                     fvPath/"faceZoneMesh"/cellSetName
diff --git a/applications/utilities/postProcessing/dataConversion/foamToTecplot360/readFields.C b/applications/utilities/postProcessing/dataConversion/foamToTecplot360/readFields.C
index c677fe439c7ef0a75b6beb96352a0c67ea42bb15..172a83cf2399cd5f8d4b8678d6e1531fcc16929a 100644
--- a/applications/utilities/postProcessing/dataConversion/foamToTecplot360/readFields.C
+++ b/applications/utilities/postProcessing/dataConversion/foamToTecplot360/readFields.C
@@ -2,8 +2,8 @@
   =========                 |
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2011 OpenFOAM Foundation
-     \\/     M anipulation  |
+    \\  /    A nd           | Copyright (C) 2011-2016 OpenFOAM Foundation
+     \\/     M anipulation  | Copyright (C) 2016 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -36,37 +36,32 @@ namespace Foam
 template<class GeoField>
 void readFields
 (
-    const vtkMesh& vMesh,
+    const meshSubsetHelper& helper,
     const typename GeoField::Mesh& mesh,
     const IOobjectList& objects,
     const HashSet<word>& selectedFields,
-    PtrList<GeoField>& fields
+    PtrList<const GeoField>& fields
 )
 {
-    // Search list of objects for volScalarFields
+    // Search list of objects for fields of type GeomField
     IOobjectList fieldObjects(objects.lookupClass(GeoField::typeName));
 
-    // Construct the vol scalar fields
-    label nFields = fields.size();
-    fields.setSize(nFields + fieldObjects.size());
+    // Construct the fields
+    fields.setSize(fieldObjects.size());
+    label nFields = 0;
 
-    forAllIter(IOobjectList, fieldObjects, iter)
+    forAllConstIter(IOobjectList, fieldObjects, iter)
     {
         if (selectedFields.empty() || selectedFields.found(iter()->name()))
         {
             fields.set
             (
-                nFields,
-                vMesh.interpolate
+                nFields++,
+                helper.interpolate
                 (
-                    GeoField
-                    (
-                        *iter(),
-                        mesh
-                    )
-                )
+                    GeoField(*iter(), mesh)
+                ).ptr()
             );
-            nFields++;
         }
     }
 
diff --git a/applications/utilities/postProcessing/dataConversion/foamToTecplot360/readFields.H b/applications/utilities/postProcessing/dataConversion/foamToTecplot360/readFields.H
index b25c56755c2f2559bf4076ce57057268cad5b131..510bd11d2536419dea3c29bfce016e2dbd5ddd8e 100644
--- a/applications/utilities/postProcessing/dataConversion/foamToTecplot360/readFields.H
+++ b/applications/utilities/postProcessing/dataConversion/foamToTecplot360/readFields.H
@@ -2,8 +2,8 @@
   =========                 |
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2011-2015 OpenFOAM Foundation
-     \\/     M anipulation  |
+    \\  /    A nd           | Copyright (C) 2011-2016 OpenFOAM Foundation
+     \\/     M anipulation  | Copyright (C) 2016 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -34,7 +34,7 @@ SourceFiles
 #ifndef readFields_H
 #define readFields_H
 
-#include "fvMesh.H"
+#include "meshSubsetHelper.H"
 #include "PtrList.H"
 #include "IOobjectList.H"
 #include "HashSet.H"
@@ -48,11 +48,11 @@ namespace Foam
 template<class GeoField>
 void readFields
 (
-    const vtkMesh& vMesh,
+    const meshSubsetHelper&,
     const typename GeoField::Mesh& mesh,
     const IOobjectList& objects,
     const HashSet<word>& selectedFields,
-    PtrList<GeoField>& fields
+    PtrList<const GeoField>& fields
 );
 
 } // End namespace Foam
diff --git a/applications/utilities/postProcessing/dataConversion/foamToTecplot360/vtkMesh.H b/applications/utilities/postProcessing/dataConversion/foamToTecplot360/vtkMesh.H
deleted file mode 100644
index ec6045b5341b0516fd7742ec5f7a84f215d60f19..0000000000000000000000000000000000000000
--- a/applications/utilities/postProcessing/dataConversion/foamToTecplot360/vtkMesh.H
+++ /dev/null
@@ -1,178 +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/>.
-
-Class
-    Foam::vtkMesh
-
-Description
-    Encapsulation of VTK mesh data. Holds mesh or meshsubset and
-    polyhedral-cell decomposition on it.
-
-SourceFiles
-    vtkMesh.C
-
-\*---------------------------------------------------------------------------*/
-
-#ifndef vtkMesh_H
-#define vtkMesh_H
-
-#include "fvMeshSubset.H"
-
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
-
-namespace Foam
-{
-
-// Forward declaration of classes
-class Time;
-
-/*---------------------------------------------------------------------------*\
-                           Class vtkMesh Declaration
-\*---------------------------------------------------------------------------*/
-
-class vtkMesh
-{
-    // Private data
-
-        //- Reference to mesh
-        fvMesh& baseMesh_;
-
-        //- Subsetting engine + sub-fvMesh
-        fvMeshSubset subsetter_;
-
-        //- Current cellSet (or empty)
-        const word setName_;
-
-//        //- Current decomposition of topology
-//        mutable autoPtr<vtkTopo> topoPtr_;
-
-
-
-    // Private Member Functions
-
-        //- Disallow default bitwise copy construct
-        vtkMesh(const vtkMesh&);
-
-        //- Disallow default bitwise assignment
-        void operator=(const vtkMesh&);
-
-
-public:
-
-    // Constructors
-
-        //- Construct from components
-        vtkMesh(fvMesh& baseMesh, const word& setName = "");
-
-
-    // Member Functions
-
-        // Access
-
-            //- Whole mesh
-            const fvMesh& baseMesh() const
-            {
-                return baseMesh_;
-            }
-
-            const fvMeshSubset& subsetter() const
-            {
-                return subsetter_;
-            }
-
-            //- Check if running subMesh
-            bool useSubMesh() const
-            {
-                return setName_.size();
-            }
-
-//            //- topology
-//            const vtkTopo& topo() const
-//            {
-//                if (topoPtr_.empty())
-//                {
-//                    topoPtr_.reset(new vtkTopo(mesh()));
-//                }
-//                return topoPtr_();
-//            }
-
-            //- Access either mesh or submesh
-            const fvMesh& mesh() const
-            {
-                if (useSubMesh())
-                {
-                    return subsetter_.subMesh();
-                }
-                else
-                {
-                    return baseMesh_;
-                }
-            }
-
-//            //- Number of field cells
-//            label nFieldCells() const
-//            {
-//                return topo().vertLabels().size();
-//            }
-//
-//            //- Number of field points
-//            label nFieldPoints() const
-//            {
-//                return mesh().nPoints() + topo().addPointCellLabels().size();
-//            }
-
-
-        // Edit
-
-            //- Read mesh
-            polyMesh::readUpdateState readUpdate();
-
-
-            //- Map volume field (does in fact do very little interpolation;
-            //  just copied from fvMeshSubset)
-            template<class GeoField>
-            tmp<GeoField> interpolate(const GeoField& fld) const
-            {
-                if (useSubMesh())
-                {
-                    tmp<GeoField> subFld = subsetter_.interpolate(fld);
-                    subFld.ref().rename(fld.name());
-                    return subFld;
-                }
-                else
-                {
-                    return fld;
-                }
-            }
-};
-
-
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
-
-} // End namespace Foam
-
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
-
-#endif
-
-// ************************************************************************* //
diff --git a/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK.C b/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK.C
index 4e42b5afbfaa25c7e4275541de7c96e959aad0f7..c2a6f3b29810977ee869310d9c56f5feb2463420 100644
--- a/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK.C
+++ b/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK.C
@@ -410,7 +410,6 @@ int main(int argc, char *argv[])
     args.optionReadIfPresent("pointSet", pointSetName);
 
 
-
     instantList timeDirs = timeSelector::select0(runTime, args);
 
     #include "createNamedMesh.H"
@@ -450,7 +449,7 @@ int main(int argc, char *argv[])
 
     mkDir(fvPath);
 
-    // Mesh wrapper; does subsetting and decomposition
+    // Mesh wrapper: does subsetting and decomposition
     vtkMesh vMesh(mesh, cellSetName);
 
     Info<< "VTK mesh topology: "
@@ -503,7 +502,7 @@ int main(int argc, char *argv[])
 
             Info<< "    FaceSet   : " << patchFileName << endl;
 
-            writeFaceSet(binary, vMesh, set, patchFileName);
+            writeFaceSet(binary, vMesh.mesh(), set, patchFileName);
 
             continue;
         }
@@ -526,7 +525,7 @@ int main(int argc, char *argv[])
 
             Info<< "    pointSet   : " << patchFileName << endl;
 
-            writePointSet(binary, vMesh, set, patchFileName);
+            writePointSet(binary, vMesh.mesh(), set, patchFileName);
 
             continue;
         }
@@ -849,7 +848,7 @@ int main(int argc, char *argv[])
                 writeSurfFields
                 (
                     binary,
-                    vMesh,
+                    vMesh.mesh(),
                     surfFileName,
                     svf
                 );
@@ -892,7 +891,7 @@ int main(int argc, char *argv[])
 
             patchWriter writer
             (
-                vMesh,
+                vMesh.mesh(),
                 binary,
                 nearCellValue,
                 patchFileName,
@@ -970,7 +969,7 @@ int main(int argc, char *argv[])
 
                     patchWriter writer
                     (
-                        vMesh,
+                        vMesh.mesh(),
                         binary,
                         nearCellValue,
                         patchFileName,
@@ -1188,7 +1187,7 @@ int main(int argc, char *argv[])
 
                 lagrangianWriter writer
                 (
-                    vMesh,
+                    vMesh.mesh(),
                     binary,
                     lagrFileName,
                     cloudName,
@@ -1218,7 +1217,7 @@ int main(int argc, char *argv[])
             {
                 lagrangianWriter writer
                 (
-                    vMesh,
+                    vMesh.mesh(),
                     binary,
                     lagrFileName,
                     cloudName,
diff --git a/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK/Make/files b/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK/Make/files
index 4deaaa68f07c4ac1117eb8673149c90d195bc8f2..02de5b47e6254aab58ae3557c2f7e2342b7df753 100644
--- a/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK/Make/files
+++ b/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK/Make/files
@@ -6,7 +6,6 @@ writeFuns.C
 writeFaceSet.C
 writePointSet.C
 writeSurfFields.C
-vtkMesh.C
 vtkTopo.C
 
 writeVTK/writeVTK.C
diff --git a/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK/internalWriter.H b/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK/internalWriter.H
index b61cfd6377aed05e7d85fe681a648c6ef1c63deb..b1836afb60e16c05542594272367a3ea69fdea25 100644
--- a/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK/internalWriter.H
+++ b/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK/internalWriter.H
@@ -41,8 +41,6 @@ SourceFiles
 #include "pointFields.H"
 #include "vtkMesh.H"
 
-using namespace Foam;
-
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
 namespace Foam
diff --git a/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK/lagrangianWriter.C b/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK/lagrangianWriter.C
index 6c168a22805b4218ecf89f1366cb15ffb77d3815..6eab8f164666e4767559491354f705df14cc4418 100644
--- a/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK/lagrangianWriter.C
+++ b/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK/lagrangianWriter.C
@@ -3,7 +3,7 @@
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
     \\  /    A nd           | Copyright (C) 2011-2016 OpenFOAM Foundation
-     \\/     M anipulation  |
+     \\/     M anipulation  | Copyright (C) 2016 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -32,23 +32,21 @@ License
 
 Foam::lagrangianWriter::lagrangianWriter
 (
-    const vtkMesh& vMesh,
+    const fvMesh& mesh,
     const bool binary,
     const fileName& fName,
     const word& cloudName,
     const bool dummyCloud
 )
 :
-    vMesh_(vMesh),
+    mesh_(mesh),
     binary_(binary),
     fName_(fName),
     cloudName_(cloudName),
     os_(fName.c_str())
 {
-    const fvMesh& mesh = vMesh_.mesh();
-
     // Write header
-    writeFuns::writeHeader(os_, binary_, mesh.time().caseName());
+    writeFuns::writeHeader(os_, binary_, mesh_.time().caseName());
     os_ << "DATASET POLYDATA" << std::endl;
 
     if (dummyCloud)
diff --git a/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK/lagrangianWriter.H b/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK/lagrangianWriter.H
index 113bfe43b0a399554f61dd0d1614491554d91d0a..24dd4a322872c92eb6f2dfa32474c7c676e728d3 100644
--- a/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK/lagrangianWriter.H
+++ b/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK/lagrangianWriter.H
@@ -3,7 +3,7 @@
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
     \\  /    A nd           | Copyright (C) 2011-2016 OpenFOAM Foundation
-     \\/     M anipulation  |
+     \\/     M anipulation  | Copyright (C) 2016 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -40,9 +40,6 @@ SourceFiles
 #include "Cloud.H"
 #include "volFields.H"
 #include "pointFields.H"
-#include "vtkMesh.H"
-
-using namespace Foam;
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
@@ -57,7 +54,7 @@ class volPointInterpolation;
 
 class lagrangianWriter
 {
-    const vtkMesh& vMesh_;
+    const fvMesh& mesh_;
 
     const bool binary_;
 
@@ -77,7 +74,7 @@ public:
         //- Construct from components
         lagrangianWriter
         (
-            const vtkMesh&,
+            const fvMesh&,
             const bool binary,
             const fileName&,
             const word&,
diff --git a/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK/lagrangianWriterTemplates.C b/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK/lagrangianWriterTemplates.C
index 8d34517dca577652c338114abe8c990fab4816c8..86cec10dc3ee015598988d5ee2b71c5ed036fc0c 100644
--- a/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK/lagrangianWriterTemplates.C
+++ b/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK/lagrangianWriterTemplates.C
@@ -39,9 +39,9 @@ void Foam::lagrangianWriter::writeIOField(const wordList& objects)
         IOobject header
         (
             object,
-            vMesh_.mesh().time().timeName(),
+            mesh_.time().timeName(),
             cloud::prefix/cloudName_,
-            vMesh_.mesh(),
+            mesh_,
             IOobject::MUST_READ,
             IOobject::NO_WRITE,
             false
diff --git a/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK/patchWriter.C b/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK/patchWriter.C
index 4a9840c1e271d18aa0314b75bf6463cdc388abae..d099745aa259b8f59f9029a52af2b1725a8859c3 100644
--- a/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK/patchWriter.C
+++ b/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK/patchWriter.C
@@ -3,7 +3,7 @@
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
     \\  /    A nd           | Copyright (C) 2011-2016 OpenFOAM Foundation
-     \\/     M anipulation  |
+     \\/     M anipulation  | Copyright (C) 2016 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -30,21 +30,20 @@ License
 
 Foam::patchWriter::patchWriter
 (
-    const vtkMesh& vMesh,
+    const fvMesh& mesh,
     const bool binary,
     const bool nearCellValue,
     const fileName& fName,
     const labelList& patchIDs
 )
 :
-    vMesh_(vMesh),
+    mesh_(mesh),
     binary_(binary),
     nearCellValue_(nearCellValue),
     fName_(fName),
     patchIDs_(patchIDs),
     os_(fName.c_str())
 {
-    const fvMesh& mesh = vMesh_.mesh();
     const polyBoundaryMesh& patches = mesh.boundaryMesh();
 
     // Write header
@@ -115,8 +114,6 @@ Foam::patchWriter::patchWriter
 
 void Foam::patchWriter::writePatchIDs()
 {
-    const fvMesh& mesh = vMesh_.mesh();
-
     DynamicList<floatScalar> fField(nFaces_);
 
     os_ << "patchID 1 " << nFaces_ << " float" << std::endl;
@@ -125,7 +122,7 @@ void Foam::patchWriter::writePatchIDs()
     {
         label patchi = patchIDs_[i];
 
-        const polyPatch& pp = mesh.boundaryMesh()[patchi];
+        const polyPatch& pp = mesh_.boundaryMesh()[patchi];
 
         if (!isA<emptyPolyPatch>(pp))
         {
diff --git a/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK/patchWriter.H b/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK/patchWriter.H
index e25bbb1122414163f440f685bc5a6688c92b3cef..aab8075da87052a6ebc22fdc00cbf041e8aede05 100644
--- a/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK/patchWriter.H
+++ b/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK/patchWriter.H
@@ -3,7 +3,7 @@
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
     \\  /    A nd           | Copyright (C) 2011-2016 OpenFOAM Foundation
-     \\/     M anipulation  |
+     \\/     M anipulation  | Copyright (C) 2016 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -40,12 +40,9 @@ SourceFiles
 #include "OFstream.H"
 #include "volFields.H"
 #include "pointFields.H"
-#include "vtkMesh.H"
 #include "indirectPrimitivePatch.H"
 #include "PrimitivePatchInterpolation.H"
 
-using namespace Foam;
-
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
 namespace Foam
@@ -59,7 +56,8 @@ class volPointInterpolation;
 
 class patchWriter
 {
-    const vtkMesh& vMesh_;
+    //- Reference to the OpenFOAM mesh (or subset)
+    const fvMesh& mesh_;
 
     const bool binary_;
 
@@ -82,7 +80,7 @@ public:
         //- Construct from components
         patchWriter
         (
-            const vtkMesh&,
+            const fvMesh&,
             const bool binary,
             const bool nearCellValue,
             const fileName&,
diff --git a/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK/readFields.C b/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK/readFields.C
index 8d515ab056391eef21df4e8177d942e254eb98a7..172a83cf2399cd5f8d4b8678d6e1531fcc16929a 100644
--- a/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK/readFields.C
+++ b/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK/readFields.C
@@ -3,7 +3,7 @@
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
     \\  /    A nd           | Copyright (C) 2011-2016 OpenFOAM Foundation
-     \\/     M anipulation  |
+     \\/     M anipulation  | Copyright (C) 2016 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -36,37 +36,32 @@ namespace Foam
 template<class GeoField>
 void readFields
 (
-    const vtkMesh& vMesh,
+    const meshSubsetHelper& helper,
     const typename GeoField::Mesh& mesh,
     const IOobjectList& objects,
     const HashSet<word>& selectedFields,
     PtrList<const GeoField>& fields
 )
 {
-    // Search list of objects for volScalarFields
+    // Search list of objects for fields of type GeomField
     IOobjectList fieldObjects(objects.lookupClass(GeoField::typeName));
 
-    // Construct the vol scalar fields
+    // Construct the fields
     fields.setSize(fieldObjects.size());
     label nFields = 0;
 
-    forAllIter(IOobjectList, fieldObjects, iter)
+    forAllConstIter(IOobjectList, fieldObjects, iter)
     {
         if (selectedFields.empty() || selectedFields.found(iter()->name()))
         {
             fields.set
             (
-                nFields,
-                vMesh.interpolate
+                nFields++,
+                helper.interpolate
                 (
-                    GeoField
-                    (
-                        *iter(),
-                        mesh
-                    )
+                    GeoField(*iter(), mesh)
                 ).ptr()
             );
-            nFields++;
         }
     }
 
diff --git a/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK/readFields.H b/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK/readFields.H
index 31b7e2632c84a7e493e0f4cd5defd3cf5b0e4c56..510bd11d2536419dea3c29bfce016e2dbd5ddd8e 100644
--- a/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK/readFields.H
+++ b/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK/readFields.H
@@ -3,7 +3,7 @@
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
     \\  /    A nd           | Copyright (C) 2011-2016 OpenFOAM Foundation
-     \\/     M anipulation  |
+     \\/     M anipulation  | Copyright (C) 2016 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -34,7 +34,7 @@ SourceFiles
 #ifndef readFields_H
 #define readFields_H
 
-#include "fvMesh.H"
+#include "meshSubsetHelper.H"
 #include "PtrList.H"
 #include "IOobjectList.H"
 #include "HashSet.H"
@@ -48,7 +48,7 @@ namespace Foam
 template<class GeoField>
 void readFields
 (
-    const vtkMesh& vMesh,
+    const meshSubsetHelper&,
     const typename GeoField::Mesh& mesh,
     const IOobjectList& objects,
     const HashSet<word>& selectedFields,
diff --git a/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK/surfaceMeshWriter.H b/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK/surfaceMeshWriter.H
index f06da45c3f7f94576fb397e326d3596d60d0b65d..0a5589c26e7edbe6866388d9bb55936b45d02d37 100644
--- a/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK/surfaceMeshWriter.H
+++ b/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK/surfaceMeshWriter.H
@@ -40,11 +40,8 @@ SourceFiles
 #include "OFstream.H"
 #include "volFields.H"
 #include "surfaceFields.H"
-#include "vtkMesh.H"
 #include "indirectPrimitivePatch.H"
 
-using namespace Foam;
-
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
 namespace Foam
diff --git a/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK/surfaceMeshWriterTemplates.C b/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK/surfaceMeshWriterTemplates.C
index 68fd29d257b538d3b36a789daba2bba931c55d08..92d878f2292b534fe915f2c5e8bae4ed2056fff2 100644
--- a/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK/surfaceMeshWriterTemplates.C
+++ b/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK/surfaceMeshWriterTemplates.C
@@ -29,7 +29,8 @@ License
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
 template<class Type>
-Foam::tmp<Field<Type>> Foam::surfaceMeshWriter::getFaceField
+Foam::tmp<Foam::Field<Type>>
+Foam::surfaceMeshWriter::getFaceField
 (
     const GeometricField<Type, fvsPatchField, surfaceMesh>& sfld
 ) const
diff --git a/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK/vtkMesh.H b/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK/vtkMesh.H
index f536ae04fa2f7e27faa7baf121366ba63e988fc4..f0a80db4ead04f7e4e1c13d77c488225d5acb809 100644
--- a/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK/vtkMesh.H
+++ b/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK/vtkMesh.H
@@ -3,7 +3,7 @@
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
     \\  /    A nd           | Copyright (C) 2011-2016 OpenFOAM Foundation
-     \\/     M anipulation  |
+     \\/     M anipulation  | Copyright (C) 2016 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -36,46 +36,35 @@ SourceFiles
 #ifndef vtkMesh_H
 #define vtkMesh_H
 
+#include "meshSubsetHelper.H"
 #include "vtkTopo.H"
-#include "fvMeshSubset.H"
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
 namespace Foam
 {
 
-// Forward declaration of classes
-class Time;
-
 /*---------------------------------------------------------------------------*\
                            Class vtkMesh Declaration
 \*---------------------------------------------------------------------------*/
 
 class vtkMesh
+:
+    public meshSubsetHelper
 {
     // Private data
 
-        //- Reference to mesh
-        fvMesh& baseMesh_;
-
-        //- Subsetting engine + sub-fvMesh
-        fvMeshSubset subsetter_;
-
-        //- Current cellSet (or empty)
-        const word setName_;
-
         //- Current decomposition of topology
         mutable autoPtr<vtkTopo> topoPtr_;
 
 
-
     // Private Member Functions
 
         //- Disallow default bitwise copy construct
-        vtkMesh(const vtkMesh&);
+        vtkMesh(const vtkMesh&) = delete;
 
         //- Disallow default bitwise assignment
-        void operator=(const vtkMesh&);
+        void operator=(const vtkMesh&) = delete;
 
 
 public:
@@ -83,31 +72,17 @@ public:
     // Constructors
 
         //- Construct from components
-        vtkMesh(fvMesh& baseMesh, const word& setName = "");
+        vtkMesh(fvMesh& baseMesh, const word& setName = word::null)
+        :
+            meshSubsetHelper(baseMesh, meshSubsetHelper::SET, setName)
+        {}
 
 
     // Member Functions
 
         // Access
 
-            //- Whole mesh
-            const fvMesh& baseMesh() const
-            {
-                return baseMesh_;
-            }
-
-            const fvMeshSubset& subsetter() const
-            {
-                return subsetter_;
-            }
-
-            //- Check if running subMesh
-            bool useSubMesh() const
-            {
-                return setName_.size();
-            }
-
-            //- topology
+            //- Topology
             const vtkTopo& topo() const
             {
                 if (topoPtr_.empty())
@@ -117,18 +92,6 @@ public:
                 return topoPtr_();
             }
 
-            //- Access either mesh or submesh
-            const fvMesh& mesh() const
-            {
-                if (useSubMesh())
-                {
-                    return subsetter_.subMesh();
-                }
-                else
-                {
-                    return baseMesh_;
-                }
-            }
 
             //- Number of field cells
             label nFieldCells() const
@@ -136,6 +99,7 @@ public:
                 return topo().cellTypes().size();
             }
 
+
             //- Number of field points
             label nFieldPoints() const
             {
@@ -145,26 +109,20 @@ public:
 
         // Edit
 
-            //- Read mesh
-            polyMesh::readUpdateState readUpdate();
-
-
-            //- Map volume field (does in fact do very little interpolation;
-            //  just copied from fvMeshSubset)
-            template<class GeoField>
-            tmp<GeoField> interpolate(const GeoField& fld) const
+            //- Read mesh, forcing topo update if necessary
+            polyMesh::readUpdateState readUpdate()
             {
-                if (useSubMesh())
-                {
-                    tmp<GeoField> subFld = subsetter_.interpolate(fld);
-                    subFld.ref().rename(fld.name());
-                    return subFld;
-                }
-                else
+                polyMesh::readUpdateState meshState
+                    = meshSubsetHelper::readUpdate();
+
+                if (meshState != polyMesh::UNCHANGED)
                 {
-                    return fld;
+                    topoPtr_.clear();
                 }
+
+                return meshState;
             }
+
 };
 
 
diff --git a/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK/writeFaceSet.C b/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK/writeFaceSet.C
index 4ad78d96a0d7075f16dc73444283fd98729ed4bc..0556b7aaf27e0022b82cbb587dea9f818f9409e3 100644
--- a/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK/writeFaceSet.C
+++ b/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK/writeFaceSet.C
@@ -3,7 +3,7 @@
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
     \\  /    A nd           | Copyright (C) 2011-2016 OpenFOAM Foundation
-     \\/     M anipulation  |
+     \\/     M anipulation  | Copyright (C) 2016 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -32,12 +32,12 @@ License
 void Foam::writeFaceSet
 (
     const bool binary,
-    const vtkMesh& vMesh,
+    const fvMesh& mesh,
     const faceSet& set,
     const fileName& fileName
 )
 {
-    const faceList& faces = vMesh.mesh().faces();
+    const faceList& faces = mesh.faces();
 
     std::ofstream ostr(fileName.c_str());
 
@@ -69,7 +69,7 @@ void Foam::writeFaceSet
         setFaces[setFacei] = faces[iter.key()];
         setFacei++;
     }
-    primitiveFacePatch fp(setFaces, vMesh.mesh().points());
+    primitiveFacePatch fp(setFaces, mesh.points());
 
 
     // Write points and faces as polygons
diff --git a/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK/writeFaceSet.H b/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK/writeFaceSet.H
index eb7c45203169a0a46c43fcd713c5e0cb4609effe..73ca4f07416e6fc9fdfff79350a6ddefdd876160 100644
--- a/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK/writeFaceSet.H
+++ b/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK/writeFaceSet.H
@@ -3,7 +3,7 @@
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
     \\  /    A nd           | Copyright (C) 2011-2016 OpenFOAM Foundation
-     \\/     M anipulation  |
+     \\/     M anipulation  | Copyright (C) 2016 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -36,7 +36,7 @@ SourceFiles
 #ifndef writeFaceSet_H
 #define writeFaceSet_H
 
-#include "vtkMesh.H"
+#include "fvMesh.H"
 #include "faceSet.H"
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
@@ -48,7 +48,7 @@ namespace Foam
 void writeFaceSet
 (
     const bool binary,
-    const vtkMesh& vMesh,
+    const fvMesh&,
     const faceSet& set,
     const fileName& fileName
 );
diff --git a/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK/writePointSet.C b/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK/writePointSet.C
index 4cafa01f0a0879ad1e40b5d3039042b18bc37f7d..e5fffb536671144a864004b16ee89eac11416612 100644
--- a/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK/writePointSet.C
+++ b/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK/writePointSet.C
@@ -37,7 +37,7 @@ namespace Foam
 void writePointSet
 (
     const bool binary,
-    const vtkMesh& vMesh,
+    const fvMesh& mesh,
     const pointSet& set,
     const fileName& fileName
 )
@@ -68,7 +68,7 @@ void writePointSet
 
     writeFuns::insert
     (
-        UIndirectList<point>(vMesh.mesh().points(), set.toc())(),
+        UIndirectList<point>(mesh.points(), set.toc())(),
         ptField
     );
 
diff --git a/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK/writePointSet.H b/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK/writePointSet.H
index 32ffd981f6bedc4a3c5b14a92d1986528249a664..d1ad15efe6d7b7b95946f479e695765eebb3aea8 100644
--- a/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK/writePointSet.H
+++ b/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK/writePointSet.H
@@ -3,7 +3,7 @@
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
     \\  /    A nd           | Copyright (C) 2011-2016 OpenFOAM Foundation
-     \\/     M anipulation  |
+     \\/     M anipulation  | Copyright (C) 2016 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -36,7 +36,7 @@ SourceFiles
 #ifndef writePointSet_H
 #define writePointSet_H
 
-#include "vtkMesh.H"
+#include "fvMesh.H"
 #include "pointSet.H"
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
@@ -48,7 +48,7 @@ namespace Foam
 void writePointSet
 (
     const bool binary,
-    const vtkMesh& vMesh,
+    const fvMesh& mesh,
     const pointSet& set,
     const fileName& fileName
 );
diff --git a/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK/writeSurfFields.C b/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK/writeSurfFields.C
index 4144a579a5497701b36afd23752a98fca3d65e99..9dc889adf4be77423e75cb965625c3cc7b52c590 100644
--- a/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK/writeSurfFields.C
+++ b/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK/writeSurfFields.C
@@ -3,7 +3,7 @@
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
     \\  /    A nd           | Copyright (C) 2011-2016 OpenFOAM Foundation
-     \\/     M anipulation  |
+     \\/     M anipulation  | Copyright (C) 2016 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -35,13 +35,11 @@ License
 void Foam::writeSurfFields
 (
     const bool binary,
-    const vtkMesh& vMesh,
+    const fvMesh& mesh,
     const fileName& fileName,
     const UPtrList<const surfaceVectorField>& surfVectorFields
 )
 {
-    const fvMesh& mesh = vMesh.mesh();
-
     std::ofstream str(fileName.c_str());
 
     writeFuns::writeHeader
diff --git a/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK/writeSurfFields.H b/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK/writeSurfFields.H
index 798e335fd343cf180e7f579919cf44fa58630718..7740e15f78f48c3817ebfca37e8ab65b17eb3f44 100644
--- a/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK/writeSurfFields.H
+++ b/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK/writeSurfFields.H
@@ -35,7 +35,7 @@ SourceFiles
 #ifndef writeSurfFields_H
 #define writeSurfFields_H
 
-#include "vtkMesh.H"
+#include "fvMesh.H"
 #include "surfaceMesh.H"
 #include "surfaceFieldsFwd.H"
 
@@ -48,7 +48,7 @@ namespace Foam
 void writeSurfFields
 (
     const bool binary,
-    const vtkMesh& vMesh,
+    const fvMesh&,
     const fileName& fileName,
     const UPtrList<const surfaceVectorField>& surfVectorFields
 );
diff --git a/applications/utilities/postProcessing/graphics/PVReaders/PVFoamReader/vtkPVFoam/Make/options b/applications/utilities/postProcessing/graphics/PVReaders/PVFoamReader/vtkPVFoam/Make/options
index d0cdd781d764d8d978df42aa7bd999d0ce185106..9f63402d646402607cc5a547da62abbdbfabfd68 100644
--- a/applications/utilities/postProcessing/graphics/PVReaders/PVFoamReader/vtkPVFoam/Make/options
+++ b/applications/utilities/postProcessing/graphics/PVReaders/PVFoamReader/vtkPVFoam/Make/options
@@ -3,6 +3,8 @@ EXE_INC = \
     -I$(LIB_SRC)/finiteVolume/lnInclude \
     -I$(LIB_SRC)/dynamicMesh/lnInclude \
     -I$(LIB_SRC)/lagrangian/basic/lnInclude \
+    -I$(LIB_SRC)/fileFormats/lnInclude \
+    -I$(LIB_SRC)/conversion/lnInclude \
     -I$(ParaView_INCLUDE_DIR) \
     -I$(ParaView_INCLUDE_DIR)/vtkkwiml \
     -I../../vtkPVReaders/lnInclude \
@@ -10,7 +12,7 @@ EXE_INC = \
 
 LIB_LIBS = \
     -ldynamicMesh \
-    -ldynamicMesh \
+    -lconversion \
     -lgenericPatchFields \
     -llagrangian \
     -L$(FOAM_LIBBIN) -lvtkPVReaders \
diff --git a/src/conversion/Allwmake b/src/conversion/Allwmake
index 4b81d4b8c1ae4055194d3a4cc096607f750683a1..3187320a6796462cce366fa3845a669791b2042c 100755
--- a/src/conversion/Allwmake
+++ b/src/conversion/Allwmake
@@ -3,8 +3,15 @@ cd ${0%/*} || exit 1    # Run from this directory
 
 # Parse arguments for library compilation
 . $WM_PROJECT_DIR/wmake/scripts/AllwmakeParseArguments
+
 set -x
 
 wmake $targetType
 
+ccm/Allwmake || {
+    echo
+    echo "WARNING: skipped optional conversion component (build issues detected)"
+    echo
+}
+
 #------------------------------------------------------------------------------
diff --git a/src/conversion/Make/files b/src/conversion/Make/files
index 19af831aeb5a682c329d131f838512a8654cbedf..3f2c0a0c98c7e6ccb2679829519124f5f4fadf6b 100644
--- a/src/conversion/Make/files
+++ b/src/conversion/Make/files
@@ -15,9 +15,16 @@ ensight/part/ensightPartCells.C
 ensight/part/ensightPartFaces.C
 ensight/part/ensightParts.C
 
+fire/FIREMeshReader.C
+fire/FIREMeshWriter.C
+fire/checkFireEdges.C
+
 starcd/STARCDMeshReader.C
 starcd/STARCDMeshWriter.C
 
 polyDualMesh/polyDualMesh.C
 
+vtk/part/foamVtkCells.C
+vtk/output/foamVtkOutput.C
+
 LIB = $(FOAM_LIBBIN)/libconversion
diff --git a/src/conversion/ccm/Allwmake b/src/conversion/ccm/Allwmake
new file mode 100755
index 0000000000000000000000000000000000000000..a3e067e1dc0b9cdef6e24986923e5cdcd85a866a
--- /dev/null
+++ b/src/conversion/ccm/Allwmake
@@ -0,0 +1,28 @@
+#!/bin/sh
+#
+# Build optional components (eg, may depend on third-party libraries)
+#------------------------------------------------------------------------------
+cd ${0%/*} || exit 1    # Run from this directory
+
+# Parse arguments for compilation (at least for error catching)
+. $WM_PROJECT_DIR/wmake/scripts/AllwmakeParseArguments
+
+. $WM_PROJECT_DIR/etc/config.sh/functions
+_foamSource $($WM_PROJECT_DIR/bin/foamEtcFile config.sh/ccmio)
+
+# Link with static libccmio only (possibly fewer issues)
+if [ "$WM_LABEL_SIZE" = 64 ]
+then
+    # The libccmio uses int32_t.
+    # The OpenFOAM adapter thus requires additional work for 64-bit labels.
+    echo "Skipping optional component libccm"
+    echo "    does not support 64-bit labels"
+elif [ -e $CCMIO_ARCH_PATH/include/libccmio/ccmio.h \
+    -a -e $CCMIO_ARCH_PATH/lib/libccmio.a ]
+then
+    wmake libso
+else
+    echo "Skipping optional component libccm"
+fi
+
+#------------------------------------------------------------------------------
diff --git a/src/conversion/ccm/Make/files b/src/conversion/ccm/Make/files
new file mode 100644
index 0000000000000000000000000000000000000000..89ec20a1db35f1404009aea7b0e367eff3d00a9a
--- /dev/null
+++ b/src/conversion/ccm/Make/files
@@ -0,0 +1,16 @@
+common/ccmBase.C
+common/ccmInternal.C
+
+reader/ccmReader.C
+reader/ccmReaderAux.C
+reader/ccmReaderMesh.C
+reader/ccmReaderSolution.C
+reader/ccmReaderOptions.C
+
+writer/ccmWriter.C
+writer/ccmWriterMesh.C
+writer/ccmWriterSolution.C
+
+/* misc/mergePoints1.C */
+
+LIB = $(FOAM_LIBBIN)/libccm
diff --git a/src/conversion/ccm/Make/options b/src/conversion/ccm/Make/options
new file mode 100644
index 0000000000000000000000000000000000000000..ef4b6bb2798726d4b87bff3808ec8807b560a1c0
--- /dev/null
+++ b/src/conversion/ccm/Make/options
@@ -0,0 +1,14 @@
+EXE_INC = \
+    /* -DWITH_MONITORING -DDEBUG_MONITORING */ \
+    /* -DDEBUG_BAFFLES */ \
+    /* -DDEBUG_CCMIOREAD */ \
+    -I$(LIB_SRC)/finiteVolume/lnInclude \
+    -I$(LIB_SRC)/meshTools/lnInclude \
+    -I$(LIB_SRC)/conversion/lnInclude \
+    -I$(LIB_SRC)/fileFormats/lnInclude \
+    -I$(CCMIO_ARCH_PATH)/include
+
+LIB_LIBS = \
+    -lfiniteVolume \
+    -lconversion \
+    -L$(CCMIO_ARCH_PATH)/lib -lccmio
diff --git a/src/conversion/ccm/common/ccm.H b/src/conversion/ccm/common/ccm.H
new file mode 100644
index 0000000000000000000000000000000000000000..639b6127cb87086bc4d522f37acce276739f7b71
--- /dev/null
+++ b/src/conversion/ccm/common/ccm.H
@@ -0,0 +1,37 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2016 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/>.
+
+Description
+    Reader/writer for handling ccm files.
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef ccm_H
+#define ccm_H
+
+#include "ccmReader.H"
+#include "ccmWriter.H"
+
+#endif
+
+// ************************************************************************* //
diff --git a/applications/utilities/postProcessing/dataConversion/foamToTecplot360/vtkMesh.C b/src/conversion/ccm/common/ccmBase.C
similarity index 52%
rename from applications/utilities/postProcessing/dataConversion/foamToTecplot360/vtkMesh.C
rename to src/conversion/ccm/common/ccmBase.C
index 0a58e5d523c41724e0c4ca743a5daef9aab2fa4e..14efe10a5e22fb30a3becd0df48ba238eb16eb52 100644
--- a/applications/utilities/postProcessing/dataConversion/foamToTecplot360/vtkMesh.C
+++ b/src/conversion/ccm/common/ccmBase.C
@@ -2,7 +2,7 @@
   =========                 |
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2011 OpenFOAM Foundation
+    \\  /    A nd           | Copyright (C) 2016 OpenCFD Ltd.
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
 License
@@ -21,62 +21,81 @@ License
     You should have received a copy of the GNU General Public License
     along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
 
-\*---------------------------------------------------------------------------*/
+\*----------------------------------------------------------------------------*/
 
-#include "vtkMesh.H"
-#include "fvMeshSubset.H"
-#include "Time.H"
-#include "cellSet.H"
+#include "ccmBase.H"
+#include "ccmInternal.H"  // include last to avoid any strange interactions
 
-// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
 
-// Construct from components
-Foam::vtkMesh::vtkMesh
+// * * * * * * * * * * * * * Protected Member Functions  * * * * * * * * * * //
+
+bool Foam::ccm::base::assertNoError
 (
-    fvMesh& baseMesh,
-    const word& setName
+    int err,
+    const char* msg
 )
-:
-    baseMesh_(baseMesh),
-    subsetter_(baseMesh),
-    setName_(setName)
 {
-    if (setName.size())
-    {
-        // Read cellSet using whole mesh
-        cellSet currentSet(baseMesh_, setName_);
+    return ccmGlobalState::assertNoError(static_cast<CCMIOError>(err), msg);
+}
 
-        // Set current subset
-        subsetter_.setLargeCellSubset(currentSet);
-    }
+
+bool Foam::ccm::base::assertNoError
+(
+    int err,
+    const std::string& msg
+)
+{
+    return ccmGlobalState::assertNoError(static_cast<CCMIOError>(err), msg);
 }
 
 
-// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+bool Foam::ccm::base::assertNoError(const char* msg) const
+{
+    return globalState_->assertNoError(msg);
+}
 
-Foam::polyMesh::readUpdateState Foam::vtkMesh::readUpdate()
+
+bool Foam::ccm::base::assertNoError(const std::string& msg) const
 {
-    polyMesh::readUpdateState meshState = baseMesh_.readUpdate();
+    return globalState_->assertNoError(msg);
+}
 
-    if (meshState != polyMesh::UNCHANGED)
-    {
-        // Note: since fvMeshSubset has no movePoints() functionality
-        // reconstruct the subset even if only movement.
 
-//        topoPtr_.clear();
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+Foam::ccm::base::base()
+:
+    globalState_(new ccmGlobalState)
+{}
+
+
+// * * * * * * * * * * * * * * * * Destructor  * * * * * * * * * * * * * * * //
+
+Foam::ccm::base::~base()
+{
+    close();
+}
 
-        if (setName_.size())
-        {
-            Info<< "Subsetting mesh based on cellSet " << setName_ << endl;
 
-            // Read cellSet using whole mesh
-            cellSet currentSet(baseMesh_, setName_);
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
 
-            subsetter_.setLargeCellSubset(currentSet);
+bool Foam::ccm::base::close()
+{
+    if (globalState_)
+    {
+        if (CCMIOIsValidEntity(globalState_->root))
+        {
+            CCMIOCloseFile(NULL, globalState_->root);
         }
-    }
+        delete globalState_;
+        globalState_ = 0;
 
-    return meshState;
+        return true;
+    }
+    else
+    {
+        return false;
+    }
 }
 
 
diff --git a/src/conversion/ccm/common/ccmBase.H b/src/conversion/ccm/common/ccmBase.H
new file mode 100644
index 0000000000000000000000000000000000000000..5f789eadd0aaa100ae07ad4952d7ecc19b627aad
--- /dev/null
+++ b/src/conversion/ccm/common/ccmBase.H
@@ -0,0 +1,131 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2016 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::ccm::base
+
+Description
+    Base functionality common to reader and writer classes
+
+Note
+    this class is in development
+    - any/all of the class names and members may change
+
+SourceFiles
+    ccmBase.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef ccmBase_H
+#define ccmBase_H
+
+#include <string>
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+namespace ccm
+{
+
+// * * * * * * * * * * * * * Forward Declarations  * * * * * * * * * * * * * //
+
+class ccmGlobalState;
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+/*---------------------------------------------------------------------------*\
+                          Class ccm::base Declaration
+\*---------------------------------------------------------------------------*/
+
+class base
+{
+    // Private Member Functions
+
+        //- Disallow default bitwise copy construct
+        base(const base&) = delete;
+
+        //- Disallow default bitwise assignment
+        void operator=(const base&) = delete;
+
+
+protected:
+
+    // Protected Data
+
+        //- Maintain overall global states (error, root-node)
+        ccmGlobalState* globalState_;
+
+
+    // Protected Member Functions
+
+        //- Die with msg if there is an error
+        //  Return true if there is no error
+        static bool assertNoError(int err, const char *msg);
+
+        //- Die with msg if there is an error
+        //  Return true if there is no error
+        static bool assertNoError(int err, const std::string& msg);
+
+
+        //- check global state for errors and die as required
+        //  Return true if there is no error
+        bool assertNoError(const char* msg) const;
+
+        //- check global state for errors and die as required
+        //  Return true if there is no error
+        bool assertNoError(const std::string& msg) const;
+
+
+public:
+
+    // Constructors
+
+        //- Construct null
+        base();
+
+
+    //- Destructor: close file
+    ~base();
+
+
+    // Member Functions
+
+        // Access
+
+        //- Explicity close the file and terminate ccmio access.
+        //  Return false if it was already closed.
+        bool close();
+
+};
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace ccm
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/conversion/ccm/common/ccmInternal.C b/src/conversion/ccm/common/ccmInternal.C
new file mode 100644
index 0000000000000000000000000000000000000000..47dd0c393397577c56db7e2702c663f1b27fcb54
--- /dev/null
+++ b/src/conversion/ccm/common/ccmInternal.C
@@ -0,0 +1,171 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2016 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 "Pstream.H"
+#include "ccmInternal.H" // include last to avoid any strange interactions
+
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+namespace Foam
+{
+defineTypeNameAndDebug(ccm, 0);
+}
+
+
+// * * * * * * * * * * * * * * Static Functions  * * * * * * * * * * * * * * //
+
+// \cond file-scope
+// Return the string corresponding to the error
+static const char* errorMsg(CCMIOError err)
+{
+    switch (err)
+    {
+        case kCCMIONoErr:
+            return "";
+            break;
+        case kCCMIONoFileErr:
+            return "No File Error";
+            break;
+        case kCCMIOPermissionErr:
+            return "Permission Error";
+            break;
+        case kCCMIOCorruptFileErr:
+            return "Corrupt File Error";
+            break;
+        case kCCMIOBadLinkErr:
+            return "Bad Link Error";
+            break;
+        case kCCMIONoNodeErr:
+            return "No Node Error";
+            break;
+        case kCCMIODuplicateNodeErr:
+            return "Duplicate Node Error";
+            break;
+        case kCCMIOWrongDataTypeErr:
+            return "Wrong Data Type Error";
+            break;
+        case kCCMIONoDataErr:
+            return "NoData Error";
+            break;
+        case kCCMIOWrongParentErr:
+            return "Wrong Parent Error";
+            break;
+        case kCCMIOBadParameterErr:
+            return "Bad Parameter Error";
+            break;
+        case kCCMIONoMemoryErr:
+            return "No Memory Error";
+            break;
+        case kCCMIOIOErr:
+            return "IO Error";
+            break;
+        case kCCMIOTooManyFacesErr:
+            return "Too Many Faces Error";
+            break;
+        case kCCMIOVersionErr:
+            return "Version Error";
+            break;
+        case kCCMIOArrayDimensionToLargeErr:
+            return "Array Dimension To Large Error";
+            break;
+        case kCCMIOInternalErr:
+            return "Internal Error";
+            break;
+        default:
+            return "unclassified error";
+            break;
+    }
+}
+//! \endcond
+
+
+// * * * * * * * * * * * * * * Public Member Functions * * * * * * * * * * * //
+
+bool Foam::ccm::ccmGlobalState::assertNoError
+(
+    CCMIOError err,
+    const char* msg
+)
+{
+    if (err != kCCMIONoErr)
+    {
+        Perr<< endl
+            << "--> FATAL ERROR:"
+            << "\n    " << msg
+            << "\n    libccmio reports -> " << errorMsg(err) << " <-\n"
+            << endl;
+
+        ::exit(1);
+    }
+
+    return (err == kCCMIONoErr);
+}
+
+
+bool Foam::ccm::ccmGlobalState::assertNoError
+(
+    CCMIOError err,
+    const std::string& msg
+)
+{
+    if (err != kCCMIONoErr)
+    {
+        assertNoError(err, msg.c_str());
+    }
+
+    return (err == kCCMIONoErr);
+}
+
+
+bool Foam::ccm::ccmGlobalState::assertNoError
+(
+    const char* msg
+) const
+{
+    return assertNoError(error, msg);
+}
+
+
+bool Foam::ccm::ccmGlobalState::assertNoError
+(
+    const std::string& msg
+) const
+{
+    return assertNoError(error, msg);
+}
+
+
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+Foam::ccm::ccmGlobalState::ccmGlobalState()
+:
+    error(kCCMIONoErr)
+{
+    CCMIOInvalidateEntity(&root);
+}
+
+
+// ************************************************************************* //
diff --git a/src/conversion/ccm/common/ccmInternal.H b/src/conversion/ccm/common/ccmInternal.H
new file mode 100644
index 0000000000000000000000000000000000000000..f528438dbea01c9f52e950dbba3774ad4b66ccab
--- /dev/null
+++ b/src/conversion/ccm/common/ccmInternal.H
@@ -0,0 +1,170 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2016 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/>.
+
+Description
+    Internal bits for wrapping libccmio - do not use directly
+
+\*---------------------------------------------------------------------------*/
+#ifndef ccmInternal_H
+#define ccmInternal_H
+
+#include "className.H"
+#include "List.H"
+
+// headers and definitions for using libccmio
+#include "libccmio/ccmio.h"
+
+// low-level routines are also needed
+#include "libccmio/ccmiocore.h"
+#include "libccmio/ccmioutility.h"
+
+// don't want these defines to leak through
+#undef TRUE
+#undef FALSE
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+// @cond
+// internal use only - skip doxygen documentation
+namespace Foam
+{
+namespace ccm
+{
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+//- Declare namespace and debug information.
+NamespaceName("ccm");
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+
+//- A C++ wrapper for the C struct
+class ccmID
+:
+    public CCMIOID
+{};
+
+//- A C++ wrapper for the C struct
+class ccmNODE
+:
+    public CCMIONode
+{
+public:
+
+    //- Recast constructor
+    ccmNODE(CCMIONode node)
+    :
+       CCMIONode(node)
+    {}
+};
+
+
+//- A C++ wrapper for the enum
+class ccmDimension
+{
+    CCMIODimensionality dims_;
+
+public:
+
+    //- Construct from components
+    ccmDimension(CCMIODimensionality dims)
+    :
+        dims_(dims)
+    {}
+
+    //- Return underlying enum
+    CCMIODimensionality operator()() const
+    {
+        return dims_;
+    }
+};
+
+
+//- Maintain overall global states (error, rootNode)
+class ccmGlobalState
+{
+public:
+
+    //- Maintain error state between calls
+    CCMIOError error;
+
+    //- Root node in the CCM file
+    ccmID root;
+
+    //- Null constructor. Start with no error, but root in invalid state.
+    ccmGlobalState();
+
+    //- True if there is an error
+    bool hasError() const
+    {
+        return (error != kCCMIONoErr);
+    }
+
+    //- Die with msg if there is an error
+    //  Return true if there is no error
+    static bool assertNoError(CCMIOError err, const char *msg);
+
+    //- Die with msg if there is an error
+    //  Return true if there is no error
+    static bool assertNoError(CCMIOError err, const std::string& msg);
+
+    //- Die with msg if there is an error
+    //  Return true if there is no error
+    bool assertNoError(const char *msg) const;
+
+    //- Die with msg if there is an error
+    //  Return true if there is no error
+    bool assertNoError(const std::string& msg) const;
+
+};
+
+
+//- MapIds for various parts
+class ccmMaps
+{
+public:
+    //- The cell map
+    ccmID cells;
+
+    //- The internalFaces map
+    ccmID internalFaces;
+
+    //- Boundary region maps
+    List<ccmID> boundary;
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace ccm
+} // End namespace Foam
+
+// @endcond
+// internal use only - skip doxygen documentation
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/conversion/ccm/misc/ListOps1.H b/src/conversion/ccm/misc/ListOps1.H
new file mode 100644
index 0000000000000000000000000000000000000000..7e8490240c748b82493c871ad3b625225c118f32
--- /dev/null
+++ b/src/conversion/ccm/misc/ListOps1.H
@@ -0,0 +1,84 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2011-2016 OpenFOAM Foundation
+     \\/     M anipulation  | Copyright (C) 2016 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/>.
+
+InNamspace
+    Foam
+
+Description
+    Various functions to operate on Lists.
+
+SourceFiles
+    ListOps.C
+    ListOpsTemplates.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef ListOps1_H
+#define ListOps1_H
+
+#include "ListOps.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+//- Reorder the elements (indices, not values) of a list.
+//  Negative ListType elements are untouched, unless pruning has been selected.
+//  With pruning, these elements are skipped and the list shrinks accordingly.
+template<class ListType>
+ListType reorder
+(
+    const labelUList& oldToNew,
+    const ListType&,
+    const bool prune
+);
+
+//- Inplace reorder the elements of a list.
+//  Negative ListType elements are untouched, unless pruning has been selected.
+//  With pruning, these elements are skipped and the list shrinks accordingly.
+template<class ListType>
+void inplaceReorder
+(
+    const labelUList& oldToNew,
+    ListType&,
+    const bool prune
+);
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#ifdef NoRepository
+    #include "ListOps1Templates.C"
+#endif
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
+
diff --git a/src/conversion/ccm/misc/ListOps1Templates.C b/src/conversion/ccm/misc/ListOps1Templates.C
new file mode 100644
index 0000000000000000000000000000000000000000..14a9ac38c9284f3e017187a76b188c73d8391d72
--- /dev/null
+++ b/src/conversion/ccm/misc/ListOps1Templates.C
@@ -0,0 +1,117 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2011-2016 OpenFOAM Foundation
+     \\/     M anipulation  | Copyright (C) 2015-2016 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 "ListOps1.H"
+
+// * * * * * * * * * * * * * * * Global Functions  * * * * * * * * * * * * * //
+
+template<class ListType>
+ListType Foam::reorder
+(
+    const labelUList& oldToNew,
+    const ListType& lst,
+    const bool prune
+)
+{
+    const label sz = lst.size();
+
+    // Create copy
+    ListType newLst(sz);
+
+    // Ensure consistent addressable size (eg, DynamicList)
+    newLst.setSize(sz);
+
+
+    label maxIdx = 0;
+    forAll(lst, elemI)
+    {
+        const label newIdx = oldToNew[elemI];
+        if (newIdx >= 0) // could also require newIdx < sz
+        {
+            newLst[newIdx] = lst[elemI];
+            if (prune && maxIdx < newIdx)
+            {
+                maxIdx = newIdx;
+            }
+        }
+        else if (!prune)
+        {
+            newLst[elemI] = lst[elemI];
+        }
+    }
+
+    if (prune && maxIdx < sz)
+    {
+        newLst.setSize(maxIdx);
+    }
+
+    return newLst;
+}
+
+
+template<class ListType>
+void Foam::inplaceReorder
+(
+    const labelUList& oldToNew,
+    ListType& lst,
+    const bool prune
+)
+{
+    const label sz = lst.size();
+
+    // Create copy
+    ListType newLst(sz);
+
+    // Ensure consistent addressable size (eg, DynamicList)
+    newLst.setSize(sz);
+
+    label maxIdx = 0;
+    forAll(lst, elemI)
+    {
+        const label newIdx = oldToNew[elemI];
+        if (newIdx >= 0) // could also require newIdx < sz
+        {
+            newLst[newIdx] = lst[elemI];
+            if (prune && maxIdx < newIdx)
+            {
+                maxIdx = newIdx;
+            }
+        }
+        else if (!prune)
+        {
+            newLst[elemI] = lst[elemI];
+        }
+    }
+
+    if (prune && maxIdx < sz)
+    {
+        newLst.setSize(maxIdx);
+    }
+
+    lst.transfer(newLst);
+}
+
+
+// ************************************************************************* //
diff --git a/src/conversion/ccm/misc/mergePoints1.C b/src/conversion/ccm/misc/mergePoints1.C
new file mode 100644
index 0000000000000000000000000000000000000000..c1114df3ca0b6850af134dc77df5358e9bca9359
--- /dev/null
+++ b/src/conversion/ccm/misc/mergePoints1.C
@@ -0,0 +1,177 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2011 OpenFOAM Foundation
+     \\/     M anipulation  | Copyright (C) 2016 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 "ListOps.H"
+#include "point.H"
+#include "Field.H"
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+
+// template<class Type, template<class> class ListType=UList>
+template<class Type>
+Foam::label Foam::mergePoints
+(
+    const bool dummy,
+    const UIndirectList<Type>& points,
+    const scalar mergeTol,
+    const bool verbose,
+    labelList& pointMap,
+    const Type& origin
+)
+{
+    // Create a old to new point mapping array
+    pointMap.setSize(points.size());
+    pointMap = -1;
+
+    if (points.empty())
+    {
+        return 0;
+    }
+
+    // No normal field operations on UIndirectList.
+    // Use a tmp pointField instead.
+    tmp<Field<Type>> tPoints(new pointField(points));
+
+    Type compareOrigin = origin;
+    if (origin == Type::max)
+    {
+        compareOrigin = sum(tPoints())/points.size();
+    }
+
+    // We're comparing distance squared to origin first.
+    // Say if starting from two close points:
+    //     x, y, z
+    //     x+mergeTol, y+mergeTol, z+mergeTol
+    // Then the magSqr of both will be
+    //     x^2+y^2+z^2
+    //     x^2+y^2+z^2 + 2*mergeTol*(x+z+y) + mergeTol^2*...
+    // so the difference will be 2*mergeTol*(x+y+z)
+
+    const scalar mergeTolSqr = Foam::sqr(scalar(mergeTol));
+
+    // Sort points by magSqr
+    const Field<Type> d(tPoints - compareOrigin);
+
+    List<scalar> magSqrD(d.size());
+    forAll(d, pointI)
+    {
+        magSqrD[pointI] = magSqr(d[pointI]);
+    }
+    labelList order;
+    sortedOrder(magSqrD, order);
+
+
+    Field<scalar> sortedTol(points.size());
+    forAll(order, sortI)
+    {
+        label pointI = order[sortI];
+
+        // Convert to scalar precision
+        const point pt
+        (
+            scalar(d[pointI].x()),
+            scalar(d[pointI].y()),
+            scalar(d[pointI].z())
+        );
+        sortedTol[sortI] = 2*mergeTol*(mag(pt.x())+mag(pt.y())+mag(pt.z()));
+    }
+
+    label newPointI = 0;
+
+    // Handle 0th point separately (is always unique)
+    label pointI = order[0];
+    pointMap[pointI] = newPointI++;
+
+
+    for (label sortI = 1; sortI < order.size(); sortI++)
+    {
+        // Get original point index
+        label pointI = order[sortI];
+        const scalar mag2 = magSqrD[order[sortI]];
+        // Convert to scalar precision
+        const point pt
+        (
+            scalar(points[pointI].x()),
+            scalar(points[pointI].y()),
+            scalar(points[pointI].z())
+        );
+
+
+        // Compare to previous points to find equal one.
+        label equalPointI = -1;
+
+        for
+        (
+            label prevSortI = sortI - 1;
+            prevSortI >= 0
+         && (mag(magSqrD[order[prevSortI]] - mag2) <= sortedTol[sortI]);
+            prevSortI--
+        )
+        {
+            label prevPointI = order[prevSortI];
+            const point prevPt
+            (
+                scalar(points[prevPointI].x()),
+                scalar(points[prevPointI].y()),
+                scalar(points[prevPointI].z())
+            );
+
+            if (magSqr(pt - prevPt) <= mergeTolSqr)
+            {
+                // Found match.
+                equalPointI = prevPointI;
+
+                break;
+            }
+        }
+
+
+        if (equalPointI != -1)
+        {
+            // Same coordinate as equalPointI. Map to same new point.
+            pointMap[pointI] = pointMap[equalPointI];
+
+            if (verbose)
+            {
+                Pout<< "Foam::mergePoints : Merging points "
+                    << pointI << " and " << equalPointI
+                    << " with coordinates:" << points[pointI]
+                    << " and " << points[equalPointI]
+                    << endl;
+            }
+        }
+        else
+        {
+            // Differs. Store new point.
+            pointMap[pointI] = newPointI++;
+        }
+    }
+
+    return newPointI;
+}
+
+
+// ************************************************************************* //
diff --git a/src/conversion/ccm/misc/mergePoints1.H b/src/conversion/ccm/misc/mergePoints1.H
new file mode 100644
index 0000000000000000000000000000000000000000..85fc56210a289be0d488e89985d0edaab10e13dc
--- /dev/null
+++ b/src/conversion/ccm/misc/mergePoints1.H
@@ -0,0 +1,73 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2011-2016 OpenFOAM Foundation
+     \\/     M anipulation  | Copyright (C) 2016 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/>.
+
+Description
+    Merge points. See below.
+
+SourceFiles
+    mergePoints.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef mergePoints1_H
+#define mergePoints1_H
+
+#include "scalar.H"
+#include "labelList.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+/*---------------------------------------------------------------------------*\
+                           Function mergePoints Declaration
+\*---------------------------------------------------------------------------*/
+
+//- Sorts and merges points. All points closer than/equal mergeTol get merged.
+//  Returns the number of unique points and a map from old to new.
+//template<class Type, template<class> class ListType=UList>
+template<class Type>
+label mergePoints
+(
+    const bool dummy,
+    const UIndirectList<Type>& points,
+    const scalar mergeTol,
+    const bool verbose,
+    labelList& pointMap,
+    const Type& origin = Type::zero
+);
+
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#ifdef NoRepository
+    #include "mergePoints1.C"
+#endif
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/conversion/ccm/reader/ccmBoundaryInfo.H b/src/conversion/ccm/reader/ccmBoundaryInfo.H
new file mode 100644
index 0000000000000000000000000000000000000000..967c6f51e1071f28df17294daa011fb78d22bc59
--- /dev/null
+++ b/src/conversion/ccm/reader/ccmBoundaryInfo.H
@@ -0,0 +1,126 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2016 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/>.
+
+Description
+    Containers for holding STARCCM boundary information
+
+\*---------------------------------------------------------------------------*/
+#ifndef ccmBoundaryInfo_H
+#define ccmBoundaryInfo_H
+
+#include "word.H"
+#include "Ostream.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+//! \cond internal-use
+// internal use only - skip doxygen documentation
+namespace Foam
+{
+namespace ccm
+{
+
+/*---------------------------------------------------------------------------*\
+                    Class ccm::ccmBoundaryInfo Declaration
+\*---------------------------------------------------------------------------*/
+
+//- Helper when reading raw boundary information
+class ccmBoundaryInfo
+{
+public:
+    //- The ccm region
+    int ccmIndex;
+
+    //- Number of faces
+    label size;
+
+    //- The openfoam patch id to map to
+    label patchId;
+
+    //- The patch name, as per the BoundaryRegion "Label" entry
+    std::string patchName;
+
+    //- The patch type, as per the BoundaryRegion "BoundaryType" entry
+    std::string patchType;
+
+
+    //- Construct null
+    ccmBoundaryInfo()
+    :
+        ccmIndex(0),
+        size(0),
+        patchId(-1),
+        patchName(),
+        patchType("patch")
+    {}
+
+
+    //- Set patch name, default to "patch_CCMID" for an empty string
+    void setPatchName(const std::string& str)
+    {
+        if (str.empty())
+        {
+            patchName = "patch_" + ::Foam::name(ccmIndex);
+        }
+        else
+        {
+            patchName = str;
+        }
+    }
+
+
+    //- Info doesn't match if the ccm boundaries are different
+    bool operator!=(const ccmBoundaryInfo& rhs) const
+    {
+        return ccmIndex != rhs.ccmIndex;
+    }
+
+   // IOstream Operators
+
+    friend Ostream& operator<<(Ostream& os, const ccmBoundaryInfo& entry)
+    {
+        os  << "BoundaryFaces-" << entry.ccmIndex;
+        os  << " size=" << entry.size;
+        os  << " name=" << entry.patchName;
+        os  << " type=" << entry.patchType;
+        os  << " foam-patch=" << entry.patchId;
+
+        return os;
+    }
+
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace ccm
+} // End namespace Foam
+
+//! \endcond
+// internal use only - skip doxygen documentation
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/conversion/ccm/reader/ccmInterfaceDefinitions.H b/src/conversion/ccm/reader/ccmInterfaceDefinitions.H
new file mode 100644
index 0000000000000000000000000000000000000000..1c9ea2c8e55da3b5ee3642f1afc118a0f339a984
--- /dev/null
+++ b/src/conversion/ccm/reader/ccmInterfaceDefinitions.H
@@ -0,0 +1,252 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2016 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/>.
+
+Description
+    Containers for holding STARCCM interface definitions
+
+\*---------------------------------------------------------------------------*/
+#ifndef ccmInterfaceDefinitions_H
+#define ccmInterfaceDefinitions_H
+
+#include "Map.H"
+#include "Ostream.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+namespace ccm
+{
+
+/*---------------------------------------------------------------------------*\
+                     Class ccm::interfaceEntry Declaration
+\*---------------------------------------------------------------------------*/
+
+//- A STARCCM interface definition is a pair of boundary ids
+class interfaceEntry
+{
+public:
+    // Public Data
+
+        //- The internal interface id
+        label id;
+
+        //- The first boundary
+        label bnd0;
+
+        //- The second boundary
+        label bnd1;
+
+    // Constructors
+
+        //- Construct null
+        interfaceEntry()
+        :
+            id(-1),
+            bnd0(-1),
+            bnd1(-1)
+        {}
+
+
+        //- Construct empty interface definition
+        interfaceEntry(const label index)
+        :
+            id(index),
+            bnd0(-1),
+            bnd1(-1)
+        {}
+
+
+        //- Construct from components
+        interfaceEntry
+        (
+            const label index,
+            const label boundary0,
+            const label boundary1
+        )
+        :
+            id(index),
+            bnd0(boundary0),
+            bnd1(boundary1)
+        {}
+
+
+    // Access
+
+        //- Check for in-place interfaces
+        static bool isInPlace(const std::string& configurationType)
+        {
+            return configurationType == "IN_PLACE";
+        }
+
+
+        //- True if the boundary id is in this interface
+        bool inInterface(label bndId) const
+        {
+            return bndId == bnd0 || bndId == bnd1;
+        }
+
+
+        //- Canonical name for boundary 0
+        word canonicalName0() const
+        {
+            return "Interface" + ::Foam::name(id) + "_0";
+        }
+
+        //- Canonical name for boundary 1
+        word canonicalName1() const
+        {
+            return "Interface" + ::Foam::name(id) + "_1";
+        }
+
+        //- Canonical name for boundary
+        word canonicalName(label bndId) const
+        {
+            if (bndId == bnd0)
+            {
+                return canonicalName0();
+            }
+            else if (bndId == bnd1)
+            {
+                return canonicalName1();
+            }
+            else
+            {
+                return word::null;
+            }
+        }
+
+
+    // IOstream Operators
+
+        friend Ostream& operator<<
+        (
+            Ostream& os,
+            const interfaceEntry& entry
+        )
+        {
+            os  << "(" << entry.bnd0 << " " << entry.bnd1 << ")";
+            return os;
+        }
+
+};
+
+
+/*---------------------------------------------------------------------------*\
+                  Class ccm::interfaceDefinitions Declaration
+\*---------------------------------------------------------------------------*/
+
+//- A list of available interface definitions
+class interfaceDefinitions
+:
+    public Map<interfaceEntry>
+{
+public:
+    // Constructor
+
+        //- Null construct
+        interfaceDefinitions()
+        :
+            Map<interfaceEntry>()
+        {}
+
+
+        //- Scan available interface entries for one matching this boundary id
+        bool add(interfaceEntry entry)
+        {
+            if
+            (
+                entry.id >= 0
+             && entry.bnd0 >= 0 && entry.bnd1 >= 0
+             && entry.bnd0 != entry.bnd1
+            )
+            {
+                this->set(entry.id, entry);
+                return true;
+            }
+            else
+            {
+                return false;
+            }
+        }
+
+
+        //- Scan available interface entries for one matching this boundary id
+        bool isInterface(label bndId)
+        {
+            forAllConstIter(Map<interfaceEntry>, *this, iter)
+            {
+                if (iter().inInterface(bndId))
+                {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+
+        //- Scan interface entries for one matching this boundary id
+        //  return the canonical name
+        word interfaceName(label bndId)
+        {
+            word ifname;
+            forAllConstIter(Map<interfaceEntry>, *this, iter)
+            {
+                ifname = iter().canonicalName(bndId);
+                if (!ifname.empty())
+                {
+                    break;
+                }
+            }
+
+            return ifname;
+        }
+
+
+    // IOstream Operators
+
+        friend Ostream& operator<<
+        (
+            Ostream& os,
+            const interfaceDefinitions& defs
+        )
+        {
+            os  << static_cast<const Map<interfaceEntry>& >(defs)
+                << nl;
+
+            return os;
+        }
+
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace ccm
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/conversion/ccm/reader/ccmReader.C b/src/conversion/ccm/reader/ccmReader.C
new file mode 100644
index 0000000000000000000000000000000000000000..5d0c6f11579837bdaf874a34af474e6949a316c8
--- /dev/null
+++ b/src/conversion/ccm/reader/ccmReader.C
@@ -0,0 +1,801 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2016 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 "ccmReader.H"
+#include "IFstream.H"
+#include "IOdictionary.H"
+#include "demandDrivenData.H"
+
+#include "ccmInternal.H"  // include last to avoid any strange interactions
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+// The cellTable integer options
+const char* Foam::ccm::reader::cellTableOpti[] =
+{
+    "MaterialId", "PorosityId", "SpinId", "GroupId", "ColorIdx", nullptr
+};
+
+// The cellTable string options - "Label" handled separately
+const char* Foam::ccm::reader::cellTableOptstr[] =
+{
+    "MaterialType", nullptr
+};
+
+// * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
+
+// Simulate GetEntityIndex
+// CCMIOGetEntityIndex(nullptr, nodeId, &internalId);
+int Foam::ccm::reader::ccmGetEntityIndex(ccmNODE node)
+{
+    int intval = 0;
+    char name[kCCMIOMaxStringLength + 1];
+
+    if
+    (
+        CCMIOGetName
+        (
+            nullptr,
+            node,
+            name
+        )
+        == kCCMIONoErr
+    )
+    {
+        char const *pos = strrchr(name, '-');
+
+        if (!pos)
+        {
+            intval = 0;
+        }
+        else
+        {
+            // Negative ID number
+            if (pos != name && (*(pos - 1) == '-'))
+                --pos;
+
+            intval = atoi(++pos);
+        }
+    }
+
+    return intval;
+}
+
+
+std::string Foam::ccm::reader::ccmReadNodestr
+(
+    const char* opt,
+    ccmNODE node
+)
+{
+    char *strval = 0;
+    std::string str;
+
+    if
+    (
+        CCMIOReadNodestr
+        (
+            nullptr,
+            node,
+            opt,
+            &strval
+        )
+     == kCCMIONoErr
+     && strval
+    )
+    {
+        str = strval;
+    }
+
+    // allocated by CCMIOReadNodestr
+    if (strval)
+    {
+        free(strval);
+    }
+
+    return str;
+}
+
+
+std::string Foam::ccm::reader::ccmReadOptstr
+(
+    const char* opt,
+    ccmID node
+)
+{
+    std::string str;
+    int len = 0;
+
+    if
+    (
+        CCMIOReadOptstr
+        (
+            nullptr,
+            node,
+            opt,
+            &len,
+            nullptr
+        )
+     == kCCMIONoErr
+     && len
+    )
+    {
+        char* strval = new char[len + 1];
+        if
+        (
+            CCMIOReadOptstr
+            (
+                nullptr,
+                node,
+                opt,
+                &len,
+                strval
+            )
+         == kCCMIONoErr
+        )
+        {
+            strval[len] = '\0';
+            str = strval;
+        }
+        delete[] strval;
+    }
+
+    return str;
+}
+
+
+Foam::word Foam::ccm::reader::validateWord
+(
+    const std::string& str
+)
+{
+    std::string::size_type ngood = 0;
+    bool prefix = false;
+    bool first  = true;
+
+    for
+    (
+        std::string::const_iterator iter = str.begin();
+        iter != str.end();
+        ++iter
+    )
+    {
+        if (word::valid(*iter))
+        {
+            ++ngood;
+            if (first)
+            {
+                first = false;
+
+                // start with a digit? need to prefix with '_'
+                if (isdigit(*iter))
+                {
+                    prefix = true;
+                    ++ngood;
+                }
+            }
+        }
+    }
+
+    if (ngood == str.size() && !prefix)
+    {
+        return str;
+    }
+
+    Foam::word out;
+    out.resize(ngood);
+    ngood = 0;
+
+    Foam::word::iterator iter2 = out.begin();
+    for
+    (
+        std::string::const_iterator iter1 = str.begin();
+        iter1 != str.end();
+        ++iter1
+    )
+    {
+        register char c = *iter1;
+
+        if (Foam::word::valid(c))
+        {
+            if (prefix)
+            {
+                prefix = false;
+                *(iter2++) = '_';
+                ++ngood;
+            }
+            *(iter2++) = c;
+            ++ngood;
+        }
+    }
+
+    out.resize(ngood);
+
+    return out;
+}
+
+
+// read map data and check error
+void Foam::ccm::reader::readMap
+(
+    const ccmID& mapId,
+    labelList& data
+)
+{
+    if (globalState_->hasError())
+    {
+        return;
+    }
+
+    CCMIOReadMap
+    (
+        &(globalState_->error),
+        mapId,
+        data.begin(),
+        kCCMIOStart,
+        kCCMIOEnd
+    );
+    assertNoError("error reading map");
+}
+
+
+// readProblemDescription:
+// - get cellTable and boundaryRegion information
+// - some regions defined here may be unused by our mesh
+void Foam::ccm::reader::readProblemDescription
+(
+    const ccmID& probNode
+)
+{
+    readInterfaceDefinitions();
+    readProblemDescription_boundaryRegion(probNode);
+    readProblemDescription_cellTable(probNode);
+
+#ifdef DEBUG_CCMIOREAD
+    Info<< "InterfaceDefinitions: " << interfaceDefinitions_ << nl
+        << "cellTable" << cellTable_ << nl
+        << "boundaryRegion" << boundaryRegion_ << endl;
+#endif
+}
+
+
+// readInterfaceDefinitions:
+// - get /InterfaceDefinitions. used by STARCCM to define in-place interfaces, etc
+// - only handle in-place one here
+void Foam::ccm::reader::readInterfaceDefinitions()
+{
+    interfaceDefinitions_.clear();
+
+    // /InterfaceDefinitions
+    // ---------------------
+
+    CCMIONode rootNode;
+    CCMIONode interfaceDefNode;
+
+    // CCMIOID -> CCMIONODE
+    if
+    (
+        CCMIOGetEntityNode
+        (
+            nullptr,
+            (globalState_->root),
+            &rootNode
+        )
+     == kCCMIONoErr
+
+        // get "/InterfaceDefinitions"
+     &&
+        CCMIOGetNode
+        (
+            nullptr,
+            rootNode,
+            "InterfaceDefinitions",
+            &interfaceDefNode
+        )
+     == kCCMIONoErr
+    )
+    {
+        CCMIONode interfaceNode;
+
+        // simulate CCMIONextEntity
+        for
+        (
+            int index = 0;
+            CCMIOGetNextChildWithLabel
+            (
+                nullptr,
+                interfaceDefNode,
+                "Interface",
+                &index,
+                &interfaceNode
+            ) == kCCMIONoErr;
+            /* nop */
+        )
+        {
+            interfaceEntry ifentry(ccmGetEntityIndex(interfaceNode));
+
+            if
+            (
+                CCMIOReadNodei
+                (
+                    nullptr,
+                    interfaceNode,
+                    "Boundary0",
+                    &(ifentry.bnd0)
+                )
+             == kCCMIONoErr
+
+             && CCMIOReadNodei
+                (
+                    nullptr,
+                    interfaceNode,
+                    "Boundary1",
+                    &(ifentry.bnd1)
+                )
+             == kCCMIONoErr
+
+                // only handle 'IN_PLACE' interfaces
+             && interfaceEntry::isInPlace
+                (
+                    ccmReadNodestr("Configuration", interfaceNode)
+                )
+            )
+            {
+                interfaceDefinitions_.add(ifentry);
+            }
+        }
+    }
+}
+
+
+// readProblemDescription - get boundaryRegion information
+//
+// CCM Node: /ProblemDescriptions/ProblemDescription-N/BoundaryRegion-N
+//
+// Requires InterfaceDefinitions first
+void Foam::ccm::reader::readProblemDescription_boundaryRegion
+(
+    const ccmID& probNode
+)
+{
+    if (option().useNumberedNames())
+    {
+        Info<< "using numbered patch/zone names" << endl;
+    }
+
+    boundaryRegion_.clear();
+
+    ccmID node;
+
+    // boundaryRegion information
+    // --------------------------
+    for
+    (
+        int nodeI = 0;
+        CCMIONextEntity
+        (
+            nullptr,
+            probNode,
+            kCCMIOBoundaryRegion,
+            &nodeI,
+            &node
+        ) == kCCMIONoErr;
+        /* nop */
+    )
+    {
+        // read boundaryRegionId
+        int Id = 0;
+        CCMIOGetEntityIndex
+        (
+            &(globalState_->error),
+            node,
+            &Id
+        );
+        assertNoError("error reading boundaryRegion index");
+
+        dictionary dict;
+
+        // Read boundary type
+        // (inlet|outlet|symmetry|wall|
+        //   cyclic|stagnation|pressure|baffle|freestream)
+        {
+            const char* opt = "BoundaryType";
+            std::string str = ccmReadOptstr(opt, node);
+
+            if (str.empty())
+            {
+                dict.add(opt, "empty");
+            }
+            else if (str == "internal")
+            {
+                // old PROSTAR bug: "monitoring" mislabeled as "internal"
+                dict.add(opt, "monitoring");
+            }
+            else
+            {
+                dict.add(opt, validateWord(str));
+            }
+        }
+
+
+        // Read boundary name:
+        // - from 'Label' field (STARCCM+)
+        // - from 'BoundaryName' field (PROSTAR and/or STARCCM+)
+        // - fallback: generate one from boundary type and boundaryRegionId
+        {
+            const char* opt = "Label";
+            std::string str;
+
+            if (option().useNumberedNames())
+            {
+                str = "patch_" + ::Foam::name(Id);
+            }
+            else if
+            (
+                option().renameInterfaces()
+             && interfaceDefinitions_.isInterface(Id)
+            )
+            {
+#ifdef DEBUG_CCMIOREAD
+                Info<< "boundary is on an interface: remap name for  " << Id << endl;
+#endif
+                // substitute immediately with interface name
+                str = interfaceDefinitions_.interfaceName(Id);
+            }
+            else if
+            (
+                (str = ccmReadOptstr(opt, node)).empty()
+             && (str = ccmReadOptstr("BoundaryName", node)).empty()
+            )
+            {
+                // fallback
+                str = word(dict["BoundaryType"]) + "_" + ::Foam::name(Id);
+            }
+
+            if (!str.empty())
+            {
+                dict.add(opt, validateWord(str));
+            }
+        }
+
+        boundaryRegion_.insert(Id, dict);
+    }
+}
+
+
+// readProblemDescription - get cellTable information
+//
+// CCM Node: /ProblemDescriptions/ProblemDescription-N/CellType-N
+//
+void Foam::ccm::reader::readProblemDescription_cellTable
+(
+    const ccmID& probNode
+)
+{
+    cellTable_.clear();
+
+    ccmID node;
+
+    // cellTable information
+    // ---------------------
+    for
+    (
+        int nodeI = 0;
+        CCMIONextEntity
+        (
+            nullptr,
+            probNode,
+            kCCMIOCellType,
+            &nodeI,
+            &node
+        ) == kCCMIONoErr;
+        /* nop */
+    )
+    {
+        // Read cellTableId (CellType)
+        int Id = 0;
+        CCMIOGetEntityIndex
+        (
+            &(globalState_->error),
+            node,
+            &Id
+        );
+        assertNoError("error reading cellTable index");
+
+        dictionary dict;
+
+        // Special treatment for "Label"
+        {
+            const char* opt = "Label";
+            std::string str;
+
+            if (!option().useNumberedNames())
+            {
+                str = ccmReadOptstr(opt, node);
+            }
+
+            if (str.empty())
+            {
+                str = "zone_" + ::Foam::name(Id);
+            }
+
+            dict.add(opt, validateWord(str));
+        }
+
+
+        // Other string options
+        for (int i=0; cellTableOptstr[i]; ++i)
+        {
+            const char* opt = cellTableOptstr[i];
+            std::string str = ccmReadOptstr(opt, node);
+
+            if (!str.empty())
+            {
+                dict.add(opt, validateWord(str));
+            }
+        }
+
+        // Add non-zero integer options
+        for (int i=0; cellTableOpti[i]; ++i)
+        {
+            const char* opt = cellTableOpti[i];
+            int intval;
+
+            if
+            (
+                CCMIOReadOpti
+                (
+                    nullptr,
+                    node,
+                    opt,
+                    &intval
+                )
+             == kCCMIONoErr
+             && intval != 0
+            )
+            {
+                dict.add(opt, intval);
+            }
+        }
+
+        cellTable_.insert(Id, dict);
+    }
+}
+
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+void Foam::ccm::reader::printInfo() const
+{
+    Info<< "Mesh Information" << nl
+        << "----------------" << nl
+        << "boundingBox: " << boundBox(points_) << nl
+        << "nPoints: " << nPoints_ << nl
+        << "nCells: " << nCells_ << nl
+        << "nFaces: " << nFaces_ << nl
+        << "nInternalFaces: " << nInternalFaces_ << nl
+        << "nBaffles: " << bafInterfaces_.size() << endl;
+}
+
+
+bool Foam::ccm::reader::readGeometry
+(
+    const scalar scaleFactor
+)
+{
+    detectGeometry();
+
+    if (geometryStatus_ == OKAY)
+    {
+        // Sanity on the scaling factor
+
+        readMeshTopology(scaleFactor <= VSMALL ? 1 : scaleFactor);
+        reorderMesh();
+
+        // assume success if we have points and cells
+        if (nCells_ && points_.size())
+        {
+            geometryStatus_ = READ;
+        }
+        else
+        {
+            geometryStatus_ = BAD;
+        }
+    }
+
+    return (geometryStatus_ == OKAY || geometryStatus_ == READ);
+}
+
+
+bool Foam::ccm::reader::hasGeometry()
+{
+    detectGeometry();
+    return (geometryStatus_ == OKAY || geometryStatus_ == READ);
+}
+
+
+bool Foam::ccm::reader::hasSolution()
+{
+    detectSolution();
+    return (solutionStatus_ == OKAY || solutionStatus_ == READ);
+}
+
+
+void Foam::ccm::reader::writeMesh
+(
+    const polyMesh& mesh,
+    IOstream::streamFormat fmt
+) const
+{
+    mesh.removeFiles();
+
+    Info<< "Writing polyMesh" << endl;
+    mesh.writeObject
+    (
+        fmt,
+        IOstream::currentVersion,
+        IOstream::UNCOMPRESSED
+    );
+    writeAux(mesh);
+}
+
+
+bool Foam::ccm::reader::remapMeshInfo
+(
+    const objectRegistry& registry,
+    const fileName& remappingDictName
+)
+{
+    dictionary remapDict;
+
+    if (remappingDictName.empty())
+    {
+        remapDict = IOdictionary
+        (
+            IOobject
+            (
+                "remapping",
+                "constant",
+                registry,
+                IOobject::READ_IF_PRESENT,
+                IOobject::NO_WRITE,
+                false
+            )
+        );
+    }
+    else
+    {
+        // specified (absolute) name: treat like MUST_READ
+        remapDict = dictionary(IFstream(remappingDictName)());
+    }
+
+    bool ok = false;
+
+    if (remapDict.empty())
+    {
+        return false;
+    }
+
+
+    // merge specified cellTable entries together
+    if (remapDict.isDict("cellTable"))
+    {
+        cellTable_.combine(remapDict.subDict("cellTable"), cellTableId_);
+        ok = true;
+    }
+
+    // rename boundaries
+    if (remapDict.isDict("boundaryRegion"))
+    {
+        boundaryRegion_.rename(remapDict.subDict("boundaryRegion"));
+        ok = true;
+    }
+
+    return ok;
+}
+
+
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+// Construct from reading a file
+Foam::ccm::reader::reader
+(
+    const fileName& file,
+    const reader::options& opts
+)
+:
+    base(),
+    options_(new options(opts)),
+    geometryStatus_(UNKNOWN),
+    solutionStatus_(UNKNOWN),
+    interfaceDefinitions_(),
+    boundaryRegion_(),
+    cellTable_(),
+    nPoints_(0),
+    nInternalFaces_(0),
+    nFaces_(0),
+    nCells_(0),
+    points_(),
+    faces_(),
+    faceOwner_(),
+    faceNeighbour_(),
+    bafInterfaces_(),
+    domInterfaces_(),
+    origFaceId_(),
+    origCellId_(),
+    cellTableId_(),
+    origBndId_(),
+    patchSizes_(),
+    solutionTable_(),
+    fieldTable_(),
+    lagrangianTable_()
+{
+    if (!option().keptSomeRegion())
+    {
+        FatalErrorIn("ccm::reader(const fileName&)")
+            << "must retain at least one region type: fluid | porous | solid"
+            << exit(FatalError);
+    }
+
+    if (!isFile(file, false))
+    {
+        FatalErrorIn("ccm::reader(const fileName&)")
+            << "Cannot read file " << file
+            << exit(FatalError);
+    }
+
+    // Reinitialize error state from the return value
+    globalState_->error = CCMIOOpenFile
+    (
+        nullptr,
+        file.c_str(),
+        kCCMIORead,
+        &(globalState_->root)
+    );
+    assertNoError("Error opening file for reading");
+
+    detectGeometry();
+    detectSolution();
+}
+
+
+// * * * * * * * * * * * * * * * * Destructor  * * * * * * * * * * * * * * * //
+
+Foam::ccm::reader::~reader()
+{
+    close();
+    deleteDemandDrivenData(options_);
+}
+
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+const Foam::ccm::reader::options& Foam::ccm::reader::option() const
+{
+    return *options_;
+}
+
+
+// ************************************************************************* //
diff --git a/src/conversion/ccm/reader/ccmReader.H b/src/conversion/ccm/reader/ccmReader.H
new file mode 100644
index 0000000000000000000000000000000000000000..aa15978dd35314388bf4de9c407fe4c41c19a710
--- /dev/null
+++ b/src/conversion/ccm/reader/ccmReader.H
@@ -0,0 +1,710 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2016 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::ccm::reader
+
+Description
+    Reads CCM files as written by PROSTAR/STARCCM
+
+    - arbitrary polyhedral meshs
+    - writes interfaces (baffles) to constant/polymesh/interfaces
+    - experimental handling of monitoring (internal) boundaries
+    - does not handle cyclics. Use createPatch to recreate these
+    - does patch names only if they are in the problem description
+    - reads cell and face solution data
+    - reads Lagrangian data
+
+    The Default_Boundary_Region (region 0) is a special region that serves
+    two purposes:
+    -# it contains all wall and baffle boundaries that have not otherwise
+       been assigned.
+    -# it holds the outer bounds of flow domains (fluid/porous/solid)
+
+    The CCM node \c Meshes/FaceBasedTopology/Cells/Interfaces holds the mapping
+    of the corresponding mesh faces, which can be used to merge these internal
+    boundaries.
+
+    If solid cells exist, there are three possible courses of action:
+    -# Remove all solid cells for subsequent flow calculations.
+       This is the default.
+    -# Treat solid cells like fluid cells, but convert the corresponding
+       Default_Boundary_Region to Default_Boundary_Solid for easier
+       identification.
+       This treatment is useful for visualization purposes.
+    -# Move solid cells to a separate mesh region.
+       This would be useful for conjugate heat transfer, but
+       is not implemented.
+
+    \par Files
+
+    The <tt>constant/remapping</tt> file is an \c IOdictionary that is
+    \c READ_IF_PRESENT and can be used to remap certain information. eg,
+
+    \verbatim
+        // rename/combine cellTable entries
+        //   newName ( listOldNames );
+        cellTable
+        {
+            fluid ( inletRegion outletRegion );
+            cat1  ( CAT1 "cat1_(Back|Front|Gamma)" );
+        }
+
+        // rename boundary regions
+        //   newName oldName;
+        boundaryRegion
+        {
+            inlet_4  inlet_1;
+            inlet_5  inlet_2;
+            inlet_6  inlet_3;
+        }
+    \endverbatim
+
+    The <tt>constant/boundaryRegion</tt> file is an \c IOMap<dictionary>
+    that is written. It contains the boundary type and names. eg,
+
+    \verbatim
+        (
+            0
+            {
+                BoundaryType    wall;
+                Label           Default_Boundary_Region;
+            }
+            1
+            {
+                BoundaryType    inlet;
+                Label           inlet_1;
+            }
+            ...
+
+            4
+            {
+                BoundaryType    pressure;
+                Label           outlet;
+            }
+        )
+    \endverbatim
+
+    The <tt>constant/cellTable</tt> file is an \c IOMap<dictionary> that is
+    written. It contains the cellTable information.
+    eg,
+
+    \verbatim
+        (
+            1
+            {
+                Label           inletRegion;
+                MaterialType    fluid;
+                MaterialId      1;
+            }
+            2
+            {
+                Label           cat1;
+                MaterialType    fluid;
+                MaterialId      1;
+                PorosityId      1;
+            }
+            3
+            {
+                Label           outletRegion;
+                MaterialType    fluid;
+                MaterialId      1;
+            }
+        )
+    \endverbatim
+
+Note
+    this class is still under development
+    - any/all of the class names and members may change
+
+SourceFiles
+    ccmReader.C
+    ccmReaderAux.C
+    ccmReaderMesh.C
+    ccmReaderSolution.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef ccmReader_H
+#define ccmReader_H
+
+#include "ccmBase.H"
+#include "STARCDCore.H"
+
+#include "labelList.H"
+#include "ListOps.H"
+#include "polyMesh.H"
+#include "boundaryRegion.H"
+#include "cellTable.H"
+#include "ccmInterfaceDefinitions.H"
+#include "ccmSolutionTable.H"
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+namespace ccm
+{
+
+// * * * * * * * * * * * * * Forward Declarations  * * * * * * * * * * * * * //
+
+class ccmID;
+class ccmNODE;
+class ccmGlobalState;
+class reader;
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+/*---------------------------------------------------------------------------*\
+                         Class ccm::reader Declaration
+\*---------------------------------------------------------------------------*/
+
+class reader
+:
+    public base,
+    protected fileFormats::STARCDCore
+{
+public:
+    // Forward Declarations
+    class options;
+
+
+private:
+
+    // Static Data
+
+        //- cellTable integer options
+        static const char* cellTableOpti[];
+
+        //- cellTable string options
+        static const char* cellTableOptstr[];
+
+
+    // Private data
+
+        //- Reader options
+        const options* options_;
+
+
+        //- Enumeration defining the status of a ccmio node
+        enum nodeStatus
+        {
+            UNKNOWN, BAD, OKAY, READ
+        };
+
+
+        //- status of the geometry (topology) information
+        nodeStatus geometryStatus_;
+
+        //- status of the solution (field) information
+        nodeStatus solutionStatus_;
+
+        // Persistent data
+
+        //- ccm node: /InterfaceDefinitions
+        interfaceDefinitions interfaceDefinitions_;
+
+        //- ccm node: ProblemDescriptions/boundaryRegion
+        boundaryRegion boundaryRegion_;
+
+        //- ccm node: ProblemDescriptions/cellType
+        cellTable cellTable_;
+
+        //- Number of points
+        label nPoints_;
+
+        //- Number of internal faces
+        label nInternalFaces_;
+
+        //- Number of faces
+        label nFaces_;
+
+        //- Number of cells
+        label nCells_;
+
+        //- Minimum mesh data
+
+        //- Points
+        pointField points_;
+
+        //- Faces
+        faceList faces_;
+
+        //- Face-owner cells
+        labelList faceOwner_;
+
+        //- Face-neighbour cells
+        labelList faceNeighbour_;
+
+        // List of interface pairs (baffles)
+        List<labelPair> bafInterfaces_;
+
+        //- List of interface pairs between mesh regions (domains)
+        List<labelPair> domInterfaces_;
+
+        //- Face sets for monitoring
+        HashTable<labelList> monitoringSets_;
+
+
+    // Mesh Maps
+
+        //- Map to original face id
+        labelList origFaceId_;
+
+        //- Map to original cell id
+        labelList origCellId_;
+
+        //- Cell table id for each cell
+        labelList cellTableId_;
+
+        //- List of the original ccm boundary region number
+        labelList origBndId_;
+
+        //- Patching and region info
+        labelList patchSizes_;
+
+        //- Solution information
+        solutionTable   solutionTable_;
+
+        //- Field information
+        fieldTable      fieldTable_;
+
+        //- Field information
+        fieldTable      lagrangianTable_;
+
+
+    // Private Member Functions
+
+        //- Disallow default bitwise copy construct
+        reader(const reader&) = delete;
+
+        //- Disallow default bitwise assignment
+        void operator=(const reader&) = delete;
+
+
+        //- Calculate patch starts from given index with current patch sizes
+        inline labelList patchStartList(label initial) const;
+
+        //- Report mesh sizes to stdout
+        void printSizes() const;
+
+        //- Simulate CCMIOGetEntityIndex for Nodes (not ids)
+        int ccmGetEntityIndex(ccmNODE node);
+
+        //- Read string option from specified ccm node
+        //  return empty string on failure
+        std::string ccmReadNodestr(const char* opt, ccmNODE node);
+
+        //- Read string option from specified ccm node
+        //  return empty string on failure
+        std::string ccmReadOptstr(const char* opt, ccmID node);
+
+        //- Strip invalid characters, prefix leading digit with '_'
+        static word validateWord(const std::string&);
+
+        //- Read map data and check error
+        void readMap(const ccmID& mapId, labelList& data);
+
+        //- Determine if the geometry looks good
+        bool detectGeometry();
+
+        //- Get interfaces, cellTable and boundaryRegion information
+        void readProblemDescription(const ccmID& probNode);
+
+        //- Get /InterfaceDefinitions, used by STARCCM to define in-place interfaces, etc
+        //  only handle in-place (IN_PLACE) ones at the moment
+        void readInterfaceDefinitions();
+
+        //- Get boundaryRegion information
+        void readProblemDescription_boundaryRegion(const ccmID& probNode);
+
+        //- Get cellTable information
+        void readProblemDescription_cellTable(const ccmID& probNode);
+
+        //- Read the geometry without any sorting or renumbering
+        void readMeshTopology(const scalar scaleFactor=1.0);
+
+        //- Read the vertices
+        labelList readVertices
+        (
+            const ccmID& verticesNode,
+            const scalar scaleFactor = 1.0
+        );
+
+        //- Read the cells
+        void readCells(const ccmID& topoNode);
+
+        //- Read the interfaces
+        void readInterfaces(const ccmID& cellsNode);
+
+        //- Read the monitoring
+        void readMonitoring(const ccmID& topoId);
+
+        //- Move solid faces from Default_Boundary_Region -> Default_Boundary_Solid
+        void juggleSolids();
+
+        //- Remove unwanted fluid/porous/solid regions
+        void removeUnwanted();
+
+        //- Remove interfaces with face >= nFace
+        void validateInterface(List<labelPair>&);
+
+        //- Renumber interface faces
+        void renumberInterfaces(const List<label>&);
+
+        //- Remove interfaces between domains (fluid/porosity; fluid/solid, etc)
+        //  reorganize baffle interfaces into [0-N/2; N/2-N] lists at the
+        //  beginning of the corresponding patch
+        void cleanupInterfaces();
+
+        //- Merge the points and faces of in-place conformal interfaces
+        void mergeInplaceInterfaces();
+
+        //- Re-order mesh and ensure upper-triangular order
+        void reorderMesh();
+
+        //- Write interface (baffle) mapping
+        void writeInterfaces(const objectRegistry&) const;
+
+        //- Write List<label> in constant/polyMesh
+        void writeMeshLabelList
+        (
+            const objectRegistry& registry,
+            const word& propertyName,
+            const labelList& list,
+            IOstream::streamFormat fmt = IOstream::ASCII
+        ) const;
+
+        // polyMesh Friend Functions
+        void addPatches(polyMesh&) const;
+
+        //- Add faceZones based on monitoring boundary conditions
+        void addFaceZones(polyMesh&) const;
+
+        //- Get information about all available solutions
+        bool detectSolution();
+
+        //- Get information about available fields
+        //  assume that all fields are available for all solution intervals
+        void determineFieldInfo(const ccmID& fieldSetNode, fieldTable&);
+
+
+    // Static Members
+
+        //- Get map of porous regions
+        static Map<word> selectPorous(const Map<dictionary>&);
+
+
+public:
+
+    // Static Members
+
+        //- Warn about repeated name
+        static void warnDuplicates(const word& context, const wordList&);
+
+
+    // Constructors
+
+        //- Open a file for reading
+        reader(const fileName&, const options& opts);
+
+
+    //- Destructor (closes file)
+    ~reader();
+
+
+    // Member Functions
+
+    // Access
+
+        //- Reference to the reader options
+        const reader::options& option() const;
+
+
+        //- Construct the polyMesh from the read geometry
+        //  provide optional remapping dictionary
+        autoPtr<polyMesh> mesh
+        (
+            const objectRegistry& registry,
+            const fileName& remappingDictName = fileName::null
+        );
+
+
+        // label nPoints() const { return nPoints_; }
+        // label nInternalFaces() const { return nInternalFaces_; }
+        // label nFaces() const { return nFaces_; }
+        // label nCells() const { return nCells_; }
+
+        // const faceList&   faces() const { return faces_; }
+        // const labelList&  faceOwner() const { return faceOwner_; }
+        // const labelList&  faceNeighbour() const { return faceNeighbour_; }
+        // const pointField& points() const { return points_; }
+
+
+    // Check
+
+        //- Return true if file has geometry associated with it
+        bool hasGeometry();
+
+        //- Return true if file has solutions associated with it
+        bool hasSolution();
+
+
+    // Edit
+
+        //- Remap cellTable and boundaryRegion according to dictionary
+        bool remapMeshInfo
+        (
+            const objectRegistry& registry,
+            const fileName& remappingDictName = fileName::null
+        );
+
+
+    // Write
+
+        //- Write the polyMesh
+        void writeMesh
+        (
+            const polyMesh&,
+            IOstream::streamFormat fmt = IOstream::BINARY
+        ) const;
+
+        //- Write cellTable, boundaryRegion and interface information
+        void writeAux(const objectRegistry&) const;
+
+        //- Detect and read geometry if possible
+        bool readGeometry(const scalar scaleFactor = 1.0);
+
+        //- Print general information about the mesh
+        void printInfo() const;
+
+        //- Clear out some information after obtaining a polyMesh
+        void clearGeom();
+
+        //- Map to original cell Id
+        const labelList& origCellId() const
+        {
+            return origCellId_;
+        }
+
+        //- Map to original face Id
+        const labelList& origFaceId() const
+        {
+            return origFaceId_;
+        }
+
+        //- Return interface definitions map
+        const interfaceDefinitions& interfaceDefinitionsInfo() const
+        {
+            return interfaceDefinitions_;
+        }
+
+        //- Return boundaryRegion table
+        const boundaryRegion& boundaryTableInfo() const
+        {
+            return boundaryRegion_;
+        }
+
+        //- Return cell table
+        const cellTable& cellTableInfo() const
+        {
+            return cellTable_;
+        }
+
+        //- Return a list of names corresponding to fluids
+        Map<word> fluids() const
+        {
+            return cellTable_.fluids();
+        }
+
+        //- Return a list of names corresponding to solids
+        Map<word> solids() const
+        {
+            return cellTable_.solids();
+        }
+
+        //- Return table of available solutions
+        const solutionTable& solutions()
+        {
+            detectSolution();
+            return solutionTable_;
+        }
+
+        //- Return table of available fields
+        const fieldTable& fields()
+        {
+            detectSolution();
+            return fieldTable_;
+        }
+
+        //- Return table of available lagrangian fields
+        const fieldTable& lagrangian()
+        {
+            detectSolution();
+            return lagrangianTable_;
+        }
+
+        //- Read solution and field combination
+        tmp<scalarField> readField
+        (
+            const word& solutionName,
+            const word& fieldName,
+            const bool wallData = false
+        );
+
+};
+
+
+/*---------------------------------------------------------------------------*\
+                         Class ccm::reader::options Declaration
+\*---------------------------------------------------------------------------*/
+
+class reader::options
+{
+    // Private data
+
+        //- Keep fluid regions (default true)
+        bool keepFluid_;
+
+        //- Keep porous regions (default true)
+        bool keepPorous_;
+
+        //- Keep solid regions (default true)
+        bool keepSolid_;
+
+        //- Merge in-place interfaces (default true)
+        bool mergeInterfaces_;
+
+        //- Rename interface boundaries as InterfaceN_0, InterfaceN_1 (default true)
+        bool renameInterfaces_;
+
+        //- Combine identically named boundaries (default false)
+        bool combineBoundaries_;
+
+        //- Remove baffles by merging their respective faces (default false)
+        bool removeBaffles_;
+
+        //- Use numbered names (eg, patch_0, zone_0) instead of human-readable
+        //  names (default false)
+        bool useNumberedNames_;
+
+        //- merge tolerance for points (default 0.05e-3)
+        scalar mergeTol_;
+
+        //- Value to assign for undefined solutions (default: NaN)
+        scalar undefScalar_;
+
+
+public:
+
+    // Constructors
+
+        //- Construct with the defaults
+        options();
+
+
+    // Member Functions
+
+    // Access
+
+        //- Keep fluid regions (default true)
+        bool keepFluid() const;
+
+        //- Keep porous regions (default true)
+        bool keepPorous() const;
+
+        //- Keep solid regions (default true)
+        bool keepSolid() const;
+
+        //- Some region (fluid, porous, solid) is kept.
+        bool keptSomeRegion() const;
+
+        //- Merge in-place interfaces (default true)
+        bool mergeInterfaces() const;
+
+        //- Rename interface boundaries as InterfaceN_0, InterfaceN_1 (default true)
+        bool renameInterfaces() const;
+
+        //- Combine identically named boundaries (default false)
+        bool combineBoundaries() const;
+
+        //- Remove baffles by merging their respective faces (default false)
+        bool removeBaffles() const;
+
+        //- Use numbered names (eg, patch_0, zone_0) instead of human-readable
+        //  names (default false)
+        bool useNumberedNames() const;
+
+        //- Merge tolerance for points (default 0.05e-3)
+        scalar mergeTol() const;
+
+        //- Value to assign for undefined solutions (default: NaN)
+        scalar undefScalar() const;
+
+
+    // Edit
+
+        //- Keep fluid regions
+        void keepFluid(bool);
+
+        //- Keep porous regions
+        void keepPorous(bool);
+
+        //- Keep solid regions
+        void keepSolid(bool);
+
+        //- Merge in-place interfaces
+        void mergeInterfaces(bool);
+
+        //- Rename interface boundaries as InterfaceN_0, InterfaceN_1
+        void renameInterfaces(bool);
+
+        //- Combine identically named boundaries
+        void combineBoundaries(bool);
+
+        //- Remove baffles by merging their respective faces
+        void removeBaffles(bool);
+
+        //- Use numbered names (eg, patch_0, zone_0) instead of human-readable
+        void useNumberedNames(bool);
+
+        //- Merge tolerance for points (default 0.05e-3)
+        void mergeTol(const scalar&);
+
+        //- Value to assign for undefined solutions (default: NaN)
+        void undefScalar(const scalar&);
+
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace ccm
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/conversion/ccm/reader/ccmReaderAux.C b/src/conversion/ccm/reader/ccmReaderAux.C
new file mode 100644
index 0000000000000000000000000000000000000000..f820dcb7f531871860e9232691cb518d8e121550
--- /dev/null
+++ b/src/conversion/ccm/reader/ccmReaderAux.C
@@ -0,0 +1,214 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2016 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/>.
+
+Description
+    read/write auxiliary files for aiding STARCD/OPENFOAM interoperability
+
+    - cellTable information
+    - boundaryRegion information
+    - interface information
+
+\*----------------------------------------------------------------------------*/
+
+#include "OFstream.H"
+#include "ccmReader.H"
+#include "ccmInternal.H"  // include last to avoid any strange interactions
+
+
+// * * * * * * * * * * * * * * * Static Functions  * * * * * * * * * * * * * //
+
+Foam::Map<Foam::word> Foam::ccm::reader::selectPorous
+(
+    const Map<dictionary>& table
+)
+{
+    Map<word> lookup;
+
+    forAllConstIter(Map<dictionary>, table, iter)
+    {
+        if (iter().lookupOrDefault<label>("PorosityId", 0) != 0)
+        {
+            lookup.insert
+            (
+                iter.key(),
+                iter().lookupOrDefault<word>
+                (
+                    "Label",
+                    "cellTable_" + Foam::name(iter.key())
+                )
+            );
+        }
+    }
+
+    return lookup;
+}
+
+
+void Foam::ccm::reader::warnDuplicates
+(
+    const word& context,
+    const wordList& lst
+)
+{
+    HashTable<label> hashed(lst.size());
+    bool duplicates = false;
+
+    forAll(lst, elemI)
+    {
+        // Check duplicate name
+        HashTable<label>::iterator iter = hashed.find(lst[elemI]);
+        if (iter != hashed.end())
+        {
+            (*iter)++;
+            duplicates = true;
+        }
+        else
+        {
+            hashed.insert(lst[elemI], 1);
+        }
+    }
+
+    // Warn about duplicate names
+    if (duplicates)
+    {
+        Info << nl << "WARNING: " << context << " with identical names:";
+        forAllConstIter(HashTable<label>, hashed, iter)
+        {
+            if (*iter > 1)
+            {
+                Info << "  " << iter.key();
+            }
+        }
+        Info << nl << endl;
+    }
+}
+
+
+// * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
+
+void Foam::ccm::reader::writeInterfaces
+(
+    const objectRegistry& registry
+) const
+{
+    // Write constant/polyMesh/interface
+    IOList<labelList> ioObj
+    (
+        IOobject
+        (
+            "interfaces",
+            "constant",
+            polyMesh::meshSubDir,
+            registry,
+            IOobject::NO_READ,
+            IOobject::AUTO_WRITE,
+            false
+        )
+    );
+
+    ioObj.note() = "as yet unsupported interfaces (baffles)";
+
+    Info<< "Writing " << ioObj.name() << " to " << ioObj.objectPath() << endl;
+
+    OFstream os(ioObj.objectPath());
+    ioObj.writeHeader(os);
+
+    os  << bafInterfaces_;
+}
+
+
+// Write List<label> in constant/polyMesh
+// - this is crucial for later conversion back to ccm/starcd
+void Foam::ccm::reader::writeMeshLabelList
+(
+    const objectRegistry& registry,
+    const word& propertyName,
+    const labelList& list,
+    IOstream::streamFormat fmt
+) const
+{
+    // Write constant/polyMesh/propertyName
+    IOList<label> ioObj
+    (
+        IOobject
+        (
+            propertyName,
+            "constant",
+            polyMesh::meshSubDir,
+            registry,
+            IOobject::NO_READ,
+            IOobject::AUTO_WRITE,
+            false
+        ),
+        list
+    );
+
+    ioObj.note() = "persistent data for STARCD <-> OPENFOAM translation";
+    Info<< "Writing " << ioObj.name() << " to " << ioObj.objectPath() << endl;
+
+    // NOTE:
+    // The cellTableId is an integer and almost always < 1000, thus ASCII
+    // will be compacter than binary and makes external scripting easier
+    //
+    ioObj.writeObject
+    (
+        fmt,
+        IOstream::currentVersion,
+        IOstream::UNCOMPRESSED
+    );
+}
+
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+void Foam::ccm::reader::writeAux
+(
+    const objectRegistry& registry
+) const
+{
+    cellTable_.writeDict(registry);
+    boundaryRegion_.writeDict(registry);
+    writeInterfaces(registry);
+
+    // Write origCellId as List<label>
+    writeMeshLabelList
+    (
+        registry,
+        "origCellId",
+        origCellId_,
+        IOstream::BINARY
+    );
+
+    // Write cellTableId as List<label>
+    // - this is crucial for later conversion back to ccm/starcd
+    writeMeshLabelList
+    (
+        registry,
+        "cellTableId",
+        cellTableId_,
+        IOstream::ASCII
+    );
+}
+
+
+// ************************************************************************* //
diff --git a/src/conversion/ccm/reader/ccmReaderMesh.C b/src/conversion/ccm/reader/ccmReaderMesh.C
new file mode 100644
index 0000000000000000000000000000000000000000..aa5058c3cefbdea6b79d7df8ace12d7e734e6b25
--- /dev/null
+++ b/src/conversion/ccm/reader/ccmReaderMesh.C
@@ -0,0 +1,2696 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2016 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 "ccmReader.H"
+
+#include "emptyPolyPatch.H"
+#include "symmetryPolyPatch.H"
+#include "wallPolyPatch.H"
+#include "SortableList.H"
+#include "IFstream.H"
+#include "OFstream.H"
+#include "IOdictionary.H"
+
+#include "ccmBoundaryInfo.H"
+#include "PackedList.H"
+#include "uindirectPrimitivePatch.H"
+#include "SortableList.H"
+#include "mergePoints1.H"
+#include "ListOps1.H"
+
+#include "ccmInternal.H" // include last to avoid any strange interactions
+
+
+// * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
+
+Foam::labelList Foam::ccm::reader::patchStartList
+(
+    label initial
+) const
+{
+    labelList startLst(patchSizes_.size(), Zero);
+
+    label patchFaceI = initial;
+    forAll(patchSizes_, patchI)
+    {
+        startLst[patchI] = patchFaceI;
+        patchFaceI += patchSizes_[patchI];
+    }
+
+    return startLst;
+}
+
+
+void Foam::ccm::reader::printSizes() const
+{
+    Info<<"nPoints:" << nPoints_
+        << "  nCells:" << nCells_
+        << "  nFaces:" << nFaces_
+        << "  nInternalFaces:" << nInternalFaces_
+        << endl;
+}
+
+
+// Determine if the geometry looks good.
+// We'll insist that everything be on the first ("default") state,
+// and is on the same file, and has a problem description
+//
+// The detection reads the problem description as well
+//
+bool Foam::ccm::reader::detectGeometry()
+{
+    // Call once
+    if (geometryStatus_ != UNKNOWN)
+    {
+        return (geometryStatus_ == OKAY || geometryStatus_ == READ);
+    }
+
+    // Explicitly restricted to 'default' state node
+    ccmID stateNode;
+
+    // Get associated problem description
+    ccmID probNode;
+
+    // Only check the first processor
+    ccmID processorNode;
+    int procI = 0;
+
+    // Geometry needs vertices and topology
+    ccmID verticesNode, topoNode;
+
+    if
+    (
+        CCMIOGetState
+        (
+            nullptr,
+            (globalState_->root),
+            "default",
+            &probNode,
+            &stateNode
+        )
+     == kCCMIONoErr
+
+     && CCMIONextEntity
+        (
+            nullptr,
+            stateNode,
+            kCCMIOProcessor,
+            &procI,
+            &processorNode
+        )
+     == kCCMIONoErr
+
+     && CCMIOReadProcessor
+        (
+            nullptr,
+            processorNode,
+            &verticesNode,
+            &topoNode,
+            nullptr,        // Ignore initialField
+            nullptr         // Ignore solutionNode
+        )
+     == kCCMIONoErr
+
+     && CCMIOIsFromSameFile((globalState_->root), verticesNode)
+     && CCMIOIsFromSameFile((globalState_->root), topoNode)
+     && CCMIOIsValidEntity(probNode)
+    )
+    {
+        readProblemDescription(probNode);
+        geometryStatus_ = OKAY;
+    }
+    else
+    {
+        // Missing/incomplete geometry node and/or problem node
+        geometryStatus_ = BAD;
+    }
+
+    return (geometryStatus_ == OKAY || geometryStatus_ == READ);
+}
+
+
+// read the geometry without any sorting
+void Foam::ccm::reader::readMeshTopology
+(
+    const scalar scaleFactor
+)
+{
+    ccmID verticesNode, topoNode;
+
+    // Use first ("default") state node
+    ccmID stateNode;
+    int stateI = 0;
+
+    // Use the first processor to find the mesh nodes
+    ccmID processorNode;
+    int procI = 0;
+
+    if
+    (
+        CCMIONextEntity
+        (
+            nullptr,
+            (globalState_->root),
+            kCCMIOState,
+            &stateI,
+            &stateNode
+        )
+     == kCCMIONoErr
+
+     && CCMIONextEntity
+        (
+            nullptr,
+            stateNode,
+            kCCMIOProcessor,
+            &procI,
+            &processorNode
+        )
+     == kCCMIONoErr
+
+     && CCMIOReadProcessor
+        (
+            nullptr,
+            processorNode,
+            &verticesNode,
+            &topoNode,
+            nullptr,        // Ignore initialField
+            nullptr         // Ignore solutionNode
+        )
+     == kCCMIONoErr
+    )
+    {
+        labelList origPointId = readVertices(verticesNode, scaleFactor);
+        readCells(topoNode);
+        readMonitoring(topoNode);
+
+        // Renumber vertex labels (ccm -> Foam)
+        {
+            label maxId = max(origPointId);
+            labelList mapToFoam(invert(maxId+1, origPointId));
+
+            forAll(faces_, faceI)
+            {
+                inplaceRenumber(mapToFoam, faces_[faceI]);
+            }
+        }
+        origPointId.clear();
+
+        // Renumber owners/neighbours cell labels (ccm -> Foam)
+        {
+            label maxId = max(origCellId_);
+            labelList mapToFoam(invert(maxId+1, origCellId_));
+
+            inplaceRenumber(mapToFoam, faceOwner_);
+            inplaceRenumber(mapToFoam, faceNeighbour_);
+        }
+
+        // Juggle solids into fluid as required
+        juggleSolids();
+
+        // Report sizes
+        printSizes();
+
+        // Remove unwanted fluid/porous/solid types
+        removeUnwanted();
+
+        // Collapse interfaces between domains (eg, fluid|porosity)
+        cleanupInterfaces();
+
+        // Use point merge to join conformal interfaces (STARCCM)
+        mergeInplaceInterfaces();
+    }
+}
+
+
+// readVertices:
+// 1) read the vertex data
+// 2) read the map (which maps the index into the data array with the Id number)
+//
+// returns the original point Id
+Foam::labelList Foam::ccm::reader::readVertices
+(
+    const ccmID& verticesNode,
+    const scalar scaleFactor
+)
+{
+    int dims = 1;
+    float scale = 1.0;
+
+    CCMIOSize size = 0;
+    ccmID mapId;
+
+#ifdef DEBUG_CCMIOREAD
+    Info<< "readVertices()" << endl;
+#endif
+
+    // Determine dimensions and mapId
+    CCMIOEntitySize
+    (
+        &(globalState_->error),
+        verticesNode,
+        &size,
+        nullptr
+    );
+
+    CCMIOReadVerticesd
+    (
+        &(globalState_->error),
+        verticesNode,
+        &dims,
+        &scale,
+        &mapId,
+        nullptr,
+        kCCMIOStart,
+        kCCMIOEnd
+    );
+    assertNoError("problem finding 'Vertices' node");
+
+    if (dims != 3)
+    {
+        FatalErrorIn("ccm::reader::readVertices")
+            << "can only handle 3-dimensional vertices"
+            << exit(FatalError);
+    }
+
+    nPoints_ = size;
+    labelList origPointId(nPoints_);
+
+    readMap
+    (
+        mapId,
+        origPointId
+    );
+
+    // Temporary storage for the points - reading piecemeal is much slower
+    List<scalar> vrts(3*nPoints_);
+
+    // The ccm data is double precision too
+    CCMIOReadVerticesd
+    (
+        &(globalState_->error),
+        verticesNode,
+        nullptr,
+        nullptr,
+        nullptr,
+        vrts.begin(),
+        kCCMIOStart,
+        kCCMIOEnd
+    );
+    assertNoError("problem reading 'Vertices' node");
+
+    // Convert to foam Points
+    points_.setSize(nPoints_);
+
+    scalar effectiveScale = scale * scaleFactor;
+    forAll(points_, i)
+    {
+        points_[i].x() = effectiveScale * vrts[i*3];
+        points_[i].y() = effectiveScale * vrts[i*3+1];
+        points_[i].z() = effectiveScale * vrts[i*3+2];
+    }
+
+    vrts.clear();
+
+#ifdef DEBUG_CCMIOREAD
+    Info<< "readVertices: " << nPoints_ << endl;
+#endif
+
+    return origPointId;
+}
+
+
+// readCells:
+// - read faces, faceOwner and faceNeighbour
+// - finally read interfaces
+void Foam::ccm::reader::readCells
+(
+    const ccmID& topoNode
+)
+{
+    CCMIOSize size = 0;
+    ccmID cellsNode, mapId;
+    ccmID nodeId;
+
+#ifdef DEBUG_CCMIOREAD
+    Info<< "readCells()" << endl;
+#endif
+
+    // Determine dimensions and mapId information for 'Cells'
+    CCMIOGetEntity
+    (
+        &(globalState_->error),
+        topoNode,
+        kCCMIOCells,
+        0,
+        &cellsNode
+    );
+
+    CCMIOEntitySize
+    (
+        &(globalState_->error),
+        cellsNode,
+        &size,
+        nullptr
+    );
+    assertNoError("cannot get 'Cells' node");
+
+    nCells_ = size;
+
+#ifdef DEBUG_CCMIOREAD
+    Info<< "readCells: " << nCells_ << endl;
+#endif
+
+    // Store cell ids so that we know which cells are which
+    origCellId_.setSize(nCells_);
+    cellTableId_.setSize(nCells_);
+
+    CCMIOReadCells
+    (
+        &(globalState_->error),
+        cellsNode,
+        &mapId,
+        cellTableId_.begin(),
+        kCCMIOStart,
+        kCCMIOEnd
+    );
+    assertNoError("Error reading 'Cells' node");
+
+    readMap
+    (
+        mapId,
+        origCellId_
+    );
+
+    // Determine dimensions and mapId information for 'InternalFaces'
+    CCMIOGetEntity
+    (
+        &(globalState_->error),
+        topoNode,
+        kCCMIOInternalFaces,
+        0,
+        &nodeId
+    );
+    CCMIOEntitySize
+    (
+        &(globalState_->error),
+        nodeId,
+        &size,
+        nullptr
+    );
+    nInternalFaces_ = size;
+
+    nFaces_ = nInternalFaces_;
+
+    // First pass:
+    //
+    // Determine patch sizes before reading internal faces
+    // also determine the original boundary regions
+
+    label nPatches = 0;
+    DynamicList<ccmBoundaryInfo> bndInfo;
+
+    // Number of children in the parent node is more than number of patches,
+    // but is a good start for allocation
+    if
+    (
+        CCMIOGetNumberOfChildren
+        (
+            nullptr,
+            topoNode.node,
+            &nPatches
+        ) == kCCMIONoErr
+    )
+    {
+        bndInfo.setCapacity(nPatches);
+    }
+
+    for
+    (
+        int index = 0;
+        CCMIONextEntity
+        (
+            nullptr,
+            topoNode,
+            kCCMIOBoundaryFaces,
+            &index,
+            &nodeId
+        ) == kCCMIONoErr
+     && CCMIOEntitySize
+        (
+            &(globalState_->error),
+            nodeId,
+            &size,
+            nullptr
+        ) == kCCMIONoErr;
+        /* nop */
+    )
+    {
+        ccmBoundaryInfo info;
+        info.size = size;
+        nFaces_   += size;
+
+        CCMIOGetEntityIndex
+        (
+            &(globalState_->error),
+            nodeId,
+            &(info.ccmIndex)
+        );
+
+        // Name directly from the node (eg, STARCCM)
+        info.setPatchName(ccmReadOptstr("Label", nodeId));
+
+        // Lookup the name, type from boundary region info:
+        Map<dictionary>::iterator dictIter = boundaryRegion_.find(info.ccmIndex);
+        if (dictIter != boundaryRegion_.end())
+        {
+            word patchName(dictIter()["Label"]);
+            word patchType(dictIter()["BoundaryType"]);
+
+            if (!patchName.empty())
+            {
+                info.patchName = patchName;
+            }
+
+            if (!patchType.empty())
+            {
+                info.patchType = patchType;
+            }
+
+            // Optional, but potentially useful information:
+            dictIter().add("BoundaryIndex", info.ccmIndex);
+            dictIter().add("size", info.size);
+        }
+
+        bndInfo.append(info);
+    }
+
+    // Redimension lists according to the overall sizes
+    faces_.setSize(nFaces_);
+    faceOwner_.setSize(nFaces_);
+    faceNeighbour_.setSize(nFaces_);
+    origFaceId_.setSize(nFaces_);
+
+
+    // May be too large, but is a good place start size
+    patchSizes_.setSize(bndInfo.size());
+    origBndId_.setSize(bndInfo.size());
+    patchSizes_ = 0;
+    origBndId_  = -1;
+    nPatches = 0;
+
+    HashTable<label, std::string> hashedNames;
+    if (option().combineBoundaries())
+    {
+        // Ensure all the interfaces are orderd up-front:
+        forAll(interfaceDefinitions_, interI)
+        {
+            const interfaceEntry& ifentry = interfaceDefinitions_[interI];
+
+            label info0Index = -1;
+            label info1Index = -1;
+
+            forAll(bndInfo, infoI)
+            {
+                if (bndInfo[infoI].ccmIndex == ifentry.bnd0)
+                {
+                    info0Index = infoI;
+                }
+                else if (bndInfo[infoI].ccmIndex == ifentry.bnd1)
+                {
+                    info1Index = infoI;
+                }
+            }
+
+            if (info0Index == info1Index || info0Index < 0 || info1Index < 0)
+            {
+                // this should never be able to happen
+                continue;
+            }
+
+            ccmBoundaryInfo& info0 = bndInfo[info0Index];
+            ccmBoundaryInfo& info1 = bndInfo[info1Index];
+
+            // Preserve interface order
+            info0.patchId = nPatches++;
+            info1.patchId = nPatches++;
+
+            // full safety:
+            info0.patchName = ifentry.canonicalName0();
+            info1.patchName = ifentry.canonicalName1();
+
+            hashedNames.insert(info0.patchName, info0Index);
+            hashedNames.insert(info1.patchName, info1Index);
+        }
+    }
+
+    forAll(bndInfo, infoI)
+    {
+        ccmBoundaryInfo& info = bndInfo[infoI];
+
+        if (info.patchId != -1)
+        {
+            // Already inserted - eg, as interface
+            origBndId_[info.patchId] = info.ccmIndex;
+        }
+        else
+        {
+            if (option().combineBoundaries())
+            {
+                // Check if patch name was already seen
+                HashTable<label, std::string>::const_iterator citer = hashedNames.find(info.patchName);
+                if (citer != hashedNames.end())
+                {
+                    info.patchId = bndInfo[citer()].patchId;
+                }
+                else
+                {
+                    hashedNames.insert(info.patchName, infoI);
+
+                    info.patchId = nPatches++;
+                    origBndId_[info.patchId] = info.ccmIndex;
+                }
+            }
+            else
+            {
+                info.patchId = nPatches++;
+                origBndId_[info.patchId] = info.ccmIndex;
+            }
+        }
+
+        patchSizes_[info.patchId] += info.size;
+    }
+
+    // Shrink to sizes actually used
+    patchSizes_.setSize(nPatches);
+    origBndId_.setSize(nPatches);
+
+    // Boundary info indices flattened and sorted by patchId
+
+    IndirectList<ccmBoundaryInfo> ccmLookupOrder(bndInfo, labelList());
+    {
+        DynamicList<label> addr(bndInfo.size());
+        for (int patchI = 0; patchI < nPatches; ++patchI)
+        {
+            forAll(bndInfo, infoI)
+            {
+                if (bndInfo[infoI].patchId == patchI)
+                {
+                    addr.append(infoI);
+                }
+            }
+        }
+
+        ccmLookupOrder.resetAddressing(addr.xfer());
+    }
+
+    if (option().combineBoundaries())
+    {
+        Info<<"patches combined by name: ";
+        if (nPatches == bndInfo.size())
+        {
+            Info<<"none" << endl;
+        }
+        else
+        {
+            Info<< bndInfo.size() << " into " << nPatches << endl;
+        }
+        // Info<< ccmLookupOrder << endl;
+    }
+
+
+    //
+    // Now we are ready to do the reading
+    //
+    CCMIOGetEntity
+    (
+        &(globalState_->error),
+        topoNode,
+        kCCMIOInternalFaces,
+        0,
+        &nodeId
+    );
+
+    // get allocation sizes
+    CCMIOReadFaces
+    (
+        &(globalState_->error),
+        nodeId,
+        kCCMIOInternalFaces,
+        &mapId,
+        &size,
+        nullptr,
+        kCCMIOStart,
+        kCCMIOEnd
+    );
+
+    List<label> mapData(nInternalFaces_);
+    List<label> faceCells(2*nInternalFaces_);
+    List<label> ccmFaces(size);
+
+    CCMIOReadFaces
+    (
+        &(globalState_->error),
+        nodeId,
+        kCCMIOInternalFaces,
+        nullptr,
+        nullptr,
+        ccmFaces.begin(),
+        kCCMIOStart,
+        kCCMIOEnd
+    );
+
+    CCMIOReadFaceCells
+    (
+        &(globalState_->error),
+        nodeId,
+        kCCMIOInternalFaces,
+        faceCells.begin(),
+        kCCMIOStart,
+        kCCMIOEnd
+    );
+    assertNoError("Error reading internal faces");
+
+    readMap
+    (
+        mapId,
+        mapData
+    );
+
+    // Copy into Foam list
+    // ccmFaces are organized as [nVert vrt1 .. vrtN]
+    unsigned int pos = 0;
+    for (label faceI = 0; faceI < nInternalFaces_; ++faceI)
+    {
+        origFaceId_[faceI]    = mapData[faceI];
+        faceOwner_[faceI]     = faceCells[2*faceI];
+        faceNeighbour_[faceI] = faceCells[2*faceI+1];
+        face& f = faces_[faceI];
+
+        f.setSize(ccmFaces[pos++]);
+        forAll(f, fp)
+        {
+            f[fp] = ccmFaces[pos++];
+        }
+    }
+
+    // Read the boundary faces
+    // ~~~~~~~~~~~~~~~~~~~~~~~
+    label patchFaceI = nInternalFaces_;
+
+    forAll(ccmLookupOrder, lookupI)
+    {
+        const ccmBoundaryInfo& info = ccmLookupOrder[lookupI];
+        const unsigned int patchSize = info.size;
+
+        if
+        (
+            CCMIOGetEntity
+            (
+                &(globalState_->error),
+                topoNode,
+                kCCMIOBoundaryFaces,
+                info.ccmIndex,
+                &nodeId
+            ) == kCCMIONoErr
+        )
+        {
+            CCMIOReadFaces
+            (
+                &(globalState_->error),
+                nodeId,
+                kCCMIOBoundaryFaces,
+                &mapId,
+                &size,    // size needed for the faces
+                nullptr,
+                kCCMIOStart,
+                kCCMIOEnd
+            );
+
+            mapData.setSize(patchSize);
+            faceCells.setSize(patchSize);
+            ccmFaces.setSize(size);
+
+            readMap
+            (
+                mapId,
+                mapData
+            );
+
+            CCMIOReadFaces
+            (
+                &(globalState_->error),
+                nodeId,
+                kCCMIOBoundaryFaces,
+                nullptr,
+                nullptr,
+                ccmFaces.begin(),
+                kCCMIOStart,
+                kCCMIOEnd
+            );
+            CCMIOReadFaceCells
+            (
+                &(globalState_->error),
+                nodeId,
+                kCCMIOBoundaryFaces,
+                faceCells.begin(),
+                kCCMIOStart,
+                kCCMIOEnd
+            );
+            assertNoError("Error reading boundary face cells - index " + ::Foam::name(info.ccmIndex));
+
+            // Copy into Foam list
+            // ccmFaces are organized as [nVert vrt1 .. vrtN]
+            unsigned int pos = 0;
+            for (unsigned int i = 0; i < patchSize; ++i, ++patchFaceI)
+            {
+                origFaceId_[patchFaceI]    = mapData[i];
+                faceOwner_[patchFaceI]     = faceCells[i];
+                faceNeighbour_[patchFaceI] = -1;
+
+                face& f = faces_[patchFaceI];
+
+                f.setSize(ccmFaces[pos++]);
+                forAll(f, fp)
+                {
+                    f[fp] = ccmFaces[pos++];
+                }
+            }
+        }
+        else
+        {
+            assertNoError("Error reading boundary faces - index " + ::Foam::name(info.ccmIndex));
+        }
+    }
+
+    readInterfaces(cellsNode);
+}
+
+
+// Read any interfaces
+//   1) interfaces between domains (fluid/solid, fluid/porosity)
+//   2) PROSTAR baffles
+//
+void Foam::ccm::reader::readInterfaces
+(
+    const ccmID& cellsNode
+)
+{
+#ifdef DEBUG_CCMIOREAD
+    Info<< "readInterfaces()" << endl;
+#endif
+
+    label nBaffleInterface = 0, nInterfaceTotal = 0;
+    CCMIOIndex size = 0, dims = 0;
+
+    bafInterfaces_.clear();
+    domInterfaces_.clear();
+
+    ccmID interfaceNode;
+
+    if
+    (
+        CCMIOGetEntity
+        (
+            nullptr,
+            cellsNode,
+            kCCMIOInterfaces,
+            0,
+            &interfaceNode
+        )
+     == kCCMIONoErr
+
+     && CCMIOGetOptInfo
+        (
+            nullptr,
+            interfaceNode,
+            "FaceIds",
+            nullptr,
+            &size,
+            &dims,
+            nullptr
+        )
+     == kCCMIONoErr
+
+     && size > 0 && dims == 2
+    )
+    {
+        nInterfaceTotal = size;
+    }
+    else
+    {
+        return;
+    }
+
+    // Get ProstarBaffles
+    // formatted as [ prostarId faceId1 faceId2 celltableId ]
+    if
+    (
+        CCMIOGetOptInfo
+        (
+            nullptr,
+            interfaceNode,
+            "ProstarBaffles",
+            nullptr,
+            &size,
+            nullptr,
+            nullptr
+        )
+     == kCCMIONoErr
+
+     && size > 0
+    )
+    {
+        // Be paranoid - force an integral value of 4
+        nBaffleInterface = (size - size % 4) / 4;
+    }
+
+    // Determine sizes
+    label nDomainInterface = nInterfaceTotal - nBaffleInterface;
+
+    // Maximum dimension
+    List<int> mapData(max(2 * nInterfaceTotal, 4 * nBaffleInterface), -1);
+
+    bafInterfaces_.setSize(nBaffleInterface);
+    domInterfaces_.setSize(nDomainInterface);
+
+    // Face number mapping
+    label maxId = max(origFaceId_);
+    labelList toFoamFaces(invert(maxId+1, origFaceId_));
+
+    if (nBaffleInterface > 0)
+    {
+        mapData = -1;
+
+        CCMIOReadOpt1i
+        (
+            &(globalState_->error),
+            interfaceNode,
+            "ProstarBaffles",
+            mapData.begin(),
+            kCCMIOStart,
+            kCCMIOEnd
+        );
+        assertNoError("problem reading interface 'ProstarBaffles'");
+
+
+        // Copy/compress the desired entries (cannot use a SubList)
+        // Transform
+        //   from [ prostarId faceId1 faceId2 celltableId ]
+        //     to [ faceId1 faceId2 ]
+        for (label i=0; i < nBaffleInterface; ++i)
+        {
+            mapData[i*2]   = mapData[i*4+1];
+            mapData[i*2+1] = mapData[i*4+2];
+        }
+
+        for (label i=2*nBaffleInterface; i < 4*nBaffleInterface; ++i)
+        {
+            mapData[i] = -1;
+        }
+
+        // Translate ccm faceId -> foam faceId
+        inplaceRenumber(toFoamFaces, mapData);
+
+        forAll(bafInterfaces_, i)
+        {
+            bafInterfaces_[i][0] = mapData[i*2];
+            bafInterfaces_[i][1] = mapData[i*2+1];
+        }
+    }
+
+    // Baffles are not domInterfaces, use hash to skip them
+    SubList<label> subMap(mapData, 2*nBaffleInterface);
+    labelHashSet hashedFace(subMap);
+
+    mapData = -1;
+
+    CCMIOReadOpt2i
+    (
+        &(globalState_->error),
+        interfaceNode,
+        "FaceIds",
+        mapData.begin(),
+        kCCMIOStart,
+        kCCMIOEnd
+    );
+    assertNoError("problem reading interface 'FaceIds'");
+
+    // Translate ccm faceId -> foam faceId
+    // newer version handles negatives indices
+    inplaceRenumber(toFoamFaces, mapData);
+
+    nDomainInterface = 0;
+    for (label i=0; i < nInterfaceTotal; ++i)
+    {
+        label face0 = mapData[i*2];
+        label face1 = mapData[i*2+1];
+
+        if
+        (
+            !hashedFace.found(face0)
+         && !hashedFace.found(face1)
+        )
+        {
+            domInterfaces_[nDomainInterface][0] = face0;
+            domInterfaces_[nDomainInterface][1] = face1;
+            ++nDomainInterface;
+            if (nDomainInterface >= domInterfaces_.size())
+            {
+                break;
+            }
+        }
+    }
+
+    // Truncate for extra safety
+    domInterfaces_.setSize(nDomainInterface);
+}
+
+
+// Read monitoring faces
+//
+void Foam::ccm::reader::readMonitoring
+(
+    const ccmID& topoId
+)
+{
+#ifdef DEBUG_CCMIOREAD
+    Info<< "readMonitoring()" << endl;
+#endif
+
+#ifdef WITH_MONITORING
+    CCMIONode topoNode, monitorParent;
+    CCMIONode monitorNode;
+
+    // CCMIOID -> CCMIONODE
+    if
+    (
+        CCMIOGetEntityNode
+        (
+            nullptr,
+            topoId,
+            &topoNode
+        )
+     == kCCMIONoErr
+
+     // Get "/Meshes/FaceBasedTopology/MonitorBoundaryRegions"
+     && CCMIOGetNode
+        (
+            nullptr,
+            topoNode,
+            "MonitorBoundaryRegions",
+            &monitorParent
+        )
+     == kCCMIONoErr
+    )
+    {
+        labelList toFoamFaces(invert(nFaces_+1, origFaceId_));
+
+        // Simulate CCMIONextEntity
+        for
+        (
+            int index = 0;
+            CCMIOGetNextChildWithLabel
+            (
+                nullptr,
+                monitorParent,
+                "boundaryFaces",
+                &index,
+                &monitorNode
+            ) == kCCMIONoErr;
+         /* nop */
+        )
+        {
+#ifdef DEBUG_MONITORING
+            Info<< "index = " << index << endl;
+#endif
+
+            int nMonFaces = 0;
+            // Simulate EntitySize ?
+            CCMIOReadNodei
+            (
+                nullptr,
+                monitorNode,
+                "NumFaces",
+                &nMonFaces
+            );
+
+            int ccmRegionId = ccmGetEntityIndex(monitorNode);
+
+            ccmID mapId;
+
+            int idVal;
+            CCMIOReadNodei
+            (
+                nullptr,
+                monitorNode,
+                "MapId",
+                &idVal
+            );
+
+#ifdef DEBUG_MONITORING
+            Info<< "monitoring mapId " << idVal
+                << " with nFaces = " << nMonFaces
+                << endl;
+#endif
+            // Is it risky changing parents here?
+            CCMIOGetEntity
+            (
+                nullptr,
+                (globalState_->root),
+                kCCMIOMap,
+                idVal,
+                &mapId
+            );
+
+            List<label> mapData(nMonFaces);
+            // mapData.setSize(nMonFaces);
+            // faceCells.setSize(nMonFaces);
+
+            readMap
+            (
+                mapId,
+                mapData
+            );
+
+#ifdef DEBUG_MONITORING
+            Info<< "map: " << mapData << nl
+                << "toFoam: " << toFoamFaces
+                << endl;
+#endif
+
+            // Translate ccm faceId -> foam faceId
+            // newer version handles negatives indices
+            inplaceRenumber(toFoamFaces, mapData);
+
+#ifdef DEBUG_MONITORING
+            Info<< "map: " << mapData << nl
+                << "ccmRegionId: " << ccmRegionId << endl;
+#endif
+
+            Map<dictionary>::const_iterator
+                iter = boundaryRegion_.find(ccmRegionId);
+
+            word zoneName;
+            if (iter != boundaryRegion_.end())
+            {
+                iter().lookup("Label") >> zoneName;
+            }
+            else
+            {
+                zoneName = "monitoring_" + Foam::name(ccmRegionId);
+            }
+
+            monitoringSets_.insert(zoneName, mapData);
+
+            // CCMIONode subNode;
+            //
+            //- simulate ReadFaceCells with kCCMIOBoundaryFaces
+            // CCMIOGetNode(nullptr, childNode, "Cells", &subNode);
+            // CCMIORead1i(nullptr, subNode, faceCells.begin(), kCCMIOStart, kCCMIOEnd);
+            //
+            // Info << "cells: " << faceCells << endl;
+        }
+    }
+#endif
+}
+
+
+// Move solid faces from Default_Boundary_Region -> Default_Boundary_Solid
+void Foam::ccm::reader::juggleSolids()
+{
+    if (!option().keepSolid())
+    {
+        return;
+    }
+
+    // Find "Default_Boundary_Region"
+    label defaultBoundaryRegion = boundaryRegion_.findIndex
+    (
+        defaultBoundaryName
+    );
+
+    // Find "Default_Boundary_Solid"
+    label defaultBoundarySolid = boundaryRegion_.findIndex
+    (
+        defaultSolidBoundaryName
+    );
+
+    // Cannot do anything if Default_Boundary_Region does not exist
+    // or if Default_Boundary_Solid already exists
+    if
+    (
+        defaultBoundaryRegion < 0
+     || defaultBoundarySolid >= 0
+    )
+    {
+        return;
+    }
+
+    // Identify solid cells
+    // ~~~~~~~~~~~~~~~~~~~~
+    label nSolids = 0;
+    boolList solidCells(cellTableId_.size(), false);
+    {
+        Map<word> solidMap = cellTable_.solids();
+
+        forAll(cellTableId_, cellI)
+        {
+            if (solidMap.found(cellTableId_[cellI]))
+            {
+                solidCells[cellI] = true;
+                ++nSolids;
+            }
+        }
+    }
+
+    if (!nSolids)
+    {
+        return;
+    }
+
+
+    // The corresponding Foam patch
+    const label patchIndex  = findIndex(origBndId_, defaultBoundaryRegion);
+    const label nPatchFaces = patchSizes_[patchIndex];
+
+    labelList patchStarts(patchStartList(nInternalFaces_));
+    label adjustPatch = 0;
+    for (label i = 0; i < nPatchFaces; ++i)
+    {
+        label faceI = patchStarts[patchIndex] + i;
+        label cellI = faceOwner_[faceI];
+
+        if (solidCells[cellI])
+        {
+            ++adjustPatch;
+        }
+    }
+
+    // No solid cells on the Default_Boundary_Region
+    if (!adjustPatch)
+    {
+        return;
+    }
+
+
+    // Insert Default_Boundary_Solid immediately after Default_Boundary_Region
+    // then we only need to adjust a single patch and can easily re-merge
+    // later
+    label nPatches = patchSizes_.size();
+    patchStarts.setSize(nPatches+1, 0);
+    patchSizes_.setSize(nPatches+1, 0);
+    origBndId_.setSize(nPatches+1, 0);
+
+    // make room for new entry
+    for (label i = nPatches; i > patchIndex; --i)
+    {
+        patchStarts[i]   = patchStarts[i-1];
+        patchSizes_[i]   = patchSizes_[i-1];
+        origBndId_[i] = origBndId_[i-1];
+    }
+
+    // Adjust start and sizes
+    patchSizes_[patchIndex] -= adjustPatch;
+    patchSizes_[patchIndex+1] = adjustPatch;
+    patchStarts[patchIndex+1] = patchStarts[patchIndex] + patchSizes_[patchIndex];
+
+    origBndId_[patchIndex+1] = boundaryRegion_.append
+    (
+        dictionary
+        (
+            IStringStream
+            (
+                "BoundaryType wall;"
+                "Label " + word(defaultSolidBoundaryName) + ";"
+            )()
+        )
+    );
+
+    label fluidFace = patchStarts[patchIndex];
+    label solidFace = patchStarts[patchIndex+1];
+
+    labelList oldToNew(identity(nFaces_));
+    for (label i = 0; i < nPatchFaces; ++i)
+    {
+        label faceI = patchStarts[patchIndex] + i;
+        label cellI = faceOwner_[faceI];
+
+        if (solidCells[cellI])
+        {
+            oldToNew[faceI] = solidFace++;
+        }
+        else
+        {
+            oldToNew[faceI] = fluidFace++;
+        }
+    }
+
+    // Re-order faces, owners/neighbours
+    inplaceReorder(oldToNew, faces_);
+    inplaceReorder(oldToNew, faceOwner_);
+    inplaceReorder(oldToNew, faceNeighbour_);
+    inplaceReorder(oldToNew, origFaceId_);
+
+    renumberInterfaces(oldToNew);
+}
+
+
+// In CCM, separate mesh domains are used for fluid/porous/solid
+// Thus the fluid/porous/solid cells correspond uniquely to a face owner
+void Foam::ccm::reader::removeUnwanted()
+{
+    // Identify fluid/porous/solid cells for removal
+    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+    label nRemove = 0;
+    boolList removeCells(cellTableId_.size(), false);
+
+    {
+        Map<word> fluidMap  = cellTable_.fluids();
+        Map<word> porousMap = selectPorous(cellTable_);
+        Map<word> solidMap  = cellTable_.solids();
+        Map<word> removeMap;
+
+        forAll(cellTableId_, cellI)
+        {
+            label tableId = cellTableId_[cellI];
+
+            if
+            (
+                porousMap.found(tableId)
+              ? !option().keepPorous()
+              : fluidMap.found(tableId)
+              ? !option().keepFluid()
+              : solidMap.found(tableId)
+              ? !option().keepSolid()
+              : false
+            )
+            {
+                removeCells[cellI] = true;
+                ++nRemove;
+                removeMap.set(tableId, cellTable_.name(tableId));
+            }
+        }
+
+        if (nRemove)
+        {
+            Map<word> keepMap;
+
+            forAllConstIter(Map<dictionary>, cellTable_, iter)
+            {
+                const label tableId = iter.key();
+                if (!removeMap.found(tableId))
+                {
+                    keepMap.set(tableId, cellTable_.name(tableId));
+                }
+            }
+
+            Info<<"remove "<< nRemove << " cells in "
+                << removeMap.size() << " unwanted cellZone(s)" << nl;
+
+            forAllConstIter(Map<word>, removeMap, iter)
+            {
+                Info<< "    zone " << iter.key() << " : "<< iter() << nl;
+            }
+
+            Info<<"retain "<< (nCells_ - nRemove) << " cells in "
+                << keepMap.size() << " cellZone(s)" << nl;
+
+            forAllConstIter(Map<word>, keepMap, iter)
+            {
+                Info<< "    zone " << iter.key() << " : "<< iter() << nl;
+            }
+        }
+    }
+
+    if (!nRemove)
+    {
+        return;
+    }
+
+    // Remove all faces where the owner corresponds to a removed cell
+    // Adjust the nInternalFaces and patch sizes accordingly
+    label adjustInternal = 0;
+    labelList adjustPatchSize(patchSizes_.size(), 0);
+
+    label newFaceI = 0;
+    label oldFaceI = nFaces_ - 1;
+    labelList oldToNew(nFaces_, -1);
+    for (label faceI = 0; faceI < nFaces_; ++faceI)
+    {
+        label cellI = faceOwner_[faceI];
+        if (removeCells[cellI])
+        {
+            if (faceI < nInternalFaces_)
+            {
+                ++adjustInternal;
+            }
+            else
+            {
+                // need to adjust the patch sizes
+                label beg = nInternalFaces_;
+                forAll(patchSizes_, patchI)
+                {
+                    label end = beg + patchSizes_[patchI];
+
+                    if (faceI >= beg && faceI < end)
+                    {
+                        ++adjustPatchSize[patchI];
+                        break;
+                    }
+
+                    beg = end;
+                }
+            }
+
+            // Put discarded faces at the end of the list
+            oldToNew[faceI] = oldFaceI--;
+        }
+        else
+        {
+            if (newFaceI != faceI)
+            {
+                faces_[newFaceI] = faces_[faceI];
+                faceOwner_[newFaceI] = faceOwner_[faceI];
+                faceNeighbour_[newFaceI] = faceNeighbour_[faceI];
+                origFaceId_[newFaceI] = origFaceId_[faceI];
+            }
+
+            // New position for the face
+            oldToNew[faceI] = newFaceI++;
+        }
+    }
+
+    nFaces_ = newFaceI;
+
+    // Redimension
+    faces_.setSize(nFaces_);
+    faceOwner_.setSize(nFaces_);
+    faceNeighbour_.setSize(nFaces_);
+    origFaceId_.setSize(nFaces_);
+
+    // Adjust internal faces and patch sizes
+
+    nInternalFaces_ -= adjustInternal;
+    forAll(patchSizes_, patchI)
+    {
+        patchSizes_[patchI] -= adjustPatchSize[patchI];
+    }
+
+    renumberInterfaces(oldToNew);
+
+    // Renumber cells
+    label nCell = 0;
+    oldToNew.setSize(nCells_, -1);
+    for (label cellI = 0; cellI < nCells_; ++cellI)
+    {
+        if (!removeCells[cellI])
+        {
+            if (nCell != cellI)
+            {
+                origCellId_[nCell] = origCellId_[cellI];
+                cellTableId_[nCell] = cellTableId_[cellI];
+            }
+            oldToNew[cellI] = nCell;
+            ++nCell;
+        }
+    }
+
+    inplaceRenumber(oldToNew, faceOwner_);
+    inplaceRenumber(oldToNew, faceNeighbour_);
+
+    // Redimension
+    nCells_ = nCell;
+    origCellId_.setSize(nCells_);
+    cellTableId_.setSize(nCells_);
+
+
+    // Remove unused points - adjust points, faces accordingly
+    oldToNew.setSize(nPoints_);
+    oldToNew = -1;
+
+    // Mark up all the used points
+    forAll(faces_, faceI)
+    {
+        const labelList& facePoints = faces_[faceI];
+
+        forAll(facePoints, ptI)
+        {
+            ++oldToNew[facePoints[ptI]];
+        }
+    }
+
+    label nPointUsed = 0;
+    forAll(oldToNew, ptI)
+    {
+        if (oldToNew[ptI] >= 0)
+        {
+            oldToNew[ptI] = nPointUsed;
+            if (ptI != nPointUsed)
+            {
+                points_[nPointUsed] = points_[ptI];
+            }
+            ++nPointUsed;
+        }
+    }
+
+    nPoints_ = nPointUsed;
+    points_.setSize(nPoints_);
+
+    forAll(faces_, faceI)
+    {
+        inplaceRenumber(oldToNew, faces_[faceI]);
+    }
+
+    // Report sizes
+    printSizes();
+}
+
+
+void Foam::ccm::reader::validateInterface
+(
+    List<labelPair>& lst
+)
+{
+    label nElem = 0;
+    forAll(lst, elemI)
+    {
+        label face0 = lst[elemI][0];
+        label face1 = lst[elemI][1];
+
+        if (face0 < nFaces_ && face1 < nFaces_)
+        {
+            if (nElem != elemI)
+            {
+                lst[nElem][0] = face0;
+                lst[nElem][1] = face1;
+            }
+            ++nElem;
+        }
+    }
+    lst.setSize(nElem);
+}
+
+
+void Foam::ccm::reader::renumberInterfaces
+(
+    const labelList& oldToNew
+)
+{
+    forAll(domInterfaces_, elemI)
+    {
+        domInterfaces_[elemI][0] = oldToNew[domInterfaces_[elemI][0]];
+        domInterfaces_[elemI][1] = oldToNew[domInterfaces_[elemI][1]];
+    }
+
+    forAll(bafInterfaces_, elemI)
+    {
+        bafInterfaces_[elemI][0] = oldToNew[bafInterfaces_[elemI][0]];
+        bafInterfaces_[elemI][1] = oldToNew[bafInterfaces_[elemI][1]];
+    }
+
+    validateInterface(domInterfaces_);
+    validateInterface(bafInterfaces_);
+}
+
+
+//
+// 1) remove interfaces between domains (fluid/porosity; fluid/solid, etc)
+// 2) reorganize baffle interfaces into [0-N/2; N/2-N] lists at the beginning
+//    of the corresponding patch
+//
+void Foam::ccm::reader::cleanupInterfaces()
+{
+    validateInterface(bafInterfaces_);
+    validateInterface(domInterfaces_);
+
+    if (bafInterfaces_.size() <= 0 && domInterfaces_.size() <= 0)
+    {
+        Info<<"0 baffle interface pairs" << endl;
+        Info<<"0 domain interface pairs" << endl;
+        return;
+    }
+
+#ifdef DEBUG_BAFFLES
+    Info<< "baffle Interfaces " << bafInterfaces_ << nl
+        << "domain Interfaces " << domInterfaces_ << nl
+        << "nCells:" << nCells_ << nl
+        << "nFaces:" << nFaces_ << nl
+        << "patchSizes:"  << patchSizes_ << nl
+        << "nInternalFaces:" << nInternalFaces_ << endl;
+
+    forAll(domInterfaces_, elemI)
+    {
+        label face0 = domInterfaces_[elemI][0];
+        label face1 = domInterfaces_[elemI][1];
+
+        Info<< "interface [" << elemI << "] = "
+            << face0 << " - " << face1 << " own/neigh = "
+            << faceOwner_[face0] << "/" << faceNeighbour_[face0] << "  "
+            << faceOwner_[face1] << "/" << faceNeighbour_[face1] << endl;
+    }
+#endif
+
+    // Only reorder faces that need it
+    labelList oldToNew(nFaces_, -1);
+
+    // - move side0 face from domInterfaces to join the internal faces
+    // - move the redundant side1 to the end of the list for later deletion
+    label begOfList = nInternalFaces_;
+    label endOfList = nFaces_ - 1;
+
+    // The patch sizes (and the start) will definitely change
+    const labelList origPatchStarts(patchStartList(nInternalFaces_));
+    labelList adjustPatchSize(patchSizes_.size(), 0);
+    labelList bafflePatchCount(patchSizes_.size(), 0);
+
+    // The new dimensions after merging the domain interfaces:
+    nInternalFaces_ += domInterfaces_.size();
+    nFaces_         -= domInterfaces_.size();
+
+    Info<< domInterfaces_.size() << " domain interface pairs";
+    if (domInterfaces_.size())
+    {
+        Info<<" to merge" << endl;
+        printSizes();
+    }
+    else
+    {
+        Info<< endl;
+    }
+
+    forAll(domInterfaces_, elemI)
+    {
+        label face0 = domInterfaces_[elemI][0];
+        label face1 = domInterfaces_[elemI][1];
+
+        oldToNew[face0] = begOfList++;
+        oldToNew[face1] = endOfList--;     // End of list for truncationx
+
+        // face0 gets a new neighbour, face1 loses its owner
+        faceNeighbour_[face0] = faceOwner_[face1];
+        faceOwner_[face1] = -1;
+
+        // Need to adjust the patch sizes
+        forAll(patchSizes_, patchI)
+        {
+            label beg = origPatchStarts[patchI];
+            label end = beg + patchSizes_[patchI];
+
+            if (face0 >= beg && face0 < end)
+            {
+                ++adjustPatchSize[patchI];
+            }
+            if (face1 >= beg && face1 < end)
+            {
+                ++adjustPatchSize[patchI];
+            }
+        }
+    }
+
+    // Count the number of baffles per patch
+    forAll(bafInterfaces_, elemI)
+    {
+        label face0 = bafInterfaces_[elemI][0];
+        label face1 = bafInterfaces_[elemI][1];
+
+        forAll(patchSizes_, patchI)
+        {
+            label beg = origPatchStarts[patchI];
+            label end = beg + patchSizes_[patchI];
+
+            if (face0 >= beg && face0 < end)
+            {
+                ++bafflePatchCount[patchI];
+            }
+            if (face1 >= beg && face1 < end)
+            {
+                ++bafflePatchCount[patchI];
+            }
+        }
+    }
+
+
+    if (option().removeBaffles())
+    {
+        // The new dimensions after merging the baffles:
+        nInternalFaces_ += bafInterfaces_.size();
+        nFaces_         -= bafInterfaces_.size();
+    }
+
+    Info<< bafInterfaces_.size() << " baffle interface pairs";
+    if (bafInterfaces_.size())
+    {
+        if (option().removeBaffles())
+        {
+            Info<< " to merge" << endl;
+            printSizes();
+        }
+        else
+        {
+            Info<< " to be sorted" << endl;
+        }
+    }
+    else
+    {
+        Info<< endl;
+    }
+
+    if (option().removeBaffles())
+    {
+        forAll(bafInterfaces_, elemI)
+        {
+            label face0 = bafInterfaces_[elemI][0];
+            label face1 = bafInterfaces_[elemI][1];
+
+            oldToNew[face0] = begOfList++;
+            oldToNew[face1] = endOfList--;     // End of list for truncation
+
+            // face0 gets a new neighbour, face1 loses its owner
+            faceNeighbour_[face0] = faceOwner_[face1];
+            faceOwner_[face1] = -1;
+        }
+
+
+        // Boundary faces continue from the new nInternalFaces
+        label pos = nInternalFaces_;
+        forAll(patchSizes_, patchI)
+        {
+            label beg = origPatchStarts[patchI];
+            label end = beg + patchSizes_[patchI];
+
+            // Fill in values for the remainder of the boundary patch
+            for (label faceI = beg; faceI < end; ++faceI)
+            {
+                if (oldToNew[faceI] < 0)
+                {
+                    oldToNew[faceI] = pos;
+                    ++pos;
+                }
+            }
+        }
+
+        // Baffles have been resolved - remove last traces
+        bafInterfaces_.clear();
+    }
+    else
+    {
+        // This check is probably unnecessary
+        forAll(bafflePatchCount, patchI)
+        {
+            if (bafflePatchCount[patchI] % 2)
+            {
+                Info<< "WARNING: patch " << patchI
+                    << " has an uneven number of baffles ("
+                    << bafflePatchCount[patchI] << ") expect strange results"
+                    << endl;
+            }
+        }
+
+
+        // Reordered faces continue from the new nInternalFaces
+        label pos = nInternalFaces_;
+        forAll(patchSizes_, patchI)
+        {
+            const label beg = origPatchStarts[patchI];
+            const label end = beg + patchSizes_[patchI];
+
+            const label nsize = bafflePatchCount[patchI];
+            if (nsize > 0)
+            {
+                // Reorganize baffle interfaces into [0-N/2; N/2-N] lists
+                // at the beginning of the corresponding patch
+                const label nsizeby2 = (nsize - nsize % 2) / 2;
+                label nsorted = 0;
+
+                // Renumber the normal (baffle) interfaces
+                forAll(bafInterfaces_, elemI)
+                {
+                    const label face0 = bafInterfaces_[elemI][0];
+                    const label face1 = bafInterfaces_[elemI][1];
+
+                    if
+                    (
+                        (face0 >= beg && face0 < end)
+                     || (face1 >= beg && face1 < end)
+                    )
+                    {
+                        oldToNew[face0] = pos + nsorted;
+                        oldToNew[face1] = pos + nsorted + nsizeby2;
+
+                        // Mark destination of the faces, but cannot renumber yet
+                        // use negative to potential overlap with other patch regions
+                        bafInterfaces_[elemI][0] = -oldToNew[face0];
+                        bafInterfaces_[elemI][1] = -oldToNew[face1];
+
+                        ++nsorted;
+                    }
+                }
+                pos += 2*nsorted;
+            }
+
+            // Fill in values for the remainder of the boundary patch
+            for (label faceI = beg; faceI < end; ++faceI)
+            {
+                if (oldToNew[faceI] < 0)
+                {
+                    oldToNew[faceI] = pos;
+                    ++pos;
+                }
+            }
+        }
+
+        // Finalize new numbers for the normal (baffle) interfaces
+        forAll(bafInterfaces_, elemI)
+        {
+            bafInterfaces_[elemI][0] = abs(bafInterfaces_[elemI][0]);
+            bafInterfaces_[elemI][1] = abs(bafInterfaces_[elemI][1]);
+        }
+    }
+
+#ifdef DEBUG_BAFFLES
+    Info<< "remap with " << oldToNew << nl
+        << "owners:" << faceOwner_ << nl
+        << "neighbours:" << faceNeighbour_ << nl
+        << endl;
+#endif
+
+    // Re-order faces, owners/neighbours
+    inplaceReorder(oldToNew, faces_);
+    inplaceReorder(oldToNew, faceOwner_);
+    inplaceReorder(oldToNew, faceNeighbour_);
+    inplaceReorder(oldToNew, origFaceId_);
+
+    if (monitoringSets_.size())
+    {
+#ifdef WITH_MONITORING
+        // Modify oldToNew mapping to account for monitoring faces that
+        // coincided with a domain interface
+        //
+        // TODO - should modify flip map as well
+        forAll(domInterfaces_, elemI)
+        {
+            label face0 = domInterfaces_[elemI][0];
+            label face1 = domInterfaces_[elemI][1];
+            oldToNew[face1] = oldToNew[face0];
+        }
+
+//         Info<< "nInternalFaces " << nInternalFaces_ << nl
+//             << "oldToNew (internal) "
+//             << SubList<label>(oldToNew, nInternalFaces_)
+//             << nl
+//             << "oldToNew (extern) "
+//             << SubList<label>(oldToNew, nFaces_ - nInternalFaces_, nInternalFaces_)
+//             << endl;
+
+        forAllIter(HashTable<labelList>, monitoringSets_, iter)
+        {
+            inplaceRenumber(oldToNew, iter());
+        }
+#endif
+    }
+
+    // We can finally drop this information now
+    domInterfaces_.clear();
+
+    // Truncate lists
+    faces_.setSize(nFaces_);
+    faceOwner_.setSize(nFaces_);
+    faceNeighbour_.setSize(nFaces_);
+    origFaceId_.setSize(nFaces_);
+
+    // Remove empty patches:
+
+    // Fix patch sizes:
+    oldToNew.setSize(patchSizes_.size());
+    oldToNew = -1;
+
+    label nPatches = 0;
+    forAll(patchSizes_, patchI)
+    {
+        patchSizes_[patchI] -= adjustPatchSize[patchI];
+        if (option().removeBaffles())
+        {
+            patchSizes_[patchI] -= bafflePatchCount[patchI];
+        }
+
+        if (patchSizes_[patchI])
+        {
+            oldToNew[patchI] = nPatches++;
+        }
+    }
+
+    inplaceReorder(oldToNew, patchSizes_);
+    inplaceReorder(oldToNew, origBndId_);
+
+    patchSizes_.setSize(nPatches);
+    origBndId_.setSize(nPatches);
+
+#ifdef DEBUG_BAFFLES
+    Info<< "nCells:" << nCells_ << nl
+        << "nFaces:" << nFaces_ << nl
+        << "PatchSizes:"  << patchSizes_ << nl
+        << "nInternalFaces:" << nInternalFaces_ << nl
+        << endl;
+#endif
+}
+
+
+//
+// Merge STARCCM in-place interfaces
+//
+void Foam::ccm::reader::mergeInplaceInterfaces()
+{
+    if (interfaceDefinitions_.empty())
+    {
+        return;
+    }
+    if (!option().mergeInterfaces())
+    {
+        Info<< interfaceDefinitions_.size() << " interface definitions"
+            << " - leaving unmerged" << endl;
+        return;
+    }
+
+    // List of patch pairs that are interfaces
+    DynamicList<labelPair> interfacePatches(interfaceDefinitions_.size());
+
+    forAll(interfaceDefinitions_, interI)
+    {
+        const interfaceEntry& ifentry = interfaceDefinitions_[interI];
+
+        labelPair patchPair
+        (
+            findIndex(origBndId_, ifentry.bnd0),
+            findIndex(origBndId_, ifentry.bnd1)
+        );
+
+        if (patchPair[0] == patchPair[1] || patchPair[0] < 0 || patchPair[1] < 0)
+        {
+            // This should not happen
+            Info<<"Warning : bad interface " << interI << " " << ifentry
+                <<" on patches " << patchPair << endl;
+        }
+        else if
+        (
+            patchSizes_[patchPair[0]] != patchSizes_[patchPair[1]]
+         || patchSizes_[patchPair[0]] == 0
+         || patchSizes_[patchPair[1]] == 0
+        )
+        {
+            Info<<"Warning : skip interface " << interI << " " << ifentry
+                <<" on patches " << patchPair << nl
+                <<"   has zero or different number of faces: ("
+                << patchSizes_[patchPair[0]]  << " " << patchSizes_[patchPair[0]] << ")"
+                << endl;
+        }
+        else
+        {
+            interfacePatches.append(patchPair);
+        }
+    }
+
+    if (interfacePatches.empty())
+    {
+        return;
+    }
+
+
+    // Local point mapping
+    labelList mergedPointMap;
+
+    // Global remapping
+    labelList oldToNew(identity(points_.size()));
+
+    const labelList origPatchStarts(patchStartList(nInternalFaces_));
+
+    label nMergedTotal = 0;
+
+    // Markup points to merge
+    PackedBoolList whichPoints(points_.size());
+
+    Info<< "interface merge points (tol=" << option().mergeTol() << "):" << endl;
+
+    DynamicList<label> interfacesToMerge(interfacePatches.size());
+    forAll(interfacePatches, interI)
+    {
+        const label patch0 = interfacePatches[interI][0];
+        const label patch1 = interfacePatches[interI][1];
+        const label nPatch0Faces = patchSizes_[patch0];
+        const label nPatch1Faces = patchSizes_[patch1];
+
+        // Markup points to merge
+        whichPoints.reset();
+        for (label local0FaceI = 0; local0FaceI < nPatch0Faces; ++local0FaceI)
+        {
+            const face& f = faces_[origPatchStarts[patch0] + local0FaceI];
+
+            forAll(f, fp)
+            {
+                // Simultaneously account for previous point merges
+                whichPoints.set(oldToNew[f[fp]]);
+            }
+        }
+        for (label local1FaceI = 0; local1FaceI < nPatch1Faces; ++local1FaceI)
+        {
+            const face& f = faces_[origPatchStarts[patch1] + local1FaceI];
+
+            forAll(f, fp)
+            {
+                // Simultaneously account for previous point merges
+                whichPoints.set(oldToNew[f[fp]]);
+            }
+        }
+
+        // The global addresses
+        labelList addr(whichPoints.used());
+
+        const UIndirectList<point> pointsToMerge(points_, addr);
+
+        label nMerged = mergePoints
+        (
+            true,
+            pointsToMerge,
+            option().mergeTol(),
+            false,
+            mergedPointMap
+        );
+
+        Info<< "    patch "  << patch0 << ", " << patch1 << ": "
+            << nMerged << " from " << pointsToMerge.size() << " points"
+            << endl;
+
+        if (nMerged)
+        {
+            // Transcribe local to global addressing
+            forAll(mergedPointMap, lookupI)
+            {
+                oldToNew[addr[lookupI]] = addr[mergedPointMap[lookupI]];
+            }
+
+            interfacesToMerge.append(interI);
+            nMergedTotal += nMerged;
+        }
+    }
+
+
+    //
+    // Nothing to do
+    //
+    if (!nMergedTotal)
+    {
+        return;
+    }
+
+    // Update point references to account for point merge:
+    forAll(faces_, faceI)
+    {
+        inplaceRenumber(oldToNew, faces_[faceI]);
+    }
+
+    // Determine which points are actually in use:
+    oldToNew.setSize(nPoints_);
+    oldToNew = -1;
+
+    // Mark up all the used points
+    forAll(faces_, faceI)
+    {
+        const labelList& facePoints = faces_[faceI];
+
+        forAll(facePoints, ptI)
+        {
+            ++oldToNew[facePoints[ptI]];
+        }
+    }
+
+    label nPointUsed = 0;
+    forAll(oldToNew, ptI)
+    {
+        if (oldToNew[ptI] >= 0)
+        {
+            oldToNew[ptI] = nPointUsed;
+            if (ptI != nPointUsed)
+            {
+                points_[nPointUsed] = points_[ptI];
+            }
+            ++nPointUsed;
+        }
+    }
+
+    // Info<< "merge " << nMergedTotal << " points from "
+    //     << nPoints_ << " to " << nPointUsed << endl;
+
+    nPoints_ = nPointUsed;
+    points_.setSize(nPoints_);
+
+    forAll(faces_, faceI)
+    {
+        inplaceRenumber(oldToNew, faces_[faceI]);
+    }
+
+
+    //
+    // Merge the faces as well
+    //
+    Info<< "interface merge faces:" << endl;
+
+    nMergedTotal = 0;
+    labelList adjustPatchSize(patchSizes_.size(), 0);
+    forAll(interfacesToMerge, mergeI)
+    {
+        const label patch0 = interfacePatches[interfacesToMerge[mergeI]][0];
+        const label patch1 = interfacePatches[interfacesToMerge[mergeI]][1];
+
+        labelList faceAddr0(patchSizes_[patch0]);
+        labelList faceAddr1(patchSizes_[patch1]);
+
+        forAll(faceAddr0, localFaceI)
+        {
+            faceAddr0[localFaceI] = origPatchStarts[patch0] + localFaceI;
+        }
+        forAll(faceAddr1, localFaceI)
+        {
+            faceAddr1[localFaceI] = origPatchStarts[patch1] + localFaceI;
+        }
+
+        if (faceAddr0.size() != faceAddr1.size())
+        {
+            // This should not occur, we avoided the same thing above
+            continue;
+        }
+
+        // Improve comparision speed by sorting by distance
+        SortableList<scalar> pts0MagSqr
+        (
+            magSqr
+            (
+                uindirectPrimitivePatch
+                (
+                    UIndirectList<face>
+                    (
+                        faces_,
+                        faceAddr0
+                    ),
+                    points_
+                ).faceCentres()
+            )
+        );
+        SortableList<scalar> pts1MagSqr
+        (
+            magSqr
+            (
+                uindirectPrimitivePatch
+                (
+                    UIndirectList<face>
+                    (
+                        faces_,
+                        faceAddr1
+                    ),
+                    points_
+                ).faceCentres()
+            )
+        );
+
+        label nMerged = 0;
+
+        // Record which faces failed to merge - use slower ad hoc merging
+        labelHashSet failed0, failed1;
+        forAll(pts0MagSqr, sortI)
+        {
+            const label face0I = faceAddr0[pts0MagSqr.indices()[sortI]];
+            const label face1I = faceAddr1[pts1MagSqr.indices()[sortI]];
+
+            // This is what we expect
+            if (face::compare(faces_[face0I], faces_[face1I]))
+            {
+                ++nMerged;
+
+                // Moved from boundary patch to internal patch
+                ++adjustPatchSize[patch0];
+                ++adjustPatchSize[patch1];
+
+                if (faceOwner_[face0I] < faceOwner_[face1I])
+                {
+                    // keep 0, discard 1
+                    faceNeighbour_[face0I] = faceOwner_[face1I];
+                    faceNeighbour_[face1I] = faceOwner_[face1I] = -1;
+                }
+                else
+                {
+                    // keep 1, discard 0
+                    faceNeighbour_[face1I] = faceOwner_[face0I];
+                    faceNeighbour_[face0I] = faceOwner_[face0I] = -1;
+                }
+            }
+            else
+            {
+                failed0.set(face0I);
+                failed1.set(face1I);
+            }
+        }
+
+        // Perhaps some sorting issues, recheck
+        if (failed0.size())
+        {
+            // Note which one were successful
+            labelHashSet done(failed0.size());
+
+            forAllConstIter(labelHashSet, failed0, iter0)
+            {
+                const label face0I = iter0.key();
+
+                forAllConstIter(labelHashSet, failed1, iter1)
+                {
+                    const label face1I = iter1.key();
+
+                    // This is what we expect
+                    if (face::compare(faces_[face0I], faces_[face1I]))
+                    {
+                        ++nMerged;
+
+                        // Moved from boundary patch to internal patch
+                        ++adjustPatchSize[patch0];
+                        ++adjustPatchSize[patch1];
+
+                        if (faceOwner_[face0I] < faceOwner_[face1I])
+                        {
+                            // keep 0, discard 1
+                            faceNeighbour_[face0I] = faceOwner_[face1I];
+                            faceNeighbour_[face1I] = faceOwner_[face1I] = -1;
+                        }
+                        else
+                        {
+                            // keep 1, discard 0
+                            faceNeighbour_[face1I] = faceOwner_[face0I];
+                            faceNeighbour_[face0I] = faceOwner_[face0I] = -1;
+                        }
+
+                        failed1.erase(face1I);   // Never check again
+                        done.set(face0I);        // Mark as done
+                        break;                   // Stop looking
+                    }
+                }
+            }
+
+            // Transfer to note how many were successful
+            failed0 = done;
+        }
+
+        Info<< "    patch "  << patch0 << ", " << patch1 << ": "
+            << nMerged << " from " << faceAddr0.size() << " faces";
+
+        if (failed0.size())
+        {
+            Info<< " (" << failed0.size() << " merged ad hoc)";
+        }
+        Info<< endl;
+
+
+        nMergedTotal += nMerged;
+    }
+
+
+    // Nothing to do
+    if (!nMergedTotal)
+    {
+        return;
+    }
+
+    // Info<< "merge " << nMergedTotal << " faces from "
+    //     << nFaces_ << " to " << (nFaces_ - nMergedTotal) << endl;
+
+    oldToNew.setSize(nFaces_);
+    oldToNew = -1;
+
+    // Remaining external faces will be shifted here:
+    label extFaceI = nInternalFaces_ + nMergedTotal;
+
+    // Start over
+    nInternalFaces_ = 0;
+    label nFaceUsed = 0;
+    for (label faceI = 0; faceI < nFaces_; ++faceI)
+    {
+        if (faceOwner_[faceI] != -1)
+        {
+            if (faceNeighbour_[faceI] != -1)
+            {
+                // Internal face
+                oldToNew[faceI] = nInternalFaces_;
+                ++nInternalFaces_;
+                ++nFaceUsed;
+            }
+            else
+            {
+                // External face
+                oldToNew[faceI] = extFaceI;
+                ++extFaceI;
+                ++nFaceUsed;
+            }
+        }
+    }
+
+    if (nFaceUsed != extFaceI)
+    {
+        FatalErrorIn("ccm::reader::mergeInplaceInterfaces")
+            << "coding error: used " << nFaceUsed
+            << " faces, but expected to use " << extFaceI << " faces"
+            << exit(FatalError);
+    }
+
+    // Re-order faces, owners/neighbours
+    inplaceReorder(oldToNew, faces_, true);
+    inplaceReorder(oldToNew, faceOwner_, true);
+    inplaceReorder(oldToNew, faceNeighbour_, true);
+    inplaceReorder(oldToNew, origFaceId_, true);
+
+    nFaces_ = nFaceUsed;
+
+    faces_.setSize(nFaces_);
+    faceOwner_.setSize(nFaces_);
+    faceNeighbour_.setSize(nFaces_);
+    origFaceId_.setSize(nFaces_);
+
+    // Fix patch sizes:
+    oldToNew.setSize(patchSizes_.size());
+    oldToNew = -1;
+
+    label nPatches = 0;
+    forAll(patchSizes_, patchI)
+    {
+        patchSizes_[patchI] -= adjustPatchSize[patchI];
+        if (patchSizes_[patchI])
+        {
+            oldToNew[patchI] = nPatches++;
+        }
+    }
+
+    inplaceReorder(oldToNew, patchSizes_);
+    inplaceReorder(oldToNew, origBndId_);
+
+    patchSizes_.setSize(nPatches);
+    origBndId_.setSize(nPatches);
+
+    // Report we are done
+    Info<< ".." << endl;
+}
+
+
+//
+// Re-order mesh into Foam convention
+// - owner < neighbour
+// - face vertices such that normal points away from owner
+// - order faces: upper-triangular for internal faces;
+//    boundary faces after internal faces
+//
+void Foam::ccm::reader::reorderMesh()
+{
+    // Set owner/neighbour so owner < neighbour
+    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+    forAll(faceOwner_, faceI)
+    {
+        label nbr = faceNeighbour_[faceI];
+        label own = faceOwner_[faceI];
+
+        if (nbr >= cellTableId_.size() || own >= cellTableId_.size())
+        {
+            FatalErrorIn("ccm::reader::reorderMesh")
+                << "face:" << faceI
+                << " nbr:" << nbr
+                << " own:" << own
+                << " nCells:" << cellTableId_.size()
+                << exit(FatalError);
+        }
+
+        if (nbr >= 0 && nbr < own)
+        {
+            faceOwner_[faceI] = faceNeighbour_[faceI];
+            faceNeighbour_[faceI] = own;
+            faces_[faceI] = faces_[faceI].reverseFace();
+        }
+
+        // And check the face
+        const face& f = faces_[faceI];
+
+        forAll(f, fp)
+        {
+            if (f[fp] < 0 || f[fp] >= points_.size())
+            {
+                FatalErrorIn("ccm::reader::reorderMesh")
+                    << "face:" << faceI << " f:" << f
+                    << abort(FatalError);
+            }
+        }
+    }
+
+    // Do upper-triangular ordering
+    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+    labelList oldToNew(faceOwner_.size(), -1);
+
+    // Create cells (inverse of face-to-cell addressing)
+    cellList cellFaceAddr;
+    primitiveMesh::calcCells
+    (
+        cellFaceAddr,
+        faceOwner_,
+        faceNeighbour_,
+        nCells_
+    );
+
+    label newFaceI = 0;
+    forAll(cellFaceAddr, cellI)
+    {
+        const labelList& cFaces = cellFaceAddr[cellI];
+        SortableList<label> nbr(cFaces.size(), -1);
+
+        forAll(cFaces, i)
+        {
+            label faceI = cFaces[i];
+            label nbrCellI = faceNeighbour_[faceI];
+
+            if (nbrCellI >= 0)
+            {
+                // Internal face. Get cell on other side
+                if (nbrCellI == cellI)
+                {
+                    nbrCellI = faceOwner_[faceI];
+                }
+
+                // cellI is master
+                if (cellI < nbrCellI)
+                {
+                    nbr[i] = nbrCellI;
+                }
+            }
+        }
+
+        nbr.sort();
+
+        forAll(nbr, i)
+        {
+            if (nbr[i] >= 0)
+            {
+                oldToNew[cFaces[nbr.indices()[i]]] = newFaceI++;
+            }
+        }
+    }
+
+    // Reorder faces accordingly
+    inplaceReorder(oldToNew, faces_);
+    inplaceReorder(oldToNew, faceOwner_);
+    inplaceReorder(oldToNew, faceNeighbour_);
+    inplaceReorder(oldToNew, origFaceId_);
+
+    forAllIter(HashTable<labelList>, monitoringSets_, iter)
+    {
+        inplaceRenumber(oldToNew, iter());
+        labelList &lst = iter();
+
+        // disallow monitoring on boundaries
+        label nElem = 0;
+        forAll(lst, i)
+        {
+            if (lst[i] >= 0 && lst[i] < nInternalFaces_)
+            {
+                if (nElem != i)
+                {
+                    lst[nElem] = lst[i];
+                }
+                nElem++;
+            }
+        }
+
+        if (nElem)
+        {
+            lst.setSize(nElem);
+        }
+        else
+        {
+            Info << "remove monitor " << iter.key() << endl;
+            monitoringSets_.erase(iter);
+        }
+    }
+}
+
+
+// Attach patches
+//
+// - patchSizes_   : obvious
+// - origBndId_ : lookup for patch name/type
+//
+void Foam::ccm::reader::addPatches
+(
+    polyMesh& mesh
+) const
+{
+    // Create patches
+    // use patch types to determine what Foam types to generate
+    List<polyPatch*> newPatches(origBndId_.size());
+
+    label meshFaceI = nInternalFaces_;
+    wordHashSet hashedNames(origBndId_.size());
+
+    // lookup patch names/types from the problem description
+    // provide some fallback vlues
+    forAll(newPatches, patchI)
+    {
+        word fallbackName("patch" + Foam::name(patchI));
+        word patchName;
+        word patchType;
+
+        Map<dictionary>::const_iterator
+            citer = boundaryRegion_.find(origBndId_[patchI]);
+
+        if (citer != boundaryRegion_.end())
+        {
+            citer().lookup("Label") >> patchName;
+            citer().lookup("BoundaryType") >> patchType;
+        }
+        else
+        {
+            patchName = fallbackName;
+            patchType = "patch";
+        }
+
+        // Avoid duplicate names
+        // - don't bother checking if the modified name is also a duplicate
+        if (hashedNames.found(patchName))
+        {
+            Info<< "renamed patch " << patchName << " to ";
+            patchName = fallbackName + "_" + patchName;
+            Info<< patchName << endl;
+        }
+        hashedNames.insert(patchName);
+
+        Info<< "patch " << patchI
+            << " (start: " << meshFaceI << " size: " << patchSizes_[patchI]
+            << ") name: " << patchName
+            << endl;
+
+        if (patchType == "wall")
+        {
+            newPatches[patchI] =
+                new wallPolyPatch
+                (
+                    patchName,
+                    patchSizes_[patchI],
+                    meshFaceI,
+                    patchI,
+                    mesh.boundaryMesh(),
+                    patchType
+                );
+        }
+        else if (patchType == "symmetry")
+        {
+            newPatches[patchI] =
+                new symmetryPolyPatch
+                (
+                    patchName,
+                    patchSizes_[patchI],
+                    meshFaceI,
+                    patchI,
+                    mesh.boundaryMesh(),
+                    patchType
+                );
+        }
+        else if (patchType == "empty")
+        {
+            // Note: not ccm name, may have been introduced by us
+            newPatches[patchI] =
+                new emptyPolyPatch
+                (
+                    patchName,
+                    patchSizes_[patchI],
+                    meshFaceI,
+                    patchI,
+                    mesh.boundaryMesh(),
+                    patchType
+                );
+        }
+        else
+        {
+            // All other ccm types become straight polyPatch:
+            // 'inlet', 'outlet', 'pressure'.
+            newPatches[patchI] =
+                new polyPatch
+                (
+                    patchName,
+                    patchSizes_[patchI],
+                    meshFaceI,
+                    patchI,
+                    mesh.boundaryMesh(),
+                    patchType
+                );
+        }
+
+        meshFaceI += patchSizes_[patchI];
+    }
+
+    if (meshFaceI != mesh.nFaces())
+    {
+        FatalErrorIn("ccm::reader::addPatches(polyMesh& mesh)")
+            << "meshFaceI:" << meshFaceI << " nFaces:" << mesh.nFaces()
+            << abort(FatalError);
+    }
+
+    mesh.addPatches(newPatches);
+}
+
+
+
+// Attach faceZones based on the monitoring boundary conditions
+void Foam::ccm::reader::addFaceZones
+(
+    polyMesh& mesh
+) const
+{
+    label nZone = monitoringSets_.size();
+    mesh.faceZones().setSize(nZone);
+
+    if (!nZone)
+    {
+        return;
+    }
+
+    nZone = 0;
+    forAllConstIter(HashTable<labelList>, monitoringSets_, iter)
+    {
+        Info<< "faceZone " << nZone
+            << " (size: " << iter().size() << ") name: "
+            << iter.key() << endl;
+
+        mesh.faceZones().set
+        (
+            nZone,
+            new faceZone
+            (
+                iter.key(),
+                iter(),
+                boolList(iter().size(), false),
+                nZone,
+                mesh.faceZones()
+            )
+        );
+
+        nZone++;
+    }
+
+    mesh.faceZones().writeOpt() = IOobject::AUTO_WRITE;
+    warnDuplicates("faceZones", mesh.faceZones().names());
+}
+
+
+// Remove most of the ccm-specific information with the exception of information
+// auxiliary to the normal polyMesh:
+//   (cellTableId_, origCellId_, origFaceId_, interfaces_, ...)
+void Foam::ccm::reader::clearGeom()
+{
+    // allow re-reading the file
+    if (geometryStatus_ == OKAY || geometryStatus_ == READ)
+    {
+        geometryStatus_ = UNKNOWN;
+    }
+
+    points_.clear();
+    faces_.clear();
+    faceOwner_.clear();
+    faceNeighbour_.clear();
+
+    origBndId_.clear();
+    patchSizes_.clear();
+}
+
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+Foam::autoPtr<Foam::polyMesh> Foam::ccm::reader::mesh
+(
+    const objectRegistry& registry,
+    const fileName& remappingDictName
+)
+{
+    if (!readGeometry())
+    {
+        return autoPtr<polyMesh>();
+    }
+
+    // merge cellTable and rename boundaryRegion
+    remapMeshInfo(registry, remappingDictName);
+
+    // Construct polyMesh
+    // ~~~~~~~~~~~~~~~~~~
+    autoPtr<polyMesh> mesh
+    (
+        new polyMesh
+        (
+            IOobject
+            (
+                polyMesh::defaultRegion,
+                "constant",
+                registry
+            ),
+            xferMove(points_),
+            xferMove(faces_),
+            xferMove(faceOwner_),
+            xferMove(faceNeighbour_)
+        )
+    );
+
+    addPatches(mesh());
+
+    // Attach cellZones based on the cellTable Id
+    // any other values can be extracted later from the cellTable dictionary
+    cellTable_.addCellZones(mesh(), cellTableId_);
+    warnDuplicates("cellZones", mesh().cellZones().names());
+
+    addFaceZones(mesh());
+
+    warnDuplicates("boundaries", mesh().boundaryMesh().names());
+    clearGeom();
+
+    return mesh;
+}
+
+
+// ************************************************************************* //
diff --git a/src/conversion/ccm/reader/ccmReaderOptions.C b/src/conversion/ccm/reader/ccmReaderOptions.C
new file mode 100644
index 0000000000000000000000000000000000000000..c6bfe87d7dc6413818aeb0a5d3f50f7883073a66
--- /dev/null
+++ b/src/conversion/ccm/reader/ccmReaderOptions.C
@@ -0,0 +1,174 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2016 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 "ccmReader.H"
+
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+Foam::ccm::reader::options::options()
+:
+    keepFluid_(true),
+    keepPorous_(true),
+    keepSolid_(true),
+    mergeInterfaces_(false),
+    renameInterfaces_(true),
+    combineBoundaries_(false),
+    removeBaffles_(false),
+    useNumberedNames_(false),
+    mergeTol_(0.05e-3),
+    undefScalar_(NAN)
+{}
+
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+bool Foam::ccm::reader::options::keepFluid() const
+{
+    return keepFluid_;
+}
+
+
+bool Foam::ccm::reader::options::keepPorous() const
+{
+    return keepPorous_;
+}
+
+
+bool Foam::ccm::reader::options::keepSolid() const
+{
+    return keepSolid_;
+}
+
+
+bool Foam::ccm::reader::options::keptSomeRegion() const
+{
+    return keepFluid_ || keepPorous_ || !keepSolid_;
+}
+
+
+bool Foam::ccm::reader::options::mergeInterfaces() const
+{
+    return mergeInterfaces_;
+}
+
+
+bool Foam::ccm::reader::options::renameInterfaces() const
+{
+    return renameInterfaces_;
+}
+
+
+bool Foam::ccm::reader::options::combineBoundaries() const
+{
+    return combineBoundaries_;
+}
+
+
+bool Foam::ccm::reader::options::removeBaffles() const
+{
+    return removeBaffles_;
+}
+
+
+bool Foam::ccm::reader::options::useNumberedNames() const
+{
+    return useNumberedNames_;
+}
+
+
+Foam::scalar Foam::ccm::reader::options::mergeTol() const
+{
+    return mergeTol_;
+}
+
+
+Foam::scalar Foam::ccm::reader::options::undefScalar() const
+{
+    return undefScalar_;
+}
+
+
+
+void Foam::ccm::reader::options::keepFluid(bool b)
+{
+    keepFluid_ = b;
+}
+
+
+void Foam::ccm::reader::options::keepPorous(bool b)
+{
+    keepPorous_ = b;
+}
+
+
+void Foam::ccm::reader::options::keepSolid(bool b)
+{
+    keepSolid_ = b;
+}
+
+
+void Foam::ccm::reader::options::mergeInterfaces(bool b)
+{
+    mergeInterfaces_ = b;
+}
+
+
+void Foam::ccm::reader::options::renameInterfaces(bool b)
+{
+    renameInterfaces_ = b;
+}
+
+
+void Foam::ccm::reader::options::combineBoundaries(bool b)
+{
+    combineBoundaries_ = b;
+}
+
+
+void Foam::ccm::reader::options::removeBaffles(bool b)
+{
+    removeBaffles_ = b;
+}
+
+
+void Foam::ccm::reader::options::useNumberedNames(bool b)
+{
+    useNumberedNames_ = b;
+}
+
+
+void Foam::ccm::reader::options::mergeTol(const scalar& val)
+{
+    mergeTol_ = val;
+}
+
+
+void Foam::ccm::reader::options::undefScalar(const scalar& val)
+{
+    undefScalar_ = val;
+}
+
+
+// ************************************************************************* //
diff --git a/src/conversion/ccm/reader/ccmReaderSolution.C b/src/conversion/ccm/reader/ccmReaderSolution.C
new file mode 100644
index 0000000000000000000000000000000000000000..04881011f355398f79489875fd0245249512daaa
--- /dev/null
+++ b/src/conversion/ccm/reader/ccmReaderSolution.C
@@ -0,0 +1,643 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2016 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 "ccmReader.H"
+#include "ccmInternal.H" // include last to avoid any strange interactions
+
+
+// * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
+
+// Get information about available fields.
+// - assume that all fields are available for all solution intervals
+// eg,
+//   /FieldSets/FieldSet-1/Phase-1/Fields/Velocity
+//   /FieldSets/FieldSet-1/Phase-1/Fields/Pressure
+//   ...
+void Foam::ccm::reader::determineFieldInfo
+(
+    const ccmID& fieldSetNode,
+    fieldTable& table
+)
+{
+    char fullName[kCCMIOMaxStringLength + 1];
+    char shortName[kCCMIOProstarShortNameLength+1];
+
+    // Loop through all phases
+    ccmID phaseNode;
+    int phaseI = 0;
+
+    while
+    (
+        CCMIONextEntity
+        (
+            NULL,
+            fieldSetNode,
+            kCCMIOFieldPhase,
+            &phaseI,
+            &phaseNode
+        )
+     == kCCMIONoErr
+    )
+    {
+        CCMIODimensionality dims;
+
+        // Examine each field in this fieldSet
+        ccmID fieldNode;
+        int fieldI = 0;
+
+        // Get full/short names and dimension (scalar/vector/tensor).
+        // Use short name as unique identifier.
+
+        while
+        (
+            CCMIONextEntity
+            (
+                NULL,
+                phaseNode,
+                kCCMIOField,
+                &fieldI,
+                &fieldNode
+            )
+         == kCCMIONoErr
+
+         && CCMIOReadField
+            (
+                NULL,
+                fieldNode,
+                fullName,
+                shortName,
+                &dims,
+                NULL
+            )
+         == kCCMIONoErr
+        )
+        {
+            // The shortName *should* be a word, but some fields seem
+            // to have blanks!
+            {
+                char *ptr = shortName;
+                while (*ptr)
+                {
+                    if (!word::valid(*ptr))
+                    {
+                        *ptr = '_';
+                    }
+                    ptr++;
+                }
+            }
+
+            word fieldName(shortName);
+
+            if (dims == kCCMIOScalar)
+            {
+                // Add to table as required
+                if (!table.found(fieldName))
+                {
+                    fieldEntry entry(fieldName, fullName);
+
+                    entry.units
+                    (
+                        ccmReadOptstr("Units", fieldNode)
+                    );
+
+                    table.append(entry);
+                }
+
+                fieldEntry& entry = table.find(fieldName)();
+
+                // Obtain sizes of data field
+                // - only process cell/face data
+                ccmID dataNode;
+                int dataI = 0;
+
+                CCMIODataLocation dataLocation;
+                CCMIOIndex maxId;
+
+                while
+                (
+                    CCMIONextEntity
+                    (
+                        NULL,
+                        fieldNode,
+                        kCCMIOFieldData,
+                        &dataI,
+                        &dataNode
+                    )
+                 == kCCMIONoErr
+
+                 && CCMIOEntitySize
+                    (
+                        NULL,
+                        dataNode,
+                        NULL,
+                        &maxId
+                    )
+                 == kCCMIONoErr
+
+                 && CCMIOReadFieldDatad
+                    (
+                        NULL,
+                        dataNode,
+                        NULL,
+                        &dataLocation,
+                        NULL,
+                        kCCMIOStart,
+                        kCCMIOEnd
+                    )
+                 == kCCMIONoErr
+                )
+                {
+                    if (dataLocation == kCCMIOCell)
+                    {
+                        entry.maxCellId(maxId);
+                    }
+                    else if (dataLocation == kCCMIOFace)
+                    {
+                        entry.maxFaceId(maxId);
+                    }
+                }
+            }
+        }
+    }
+}
+
+
+// Get information about all available solutions.
+// - it is sufficient to examine the first processor
+//
+// The "States":
+//      Restart_1/Processor-1
+//  -OR-
+//      Transient_1/Processor-1
+//      ...
+//      Transient_N/Processor-1
+//
+// point to the "FieldSets":
+//      FieldSet-1/RestartInfo
+//
+bool Foam::ccm::reader::detectSolution()
+{
+    // call once
+    if (solutionStatus_ != UNKNOWN)
+    {
+        return (solutionStatus_ == OKAY || solutionStatus_ == READ);
+    }
+
+    // loop through all States
+    ccmID stateNode;
+    int stateI = 0;
+    while
+    (
+        CCMIONextEntity
+        (
+            NULL,
+            (globalState_->root),
+            kCCMIOState,
+            &stateI,
+            &stateNode
+        )
+     == kCCMIONoErr
+    )
+    {
+        // Loop through all processors/solutions
+        ccmID processorNode;
+        ccmID solutionNode;
+        int procI = 0;
+
+        while
+        (
+            CCMIONextEntity
+            (
+                NULL,
+                stateNode,
+                kCCMIOProcessor,
+                &procI,
+                &processorNode
+            )
+         == kCCMIONoErr
+
+         && CCMIOReadProcessor
+            (
+                NULL,
+                processorNode,
+                NULL,           // Ignore verticesNode
+                NULL,           // Ignore topologyNode
+                NULL,           // Ignore initialField
+                &solutionNode
+            )
+         == kCCMIONoErr
+        )
+        {
+            // Restrict to solutions with RestartInfo on the first cpu
+            // (there is normally only one set of restart data)
+            ccmID restartNode;
+            int restartI = 0;
+
+            char  solutionName[kCCMIOMaxStringLength + 1];
+            int   iteration = 0;
+            float timeValue = 0;
+
+            if
+            (
+                CCMIONextEntity
+                (
+                    NULL,
+                    solutionNode,
+                    kCCMIORestart,
+                    &restartI,
+                    &restartNode
+                )
+             == kCCMIONoErr
+
+             && CCMIOEntityName
+                (
+                    NULL,
+                    stateNode,
+                    solutionName
+                )
+             == kCCMIONoErr
+
+             && CCMIOReadRestartInfo
+                (
+                    NULL,
+                    restartNode,
+                    NULL,          // Ignore solverName
+                    &iteration,
+                    &timeValue,
+                    NULL,          // Ignore timeUnits
+                    NULL           // Ignore startAngle
+                )
+             == kCCMIONoErr
+            )
+            {
+                solutionTable_.append
+                (
+                    solutionEntry(solutionName, iteration, timeValue)
+                );
+            }
+
+            // Determine field information
+            determineFieldInfo(solutionNode, fieldTable_);
+
+            // check Lagrangian data
+            ccmID lagrangianNode;
+            ccmID lagrangianSolutions;
+            int lagrangianI = 0;
+
+            if
+            (
+                CCMIONextEntity
+                (
+                    NULL,
+                    processorNode,
+                    kCCMIOLagrangianData,
+                    &lagrangianI,
+                    &lagrangianNode
+                )
+             == kCCMIONoErr
+
+             && CCMIOReadLagrangianData
+                (
+                    NULL,
+                    lagrangianNode,
+                    NULL,
+                    &lagrangianSolutions
+                )
+             == kCCMIONoErr
+            )
+            {
+                determineFieldInfo(lagrangianSolutions, lagrangianTable_);
+            }
+        }
+    }
+
+    if (solutionTable_.size() && fieldTable_.size())
+    {
+        solutionStatus_ = OKAY;
+    }
+    else
+    {
+        solutionStatus_ = BAD;
+    }
+
+    return (solutionStatus_ == OKAY || solutionStatus_ == READ);
+}
+
+#define SOLID_STRESS_HACK
+
+//
+// Read solution and field combination
+//
+Foam::tmp<Foam::scalarField>
+Foam::ccm::reader::readField
+(
+    const word& solutionName,
+    const word& fieldName,
+    const bool wallData
+)
+{
+    // get State by name
+    ccmID stateNode;
+    ccmID solutionNode;
+
+    if
+    (
+        CCMIOGetState
+        (
+            NULL,
+            (globalState_->root),
+            solutionName.c_str(),
+            NULL,
+            &stateNode
+        )
+     != kCCMIONoErr
+
+     || !fieldTable_.found(fieldName)
+    )
+    {
+        return tmp<scalarField>(new Field<scalar>());
+    }
+
+    CCMIODataLocation requestedLocation = kCCMIOCell;
+
+    fieldEntry& entry = fieldTable_.find(fieldName)();
+
+    label maxId = entry.maxCellId();
+
+    if (wallData)
+    {
+        maxId = entry.maxFaceId();
+        requestedLocation = kCCMIOFace;
+    }
+
+    // we can skip empty fields immediately
+    if (!maxId)
+    {
+        return tmp<scalarField>(new Field<scalar>());
+    }
+
+    char shortName[kCCMIOProstarShortNameLength+1];
+
+    List<label>  mapData;
+    List<scalar> rawData;
+
+    tmp<scalarField> tscalarData
+    (
+        new scalarField(maxId + 1, option().undefScalar())
+    );
+    scalarField& scalarData = tscalarData.ref();
+
+
+    CCMIODimensionality dims;
+
+    // Loop across all processors
+    ccmID processorNode;
+    int procI = 0;
+
+    while
+    (
+        CCMIONextEntity
+        (
+            NULL,
+            stateNode,
+            kCCMIOProcessor,
+            &procI,
+            &processorNode
+        )
+     == kCCMIONoErr
+
+    && CCMIOReadProcessor
+        (
+            NULL,
+            processorNode,
+            NULL,           // Ignore verticesNode
+            NULL,           // Ignore topologyNode
+            NULL,           // Ignore initialField
+            &solutionNode
+        )
+     == kCCMIONoErr
+    )
+    {
+        // loop through all phases
+        int phaseI = 0;
+        ccmID phaseNode;
+
+        while
+        (
+            CCMIONextEntity
+            (
+                NULL,
+                solutionNode,
+                kCCMIOFieldPhase,
+                &phaseI,
+                &phaseNode
+            )
+         == kCCMIONoErr
+        )
+        {
+            // Get Field that matches the name
+            ccmID fieldNode;
+            int fieldI = 0;
+
+            while
+            (
+                CCMIONextEntity
+                (
+                    NULL,
+                    phaseNode,
+                    kCCMIOField,
+                    &fieldI,
+                    &fieldNode
+                )
+                == kCCMIONoErr
+            )
+            {
+                // Get full/short names and dimension (scalar/vector/tensor)
+                // use short name as unique identifier
+                CCMIOReadField
+                (
+                    &(globalState_->error),
+                    fieldNode,
+                    NULL,
+                    shortName,
+                    &dims,
+                    NULL
+                );
+                assertNoError
+                (
+                    "reading post data field: "
+                  + string(shortName)
+                );
+
+                if (fieldName == shortName && dims == kCCMIOScalar)
+                {
+                    // Obtain sizes of data field
+                    ccmID dataNode;
+                    ccmID mapId;
+                    int dataI = 0;
+                    while
+                    (
+                        CCMIONextEntity
+                        (
+                            NULL,
+                            fieldNode,
+                            kCCMIOFieldData,
+                            &dataI,
+                            &dataNode
+                        )
+                     == kCCMIONoErr
+                    )
+                    {
+                        CCMIODataLocation dataLocation;
+                        CCMIOSize  n;
+
+                        // Only process cell/face data
+                        if
+                        (
+                            CCMIOEntitySize
+                            (
+                                NULL,
+                                dataNode,
+                                &n,
+                                NULL
+                            )
+                         == kCCMIONoErr
+
+                         && CCMIOReadFieldDatad
+                            (
+                                NULL,
+                                dataNode,
+                                &mapId,
+                                &dataLocation,
+                                NULL,
+                                kCCMIOStart,
+                                kCCMIOEnd
+                            )
+                         == kCCMIONoErr
+
+                         && dataLocation == requestedLocation
+                        )
+                        {
+
+#ifdef SOLID_STRESS_HACK
+                            bool okayCombination = true;
+
+                            CCMIOSize len;
+                            if
+                            (
+                                CCMIOEntityDescription
+                                (
+                                    NULL,
+                                    dataNode,
+                                    &len,
+                                    NULL
+                                )
+                             == kCCMIONoErr
+                            )
+                            {
+                                char* dataLabel = new char[len + 1];
+
+                                if
+                                (
+                                    CCMIOEntityDescription
+                                    (
+                                        NULL,
+                                        dataNode,
+                                        &len,
+                                        dataLabel
+                                    )
+                                 == kCCMIONoErr
+                                )
+                                {
+                                    if
+                                    (
+                                        (
+                                            strstr(fieldName.c_str(), "SIG")
+                                         || strstr(fieldName.c_str(), "EPS")
+                                        )
+                                     && strstr(dataLabel,  "So") == NULL
+                                    )
+                                    {
+                                        okayCombination = false;
+                                        // skip non-solid
+                                    }
+                                }
+
+                                delete[] dataLabel;
+                            }
+
+                            if (!okayCombination)
+                            {
+                                continue;
+                            }
+#endif
+
+                            mapData.setSize(n);
+                            rawData.setSize(n);
+
+                            readMap
+                            (
+                                mapId,
+                                mapData
+                            );
+
+                            CCMIOReadFieldDatad
+                            (
+                                &(globalState_->error),
+                                dataNode,
+                                NULL,
+                                NULL,
+                                rawData.begin(),
+                                kCCMIOStart,
+                                kCCMIOEnd
+                            );
+                            assertNoError
+                            (
+                                "reading post data field: "
+                              + string(shortName)
+                            );
+
+                            // transcribe to output list
+                            forAll(mapData, i)
+                            {
+                                label cellId = mapData[i];
+                                scalarData[cellId] = rawData[i];
+                            }
+
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    // Overwrite possible junk in cell 0 (often used as /dev/null)
+    scalarData[0] = option().undefScalar();
+
+    return tscalarData;
+}
+
+
+// ************************************************************************* //
diff --git a/src/conversion/ccm/reader/ccmSolutionTable.H b/src/conversion/ccm/reader/ccmSolutionTable.H
new file mode 100644
index 0000000000000000000000000000000000000000..e223d1c7480cca60c7b476de073ce0b7e9ad0010
--- /dev/null
+++ b/src/conversion/ccm/reader/ccmSolutionTable.H
@@ -0,0 +1,452 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2016 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/>.
+
+Description
+    Containers for holding ccm solution and field listings.
+
+\*---------------------------------------------------------------------------*/
+#ifndef ccmSolutionTable_H
+#define ccmSolutionTable_H
+
+#include "SLList.H"
+#include "stringListOps.H"
+#include "Ostream.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+namespace ccm
+{
+
+/*---------------------------------------------------------------------------*\
+                      Class ccm::namesList Declaration
+\*---------------------------------------------------------------------------*/
+
+//- A linked-list that is searchable by the 'name()' of the items
+template<class T>
+class namesList
+:
+    public SLList<T>
+{
+public:
+    typedef typename SLList<T>::const_iterator const_iterator;
+    typedef typename SLList<T>::iterator iterator;
+
+    // Constructors
+
+        //- Null construct
+        namesList()
+        {}
+
+
+    // Access
+
+        //- Return true if a list element has a name that matches key
+        bool found(const word& key) const
+        {
+            for
+            (
+                const_iterator iter = SLList<T>::begin();
+                iter != SLList<T>::end();
+                ++iter
+            )
+            {
+                if (iter().name() == key)
+                {
+                    return true;
+                }
+            }
+
+            return false;
+        }
+
+
+        //- Find a list element has a name matching key
+        iterator find(const word& key)
+        {
+            for
+            (
+                iterator iter = SLList<T>::begin();
+                iter != SLList<T>::end();
+                ++iter
+            )
+            {
+                if (iter().name() == key)
+                {
+                    return iter;
+                }
+            }
+
+            return SLList<T>::end();
+        }
+
+
+        //- Return a list of names matching whiteList and not matching blackList
+        List<word> findNames
+        (
+            const UList<wordRe>& whiteLst,
+            const UList<wordRe>& blackLst = UList<wordRe>()
+        ) const
+        {
+            List<word> matched(SLList<T>::size());
+
+            label matchI = 0;
+            for
+            (
+                const_iterator iter = SLList<T>::begin();
+                iter != SLList<T>::end();
+                ++iter
+            )
+            {
+                const word& name = iter().name();
+
+                if
+                (
+                    findStrings(whiteLst, name)
+                 && !findStrings(blackLst, name)
+                )
+                {
+                    matched[matchI++] = name;
+                }
+            }
+            matched.setSize(matchI);
+
+            return matched;
+        }
+
+};
+
+
+/*---------------------------------------------------------------------------*\
+                      Class ccm::fieldEntry Declaration
+\*---------------------------------------------------------------------------*/
+
+//- A ccm field entry with short name, name, maxId and type
+//  shortName => ( fullName, maxId, type );
+class fieldEntry
+{
+    // Private Data
+
+        //- The field name (PROSTAR short name)
+        word name_;
+
+        //- The full field name
+        string fullName_;
+
+        //- The field units
+        string units_;
+
+        //- The max cell id for the field
+        label maxCellId_;
+
+        //- The max face id for the field
+        label maxFaceId_;
+
+public:
+
+    // Constructors
+
+        //- Construct from components with optional units
+        fieldEntry
+        (
+            const word& shortName,
+            const string& fullName,
+            const char* units = nullptr
+        )
+        :
+            name_(shortName),
+            fullName_(fullName),
+            units_(),
+            maxCellId_(0),
+            maxFaceId_(0)
+        {
+            if (units && *units)
+            {
+                units_ = units;
+            }
+        }
+
+
+    // Access
+
+        //- The field name (PROSTAR short name)
+        const word& name() const
+        {
+            return name_;
+        }
+
+        //- The full field name
+        const string& fullName() const
+        {
+            return fullName_;
+        }
+
+        //- The field units
+        const string& units() const
+        {
+            return units_;
+        }
+
+        //- The max cell id for the field
+        label maxCellId() const
+        {
+            return maxCellId_;
+        }
+
+        //- The max face id for the field
+        label maxFaceId() const
+        {
+            return maxFaceId_;
+        }
+
+
+    // Edit
+
+        //- Set the field units
+        void units(const char* units)
+        {
+            if (units && *units)
+            {
+                units_ = units;
+            }
+        }
+
+        //- Set the field units
+        void units(const std::string& units)
+        {
+            if (!units.empty())
+            {
+                units_ = units;
+            }
+        }
+
+        //- Set the max cell Id for the field
+        void maxCellId(const int newMax)
+        {
+            if (maxCellId_ < newMax)
+            {
+                maxCellId_ = newMax;
+            }
+        }
+
+
+        //- Set the max face Id for the field
+        void maxFaceId(const int newMax)
+        {
+            if (maxFaceId_ < newMax)
+            {
+                maxFaceId_ = newMax;
+            }
+        }
+
+
+    // IOstream Operators
+
+        friend Ostream& operator<<
+        (
+            Ostream& os,
+            const fieldEntry& entry
+        )
+        {
+            os  << entry.name_ << " => " << entry.fullName_
+                << " [" << entry.units_.c_str()
+                << "] maxCell: " << entry.maxCellId_
+                << " maxFace: " << entry.maxFaceId_;
+
+            return os;
+        }
+
+};
+
+/*---------------------------------------------------------------------------*\
+                    Class ccm::solutionEntry Declaration
+\*---------------------------------------------------------------------------*/
+
+//- A ccm solution entry with name, iteration and time
+//  stateName => ( iteration, time );
+class solutionEntry
+{
+    // Private Data
+
+        //- The solution name
+        word name_;
+
+        //- The solution iteration/timestep
+        label iter_;
+
+        //- The solution time (sec)
+        scalar time_;
+
+public:
+
+    // Constructors
+
+        //- Construct from components
+        solutionEntry
+        (
+            const word& name,
+            const label& iteration,
+            const scalar& timeValue = 0
+        )
+        :
+            name_(name),
+            iter_(iteration),
+            time_(timeValue)
+        {}
+
+    // Access
+
+        //- The solution name
+        const word& name() const
+        {
+            return name_;
+        }
+
+        //- The solution iteration/timestep
+        label iteration() const
+        {
+            return iter_;
+        }
+
+        //- The solution time (sec)
+        scalar timeValue() const
+        {
+            return time_;
+        }
+
+
+    // IOstream Operators
+
+        friend Ostream& operator<<
+        (
+            Ostream& os,
+            const solutionEntry& entry
+        )
+        {
+            os  << entry.name_ << " =>"
+                << " iter: " << entry.iter_
+                << " time: " << entry.time_;
+
+            return os;
+        }
+
+};
+
+
+/*---------------------------------------------------------------------------*\
+                    Class ccm::solutionTable Declaration
+\*---------------------------------------------------------------------------*/
+
+// Typedef: ccm::solutionTable
+// A list of all the available solutions
+typedef namesList<solutionEntry> solutionTable;
+
+
+/*---------------------------------------------------------------------------*\
+                      Class ccm::fieldTable Declaration
+\*---------------------------------------------------------------------------*/
+
+//- A list of the available fields
+class fieldTable
+:
+    public namesList<fieldEntry>
+{
+public:
+
+    // Constructor
+
+        //- Null construct
+        fieldTable()
+        :
+            namesList<fieldEntry>()
+        {}
+
+    // Access
+
+        //- The maximum cell Id referenced in the list
+        label maxCellId() const
+        {
+            label maxId = 0;
+            forAllConstIter(namesList<fieldEntry>, *this, iter)
+            {
+                label currMax = (iter()).maxCellId();
+
+                if (maxId < currMax)
+                {
+                    maxId = currMax;
+                }
+            }
+
+            return maxId;
+        }
+
+
+        //- The maximum face Id referenced in the list
+        label maxFaceId() const
+        {
+            label maxId = 0;
+
+            forAllConstIter(namesList<fieldEntry>, *this, iter)
+            {
+                label currMax = (iter()).maxFaceId();
+
+                if (maxId < currMax)
+                {
+                    maxId = currMax;
+                }
+            }
+
+            return maxId;
+        }
+
+
+    // IOstream Operators
+
+        friend Ostream& operator<<
+        (
+            Ostream& os,
+            const fieldTable& tbl
+        )
+        {
+            os  << static_cast<const namesList<fieldEntry>& >(tbl)
+                << nl
+                << "maxCell: "  << tbl.maxCellId()
+                << " maxFace: " << tbl.maxFaceId();
+
+            return os;
+        }
+
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace ccm
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/conversion/ccm/remapping b/src/conversion/ccm/remapping
new file mode 100644
index 0000000000000000000000000000000000000000..d2013ecfcb0691a8d0486724389fbbdbd33f398f
--- /dev/null
+++ b/src/conversion/ccm/remapping
@@ -0,0 +1,96 @@
+/*--------------------------------*- C++ -*----------------------------------*\
+| =========                 |                                                 |
+| \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox           |
+|  \\    /   O peration     | Version:  v1612+                                |
+|   \\  /    A nd           | Web:      www.OpenFOAM.com                      |
+|    \\/     M anipulation  |                                                 |
+\*---------------------------------------------------------------------------*/
+FoamFile
+{
+    version     2.0;
+    format      ascii;
+    class       dictionary;
+    note        "remapping for STARCD <-> OPENFOAM translation";
+    object      remapping;
+}
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+//
+// rename/combine cellTable entries
+//    target  ( source names )
+//
+cellTable
+{
+    fluid ( FLUID "cat[0-9]_?(In|Out)_?Layer" );
+    cat1  ( CAT1 "cat1_?(Front|Back|Gamma)" );
+    cat2  ( CAT2 "cat2_?(Front|Back|Gamma)" );
+    cat3  ( CAT3 "cat3_?(Front|Back|Gamma)" );
+    cat4  ( CAT4 "cat4_?(Front|Back|Gamma)" );
+}
+
+
+//
+// rename boundary regions
+//     newName <- oldName
+//
+boundaryRegion
+{
+    walls       Default_Boundary_Region;
+    inlet1      inlet_1;
+    inlet2      inlet_2;
+    inlet3      inlet_3;
+    inlet4      inlet_4;
+    inlet5      inlet_5;
+    inlet6      inlet_6;
+    outlet1     outlet_1;
+    outlet2     outlet_2;
+    outlet3     outlet_3;
+    outlet4     outlet_4;
+    outlet5     outlet_5;
+    outlet6     outlet_6;
+}
+
+
+boundary
+{
+    empty       symmetryPlane;
+}
+
+
+//
+// map OPENFOAM field names to CCM fields
+//
+fields
+{
+    tracer0
+    {
+        name    CONC_001;
+        Label   "tracer0";
+    }
+
+    tracer1
+    {
+        name    CONC_002;
+        Label   "tracer1";
+    }
+
+    tracer2
+    {
+        name    CONC_003;
+        Label   "tracer2";
+    }
+
+    divPhi
+    {
+        name    CONC_004;
+        Label   "divPhi";
+    }
+
+    Ma
+    {
+        name    CONC_100;
+        Label   "Mach";
+    }
+}
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
diff --git a/src/conversion/ccm/writer/ccmWriter.C b/src/conversion/ccm/writer/ccmWriter.C
new file mode 100644
index 0000000000000000000000000000000000000000..c78a2fa068626d15cf77f154581529ff6546d631
--- /dev/null
+++ b/src/conversion/ccm/writer/ccmWriter.C
@@ -0,0 +1,389 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2016 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 "ccmWriter.H"
+#include "cellModeller.H"
+#include "demandDrivenData.H"
+#include "ccmInternal.H" // include last to avoid any strange interactions
+
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+// name for the topology file reference
+Foam::string Foam::ccm::writer::defaultMeshName = "meshExport";
+
+
+// * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
+
+// Create a filled linear map with 'size' from 'start + 1'
+void Foam::ccm::writer::addLinearMap
+(
+    const string& mapName,
+    ccmID& mapId,
+    label size,
+    label start
+) const
+{
+    if (globalState_->hasError() || size <= 0)
+    {
+        return;
+    }
+
+    CCMIONewEntity
+    (
+        &(globalState_->error),
+        (globalState_->root),
+        kCCMIOMap,
+        mapName.c_str(),
+        &mapId
+    );
+    assertNoError("creating linearMap '" + mapName + "' node");
+
+    List<int> data(size);
+    forAll(data, i)
+    {
+        data[i] = start + 1 + i;
+    }
+
+    CCMIOWriteMap
+    (
+        &(globalState_->error),
+        mapId,
+        size,
+        (start + size),
+        data.begin(),
+        kCCMIOStart,
+        kCCMIOEnd
+    );
+    assertNoError("writing linearMap '" + mapName + "' data");
+}
+
+
+Foam::label Foam::ccm::writer::findDefaultBoundary() const
+{
+    return mesh_.boundaryMesh().findPatchID(defaultBoundaryName);
+}
+
+
+void Foam::ccm::writer::writeBoundaryRegion
+(
+    const ccmID& probNode
+) const
+{
+    // Create dictionary lookup for constant/boundaryRegion
+    dictionary typeDict;
+
+    forAllConstIter(Map<dictionary>, boundaryRegion_, iter)
+    {
+        const dictionary& dict = iter();
+        if
+        (
+            dict.found("Label")
+         && dict.found("BoundaryType")
+        )
+        {
+            word nameEntry, typeEntry;
+
+            dict.lookup("Label") >> nameEntry;
+            dict.lookup("BoundaryType") >> typeEntry;
+
+            if (!typeDict.found(nameEntry))
+            {
+                typeDict.add(nameEntry, typeEntry);
+            }
+        }
+    }
+
+    const polyBoundaryMesh& patches = mesh_.boundaryMesh();
+
+    label defaultId = findDefaultBoundary();
+
+    // Force write Default_Boundary_Region as BoundaryRegion-0
+    {
+        ccmID nodeId;
+        CCMIONewIndexedEntity
+        (
+            &(globalState_->error),
+            probNode,
+            kCCMIOBoundaryRegion,
+            0,
+            nullptr,
+            &nodeId
+        );
+        CCMIOWriteOptstr
+        (
+            nullptr,
+            nodeId,
+            "Label",
+            defaultBoundaryName
+        );
+        CCMIOWriteOptstr
+        (
+            nullptr,
+            nodeId,
+            "BoundaryType",
+            "wall"
+        );
+    }
+
+    forAll(patches, patchI)
+    {
+        word patchName = patches[patchI].name();
+        word patchType = patches[patchI].type();
+
+        label regionId = patchI;
+        if (regionId == defaultId)
+        {
+            continue;  // Skip - already written
+        }
+        else if (defaultId == -1 || regionId < defaultId)
+        {
+            regionId++;
+        }
+
+        // Use BoundaryType from constant/boundaryRegion
+        typeDict.readIfPresent(patchName, patchType);
+
+        ccmID nodeId;
+        CCMIONewIndexedEntity
+        (
+            &(globalState_->error),
+            probNode,
+            kCCMIOBoundaryRegion,
+            regionId,
+            nullptr,
+            &nodeId
+        );
+        CCMIOWriteOptstr
+        (
+            nullptr,
+            nodeId,
+            "Label",
+            patchName.c_str()
+        );
+        CCMIOWriteOptstr
+        (
+            nullptr,
+            nodeId,
+            "BoundaryType",
+            patchType.c_str()
+        );
+    }
+}
+
+
+void Foam::ccm::writer::writeCellTable
+(
+    const ccmID& probNode
+) const
+{
+    if (!cellTable_.size())
+    {
+        Info<< "No cellTable" << endl;
+        return;
+    }
+
+    ccmID nodeId;
+
+    forAllConstIter(Map<dictionary>, cellTable_, iter)
+    {
+        label intVal = iter.key();
+        const dictionary& dict = iter();
+
+        CCMIONewIndexedEntity
+        (
+            &(globalState_->error),
+            probNode,
+            kCCMIOCellType,
+            intVal,
+            nullptr,
+            &nodeId
+        );
+
+        wordList toc = dict.toc();
+        forAll(toc, i)
+        {
+            word keyword = toc[i];
+            int pos = keyword.find("Id");
+
+            // Tags containing 'Id' are integers
+            if (pos > 0)
+            {
+                dict.lookup(keyword) >> intVal;
+                CCMIOWriteOpti
+                (
+                    nullptr,
+                    nodeId,
+                    keyword.c_str(),
+                    intVal
+                );
+            }
+            else if (pos < 0)
+            {
+                word strVal;
+                dict.lookup(keyword) >> strVal;
+
+                CCMIOWriteOptstr
+                (
+                    nullptr,
+                    nodeId,
+                    keyword.c_str(),
+                    strVal.c_str()
+                );
+            }
+        }
+    }
+}
+
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+// Write out problem description
+void Foam::ccm::writer::writeProblem
+(
+    const ccmID& stateNode
+) const
+{
+    ccmID probNode;
+    CCMIONewEntity
+    (
+        &(globalState_->error),
+        (globalState_->root),
+        kCCMIOProblemDescription,
+        nullptr,
+        &probNode
+    );
+
+    writeCellTable(probNode);
+    writeBoundaryRegion(probNode);
+
+    // let the state know about our problem
+    CCMIOWriteState
+    (
+        &(globalState_->error),
+        stateNode,
+        probNode,
+        nullptr
+    );
+}
+
+
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+// Construct for writing geometry
+Foam::ccm::writer::writer
+(
+    const fileName& file,
+    const polyMesh& mesh,
+    const bool backup
+)
+:
+    base(),
+    maps_(new ccmMaps),
+    mesh_(mesh),
+    // Mapping between OpenFOAM and PROSTAR primitives
+    prostarShapeLookup_
+    ({
+        { cellModeller::lookup("hex")->index(),   STARCDCore::starcdHex },
+        { cellModeller::lookup("prism")->index(), STARCDCore::starcdPrism },
+        { cellModeller::lookup("tet")->index(),   STARCDCore::starcdTet },
+        { cellModeller::lookup("pyr")->index(),   STARCDCore::starcdPyr }
+    }),
+    boundaryRegion_(mesh),
+    cellTable_(mesh)
+{
+    // Writing/re-writing existing files is too annoying - start anew
+    if (backup)
+    {
+        if (Foam::mvBak(file))
+        {
+            Info<< "moved existing file -> " << fileName(file + ".bak*") << nl;
+        }
+    }
+    else if (exists(file, false))
+    {
+        Foam::rm(file);
+        Info<< "removed existing file: " << file << nl;
+    }
+
+    // Reinitialize error state from the return value
+    globalState_->error = CCMIOOpenFile
+    (
+        nullptr,
+        file.c_str(),
+        kCCMIOWrite,
+        &(globalState_->root)
+    );
+    assertNoError("Error opening file for writing");
+
+    // We always need the cell map
+    addLinearMap
+    (
+        "cell Map",
+        maps_->cells,
+        mesh_.nCells()
+    );
+
+    // We often need the internalFaces map
+    addLinearMap
+    (
+        "internalFaces Map",
+        maps_->internalFaces,
+        mesh_.nInternalFaces()
+    );
+
+    const polyBoundaryMesh& patches = mesh_.boundaryMesh();
+    maps_->boundary.setSize(patches.size());
+
+    // We always need maps for the boundary regions
+    forAll(patches, patchI)
+    {
+        if (patches[patchI].size() > 0)
+        {
+            string mapName = "boundaryMap-" + Foam::name(patchI);
+
+            addLinearMap
+            (
+                mapName,
+                maps_->boundary[patchI],
+                patches[patchI].size(),
+                patches[patchI].start()
+            );
+        }
+    }
+}
+
+
+// * * * * * * * * * * * * * * * * Destructor  * * * * * * * * * * * * * * * //
+
+Foam::ccm::writer::~writer()
+{
+    close();
+
+    deleteDemandDrivenData(maps_);
+}
+
+
+// ************************************************************************* //
diff --git a/src/conversion/ccm/writer/ccmWriter.H b/src/conversion/ccm/writer/ccmWriter.H
new file mode 100644
index 0000000000000000000000000000000000000000..a2d57148c9bb8633630cc79f4fbdd60ce08a2cb1
--- /dev/null
+++ b/src/conversion/ccm/writer/ccmWriter.H
@@ -0,0 +1,256 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2016 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::ccm::writer
+
+Description
+    Write OpenFOAM meshes and/or results to CCM format
+
+    - partial support for interfaces
+    - no monitoring (internal) boundaries
+    - does not handle Lagrangian data
+
+    \par Files
+
+    The <tt>constant/boundaryRegion</tt> and <tt>constant/cellTable</tt> files,
+    which are described in ccmReader, are used to construct the CCM
+    \c ProblemDescription node.
+
+    The <tt>constant/remapping</tt> file is an \c IOdictionary that is
+    \c READ_IF_PRESENT and can be used to remap certain information.
+    eg,
+
+    \verbatim
+        // map OpenFOAM scalar fields to CCM output fields
+        fields
+        {
+            tracer0
+            {
+                name    CONC_001;
+                Label   "tracer0";
+            }
+            tracer1
+            {
+                name    CONC_002;
+                Label   "tracer1";
+            }
+            tracer2
+            {
+                name    CONC_003;
+                Label   "tracer2";
+            }
+            divPhi
+            {
+                name    CONC_004;
+                Label   "divPhi";
+            }
+            // an example with units:
+            p
+            {
+                name    P;
+                Label   "Pressure";
+                units   "Pa";
+            }
+        }
+    \endverbatim
+
+Note
+    This class is in development
+    - any/all of the class names and members may change
+
+SourceFiles
+    ccmWriter.C
+    ccmWriterMesh.C
+    ccmWriterSolution.C
+
+\*---------------------------------------------------------------------------*/
+#ifndef ccmWriter_H
+#define ccmWriter_H
+
+#include "ccmBase.H"
+#include "STARCDCore.H"
+
+#include "fvMesh.H"
+#include "boundaryRegion.H"
+#include "cellTable.H"
+#include "IOobjectList.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+namespace ccm
+{
+
+// * * * * * * * * * * * * * Forward Declarations  * * * * * * * * * * * * * //
+
+class ccmID;
+class ccmMaps;
+class ccmDimension;
+class ccmGlobalState;
+
+/*---------------------------------------------------------------------------*\
+                         Class ccm::writer Declaration
+\*---------------------------------------------------------------------------*/
+
+class writer
+:
+    public base,
+    protected fileFormats::STARCDCore
+{
+    // Private Data
+
+    // Static Data
+
+        //- The OpenFOAM -> CCM field mappings
+        static dictionary defaultNameMapping;
+
+
+    // Member Data
+
+        //- MapIds for various components (cell, internalFaces, boundaries)
+        ccmMaps* maps_;
+
+        //- mesh reference
+        const polyMesh& mesh_;
+
+        //- Lookup between cellModel shape and PROSTAR shape
+        const Map<label> prostarShapeLookup_;
+
+        //- ccm node: ProblemDescriptions/boundaryRegion
+        boundaryRegion boundaryRegion_;
+
+        // ccm node: ProblemDescriptions/cellType
+        cellTable cellTable_;
+
+
+    // Private Member Functions
+
+        //- Disallow default bitwise copy construct
+        writer(const writer&) = delete;
+
+        //- Disallow default bitwise assignment
+        void operator=(const writer&) = delete;
+
+
+        //- create a filled linear map with 'size' from 'start + 1'
+        void addLinearMap
+        (
+            const string& mapName,
+            ccmID& mapId,
+            label size,
+            label start = 0
+        ) const;
+
+        void writeBoundaryRegion(const ccmID& probNode) const;
+
+        void writeCellTable(const ccmID& probNode) const;
+
+        //- write out problem description
+        void writeProblem(const ccmID& stateNode) const;
+
+        //- Return the PROSTAR face id for the given cell/face
+        label prostarCellFaceId(const label& cellId, const label& faceI) const;
+
+        // write the faces, the number of vertices appears before each entry
+        void writeFaces
+        (
+            const ccmID& nodeId,
+            const ccmID& mapId,
+            bool  isBoundary,    // boundary or internal faces
+            label size,
+            label start = 0
+        ) const;
+
+        void writeVertices(const ccmID& verticesNode) const;
+
+        // - write internal faces with owner/neighbour
+        void writeInternalFaces(const ccmID& topoNode) const;
+
+        // - write boundary faces with owner
+        void writeBoundaryFaces(const ccmID& topoNode) const;
+
+        void writeCells(const ccmID& topoNode);
+
+        void writeInterfaces(const ccmID& cellsNode) const;
+
+        bool newFieldNode
+        (
+            const ccmID& phaseNode,
+            const word& fieldName,
+            const dictionary& nameMapping,
+            const ccmDimension& ccmDim,
+            ccmID& fieldNode
+        ) const;
+
+
+        //- Return patch named 'Default_Boundary_Region' or -1 on error
+        label findDefaultBoundary() const;
+
+public:
+
+    // Static data members
+
+        //- The name for the topology file reference
+        static string defaultMeshName;
+
+
+    // Constructors
+
+        //- Open a file for writing, with backup/overwrite existing file
+        writer(const fileName&, const polyMesh&, const bool backup=true);
+
+
+    //- Destructor (closes file)
+    ~writer();
+
+
+    // Member Functions
+
+        // Write
+
+        //- Write the mesh
+        void writeGeometry();
+
+        //- Write the solutions
+        //  provide optional remapping dictionary
+        void writeSolution
+        (
+            const IOobjectList&,
+            const fileName& remappingDictName = fileName::null
+        );
+
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace ccm
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/conversion/ccm/writer/ccmWriterMesh.C b/src/conversion/ccm/writer/ccmWriterMesh.C
new file mode 100644
index 0000000000000000000000000000000000000000..506e9fc1c9c419e4ce8527eb1de1675ddc388fce
--- /dev/null
+++ b/src/conversion/ccm/writer/ccmWriterMesh.C
@@ -0,0 +1,831 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2016 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 "ccmWriter.H"
+#include "ccmInternal.H"  // include last to avoid any strange interactions
+
+
+// * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
+
+Foam::label Foam::ccm::writer::prostarCellFaceId
+(
+    const label& cellId,
+    const label& faceI
+) const
+{
+    const faceList&   faces = mesh_.faces();
+    const cellShape&  shape = mesh_.cellShapes()[cellId];
+    const labelList& cFaces = mesh_.cells()[cellId];
+
+    label cellFaceId = findIndex(cFaces, faceI);
+    label mapIndex = shape.model().index();
+
+    if (ccm::debug > 1)
+    {
+        Info<< " face[" << faceI << "]: " << faces[faceI] << nl
+            << " owner: " << cellId
+            << " cellFace: " << cellFaceId
+            << " cellFaces: " << cFaces
+            << " shape [index " << mapIndex << "] "  << shape
+            << endl;
+
+        Info << "cellFaces" << nl;
+        forAll(cFaces, cFaceI)
+        {
+            Info<< "  face [" << cFaces[cFaceI] << "] = "
+                << faces[cFaces[cFaceI]] << nl;
+        }
+
+        Info << "shapeFaces" << nl;
+        {
+            const faceList sFaces = shape.faces();
+            forAll(sFaces, sFaceI)
+            {
+                Info<< "  sFace[" << sFaceI << "] = "
+                    << sFaces[sFaceI] << nl;
+            }
+        }
+    }
+
+    // The face order returned by primitiveMesh::cells() is not
+    // necessarily the same as defined by
+    // primitiveMesh::cellShapes().
+    // Thus do the lookup for registered primitive types.
+    // Finally, remap the cellModel face number to the ProstarFaceId
+    if (prostarShapeLookup_.found(mapIndex))
+    {
+        const faceList sFaces = shape.faces();
+        forAll(sFaces, sFaceI)
+        {
+            if (faces[faceI] == sFaces[sFaceI])
+            {
+                cellFaceId = sFaceI;
+
+                if (ccm::debug > 1)
+                {
+                    Info << " FoamCellFace: " << sFaceI;
+                }
+
+                break;
+            }
+        }
+
+        mapIndex = prostarShapeLookup_[mapIndex];
+        cellFaceId = STARCDCore::foamToStarFaceAddr[mapIndex][cellFaceId];
+
+        if (ccm::debug > 1)
+        {
+            Info<< " ProstarShape: " << mapIndex
+                << " ProstarFaceId: " << cellFaceId + 1
+                << endl;
+        }
+    }
+
+    // PROSTAR used 1-based indices
+    return cellFaceId + 1;
+}
+
+
+// write the faces, the number of vertices appears before each entry
+void Foam::ccm::writer::writeFaces
+(
+    const ccmID& nodeId,
+    const ccmID& mapId,
+    bool  isBoundary,
+    label size,
+    label start
+) const
+{
+    if (globalState_->hasError() || size <= 0)
+    {
+        return;
+    }
+
+    CCMIOEntity nodeType;
+    if (isBoundary)
+    {
+        nodeType = kCCMIOBoundaryFaces;
+    }
+    else
+    {
+        nodeType = kCCMIOInternalFaces;
+    }
+
+    const faceList&  faces = mesh_.faces();
+    const labelList& owner = mesh_.faceOwner();
+    const labelList& neigh = mesh_.faceNeighbour();
+
+    // 1. write vertices to define faces
+
+    // Determine allocation size
+    label streamSize = size;
+    for (label faceI = 0; faceI < size; ++faceI)
+    {
+        streamSize += faces[faceI + start].size();
+    }
+
+    // Build a vertex stream
+    List<int> ccmStream(streamSize);
+    streamSize = 0;
+    for (label faceI = 0; faceI < size; ++faceI)
+    {
+        const labelList& vrtList = faces[faceI + start];
+
+        ccmStream[streamSize++] = vrtList.size();
+        forAll(vrtList, i)
+        {
+            ccmStream[streamSize++] = vrtList[i] + 1;
+        }
+    }
+
+    if (ccm::debug)
+    {
+        Info<<"CCMIOWriteFaces()  size:" << size << " streamSize:" << streamSize << endl;
+    }
+
+    CCMIOWriteFaces
+    (
+        &(globalState_->error),
+        nodeId,
+        nodeType,
+        mapId,
+        streamSize,
+        ccmStream.begin(),
+        kCCMIOStart,
+        kCCMIOEnd
+    );
+
+    if (globalState_->hasError())
+    {
+        return;
+    }
+
+    // 2. write face owner/neighbours
+    if (isBoundary)
+    {
+        streamSize = size;
+        if (ccmStream.size() < streamSize)
+        {
+            ccmStream.setSize(streamSize);
+        }
+
+        for (int faceI = 0; faceI < size; ++faceI)
+        {
+            ccmStream[faceI] = owner[faceI + start] + 1;
+        }
+    }
+    else
+    {
+        // kCCMIOInternalFaces
+        streamSize = 2 * size;
+        if (ccmStream.size() < streamSize)
+        {
+            ccmStream.setSize(streamSize);
+        }
+
+        for (int faceI = 0; faceI < size; ++faceI)
+        {
+            ccmStream[faceI*2]   = owner[faceI + start] + 1;
+            ccmStream[faceI*2+1] = neigh[faceI + start] + 1;
+        }
+    }
+
+    if (ccm::debug)
+    {
+        Info<<"CCMIOWriteFaceCells()  size:" << size;
+        if (isBoundary)
+        {
+            Info<< " boundary";
+        }
+        else
+        {
+            Info<<" internal";
+        }
+        Info<<"Faces"<<endl;
+    }
+
+    CCMIOWriteFaceCells
+    (
+        &(globalState_->error),
+        nodeId,
+        nodeType,
+        mapId,
+        ccmStream.begin(),
+        kCCMIOStart,
+        kCCMIOEnd
+    );
+
+    // determine corresponding ProstarFaceId
+    if (isBoundary)
+    {
+        streamSize = size;
+        if (ccmStream.size() < streamSize)
+        {
+            ccmStream.setSize(streamSize);
+        }
+
+        for (int faceIdx = 0; faceIdx < size; ++faceIdx)
+        {
+            label faceI = faceIdx + start;
+            // boundary face - owner only
+            ccmStream[faceIdx] = prostarCellFaceId(owner[faceI], faceI);
+        }
+
+        CCMIOWriteOpt1i
+        (
+            &(globalState_->error),
+            nodeId,
+            "ProstarFaceId",
+            size,
+            ccmStream.begin(),
+            kCCMIOStart,
+            kCCMIOEnd
+        );
+    }
+    else
+    {
+        // kCCMIOInternalFaces
+        streamSize = 2 * size;
+        if (ccmStream.size() < streamSize)
+        {
+            ccmStream.setSize(streamSize);
+        }
+
+        for (int faceIdx = 0; faceIdx < size; ++faceIdx)
+        {
+            label faceI = faceIdx + start;
+            ccmStream[faceIdx*2]   = prostarCellFaceId(owner[faceI], faceI);
+            ccmStream[faceIdx*2+1] = prostarCellFaceId(neigh[faceI], faceI);
+        }
+
+        CCMIOWriteOpt2i
+        (
+            &(globalState_->error),
+            nodeId,
+            "ProstarFaceId",
+            size,
+            2,
+            ccmStream.begin(),
+            kCCMIOStart,
+            kCCMIOEnd
+        );
+    }
+}
+
+
+// writeVertices
+// 1) write the vertex map (starting with 1)
+// 2) write the vertex data
+//
+void Foam::ccm::writer::writeVertices
+(
+    const ccmID& verticesNode
+) const
+{
+    const pointField& pts = mesh_.points();
+
+    Info<< "writing points: " << pts.size() << endl;
+
+    // 1. mapping data array index to the vertex Id (starting with 1)
+    ccmID vertexMap;
+    addLinearMap
+    (
+        "vertex Map",
+        vertexMap,
+        pts.size()
+    );
+
+    // 2. write vertices - scale [m] -> [mm] for consistency with PROSTAR
+    //    but is probably immaterial
+    const float scaling(0.001);   // to recover meters
+    List<float> vrts(3*pts.size());
+    forAll(pts, i)
+    {
+        vrts[3*i]    = 1000.0 * pts[i].x();
+        vrts[3*i+1]  = 1000.0 * pts[i].y();
+        vrts[3*i+2]  = 1000.0 * pts[i].z();
+    }
+
+    CCMIOWriteVerticesf
+    (
+        &(globalState_->error),
+        verticesNode,
+        3, scaling,
+        vertexMap,
+        vrts.begin(),
+        kCCMIOStart,
+        kCCMIOEnd
+    );
+    assertNoError("writing 'Vertices' node");
+}
+
+
+// writeInternalFaces
+// 1) write the face map (starting with 1)
+// 2) write owner/neighbour
+//
+void Foam::ccm::writer::writeInternalFaces
+(
+    const ccmID& topoNode
+) const
+{
+    label nFaces = mesh_.nInternalFaces();
+
+    Info<< "writing internalFaces: " << nFaces << endl;
+    if (nFaces > 0)
+    {
+        ccmID nodeId;
+        CCMIONewEntity
+        (
+            &(globalState_->error),
+            topoNode,
+            kCCMIOInternalFaces,
+            nullptr,
+            &nodeId
+        );
+        assertNoError("creating internalFaces node");
+
+        writeFaces
+        (
+            nodeId,
+            maps_->internalFaces,
+            false,
+            nFaces
+        );
+        assertNoError("writing internalFaces");
+    }
+}
+
+
+// writeBoundaryFaces:
+// - write faces with owner
+void Foam::ccm::writer::writeBoundaryFaces
+(
+    const ccmID& topoNode
+) const
+{
+    const polyBoundaryMesh& patches = mesh_.boundaryMesh();
+
+    // 4. write boundary faces
+    Info<< "writing boundaryFaces:" << flush;
+
+    label defaultId = findDefaultBoundary();
+
+    // write Default_Boundary_Region as BoundaryFaces-0
+    if (defaultId >= 0)
+    {
+        Info<< " " << patches[defaultId].size() << flush;
+
+        ccmID nodeId;
+        CCMIONewIndexedEntity
+        (
+            &(globalState_->error),
+            topoNode,
+            kCCMIOBoundaryFaces,
+            0,
+            nullptr,
+            &nodeId
+        );
+        assertNoError
+        (
+            "creating boundaryFaces node patch: " + patches[defaultId].name()
+        );
+
+        writeFaces
+        (
+            nodeId,
+            maps_->boundary[defaultId],
+            true,
+            patches[defaultId].size(),
+            patches[defaultId].start()
+        );
+        assertNoError
+        (
+            "writing boundaryFaces patch: " + patches[defaultId].name()
+        );
+    }
+
+    //
+    // write boundary faces - skip Default_Boundary_Region
+    //
+    forAll(patches, patchI)
+    {
+        label regionId = patchI;
+        if (regionId == defaultId)
+        {
+            continue;  // skip - already written
+        }
+        else if (defaultId == -1 || regionId < defaultId)
+        {
+            regionId++;
+        }
+
+        Info<< " " << patches[patchI].size() << flush;
+
+        if (patches[patchI].size() > 0)
+        {
+            ccmID nodeId;
+            CCMIONewIndexedEntity
+            (
+                &(globalState_->error),
+                topoNode,
+                kCCMIOBoundaryFaces,
+                regionId,
+                nullptr,
+                &nodeId
+            );
+            assertNoError
+            (
+                "creating boundaryFaces node patch: " + patches[patchI].name()
+            );
+
+            writeFaces
+            (
+                nodeId,
+                maps_->boundary[patchI],
+                true,
+                patches[patchI].size(),
+                patches[patchI].start()
+            );
+            assertNoError
+            (
+                "writing boundaryFaces patch: " + patches[patchI].name()
+            );
+        }
+    }
+
+    Info<< endl;
+}
+
+
+// writeCells:
+// - write faces with owner/neighbour
+// - write interfaces
+void Foam::ccm::writer::writeCells
+(
+    const ccmID& topoNode
+)
+{
+    Info<< "writing cells: " << mesh_.nCells() << endl;
+
+    // 1. cellTableId
+    //    - if possible, read from constant/polyMesh/cellTableId
+    //    - otherwise use cellZone information
+    List<int> mapData(mesh_.nCells(), -1);
+    bool useCellZones = false;
+
+    IOList<label> ioList
+    (
+        IOobject
+        (
+            "cellTableId",
+            "constant",
+            polyMesh::meshSubDir,
+            mesh_,
+            IOobject::READ_IF_PRESENT,
+            IOobject::NO_WRITE,
+            false
+        )
+    );
+
+
+    if (ioList.headerOk())
+    {
+        if (ioList.size() == mesh_.nCells())
+        {
+            mapData.transfer(ioList);
+        }
+        else
+        {
+            WarningIn("ccm::writer::writeCells(polyMesh&)")
+                << ioList.objectPath() << endl
+                << "    Has incorrect number of cells: "
+                << ioList.size() << " instead of " << mesh_.nCells()
+                << " -    use cellZone information instead"
+                << endl;
+
+            ioList.clear();
+            useCellZones = true;
+        }
+    }
+    else
+    {
+        useCellZones = true;
+    }
+
+
+    if (useCellZones)
+    {
+        if (!cellTable_.size())
+        {
+            Info<< "created cellTable from cellZones" << endl;
+            cellTable_ = mesh_;
+        }
+
+        // track if there are unzoned cells
+        label nUnzoned = mesh_.nCells();
+
+        // get the cellZone <-> cellTable correspondence
+        Info<< "matching cellZones to cellTable" << endl;
+
+        forAll(mesh_.cellZones(), zoneI)
+        {
+            const cellZone& cZone = mesh_.cellZones()[zoneI];
+            if (cZone.size())
+            {
+                nUnzoned -= cZone.size();
+
+                label tableId = cellTable_.findIndex(cZone.name());
+                if (tableId < 0)
+                {
+                    dictionary dict;
+
+                    dict.add("Label", cZone.name());
+                    dict.add("MaterialType", "fluid");
+                    tableId = cellTable_.append(dict);
+                }
+
+                forAll(cZone, i)
+                {
+                    mapData[cZone[i]] = tableId;
+                }
+            }
+        }
+
+        if (nUnzoned)
+        {
+            dictionary dict;
+
+            dict.add("Label", "__unzonedCells__");
+            dict.add("MaterialType", "fluid");
+            label tableId = cellTable_.append(dict);
+
+            forAll(mapData, i)
+            {
+                if (mapData[i] < 0)
+                {
+                    mapData[i] = tableId;
+                }
+            }
+        }
+    }
+
+    // 2. mapping data array index to the cell Id (starting with 1)
+    ccmID cellsNode;
+    CCMIONewEntity
+    (
+        &(globalState_->error),
+        topoNode,
+        kCCMIOCells,
+        nullptr,
+        &cellsNode
+    );
+    assertNoError("creating 'Cells' node");
+
+    CCMIOWriteCells
+    (
+        &(globalState_->error),
+        cellsNode,
+        maps_->cells,
+        mapData.begin(),
+        kCCMIOStart, kCCMIOEnd
+    );
+    assertNoError("writing 'Cells' node");
+
+    // 3. add cell topology information, if possible
+    const cellShapeList& shapes = mesh_.cellShapes();
+    forAll(shapes, cellI)
+    {
+        label mapIndex = shapes[cellI].model().index();
+
+        // A registered primitive type
+        if (prostarShapeLookup_.found(mapIndex))
+        {
+            mapData[cellI] = prostarShapeLookup_[mapIndex];
+        }
+        else
+        {
+            mapData[cellI] = STARCDCore::starcdPoly;  // Treat as polyhedral
+        }
+    }
+
+    CCMIOWriteOpt1i
+    (
+        &(globalState_->error),
+        cellsNode,
+        "CellTopologyType",
+        mesh_.nCells(),
+        mapData.begin(),
+        kCCMIOStart, kCCMIOEnd
+    );
+
+    // 4. write interfaces
+    writeInterfaces(cellsNode);
+}
+
+
+// write interfaces
+//   1) PROSTAR baffles
+//
+void Foam::ccm::writer::writeInterfaces
+(
+    const ccmID& cellsNode
+) const
+{
+    IOList<labelList> interfaces
+    (
+        IOobject
+        (
+            "interfaces",
+            "constant",
+            "polyMesh",
+            mesh_,
+            IOobject::READ_IF_PRESENT,
+            IOobject::NO_WRITE,
+            false
+        )
+    );
+
+    if (interfaces.headerOk() && interfaces.size() > 0)
+    {
+        List<int> mapData(2 * interfaces.size());
+
+        forAll(interfaces, i)
+        {
+            mapData[i*2]   = interfaces[i][0];
+            mapData[i*2+1] = interfaces[i][1];
+        }
+
+        ccmID nodeId;
+        if
+        (
+            CCMIONewEntity
+            (
+                &(globalState_->error),
+                cellsNode,
+                kCCMIOInterfaces,
+                0,
+                &nodeId
+            )
+         != kCCMIONoErr
+
+         || CCMIOWriteOpt2i
+            (
+                &(globalState_->error),
+                nodeId,
+                "FaceIds",
+                interfaces.size(),
+                2,
+                mapData.begin(), kCCMIOStart, kCCMIOEnd
+            )
+         != kCCMIONoErr
+        )
+        {
+            assertNoError("writing interfaces 'FaceIds'");
+        }
+    }
+}
+
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+void Foam::ccm::writer::writeGeometry()
+{
+    // use first ("default") state node
+    ccmID stateNode;
+    // int stateI = 0;
+
+    // create "default" State
+    CCMIONewState
+    (
+        &(globalState_->error),
+        (globalState_->root),
+        "default",
+        nullptr,
+        nullptr,
+        &stateNode
+    );
+    assertNoError("could not create default state");
+
+    // use first processor - create a new one
+    ccmID processorNode;
+    int procI = 0;
+
+    if
+    (
+        CCMIONextEntity
+        (
+            nullptr,
+            stateNode,
+            kCCMIOProcessor,
+            &procI,
+            &processorNode
+        )
+     != kCCMIONoErr
+    )
+    {
+        CCMIONewEntity
+        (
+            &(globalState_->error),
+            stateNode,
+            kCCMIOProcessor,
+            nullptr,
+            &processorNode
+        );
+        assertNoError("could not create processor node");
+    }
+
+    // remove old data (if it exists)
+    CCMIOClearProcessor
+    (
+        nullptr, stateNode, processorNode,
+        true,   // Clear vertices
+        true,   // Clear topology
+        true,   // Clear initial field
+        true,   // Clear solution
+        true    // Clear lagrangian
+    );
+
+#if 0
+    CCMIOWriteOptstr
+    (
+        nullptr,
+        processorNode,
+        "CreatingProgram",
+        "ccm::writer"
+    );
+#endif
+
+    //
+    // create vertices and topology nodes
+    //
+    ccmID verticesNode, topoNode;
+    if
+    (
+        CCMIONewEntity
+        (
+            &(globalState_->error),
+            (globalState_->root),
+            kCCMIOVertices,
+            nullptr,
+            &verticesNode
+        )
+     == kCCMIONoErr
+
+     && CCMIONewEntity
+        (
+            &(globalState_->error),
+            (globalState_->root),
+            kCCMIOTopology,
+            nullptr,
+            &topoNode
+        )
+     == kCCMIONoErr
+    )
+    {
+        writeVertices(verticesNode);
+        writeInternalFaces(topoNode);
+        writeBoundaryFaces(topoNode);
+        writeCells(topoNode);
+        writeProblem(stateNode);
+    }
+
+    // Now we have the mesh (vertices and topology),
+    // we can write out the processor information.
+    CCMIOWriteProcessor
+    (
+        &(globalState_->error),
+        processorNode,
+        nullptr, &verticesNode, // no verticesFile, write verticesNode
+        nullptr, &topoNode,     // no topologyFile, write topoNode
+        nullptr, nullptr,       // initialField unchanged
+        nullptr, nullptr        // no solutionFile, solution unchanged
+    );
+    assertNoError("Error after writing geometry processor");
+
+}
+
+
+// ************************************************************************* //
diff --git a/src/conversion/ccm/writer/ccmWriterSolution.C b/src/conversion/ccm/writer/ccmWriterSolution.C
new file mode 100644
index 0000000000000000000000000000000000000000..c351ef76c86650be2bd3218bfb0d13e409ec4279
--- /dev/null
+++ b/src/conversion/ccm/writer/ccmWriterSolution.C
@@ -0,0 +1,814 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2016 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 "ccmWriter.H"
+#include "dictionary.H"
+#include "IFstream.H"
+#include "IStringStream.H"
+#include "volFields.H"
+#include "surfaceFields.H"
+#include "ccmInternal.H"  // include last to avoid any strange interactions
+
+
+// * * * * * * * * * * * * * * * Static Data Member  * * * * * * * * * * * * //
+
+// names for known field types
+Foam::dictionary Foam::ccm::writer::defaultNameMapping
+(
+    IStringStream
+    (
+        // volScalarField
+        "p   { name P; Label \"Pressure\"; units \"Pa\"; }"
+     // "?? { name PTER; Label \"Thermo. Pressure\"; units \"Pa\"; }"
+     // "yPlus { name YPLU; Label \"YPLUS\"; }"
+     // "?? { name DIST; Label \"Normal Distance\"; units \"m\"; }"
+     // "?? { name H; Label \"Enthalpy\"; units \"J/kg\"; }"
+        "rho { name DENS; Label \"Density\"; units \"kg/m3\"; }"
+        "T   { name T; Label \"Temperature\"; units \"K\"; }"
+        "k   { name TE; Label \"Turb Kinetic Energy\"; units \"m2/s2\"; }"
+        "epsilon { name ED; Label \"Dissipation Rate\"; units \"m2/s3\"; }"
+        "mu  { name LAMV; Label \"Molecular Viscosity\"; units \"Pa s\"; }"
+        "mut { name VIS;  Label \"Turbulent Viscosity\"; units \"Pa s\"; }"
+        // volVectorField
+        "U   { name ALL; Label \"Velocity\"; units \"m/s\"; }"
+        // U-components:
+        "_0U { name SU; Label \"Velocity Component U\"; units \"m/s\"; }"
+        "_1U { name SV; Label \"Velocity Component V\"; units \"m/s\"; }"
+        "_2U { name SW; Label \"Velocity Component W\"; units \"m/s\"; }"
+        // surfaceScalarField
+        "phi { name MassFlux; Label \"Mass Flux\"; units \"kg/s\"; }"
+    )()
+);
+
+
+// * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
+
+bool Foam::ccm::writer::newFieldNode
+(
+    const ccmID& phaseNode,
+    const word& fieldName,
+    const dictionary& nameMapping,
+    const ccmDimension& dims,
+    ccmID& fieldNode
+) const
+{
+    if (!nameMapping.found(fieldName) || !nameMapping.isDict(fieldName))
+    {
+        return false;
+    }
+
+    const dictionary& dict = nameMapping.subDict(fieldName);
+
+    word shortName;
+    if (!dict.readIfPresent("name", shortName))
+    {
+        return false;
+    }
+
+    string ccmLabel(fieldName);
+    dict.readIfPresent("Label", ccmLabel);
+
+    CCMIONewField
+    (
+        &(globalState_->error),
+        phaseNode,
+        ccmLabel.c_str(),
+        shortName.c_str(),
+        dims(),
+        &fieldNode
+    );
+
+
+    string units;
+    if (dims() == kCCMIOScalar && dict.readIfPresent("units", units))
+    {
+        CCMIOWriteOptstr
+        (
+            &(globalState_->error),
+            fieldNode,
+            "Units",
+            units.c_str()
+        );
+    }
+
+    return true;
+
+}
+
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+void Foam::ccm::writer::writeSolution
+(
+    const IOobjectList& objects,
+    const fileName& remappingDictName
+)
+{
+    const polyBoundaryMesh& patches = mesh_.boundaryMesh();
+
+    if (!isA<fvMesh>(mesh_))
+    {
+        WarningIn("ccm::writer::writeSolution(const IOobjectList&, const fileName&)")
+            << "cannot write solutions with a polyMesh instead of a fvMesh"
+            << endl;
+        return;
+    }
+
+    dictionary remapDict;
+
+    if (remappingDictName.empty())
+    {
+        remapDict = IOdictionary
+        (
+            IOobject
+            (
+                "remapping",
+                mesh_.time().constant(),
+                mesh_,
+                IOobject::READ_IF_PRESENT,
+                IOobject::NO_WRITE,
+                false
+            )
+        );
+    }
+    else
+    {
+        // Specified (absolute/relative) name: treat like MUST_READ
+        remapDict = dictionary(IFstream(remappingDictName)());
+    }
+
+
+    dictionary nameMapping(defaultNameMapping);
+
+    // Merge without overwrite
+    if (remapDict.isDict("fields"))
+    {
+        nameMapping |= remapDict.subDict("fields");
+    }
+
+
+    ccmID stateNode, processorNode, nodeId;
+    int procI = 0;
+
+    // Create first ("default") state node
+    CCMIONewState
+    (
+        &(globalState_->error),
+        (globalState_->root),
+        "default",
+        nullptr,
+        nullptr,
+        &stateNode
+    );
+    assertNoError("could not create default state");
+
+    // Use first processor or create a new one
+    procI = 0;
+    if
+    (
+        CCMIONextEntity
+        (
+            nullptr,
+            stateNode,
+            kCCMIOProcessor,
+            &procI,
+            &processorNode
+        )
+     != kCCMIONoErr
+    )
+    {
+        CCMIONewEntity
+        (
+            &(globalState_->error),
+            stateNode,
+            kCCMIOProcessor,
+            nullptr,
+            &processorNode
+        );
+        assertNoError("could not create processor node");
+    }
+
+    // Remove old data (if it exists)
+    CCMIOClearProcessor
+    (
+        nullptr, stateNode, processorNode,
+        true,   // Clear vertices
+        true,   // Clear topology
+        true,   // Clear initial field
+        true,   // Clear solution
+        true    // Clear lagrangian
+    );
+
+    //
+    // Create vertices and topology nodes
+    // and references to vertices and topology files
+    //
+    {
+        ccmID verticesNode, topoNode;
+        CCMIONewEntity
+        (
+            &(globalState_->error),
+            (globalState_->root),
+            kCCMIOVertices,
+            nullptr,
+            &verticesNode
+        );
+
+        CCMIONewEntity
+        (
+            &(globalState_->error),
+            (globalState_->root),
+            kCCMIOTopology,
+            nullptr,
+            &topoNode
+        );
+
+        string topoFileName = defaultMeshName + ".ccmg";
+
+        CCMIOWriteProcessor
+        (
+            &(globalState_->error),
+            processorNode,
+            topoFileName.c_str(), &verticesNode,// verticesFile, verticesNode
+            topoFileName.c_str(), &topoNode,    // topologyFile, topologyNode
+            nullptr, nullptr,                   // initialField unchanged
+            nullptr, nullptr                    // no solutionFile, solution unchanged
+        );
+    }
+    assertNoError("Error after writing geometry processor");
+
+    CCMIONewState
+    (
+        &(globalState_->error),
+        (globalState_->root),
+        "Restart_1",
+        nullptr,
+        nullptr,
+        &stateNode
+    );
+    assertNoError("could not create Restart_1 state");
+
+    // Use first processor or create a new one
+    procI = 0;
+    if
+    (
+        CCMIONextEntity
+        (
+            nullptr,
+            stateNode,
+            kCCMIOProcessor,
+            &procI,
+            &processorNode
+        )
+     != kCCMIONoErr
+    )
+    {
+        CCMIONewEntity
+        (
+            &(globalState_->error),
+            stateNode,
+            kCCMIOProcessor,
+            nullptr,
+            &processorNode
+        );
+        assertNoError("could not create 'Processor' node");
+    }
+
+    // Remove old data (if it exists)
+    CCMIOClearProcessor
+    (
+        nullptr, stateNode, processorNode,
+        true,   // Clear vertices
+        true,   // Clear topology
+        true,   // Clear initial field
+        true,   // Clear solution
+        true    // Clear lagrangian
+    );
+
+    // Write out some simple solution data
+    ccmID phaseNode, fieldSetNode;
+
+    // Use first FieldSet
+    int fieldSetI = 0;
+    if
+    (
+        CCMIONextEntity
+        (
+            nullptr,
+            (globalState_->root),
+            kCCMIOFieldSet,
+            &fieldSetI,
+            &fieldSetNode
+        )
+     != kCCMIONoErr
+    )
+    {
+        CCMIONewEntity
+        (
+            &(globalState_->error),
+            (globalState_->root),
+            kCCMIOFieldSet,
+            nullptr,
+            &fieldSetNode
+        );
+        assertNoError("could not create FieldSet node");
+    }
+
+    // RestartInfo
+    {
+        ccmID restartInfoNode, restartDataNode;
+        CCMIONewEntity
+        (
+            &(globalState_->error),
+            fieldSetNode,
+            kCCMIORestart,
+            nullptr,
+            &restartInfoNode
+        );
+        assertNoError("could not create restartInfoNode node");
+
+        // Get time information
+        const Time& runTime = mesh_.time();
+        label timeIndex = 0;
+        // scalar timeValue = runTime.timeName();
+        if
+        (
+            runTime.timeName() != runTime.constant()
+         && runTime.timeName() != "0"
+        )
+        {
+            IOobject io
+            (
+                "time",
+                runTime.timeName(),
+                "uniform",
+                runTime,
+                IOobject::READ_IF_PRESENT,
+                IOobject::NO_WRITE,
+                false
+            );
+
+            if (io.typeHeaderOk<IOdictionary>(true))
+            {
+                IOdictionary timeObject
+                (
+                    IOobject
+                    (
+                        "time",
+                        runTime.timeName(),
+                        "uniform",
+                        runTime,
+                        IOobject::MUST_READ,
+                        IOobject::NO_WRITE,
+                        false
+                    )
+                );
+
+                timeObject.lookup("index") >> timeIndex;
+            }
+        }
+
+        CCMIOWriteRestartInfo
+        (
+            &(globalState_->error),
+            restartInfoNode,
+            "ccm::writer",      // solverName
+            timeIndex,          // iteration
+            0.0,                // time
+            nullptr,            // timeUnits: default  (s)
+            0.0                 // startAngle: non-rotating mesh
+        );
+
+        // RestartData
+        CCMIONewEntity
+        (
+            &(globalState_->error),
+            restartInfoNode,
+            kCCMIORestartData,
+            nullptr,
+            &restartDataNode
+        );
+        assertNoError("could not create restartDataNode node");
+
+        // Minimal information required by PROSTAR
+
+        CCMIOWriteOptf
+        (
+            nullptr,
+            restartDataNode,
+            "SCALE",
+            0.001        // [m] -> [mm]: PROSTAR is still a bit buggy here
+        );
+
+
+        // Add Phase data
+        CCMIONewIndexedEntity
+        (
+            &(globalState_->error),
+            restartDataNode,
+            kCCMIOFieldPhase,
+            1,
+            nullptr,
+            &nodeId
+        );
+
+        // HACK:  for calculating Mach number
+        // FIXME: use thermodynamicProperties /or/ thermophysicalProperties
+        CCMIOWriteOptf
+        (
+            nullptr,
+            nodeId,
+            "Material Specific Heat",
+            1007
+        );
+
+        CCMIOWriteOptf
+        (
+            nullptr,
+            nodeId,
+            "Material Molecular Weight",
+            28.96
+        );
+    }
+
+    CCMIONewIndexedEntity
+    (
+        &(globalState_->error),
+        fieldSetNode,
+        kCCMIOFieldPhase,
+        1,
+        "Phase_0001",
+        &phaseNode
+    );
+
+
+
+    forAllConstIter(IOobjectList, objects, iter)
+    {
+        word fieldName = (*iter()).name();
+        bool variableGood =
+        (
+            nameMapping.found(fieldName)
+         && (*iter()).typeHeaderOk<volScalarField>(false)
+        );
+
+        if (!variableGood)
+        {
+            // Only retain registered fields that are also readable
+            continue;
+        }
+
+        word fieldType = (*iter()).headerClassName();
+
+        if (fieldType == volScalarField::typeName)
+        {
+            Info<< " " << fieldName << flush;
+
+            volScalarField field
+            (
+                IOobject
+                (
+                    fieldName,
+                    mesh_.time().timeName(),
+                    mesh_,
+                    IOobject::READ_IF_PRESENT,
+                    IOobject::NO_WRITE
+                ),
+                refCast<const fvMesh>(mesh_)
+            );
+
+            ccmID fieldNode;
+
+            // Info<< " call newFieldNode " << fieldName << flush;
+
+            if
+            (
+                !newFieldNode
+                (
+                    phaseNode,
+                    fieldName,
+                    nameMapping,
+                    kCCMIOScalar,
+                    fieldNode
+                )
+            )
+            {
+                continue;
+            }
+
+            // Write cellData first
+            CCMIONewEntity
+            (
+                &(globalState_->error),
+                fieldNode,
+                kCCMIOFieldData,
+                nullptr,
+                &nodeId
+            );
+
+            CCMIOWriteFieldDatad
+            (
+                &(globalState_->error),
+                nodeId,
+                maps_->cells,
+                kCCMIOCell,
+                const_cast<scalar*>
+                (
+                    field.primitiveField().begin()
+                ),
+                kCCMIOStart,
+                kCCMIOEnd
+            );
+            assertNoError("writing internalField " + fieldName);
+
+            // Write boundaryData
+            forAll(patches, patchI)
+            {
+                CCMIONewEntity
+                (
+                    &(globalState_->error),
+                    fieldNode,
+                    kCCMIOFieldData,
+                    nullptr,
+                    &nodeId
+                );
+
+                CCMIOWriteFieldDatad
+                (
+                    &(globalState_->error),
+                    nodeId,
+                    maps_->boundary[patchI],
+                    kCCMIOFace,
+                    const_cast<scalar*>
+                    (
+                        field.boundaryField()[patchI].begin()
+                    ),
+                    kCCMIOStart,
+                    kCCMIOEnd
+                );
+            }
+
+            assertNoError("writing boundaryField " + fieldName);
+        }
+        else if (fieldType == volVectorField::typeName && fieldName == "U")
+        {
+            Info<< " " << fieldName << flush;
+
+            volVectorField vfield
+            (
+                IOobject
+                (
+                    fieldName,
+                    mesh_.time().timeName(),
+                    mesh_,
+                    IOobject::READ_IF_PRESENT,
+                    IOobject::NO_WRITE
+                ),
+                refCast<const fvMesh>(mesh_)
+            );
+
+
+            ccmID vectorNode;
+            newFieldNode
+            (
+                phaseNode,
+                fieldName,
+                nameMapping,
+                kCCMIOVector,
+                vectorNode
+            );
+
+            for (direction cmpt=0; cmpt < pTraits<vector>::nComponents; ++cmpt)
+            {
+                word componentName("_" + Foam::name(cmpt) + fieldName);
+                volScalarField field(vfield.component(cmpt));
+
+                CCMIOComponent ccmComponent = kCCMIOVectorX;
+                switch(cmpt) {
+                    case 0:
+                        ccmComponent = kCCMIOVectorX;
+                        break;
+                    case 1:
+                        ccmComponent = kCCMIOVectorY;
+                        break;
+                    case 2:
+                        ccmComponent = kCCMIOVectorZ;
+                        break;
+                }
+
+                ccmID componentNode;
+                if
+                (
+                    !newFieldNode
+                    (
+                        phaseNode,
+                        componentName,
+                        nameMapping,
+                        kCCMIOScalar,
+                        componentNode
+                    )
+                )
+                {
+                    continue;
+                }
+
+                // Re-register with vector field
+                CCMIOWriteMultiDimensionalFieldData
+                (
+                    &(globalState_->error),
+                    vectorNode,
+                    ccmComponent,
+                    componentNode
+                );
+
+                // Write cellData first
+                CCMIONewEntity
+                (
+                    &(globalState_->error),
+                    componentNode,
+                    kCCMIOFieldData,
+                    nullptr,
+                    &nodeId
+                );
+
+                CCMIOWriteFieldDatad
+                (
+                    &(globalState_->error),
+                    nodeId,
+                    maps_->cells,
+                    kCCMIOCell,
+                    const_cast<scalar*>
+                    (
+                        field.primitiveField().begin()
+                    ),
+                    kCCMIOStart, kCCMIOEnd
+                );
+                assertNoError
+                (
+                    "writing internalField " + fieldName + " " + componentName
+                );
+
+
+                // Write boundaryData
+                forAll(patches, patchI)
+                {
+                    CCMIONewEntity
+                    (
+                        &(globalState_->error),
+                        componentNode,
+                        kCCMIOFieldData,
+                        nullptr,
+                        &nodeId
+                    );
+
+                    CCMIOWriteFieldDatad
+                    (
+                        &(globalState_->error),
+                        nodeId,
+                        maps_->boundary[patchI],
+                        kCCMIOFace,
+                        const_cast<scalar*>
+                        (
+                            field.boundaryField()[patchI].begin()
+                        ),
+                        kCCMIOStart, kCCMIOEnd
+                    );
+                }
+
+                assertNoError
+                (
+                    "writing boundaryField " + fieldName + " " + componentName
+                );
+            }
+        }
+        else if (fieldType == surfaceScalarField::typeName)
+        {
+#if 0
+            // Still have problems reading surface fields in PROSTAR
+            Info<< " " << fieldName << flush;
+
+            surfaceScalarField field
+            (
+                IOobject
+                (
+                    fieldName,
+                    mesh_.time().timeName(),
+                    mesh_,
+                    IOobject::READ_IF_PRESENT,
+                    IOobject::NO_WRITE
+                ),
+                refCast<const fvMesh>(mesh_)
+            );
+
+            ccmID fieldNode;
+
+            // Info<< " call newFieldNode " << fieldName << flush;
+
+            if
+            (
+                !newFieldNode
+                (
+                    phaseNode,
+                    fieldName,
+                    nameMapping,
+                    kCCMIOScalar,
+                    fieldNode
+                )
+            )
+            {
+                continue;
+            }
+
+            // Write cell faceData first
+            CCMIONewEntity
+            (
+                &(globalState_->error),
+                fieldNode,
+                kCCMIOFieldData,
+                nullptr,
+                &nodeId
+            );
+
+            CCMIOWriteFieldDatad
+            (
+                &(globalState_->error),
+                nodeId,
+                maps_->internalFaces,
+                kCCMIOFace,
+                const_cast<scalar*>
+                (
+                    field.primitiveField().begin()
+                ),
+                kCCMIOStart,
+                kCCMIOEnd
+            );
+            assertNoError("writing internalField " + fieldName);
+
+            // Write boundaryData
+            forAll(patches, patchI)
+            {
+                CCMIONewEntity
+                (
+                    &(globalState_->error),
+                    fieldNode,
+                    kCCMIOFieldData,
+                    nullptr,
+                    &nodeId
+                );
+
+                CCMIOWriteFieldDatad
+                (
+                    &(globalState_->error),
+                    nodeId,
+                    maps_->boundary[patchI],
+                    kCCMIOFace,
+                    field.boundaryField()[patchI].begin(),
+                    kCCMIOStart,
+                    kCCMIOEnd
+                );
+            }
+
+            assertNoError("writing boundaryField " + fieldName);
+#endif
+        }
+    }
+    Info<< endl;
+
+    assertNoError("Error before writing processor");
+
+    CCMIOWriteProcessor
+    (
+        &(globalState_->error),
+        processorNode,
+        nullptr, nullptr,     // vertices
+        nullptr, nullptr,     // topology
+        nullptr, nullptr,     // initial field
+        nullptr, &fieldSetNode
+    );
+    assertNoError("Error after writing processor");
+}
+
+
+// ************************************************************************* //
diff --git a/src/conversion/common/reader/meshReader.C b/src/conversion/common/reader/meshReader.C
index c7e2d67980f6a0419c5a8168a04d274b445aeebd..4a6356910cb76d13e11e8d34a6c0d8273068af13 100644
--- a/src/conversion/common/reader/meshReader.C
+++ b/src/conversion/common/reader/meshReader.C
@@ -213,7 +213,13 @@ Foam::meshReader::meshReader
     baffleFaces_(0),
     cellTableId_(0),
     cellTable_()
-{}
+{
+    // Sanity
+    if (scaleFactor_ <= VSMALL)
+    {
+        scaleFactor_ = 1;
+    }
+}
 
 
 // * * * * * * * * * * * * * * * * Destructor  * * * * * * * * * * * * * * * //
diff --git a/src/conversion/common/reader/meshReader.H b/src/conversion/common/reader/meshReader.H
index 54d5876e96448902374c72fb9fe7b06076da3756..b53d0c1e4dfbcdd93da2f09f35b3e47f1b177a7c 100644
--- a/src/conversion/common/reader/meshReader.H
+++ b/src/conversion/common/reader/meshReader.H
@@ -275,7 +275,7 @@ public:
     // Constructors
 
         //- Construct from fileName
-        meshReader(const fileName&, const scalar scaleFactor = 1.0);
+        meshReader(const fileName&, const scalar scaling = 1.0);
 
 
     //- Destructor
diff --git a/src/conversion/common/writer/meshWriter.C b/src/conversion/common/writer/meshWriter.C
index 36d6426757254d8ab97ecf9da6d3bbfaeadcbb2c..760cccea7a23697c655ef3fed30b4a42bf9568d2 100644
--- a/src/conversion/common/writer/meshWriter.C
+++ b/src/conversion/common/writer/meshWriter.C
@@ -3,7 +3,7 @@
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
     \\  /    A nd           | Copyright (C) 2011-2013 OpenFOAM Foundation
-     \\/     M anipulation  |
+     \\/     M anipulation  | Copyright (C) 2016 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -68,15 +68,24 @@ lookup
 
 // * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
 
-Foam::meshWriter::meshWriter(const polyMesh& mesh, const scalar scaleFactor)
+Foam::meshWriter::meshWriter
+(
+    const polyMesh& mesh,
+    const scalar scaling
+)
 :
     mesh_(mesh),
-    scaleFactor_(scaleFactor),
-    writeBoundary_(true),
+    scaleFactor_(scaling),
     boundaryRegion_(),
     cellTable_(),
     cellTableId_()
-{}
+{
+    // Sanity
+    if (scaleFactor_ <= VSMALL)
+    {
+        scaleFactor_ = 1;
+    }
+}
 
 
 // * * * * * * * * * * * * * * * * Destructor  * * * * * * * * * * * * * * * //
diff --git a/src/conversion/common/writer/meshWriter.H b/src/conversion/common/writer/meshWriter.H
index 51f4c4014a911bd39d48b9da74d6796f5aa958a3..d17322fde0754480c3eb7a2a6864fba89b318d6f 100644
--- a/src/conversion/common/writer/meshWriter.H
+++ b/src/conversion/common/writer/meshWriter.H
@@ -98,9 +98,6 @@ protected:
         //- Scaling factor for points (eg, [m] -> [mm])
         scalar scaleFactor_;
 
-        //- Write bnd file
-        bool writeBoundary_;
-
         //- boundaryRegion persistent data saved as a dictionary
         boundaryRegion boundaryRegion_;
 
@@ -125,13 +122,14 @@ public:
         //- Specify a default mesh name
         static string defaultMeshName;
 
+
     // Constructors
 
-        //- Create a writer object
+        //- Create a writer object with given output scaling
         meshWriter
         (
             const polyMesh&,
-            const scalar scaleFactor = 1.0
+            const scalar scaling = 1.0
         );
 
 
@@ -141,28 +139,13 @@ public:
 
     // Member Functions
 
-        // Edit
-
-            //- Set points scaling
-            void scaleFactor(const scalar scaling)
-            {
-                scaleFactor_ = scaling;
-            }
+    // Write
 
-            //- Suppress writing boundary (bnd) file
-            void noBoundary()
-            {
-                writeBoundary_ = false;
-            }
-
-
-        // Write
-
-            //- Write volume mesh. Subclass must supply this method
-            virtual bool write
-            (
-                const fileName& timeName = fileName::null
-            ) const = 0;
+        //- Write volume mesh. Subclass must supply this method
+        virtual bool write
+        (
+            const fileName& timeName = fileName::null
+        ) const = 0;
 
 };
 
diff --git a/src/conversion/fire/FIREMeshReader.C b/src/conversion/fire/FIREMeshReader.C
new file mode 100644
index 0000000000000000000000000000000000000000..ef8576cf5f543665a282d97ecf9250a20cbf607c
--- /dev/null
+++ b/src/conversion/fire/FIREMeshReader.C
@@ -0,0 +1,549 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2016 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 "FIREMeshReader.H"
+#include "wallPolyPatch.H"
+#include "ListOps.H"
+#include "IFstream.H"
+
+
+// * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
+
+Foam::word Foam::fileFormats::FIREMeshReader::validateWord
+(
+    const std::string& str
+)
+{
+    std::string::size_type ngood = 0;
+    bool prefix = false;
+    bool first  = true;
+
+    for
+    (
+        std::string::const_iterator iter = str.begin();
+        iter != str.end();
+        ++iter
+    )
+    {
+        if (word::valid(*iter))
+        {
+            ++ngood;
+            if (first)
+            {
+                first = false;
+
+                // start with a digit? need to prefix with '_'
+                if (isdigit(*iter))
+                {
+                    prefix = true;
+                }
+            }
+        }
+    }
+
+    if (prefix)
+    {
+        ++ngood;
+    }
+    else if (ngood == str.size())
+    {
+        return str;
+    }
+
+    Foam::word out;
+    out.resize(ngood);
+    ngood = 0;
+
+    Foam::word::iterator iter2 = out.begin();
+    for
+    (
+        std::string::const_iterator iter1 = str.begin();
+        iter1 != str.end();
+        ++iter1
+    )
+    {
+        register char c = *iter1;
+
+        if (Foam::word::valid(c))
+        {
+            if (prefix)
+            {
+                prefix = false;
+                *(iter2++) = '_';
+                ++ngood;
+            }
+            *(iter2++) = c;
+            ++ngood;
+        }
+    }
+
+    out.resize(ngood);
+
+    return out;
+}
+
+
+void Foam::fileFormats::FIREMeshReader::readPoints
+(
+    ISstream& is,
+    const scalar scaleFactor
+)
+{
+    const label n = FIRECore::readPoints(is, points_);
+    // the above has FatalError if there are no points
+
+    Info<< "Number of points = " << n << endl;
+    if (scaleFactor > 1.0 + SMALL || scaleFactor < 1.0 - SMALL)
+    {
+        points_ *= scaleFactor;
+    }
+}
+
+
+void Foam::fileFormats::FIREMeshReader::readFaces(ISstream& is)
+{
+    const label nFaces = getFireLabel(is);
+    Info<< "Number of faces  = " << nFaces << endl;
+    meshFaces_.setSize(nFaces);
+
+    if (nFaces > 0)
+    {
+        forAll(meshFaces_, faceI)
+        {
+            const label size = getFireLabel(is);
+
+            face& f = meshFaces_[faceI];
+            f.setSize(size);
+            forAll(f, fp)
+            {
+                f[fp] = getFireLabel(is);
+            }
+
+            // flip in-place
+            f.flip();
+        }
+    }
+    else
+    {
+        FatalErrorInFunction
+            << "no faces in file " << is.name()
+            << abort(FatalError);
+    }
+}
+
+
+void Foam::fileFormats::FIREMeshReader::readCells(ISstream& is)
+{
+    const label nCells = getFireLabel(is);
+    Info<< "Number of cells  = " << nCells << endl;
+
+    owner_.setSize(meshFaces_.size());
+    neigh_.setSize(meshFaces_.size());
+
+    owner_ = -1;
+    neigh_ = -1;
+
+    if (nCells > 0)
+    {
+        for (label cellI = 0; cellI < nCells; ++cellI)
+        {
+            const label nface = getFireLabel(is);
+
+            for (label i = 0; i < nface; ++i)
+            {
+                const label faceI = getFireLabel(is);
+
+                if (owner_[faceI] == -1)
+                {
+                    owner_[faceI] = cellI;
+                }
+                else if (neigh_[faceI] == -1)
+                {
+                    neigh_[faceI] = cellI;
+                }
+                else
+                {
+                    Warning
+                        << "bad cell connectivity for face " << faceI
+                        << " on cell " << cellI
+                        << endl;
+                }
+            }
+        }
+    }
+    else
+    {
+        FatalErrorInFunction
+            << "no cells in file " << is.name()
+            << abort(FatalError);
+    }
+
+    cellTableId_.setSize(nCells);
+    cellTableId_ = -1;
+}
+
+
+void Foam::fileFormats::FIREMeshReader::readSelections(ISstream& is)
+{
+    const label nSelect = getFireLabel(is);
+    Info<< "Number of select = " << nSelect << endl;
+
+    label nCellSelections = 0;
+    label nFaceSelections = 0;
+
+    faceZoneId_.setSize(meshFaces_.size());
+    faceZoneId_ = -1;
+
+    DynamicList<word> faceNames(32);
+
+    for (label selI = 0; selI < nSelect; ++selI)
+    {
+        std::string name    = getFireString(is);
+        const label selType = getFireLabel(is);
+        const label count   = getFireLabel(is);
+
+        if (selType == FIRECore::cellSelection)
+        {
+            // index starting at 1
+            const label selId = ++nCellSelections;
+
+            cellTable_.setName(selId, validateWord(name));
+            cellTable_.setMaterial(selId, "fluid");
+
+            for (label i = 0; i < count; ++i)
+            {
+                const label cellId = getFireLabel(is);
+
+                cellTableId_[cellId] = selId;
+            }
+        }
+        else if (selType == FIRECore::faceSelection)
+        {
+            // index starting at 0
+            const label selId = nFaceSelections++;
+
+            faceNames.append(validateWord(name));
+
+            for (label i = 0; i < count; ++i)
+            {
+                const label faceId = getFireLabel(is);
+
+                faceZoneId_[faceId] = selId;
+            }
+        }
+        else
+        {
+            // discard other selection types (eg, nodes)
+            for (label i = 0; i < count; ++i)
+            {
+                getFireLabel(is);
+            }
+        }
+    }
+
+    Info<< nFaceSelections << " face selections" << endl;
+    Info<< nCellSelections << " cell selections" << endl;
+
+    // add extra for missed boundary faces
+    faceNames.append("__MISSED_FACES__");
+    faceNames_.transfer(faceNames);
+}
+
+
+void Foam::fileFormats::FIREMeshReader::reorganize()
+{
+    nInternalFaces_ = 0;
+
+    // pass 1:
+    // count internal faces and also swap owner <-> neigh as required
+    forAll(meshFaces_, faceI)
+    {
+        if (neigh_[faceI] != -1)
+        {
+            ++nInternalFaces_;
+
+            if (owner_[faceI] > neigh_[faceI])
+            {
+                Swap(owner_[faceI], neigh_[faceI]);
+            }
+        }
+    }
+
+    label posInternal = 0;
+    label posExternal = nInternalFaces_;
+
+    labelList oldToNew(meshFaces_.size(), -1);
+
+    // pass 2:
+    // mapping to ensure proper division of internal / external
+    forAll(meshFaces_, faceI)
+    {
+        if (neigh_[faceI] == -1)
+        {
+            oldToNew[faceI] = posExternal++;
+        }
+        else
+        {
+            oldToNew[faceI] = posInternal++;
+        }
+    }
+
+    inplaceReorder(oldToNew, meshFaces_);
+    inplaceReorder(oldToNew, owner_);
+    inplaceReorder(oldToNew, neigh_);
+    inplaceReorder(oldToNew, faceZoneId_);
+
+    // determine the patch sizes - faceNames_ already has extra place for missed faces
+    const label zoneMissed = faceNames_.size() - 1;
+    patchSizes_.setSize(faceNames_.size());
+    patchSizes_ = 0;
+
+    patchStarts_.setSize(patchSizes_.size());
+    patchStarts_ = 0;
+
+    for (label faceI = nInternalFaces_; faceI < meshFaces_.size(); ++faceI)
+    {
+        label zoneI = faceZoneId_[faceI];
+        if (zoneI == -1)
+        {
+            ++patchSizes_[zoneMissed];
+        }
+        else
+        {
+            ++patchSizes_[zoneI];
+        }
+    }
+
+    if (patchSizes_[zoneMissed])
+    {
+        Info<<"collecting " << patchSizes_[zoneMissed]
+            << " missed boundary faces to final patch" << endl;
+    }
+
+    oldToNew = -1;
+
+    // calculate the patch starts
+    {
+        label pos = nInternalFaces_;
+
+        forAll(patchStarts_, patchI)
+        {
+            patchStarts_[patchI] = pos;
+            pos += patchSizes_[patchI];
+        }
+
+        forAll(patchSizes_, patchI)
+        {
+            patchSizes_[patchI] = 0;
+        }
+    }
+
+    // reordering
+    for (label faceI = nInternalFaces_; faceI < meshFaces_.size(); ++faceI)
+    {
+        label patchI = faceZoneId_[faceI];
+        if (patchI == -1)
+        {
+            oldToNew[faceI] = patchStarts_[zoneMissed] + patchSizes_[zoneMissed];
+            ++patchSizes_[zoneMissed];
+        }
+        else
+        {
+            oldToNew[faceI] = patchStarts_[patchI] + patchSizes_[patchI];
+            ++patchSizes_[patchI];
+        }
+    }
+
+    // discard old information
+    faceZoneId_.clear();
+
+    inplaceReorder(oldToNew, meshFaces_);
+    inplaceReorder(oldToNew, owner_);
+    inplaceReorder(oldToNew, neigh_);
+
+    //--- neigh_.setSize(nInternalFaces_);
+
+    // finally reduce to the number of patches actually used
+    patchNames_.setSize(patchSizes_.size());
+    oldToNew = -1;
+
+    label nPatches = 0;
+    forAll(patchSizes_, patchI)
+    {
+        if (patchSizes_[patchI])
+        {
+            patchNames_[nPatches] = faceNames_[patchI];
+
+            oldToNew[patchI] = nPatches;
+            ++nPatches;
+        }
+    }
+
+    inplaceReorder(oldToNew, patchStarts_);
+    inplaceReorder(oldToNew, patchSizes_);
+
+    patchStarts_.setSize(nPatches);
+    patchSizes_.setSize(nPatches);
+    patchNames_.setSize(nPatches);
+}
+
+
+void Foam::fileFormats::FIREMeshReader::addPatches(polyMesh& mesh) const
+{
+    // create patches
+    List<polyPatch*> newPatches(patchSizes_.size());
+
+    label meshFaceI = nInternalFaces_;
+
+    forAll(patchStarts_, patchI)
+    {
+        Info<< "patch " << patchI
+            << " (start: " << meshFaceI << " size: " << patchSizes_[patchI]
+            << ") name: " << patchNames_[patchI]
+            << endl;
+
+        // don't know anything better - just make it a wall
+        newPatches[patchI] = new polyPatch
+        (
+            patchNames_[patchI],
+            patchSizes_[patchI],
+            meshFaceI,
+            patchI,
+            mesh.boundaryMesh(),
+            word::null
+        );
+
+        meshFaceI += patchSizes_[patchI];
+    }
+
+    mesh.addPatches(newPatches);
+}
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+bool Foam::fileFormats::FIREMeshReader::readGeometry(const scalar scaleFactor)
+{
+    IOstream::streamFormat fmt = IOstream::ASCII;
+
+    const word ext = geometryFile_.ext();
+    bool supported = FIRECore::file3dExtensions.found(ext);
+    if (supported)
+    {
+        FIRECore::fileExt3d fireFileType = FIRECore::file3dExtensions[ext];
+        if (fireFileType == FIRECore::POLY_ASCII)
+        {
+            fmt = IOstream::ASCII;
+        }
+        else if (fireFileType == FIRECore::POLY_BINARY)
+        {
+            fmt = IOstream::BINARY;
+        }
+        else
+        {
+            // compressed or something
+            supported = false;
+        }
+    }
+
+    if (!supported)
+    {
+        FatalErrorInFunction
+            << "File-type '" << ext
+            << "' is not supported for reading as a FIRE mesh." << nl
+            << "If it is a compressed file, use gunzip first."
+            << abort(FatalError);
+    }
+
+    IFstream is(geometryFile_, fmt, false);
+
+    readPoints(is, scaleFactor);
+    readFaces(is);
+    readCells(is);
+    readSelections(is);
+
+    return true;
+}
+
+Foam::autoPtr<Foam::polyMesh> Foam::fileFormats::FIREMeshReader::mesh
+(
+    const objectRegistry& registry
+)
+{
+    readGeometry(scaleFactor_);
+    reorganize();
+
+    Info<< "Creating a polyMesh" << endl;
+
+    autoPtr<polyMesh> mesh
+    (
+        new polyMesh
+        (
+            IOobject
+            (
+                polyMesh::defaultRegion,
+                "constant",
+                registry
+            ),
+            xferMove(points_),
+            xferMove(meshFaces_),
+            xferMove(owner_),
+            xferMove(neigh_)
+        )
+    );
+
+    // adding patches also checks the mesh
+    addPatches(mesh());
+
+    cellTable_.addCellZones(mesh(), cellTableId_);
+
+    // addFaceZones(mesh());
+
+    return mesh;
+}
+
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+Foam::fileFormats::FIREMeshReader::FIREMeshReader
+(
+    const fileName& name,
+    const scalar scaleFactor
+)
+:
+    meshReader(name, scaleFactor),
+    owner_(0),
+    neigh_(0),
+    faceZoneId_(0),
+    faceNames_()
+{}
+
+
+// * * * * * * * * * * * * * * * * Destructor  * * * * * * * * * * * * * * * //
+
+Foam::fileFormats::FIREMeshReader::~FIREMeshReader()
+{}
+
+
+// ************************************************************************* //
diff --git a/src/conversion/fire/FIREMeshReader.H b/src/conversion/fire/FIREMeshReader.H
new file mode 100644
index 0000000000000000000000000000000000000000..0c95cb5fa21eb39a0110168d8d4d90e0436478ac
--- /dev/null
+++ b/src/conversion/fire/FIREMeshReader.H
@@ -0,0 +1,140 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2016 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::fileFormats::FIREMeshReader
+
+Description
+    Read AVL/FIRE fpma, fpmb files.
+
+Note
+    Does not handle compressed versions (fpmaz, fpmbz) of these files.
+
+SourceFiles
+    FIREMeshReader.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef FIREMeshReader_H
+#define FIREMeshReader_H
+
+#include "meshReader.H"
+#include "FIRECore.H"
+#include "labelList.H"
+#include "IFstream.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+// forward declarations
+class polyMesh;
+
+namespace fileFormats
+{
+
+/*---------------------------------------------------------------------------*\
+                 Class fileFormats::FIREMeshReader Declaration
+\*---------------------------------------------------------------------------*/
+
+class FIREMeshReader
+:
+    public meshReader,
+    public FIRECore
+{
+
+protected:
+
+    // Protected Data
+
+        labelList  owner_;
+        labelList  neigh_;
+
+        labelList  faceZoneId_;
+        wordList   faceNames_;
+
+
+    // Protected Member Functions
+
+        //- Disallow default bitwise copy construct
+        FIREMeshReader(const FIREMeshReader&) = delete;
+
+        //- Disallow default bitwise assignment
+        void operator=(const FIREMeshReader&) = delete;
+
+
+        //- Validate word (eg, avoid leading digits)
+        static word validateWord(const std::string&);
+
+
+        //- Read the mesh from the file(s)
+        virtual bool readGeometry(const scalar scaleFactor = 1.0);
+
+        //- Read points from file
+        void readPoints(ISstream&, const scalar scaleFactor = 1.0);
+
+        //- Read points from file
+        void readFaces(ISstream&);
+
+        //- Read cell connectivities from file
+        void readCells(ISstream&);
+
+        //- Read cell/face selections from file
+        void readSelections(ISstream&);
+
+        //-
+        void reorganize();
+
+        void addPatches(polyMesh&) const;
+
+
+public:
+
+    // Constructors
+
+        //- Construct by reading file, optionally with scaling
+        FIREMeshReader(const fileName&, const scalar scaleFactor = 1.0);
+
+
+    //- Destructor
+    virtual ~FIREMeshReader();
+
+
+    // Member Functions
+
+        //- Create and return polyMesh
+        virtual autoPtr<polyMesh> mesh(const objectRegistry&);
+
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace fileFormats
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/conversion/fire/FIREMeshWriter.C b/src/conversion/fire/FIREMeshWriter.C
new file mode 100644
index 0000000000000000000000000000000000000000..29746005d077f069cf23f14214cfdd7c86d059c0
--- /dev/null
+++ b/src/conversion/fire/FIREMeshWriter.C
@@ -0,0 +1,364 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2016 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 "FIREMeshWriter.H"
+#include "Time.H"
+#include "HashTable.H"
+#include "OFstream.H"
+#include "processorPolyPatch.H"
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+bool Foam::fileFormats::FIREMeshWriter::binary         = false;
+bool Foam::fileFormats::FIREMeshWriter::compress       = false;
+bool Foam::fileFormats::FIREMeshWriter::prefixBoundary = true;
+
+
+//! \cond fileScope
+//- Output newline in ascii mode, no-op in binary mode
+inline static void newline(Foam::OSstream& os)
+{
+    if (os.format() == Foam::IOstream::ASCII)
+    {
+        os  << Foam::endl;
+    }
+}
+
+//! \endcond
+
+
+// * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
+
+
+bool Foam::fileFormats::FIREMeshWriter::writeGeometry(OSstream& os) const
+{
+    const faceList&   faces  = mesh_.faces();
+    const pointField& points = mesh_.points();
+    const cellList&   cells  = mesh_.cells();
+
+    // Points
+    // ~~~~~~
+
+    // Set the precision of the points data to 10
+    os.precision(10);
+
+    Info<< "points: " << points.size() << endl;
+    putFireLabel(os, points.size());
+    newline(os);
+
+    forAll(points, ptI)
+    {
+        // scaling is normally 1 (ie, none)
+        putFirePoint(os, scaleFactor_ * points[ptI]);
+    }
+    newline(os); // readability
+
+
+    // Faces
+    // ~~~~~
+    // OPENFOAM - faces normals are outward-facing.
+    // FIRE     - faces normals are inward-facing.
+
+    Info<< "faces:  " << faces.size() << endl;
+    putFireLabel(os, faces.size());
+    newline(os);
+
+    forAll(faces, faceI)
+    {
+        // flip face
+        putFireLabels(os, faces[faceI].reverseFace());
+    }
+    newline(os); // readability
+
+
+    // Cells
+    // ~~~~~
+
+    Info<< "cells:  " << cells.size() << endl;
+    putFireLabel(os, cells.size());
+    newline(os);
+
+    forAll(cells, cellId)
+    {
+        putFireLabels(os, cells[cellId]);
+    }
+    newline(os); // readability
+
+    return os.good();
+}
+
+
+bool Foam::fileFormats::FIREMeshWriter::writeSelections(OSstream& os) const
+{
+    label nZones = 0;
+    label nPatches = 0;
+
+    // remap name between patches and cell-zones conflicts!
+
+    HashTable<word, label> patchNames;
+    HashTable<word, label> zoneNames;
+
+    wordHashSet usedPatchNames;
+    wordHashSet usedZoneNames;
+
+    // boundaries, skipping empty and processor patches
+    forAll(mesh_.boundaryMesh(), patchI)
+    {
+        const polyPatch& patch = mesh_.boundaryMesh()[patchI];
+        if (patch.size() && !isA<processorPolyPatch>(patch))
+        {
+            ++nPatches;
+
+            const word oldName = patch.name();
+            word newName;
+            if (prefixBoundary)
+            {
+                newName = "BND_" + oldName;
+
+                if (usedPatchNames.found(newName))
+                {
+                    newName = "BND_patch" + ::Foam::name(patchI);
+                }
+            }
+            else
+            {
+                newName = oldName;
+
+                if (usedPatchNames.found(newName))
+                {
+                    newName = "patch" + ::Foam::name(patchI);
+                }
+            }
+
+            usedPatchNames.set(newName);
+            patchNames.set(patchI, newName);
+        }
+    }
+
+
+    // cellzones, skipping empty zones
+    forAll(mesh_.cellZones(), zoneI)
+    {
+        const cellZone& cZone = mesh_.cellZones()[zoneI];
+        if (cZone.size())
+        {
+            ++nZones;
+
+            const word oldName = cZone.name();
+            word newName = oldName;
+
+            if (usedPatchNames.found(newName) || usedZoneNames.found(newName))
+            {
+                newName = "CEL_zone" + ::Foam::name(zoneI);
+            }
+
+            usedZoneNames.set(newName);
+            zoneNames.set(zoneI, newName);
+        }
+    }
+
+
+    //
+    // actually write things
+    //
+
+    putFireLabel(os, (nZones + nPatches));
+    newline(os);
+
+    // do cell zones
+    forAll(mesh_.cellZones(), zoneI)
+    {
+        const cellZone& cZone = mesh_.cellZones()[zoneI];
+
+        if (cZone.size())
+        {
+            Info<< "cellZone " << zoneI
+                << " (size: "  << cZone.size()
+                << ") name: "  << zoneNames[zoneI] << nl;
+
+            putFireString(os, zoneNames[zoneI]);
+            putFireLabel(os, static_cast<int>(FIRECore::cellSelection));
+            newline(os);
+
+            putFireLabels(os, cZone);
+            newline(os); // readability
+        }
+    }
+
+    // do boundaries, skipping empty and processor patches
+    forAll(mesh_.boundaryMesh(), patchI)
+    {
+        const polyPatch& patch = mesh_.boundaryMesh()[patchI];
+        if (patch.size() && !isA<processorPolyPatch>(patch))
+        {
+            Info<< "patch " << patchI
+                << " (start: " << patch.start() << " size: " << patch.size()
+                << ") name: " << patchNames[patchI]
+                << endl;
+
+            putFireString(os, patchNames[patchI]);
+            putFireLabel(os, static_cast<int>(FIRECore::faceSelection));
+            newline(os);
+
+            putFireLabels(os, patch.size(), patch.start());
+            newline(os); // readability
+        }
+
+        newline(os); // readability
+    }
+
+    return os.good();
+}
+
+
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+Foam::fileFormats::FIREMeshWriter::FIREMeshWriter
+(
+    const polyMesh& mesh,
+    const scalar scaleFactor
+)
+:
+    meshWriter(mesh, scaleFactor)
+{}
+
+
+// * * * * * * * * * * * * * * * * Destructor  * * * * * * * * * * * * * * * //
+
+Foam::fileFormats::FIREMeshWriter::~FIREMeshWriter()
+{}
+
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+bool Foam::fileFormats::FIREMeshWriter::write(const fileName& meshName) const
+{
+    bool useBinary   = binary;
+    bool useCompress = compress;
+
+    fileName baseName(meshName);
+    if (baseName.empty())
+    {
+        baseName = meshWriter::defaultMeshName;
+
+        const Time& t = mesh_.time();
+
+        if
+        (
+            t.timeName() != "0"
+         && t.timeName() != t.constant()
+        )
+        {
+            baseName += "_" + t.timeName();
+        }
+    }
+    else
+    {
+        const word ext = baseName.ext();
+
+        if (FIRECore::file3dExtensions.found(ext))
+        {
+            FIRECore::fileExt3d fireFileType = FIRECore::file3dExtensions[ext];
+            if (fireFileType == FIRECore::POLY_ASCII)
+            {
+                useBinary   = false;
+                useCompress = false;
+            }
+            else if (fireFileType == FIRECore::POLY_BINARY)
+            {
+                useBinary   = true;
+                useCompress = false;
+            }
+            else if (fireFileType == FIRECore::POLY_ASCII_COMPRESSED)
+            {
+                useBinary   = false;
+                useCompress = true;
+            }
+            else if (fireFileType == FIRECore::POLY_BINARY_COMPRESSED)
+            {
+                useBinary   = true;
+                useCompress = true;
+            }
+        }
+
+        baseName = baseName.lessExt();
+    }
+
+
+    // A slight hack. Cannot generate compressed files with the desired ending
+    // So create and rename later
+    const fileName filename = FIRECore::fireFileName
+    (
+        baseName,
+        useBinary ? FIRECore::POLY_BINARY : FIRECore::POLY_ASCII
+    );
+
+    autoPtr<OFstream> osPtr
+    (
+        new OFstream
+        (
+            filename,
+            (useBinary   ? IOstream::BINARY : IOstream::ASCII),
+            IOstream::currentVersion,
+            (useCompress ? IOstream::COMPRESSED : IOstream::UNCOMPRESSED)
+        )
+    );
+
+    if (osPtr->good())
+    {
+        Info<< "Writing output to ";
+        if (useCompress)
+        {
+            // output .fpmaz instead of .fpma
+            Info<< '"' << osPtr().name().c_str() << "z\"" << endl;
+        }
+        else
+        {
+            Info<< osPtr().name() << endl;
+        }
+
+        writeGeometry(osPtr());
+        writeSelections(osPtr());
+
+        osPtr.clear();    // implicitly close the file
+
+        if (useCompress)
+        {
+            // rename .fpma.gz -> .fpmaz
+            // The '.gz' is automatically added by OFstream in compression mode
+            Foam::mv(filename + ".gz", filename + "z");
+        }
+    }
+    else
+    {
+        Info<<"could not open file for writing " << filename << endl;
+        return false;
+    }
+
+    return true;
+}
+
+
+// ************************************************************************* //
diff --git a/src/conversion/fire/FIREMeshWriter.H b/src/conversion/fire/FIREMeshWriter.H
new file mode 100644
index 0000000000000000000000000000000000000000..7d61a0461eb9a585787174f1d8cba60a1b308690
--- /dev/null
+++ b/src/conversion/fire/FIREMeshWriter.H
@@ -0,0 +1,130 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2016 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::fileFormats::FIREMeshWriter
+
+Description
+    Writes polyMesh in AVL/FIRE polyhedra format (fpma, fpmb)
+
+    It is also possible to write compressed formats (fpmaz, fpmbz)
+
+Note
+   The fpma, fpmb formats are relatively poorly documented, but are manageable
+   to read and write. It is, however, not recommended to import them directly
+   into AVL/FIRE (the GUI) since it is generally not robust enough.
+   Instead use their file-convertor to reconvert them into their native format.
+
+   In the AVL/FIRE polyhedra format, the faces normals point inwards, whereas
+   the OpenFOAM face normals always point outwards.
+
+SourceFiles
+    FIREMeshWriter.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef FIREMeshWriter_H
+#define FIREMeshWriter_H
+
+#include "meshWriter.H"
+#include "FIRECore.H"
+#include "IOstream.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+namespace fileFormats
+{
+
+/*---------------------------------------------------------------------------*\
+                 Class fileFormats::FIREMeshWriter Declaration
+\*---------------------------------------------------------------------------*/
+
+class FIREMeshWriter
+:
+    public meshWriter,
+    public FIRECore
+{
+    // Private Member Functions
+
+        //- Disallow default bitwise copy construct
+        FIREMeshWriter(const FIREMeshWriter&) = delete;
+
+        //- Disallow default bitwise assignment
+        void operator=(const FIREMeshWriter&) = delete;
+
+        //- Write points, faces, cells
+        bool writeGeometry(OSstream&) const;
+
+        //- Write selections
+        bool writeSelections(OSstream&) const;
+
+public:
+
+    // Static data members
+
+        //- Write binary (default ascii)
+        static bool binary;
+
+        //- Write with compression (default false)
+        static bool compress;
+
+        //- Prefix patches with 'BND_' before writing (default true)
+        static bool prefixBoundary;
+
+
+    // Constructors
+
+        //- Prepare for writing, optionally with scaling
+        FIREMeshWriter(const polyMesh&, const scalar scaleFactor = 1.0);
+
+
+    //- Destructor
+    virtual ~FIREMeshWriter();
+
+
+    // Member Functions
+
+        // Write
+
+            //- Write volume mesh
+            virtual bool write
+            (
+                const fileName& meshName = fileName::null
+            ) const;
+
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace fileFormats
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/conversion/fire/checkFireEdges.C b/src/conversion/fire/checkFireEdges.C
new file mode 100644
index 0000000000000000000000000000000000000000..eb7c3bf9da11bc5bf539062a256357bf83b5852b
--- /dev/null
+++ b/src/conversion/fire/checkFireEdges.C
@@ -0,0 +1,299 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2016 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 "checkFireEdges.H"
+#include "polyMesh.H"
+#include "edge.H"
+#include "HashSet.H"
+#include "ListOps.H"
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+namespace Foam
+{
+    //! \cond fileScope
+    //- Print face information for debugging purposes
+    static inline void printFace
+    (
+        const face& f,
+        label faceI,
+        const edge& currEdge
+    )
+    {
+        Info<< "face " << faceI << ':';
+        forAll(f, fpI)
+        {
+            Info<< ' ';
+            if (f[fpI] == currEdge[0] || f[fpI] == currEdge[1])
+            {
+                Info<< '_';   // highlight the node
+            }
+            Info<< f[fpI];
+        }
+        Info<< endl;
+    }
+    //! \endcond
+}
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+Foam::label Foam::checkFireEdges
+(
+    const faceList& faces,
+    const labelListList& pointFaces,
+    const UList<point>& points
+)
+{
+    label nFailedEdges = 0;
+    const bool fullCheck = true;
+    typedef HashSet<edge, Hash<edge>> edgeHashSet;
+
+    Info<< "Checking edges according to AVL/FIRE on-the-fly methodology..."
+        << endl;
+
+    labelHashSet strayPoints(100);
+    edgeHashSet  failedEdges(100);
+
+    forAll(faces, faceI)
+    {
+        const face& faceA = faces[faceI];
+
+        forAll(faceA, edgeI)
+        {
+            const edge currEdge = faceA.faceEdge(edgeI);
+
+            // all faces attached to the first point
+            const labelList& otherFaceIds = pointFaces[currEdge[0]];
+
+            forAll(otherFaceIds, otherI)
+            {
+                const int otherFaceI = otherFaceIds[otherI];
+                const face& faceB = faces[otherFaceI];
+
+                // only check once
+                if (otherFaceI <= faceI && !fullCheck)
+                {
+                    continue;
+                }
+
+                // get local edges on the second face
+                int other_p0 = -1;
+                int other_p1 = -1;
+                int size_m1  = faceB.size() - 1;
+
+                forAll(faceB, ptI)
+                {
+                    if (faceB[ptI] == currEdge[0])
+                    {
+                        other_p0 = ptI;
+                    }
+
+                    if (faceB[ptI] == currEdge[1])
+                    {
+                        other_p1 = ptI;
+                    }
+                }
+
+                if
+                (
+                    // did not have both points - can skip
+                    other_p0 == -1
+                 || other_p1 == -1
+                    // a normal edge
+                 || abs(other_p0 - other_p1) == 1
+                    // handle wrapping
+                 || (other_p0 == 0 && other_p1 == size_m1)
+                 || (other_p1 == 0 && other_p0 == size_m1)
+                )
+                {
+                    continue;
+                }
+
+                // find the "stray" point
+                int stray = -1;
+                if (abs(other_p0 - other_p1) == 2)
+                {
+                    // a normal case
+                    stray = (other_p0 + other_p1) / 2;
+                }
+                else if
+                (
+                    (other_p0 == 0 && other_p1+1 == size_m1)
+                 || (other_p1 == 0 && other_p0+1 == size_m1)
+                )
+                {
+                    stray = size_m1;
+                }
+
+                if (stray > 0)
+                {
+                    strayPoints.set(faceB[stray]);
+                }
+
+                failedEdges.set(currEdge);
+
+                ++nFailedEdges;
+
+                Info<< nl
+                    << "Broken edge calculated between points  "
+                    << currEdge[0] << "  " << currEdge[1] << endl;
+
+                printFace(faceA, faceI, currEdge);
+                printFace(faceB, otherFaceI, currEdge);
+            }
+        }
+    }
+
+    if (nFailedEdges)
+    {
+        Info<< endl;
+    }
+    Info<< "detected " << nFailedEdges << " edge failures";
+
+    // reduce to the actual number of edges
+    nFailedEdges = failedEdges.size();
+
+    // report the locations
+    if (nFailedEdges)
+    {
+        Info<< " over " << nFailedEdges << " edges" << endl;
+
+        Info<< nl
+            << "edge points" << nl
+            << "~~~~~~~~~~~" << endl;
+
+
+        forAllConstIter(edgeHashSet, failedEdges, citer)
+        {
+            edge thisEdge = citer.key();
+            if (thisEdge.start() > thisEdge.end())
+            {
+                thisEdge.flip();
+            }
+
+            if (&points)
+            {
+                forAll(thisEdge, keyI)
+                {
+                    const label ptI = thisEdge[keyI];
+                    Info<< "point " << ptI << ": " << points[ptI] << endl;
+                }
+            }
+            else
+            {
+                forAll(thisEdge, keyI)
+                {
+                    const label ptI = thisEdge[keyI];
+                    Info<< "point " << ptI << endl;
+                }
+            }
+            Info<< endl;
+        }
+
+        Info<< nl
+            << "stray points" << nl
+            << "~~~~~~~~~~~~" << endl;
+
+        {
+            labelList keys = strayPoints.sortedToc();
+
+            if (&points)
+            {
+                forAll(keys, keyI)
+                {
+                    const label ptI = keys[keyI];
+                    Info<< "stray " << ptI << ": " << points[ptI] << endl;
+                }
+            }
+            else
+            {
+                forAll(keys, keyI)
+                {
+                    const label ptI = keys[keyI];
+                    Info<< "stray " << ptI << endl;
+                }
+            }
+
+        }
+        Info<< endl;
+    }
+    else
+    {
+        Info<< endl;
+    }
+
+    return nFailedEdges;
+}
+
+
+Foam::label Foam::checkFireEdges
+(
+    const faceList& faces,
+    const UList<point>& points
+)
+{
+    label nPoints = -1;
+
+    if (&points)
+    {
+        nPoints = points.size();
+
+    }
+    else
+    {
+        // get the max point addressed
+        forAll(faces, faceI)
+        {
+            const face& f = faces[faceI];
+            forAll(f, fp)
+            {
+                if (nPoints < f[fp])
+                {
+                    nPoints = f[fp];
+                }
+            }
+        }
+
+        ++nPoints;
+    }
+
+    labelListList pointFaces(nPoints);
+    invertManyToMany(nPoints, faces, pointFaces);
+
+    return checkFireEdges(faces, pointFaces, points);
+}
+
+
+Foam::label Foam::checkFireEdges
+(
+    const polyMesh& mesh
+)
+{
+    return checkFireEdges(mesh.faces(), mesh.pointFaces(), mesh.points());
+}
+
+
+// ************************************************************************* //
diff --git a/src/conversion/fire/checkFireEdges.H b/src/conversion/fire/checkFireEdges.H
new file mode 100644
index 0000000000000000000000000000000000000000..0f0bffe2c884c4632707499e3000e1349abc0e57
--- /dev/null
+++ b/src/conversion/fire/checkFireEdges.H
@@ -0,0 +1,79 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2016 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/>.
+
+Description
+    Checks the mesh for edge connectivity as expected by the AVL/FIRE
+    on-the-fly calculations.
+    Errors flagged here are not necessarily topological errors at all.
+
+SourceFiles
+    checkFireEdges.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef checkFireEdges_H
+#define checkFireEdges_H
+
+#include "faceList.H"
+#include "labelList.H"
+#include "point.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+class polyMesh;
+
+//- check edge connectivity
+label checkFireEdges
+(
+    const faceList&,
+    const labelListList& pointFaces,
+    const UList<point>& = UList<point>::null()
+);
+
+
+//- check edge connectivity with pointFaces mapping calculated automatically
+label checkFireEdges
+(
+    const faceList&,
+    const UList<point>& = UList<point>::null()
+);
+
+
+//- check edge connectivity
+label checkFireEdges(const polyMesh&);
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/conversion/starcd/STARCDMeshReader.C b/src/conversion/starcd/STARCDMeshReader.C
index e5ddc1bcb5827db70197294f53738e9acd18c9c0..0229eaa0d20b854e84bd92c82652e703c7945e62 100644
--- a/src/conversion/starcd/STARCDMeshReader.C
+++ b/src/conversion/starcd/STARCDMeshReader.C
@@ -3,7 +3,7 @@
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
     \\  /    A nd           | Copyright (C) 2011-2016 OpenFOAM Foundation
-     \\/     M anipulation  |
+     \\/     M anipulation  | Copyright (C) 2016 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -35,26 +35,9 @@ License
 
 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
 
-const char* const Foam::fileFormats::STARCDMeshReader::defaultBoundaryName =
-    "Default_Boundary_Region";
-
-const char* const Foam::fileFormats::STARCDMeshReader::defaultSolidBoundaryName =
-    "Default_Boundary_Solid";
-
-bool Foam::fileFormats::STARCDMeshReader::keepSolids = false;
-
-const int Foam::fileFormats::STARCDMeshReader::starToFoamFaceAddr[4][6] =
-{
-    { 4, 5, 2, 3, 0, 1 },     // 11 = pro-STAR hex
-    { 0, 1, 4, -1, 2, 3 },    // 12 = pro-STAR prism
-    { 3, -1, 2, -1, 1, 0 },   // 13 = pro-STAR tetra
-    { 0, -1, 4, 2, 1, 3 }     // 14 = pro-STAR pyramid
-};
-
-
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
-
-void Foam::fileFormats::STARCDMeshReader::readToNewline(IFstream& is)
+//! \cond fileScope
+//- Read and discard to newline
+static void readToNewline(Foam::IFstream& is)
 {
     char ch = '\n';
     do
@@ -63,34 +46,7 @@ void Foam::fileFormats::STARCDMeshReader::readToNewline(IFstream& is)
     }
     while ((is) && ch != '\n');
 }
-
-
-bool Foam::fileFormats::STARCDMeshReader::readHeader(IFstream& is, word fileSignature)
-{
-    if (!is.good())
-    {
-        FatalErrorInFunction
-            << abort(FatalError);
-    }
-
-    word header;
-    label majorVersion;
-
-    is >> header;
-    is >> majorVersion;
-
-    // skip the rest of the line
-    readToNewline(is);
-
-    // add other checks ...
-    if (header != fileSignature)
-    {
-        Info<< "header mismatch " << fileSignature << "  " << is.name()
-            << endl;
-    }
-
-    return true;
-}
+//! \endcond
 
 
 // * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
@@ -118,20 +74,19 @@ Body:
   <vertexId>  <x>  <y>  <z> [newline]
 
 \*---------------------------------------------------------------------------*/
-void Foam::fileFormats::STARCDMeshReader::readPoints
+Foam::label Foam::fileFormats::STARCDMeshReader::readPoints
 (
     const fileName& inputName,
     const scalar scaleFactor
 )
 {
-    const word fileSignature = "PROSTAR_VERTEX";
     label nPoints = 0, maxId = 0;
 
     // Pass 1:
     // get # points and maximum vertex label
     {
         IFstream is(inputName);
-        readHeader(is, fileSignature);
+        readHeader(is, STARCDCore::HEADER_VRT);
 
         label lineLabel;
         scalar x, y, z;
@@ -164,7 +119,7 @@ void Foam::fileFormats::STARCDMeshReader::readPoints
     if (nPoints > 0)
     {
         IFstream is(inputName);
-        readHeader(is, fileSignature);
+        readHeader(is, STARCDCore::HEADER_VRT);
 
         label lineLabel;
 
@@ -200,6 +155,8 @@ void Foam::fileFormats::STARCDMeshReader::readPoints
             << "no points in file " << inputName
             << abort(FatalError);
     }
+
+    return maxId;
 }
 
 
@@ -245,7 +202,6 @@ Strictly speaking, we only need the cellModeller for adding boundaries.
 
 void Foam::fileFormats::STARCDMeshReader::readCells(const fileName& inputName)
 {
-    const word fileSignature = "PROSTAR_CELL";
     label nFluids = 0, nSolids = 0, nBaffles = 0, nShells = 0;
     label maxId = 0;
 
@@ -257,7 +213,7 @@ void Foam::fileFormats::STARCDMeshReader::readCells(const fileName& inputName)
     // also see if polyhedral cells were used
     {
         IFstream is(inputName);
-        readHeader(is, fileSignature);
+        readHeader(is, STARCDCore::HEADER_CEL);
 
         label lineLabel, shapeId, nLabels, cellTableId, typeId;
 
@@ -279,7 +235,7 @@ void Foam::fileFormats::STARCDMeshReader::readCells(const fileName& inputName)
                 nLabels -= 8;
             }
 
-            if (typeId == starcdFluidType)
+            if (typeId == STARCDCore::starcdFluidType)
             {
                 nFluids++;
                 maxId = max(maxId, starCellId);
@@ -290,10 +246,10 @@ void Foam::fileFormats::STARCDMeshReader::readCells(const fileName& inputName)
                     cellTable_.setMaterial(cellTableId, "fluid");
                 }
             }
-            else if (typeId == starcdSolidType)
+            else if (typeId == STARCDCore::starcdSolidType)
             {
                 nSolids++;
-                if (keepSolids)
+                if (keepSolids_)
                 {
                     maxId = max(maxId, starCellId);
                 }
@@ -305,13 +261,13 @@ void Foam::fileFormats::STARCDMeshReader::readCells(const fileName& inputName)
                 }
 
             }
-            else if (typeId == starcdBaffleType)
+            else if (typeId == STARCDCore::starcdBaffleType)
             {
                 // baffles have no cellTable entry
                 nBaffles++;
                 maxId = max(maxId, starCellId);
             }
-            else if (typeId == starcdShellType)
+            else if (typeId == STARCDCore::starcdShellType)
             {
                 nShells++;
                 if (!cellTable_.found(cellTableId))
@@ -326,7 +282,7 @@ void Foam::fileFormats::STARCDMeshReader::readCells(const fileName& inputName)
 
     Info<< "Number of fluids  = " << nFluids << nl
         << "Number of baffles = " << nBaffles << nl;
-    if (keepSolids)
+    if (keepSolids_)
     {
         Info<< "Number of solids  = " << nSolids << nl;
     }
@@ -338,7 +294,7 @@ void Foam::fileFormats::STARCDMeshReader::readCells(const fileName& inputName)
 
 
     label nCells;
-    if (keepSolids)
+    if (keepSolids_)
     {
         nCells = nFluids + nSolids;
     }
@@ -374,7 +330,7 @@ void Foam::fileFormats::STARCDMeshReader::readCells(const fileName& inputName)
     else
     {
         IFstream is(inputName);
-        readHeader(is, fileSignature);
+        readHeader(is, STARCDCore::HEADER_CEL);
 
         labelList starLabels(64);
         label lineLabel, shapeId, nLabels, cellTableId, typeId;
@@ -407,7 +363,11 @@ void Foam::fileFormats::STARCDMeshReader::readCells(const fileName& inputName)
             }
 
             // skip solid cells
-            if (typeId == starcdSolidType && !keepSolids)
+            if
+            (
+                typeId == STARCDCore::starcdSolidType
+             && !keepSolids_
+            )
             {
                 continue;
             }
@@ -418,16 +378,16 @@ void Foam::fileFormats::STARCDMeshReader::readCells(const fileName& inputName)
             // fluid/solid cells
             switch (shapeId)
             {
-                case starcdHex:
+                case STARCDCore::starcdHex:
                     curModelPtr = hexModel;
                     break;
-                case starcdPrism:
+                case STARCDCore::starcdPrism:
                     curModelPtr = prismModel;
                     break;
-                case starcdTet:
+                case STARCDCore::starcdTet:
                     curModelPtr = tetModel;
                     break;
-                case starcdPyr:
+                case STARCDCore::starcdPyr:
                     curModelPtr = pyrModel;
                     break;
             }
@@ -471,7 +431,7 @@ void Foam::fileFormats::STARCDMeshReader::readCells(const fileName& inputName)
                 cellFaces_[celli] = cellShapes_[celli].faces();
                 celli++;
             }
-            else if (shapeId == starcdPoly)
+            else if (shapeId == STARCDCore::starcdPoly)
             {
                 // polyhedral cell
                 label nFaces = starLabels[0] - 1;
@@ -548,7 +508,7 @@ void Foam::fileFormats::STARCDMeshReader::readCells(const fileName& inputName)
                 cellFaces_[celli]   = faces;
                 celli++;
             }
-            else if (typeId == starcdBaffleType)
+            else if (typeId == STARCDCore::starcdBaffleType)
             {
                 // baffles
 
@@ -639,7 +599,6 @@ void Foam::fileFormats::STARCDMeshReader::readBoundary
     const fileName& inputName
 )
 {
-    const word fileSignature = "PROSTAR_BOUNDARY";
     label nPatches = 0, nFaces = 0, nBafflePatches = 0, maxId = 0;
     label lineLabel, starCellId, cellFaceId, starRegion, configNumber;
     word patchType;
@@ -649,15 +608,17 @@ void Foam::fileFormats::STARCDMeshReader::readBoundary
     labelList origRegion(1000, label(0));
     patchTypes_.setSize(1000);
 
-    // this is what we seem to need
-    // these MUST correspond to starToFoamFaceAddr
     //
-    Map<label> faceLookupIndex;
-
-    faceLookupIndex.insert(hexModel->index(), 0);
-    faceLookupIndex.insert(prismModel->index(), 1);
-    faceLookupIndex.insert(tetModel->index(), 2);
-    faceLookupIndex.insert(pyrModel->index(), 3);
+    // Mapping between OpenFOAM and PROSTAR primitives
+    // - needed for face mapping
+    //
+    const Map<label> prostarShapeLookup =
+    {
+        { hexModel->index(),   STARCDCore::starcdHex },
+        { prismModel->index(), STARCDCore::starcdPrism },
+        { tetModel->index(),   STARCDCore::starcdTet },
+        { pyrModel->index(),   STARCDCore::starcdPyr }
+    };
 
     // Pass 1:
     // collect
@@ -675,7 +636,7 @@ void Foam::fileFormats::STARCDMeshReader::readBoundary
 
         if (is.good())
         {
-            readHeader(is, fileSignature);
+            readHeader(is, STARCDCore::HEADER_BND);
 
             while ((is >> lineLabel).good())
             {
@@ -863,7 +824,7 @@ void Foam::fileFormats::STARCDMeshReader::readBoundary
     if (nPatches > 1 && mapToFoamCellId_.size() > 1)
     {
         IFstream is(inputName);
-        readHeader(is, fileSignature);
+        readHeader(is, STARCDCore::HEADER_BND);
 
         while ((is >> lineLabel).good())
         {
@@ -899,11 +860,11 @@ void Foam::fileFormats::STARCDMeshReader::readBoundary
                 // restrict lookup to volume cells (no baffles)
                 if (cellId < cellShapes_.size())
                 {
-                    label index = cellShapes_[cellId].model().index();
-                    if (faceLookupIndex.found(index))
+                    label mapIndex = cellShapes_[cellId].model().index();
+                    if (prostarShapeLookup.found(mapIndex))
                     {
-                        index = faceLookupIndex[index];
-                        cellFaceId = starToFoamFaceAddr[index][cellFaceId];
+                        mapIndex = prostarShapeLookup[mapIndex];
+                        cellFaceId = STARCDCore::starToFoamFaceAddr[mapIndex][cellFaceId];
                     }
                 }
                 else
@@ -1046,10 +1007,20 @@ void Foam::fileFormats::STARCDMeshReader::cullPoints()
 
 bool Foam::fileFormats::STARCDMeshReader::readGeometry(const scalar scaleFactor)
 {
-    readPoints(geometryFile_ + ".vrt", scaleFactor);
-    readCells(geometryFile_ + ".cel");
+    readPoints
+    (
+        starFileName(geometryFile_, STARCDCore::VRT_FILE),
+        scaleFactor
+    );
+    readCells
+    (
+        starFileName(geometryFile_, STARCDCore::CEL_FILE)
+    );
     cullPoints();
-    readBoundary(geometryFile_ + ".bnd");
+    readBoundary
+    (
+        starFileName(geometryFile_, STARCDCore::BND_FILE)
+    );
 
     return true;
 }
@@ -1061,10 +1032,12 @@ Foam::fileFormats::STARCDMeshReader::STARCDMeshReader
 (
     const fileName& prefix,
     const objectRegistry& registry,
-    const scalar scaleFactor
+    const scalar scaleFactor,
+    const bool keepSolids
 )
 :
     meshReader(prefix, scaleFactor),
+    keepSolids_(keepSolids),
     cellShapes_(0),
     mapToFoamPointId_(0),
     mapToFoamCellId_(0)
diff --git a/src/conversion/starcd/STARCDMeshReader.H b/src/conversion/starcd/STARCDMeshReader.H
index 5025a5c11a2437c2e89cbb3d5a856e0b4b586fa6..9288002991546735fd32642f4d50ce03507ba7b7 100644
--- a/src/conversion/starcd/STARCDMeshReader.H
+++ b/src/conversion/starcd/STARCDMeshReader.H
@@ -3,7 +3,7 @@
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
     \\  /    A nd           | Copyright (C) 2011-2016 OpenFOAM Foundation
-     \\/     M anipulation  |
+     \\/     M anipulation  | Copyright (C) 2016 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -43,6 +43,7 @@ SourceFiles
 #define STARCDMeshReader_H
 
 #include "meshReader.H"
+#include "STARCDCore.H"
 #include "boundaryRegion.H"
 #include "cellShape.H"
 #include "IFstream.H"
@@ -61,18 +62,27 @@ namespace fileFormats
 
 class STARCDMeshReader
 :
-    public meshReader
+    public meshReader,
+    protected STARCDCore
 {
+    // Private data
 
-protected:
+        //- Retain solid cell types
+        bool keepSolids_;
 
-    // Protected Data
 
-        static const char* const defaultBoundaryName;
-        static const char* const defaultSolidBoundaryName;
+    // Private member functions
+
+        //- Disallow default bitwise copy construct
+        STARCDMeshReader(const STARCDMeshReader&) = delete;
+
+        //- Disallow default bitwise assignment
+        void operator=(const STARCDMeshReader&) = delete;
 
-        //- Face addressing from pro-STAR faces -> OpenFOAM faces
-        static const int starToFoamFaceAddr[4][6];
+
+protected:
+
+    // Protected Data
 
         //- Cell shapes
         cellShapeList cellShapes_;
@@ -92,8 +102,8 @@ protected:
         //- Read the mesh from the file(s)
         virtual bool readGeometry(const scalar scaleFactor = 1.0);
 
-        //- Read points from file
-        void readPoints(const fileName&, const scalar scaleFactor);
+        //- Read points from file, return the max prostar id used.
+        label readPoints(const fileName&, const scalar scaleFactor);
 
         //- Read cell connectivities from file
         virtual void readCells(const fileName&);
@@ -107,57 +117,9 @@ protected:
         //- Read auxiliary data from constant/{boundaryRegion,cellTable}
         void readAux(const objectRegistry&);
 
-        //- Read and discard to newline
-        static void readToNewline(IFstream&);
-
-        //- Read header
-        static bool readHeader(IFstream&, word fileSignature);
-
-
-private:
-
-    // Private member functions
-
-        //- Disallow default bitwise copy construct
-        STARCDMeshReader(const STARCDMeshReader&) = delete;
-
-        //- Disallow default bitwise assignment
-        void operator=(const STARCDMeshReader&) = delete;
-
-
-protected:
-
-        enum cellType
-        {
-            starcdFluidType   = 1,
-            starcdSolidType   = 2,
-            starcdBaffleType  = 3,
-            starcdShellType   = 4,
-            starcdLineType    = 5,
-            starcdPointType   = 6
-        };
-
-        enum shapeType
-        {
-            starcdPoint = 1,
-            starcdLine  = 2,
-            starcdShell = 3,
-            starcdHex   = 11,
-            starcdPrism = 12,
-            starcdTet   = 13,
-            starcdPyr   = 14,
-            starcdPoly  = 255
-        };
-
 
 public:
 
-    // Static data
-
-        //- Keep solids (default false)
-        static bool keepSolids;
-
-
     // Constructors
 
         //- Construct from case name
@@ -165,12 +127,14 @@ public:
         (
             const fileName& prefix,
             const objectRegistry&,
-            const scalar scaleFactor = 1.0
+            const scalar scaleFactor = 1.0,
+            const bool keepSolids = false
         );
 
 
     //- Destructor
     virtual ~STARCDMeshReader();
+
 };
 
 
diff --git a/src/conversion/starcd/STARCDMeshWriter.C b/src/conversion/starcd/STARCDMeshWriter.C
index 3436fc5a76c33ecf269be571bd3f05b809443339..c4ccf80faa70a339cc053cd3ab6b9a3f67fca1b7 100644
--- a/src/conversion/starcd/STARCDMeshWriter.C
+++ b/src/conversion/starcd/STARCDMeshWriter.C
@@ -3,7 +3,7 @@
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
     \\  /    A nd           | Copyright (C) 2011-2016 OpenFOAM Foundation
-     \\/     M anipulation  |
+     \\/     M anipulation  | Copyright (C) 2016 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -29,20 +29,6 @@ License
 #include "SortableList.H"
 #include "OFstream.H"
 
-// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
-
-const char* Foam::fileFormats::STARCDMeshWriter::defaultBoundaryName =
-    "Default_Boundary_Region";
-
-const Foam::label Foam::fileFormats::STARCDMeshWriter::foamToStarFaceAddr[4][6] =
-{
-    { 4, 5, 2, 3, 0, 1 },     // 11 = pro-STAR hex
-    { 0, 1, 4, 5, 2, -1 },    // 12 = pro-STAR prism
-    { 5, 4, 2, 0, -1, -1 },   // 13 = pro-STAR tetra
-    { 0, 4, 3, 5, 2, -1 }     // 14 = pro-STAR pyramid
-};
-
-
 // * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
 
 Foam::label Foam::fileFormats::STARCDMeshWriter::findDefaultBoundary() const
@@ -171,66 +157,37 @@ void Foam::fileFormats::STARCDMeshWriter::getCellTable()
 }
 
 
-void Foam::fileFormats::STARCDMeshWriter::writeHeader(Ostream& os, const char* filetype)
-{
-    os  << "PROSTAR_" << filetype << nl
-        << 4000
-        << " " << 0
-        << " " << 0
-        << " " << 0
-        << " " << 0
-        << " " << 0
-        << " " << 0
-        << " " << 0
-        << endl;
-}
-
-
-void Foam::fileFormats::STARCDMeshWriter::writePoints(const fileName& prefix) const
-{
-    OFstream os(prefix + ".vrt");
-    writeHeader(os, "VERTEX");
-
-    // Set the precision of the points data to 10
-    os.precision(10);
-
-    // force decimal point for Fortran input
-    os.setf(std::ios::showpoint);
-
-    const pointField& points = mesh_.points();
-
-    Info<< "Writing " << os.name() << " : "
-        << points.size() << " points" << endl;
-
-    forAll(points, ptI)
-    {
-        // convert [m] -> [mm]
-        os
-            << ptI + 1 << " "
-            << scaleFactor_ * points[ptI].x() << " "
-            << scaleFactor_ * points[ptI].y() << " "
-            << scaleFactor_ * points[ptI].z() << nl;
-    }
-    os.flush();
-
-}
-
-
 void Foam::fileFormats::STARCDMeshWriter::writeCells
 (
     const fileName& prefix
 ) const
 {
-    OFstream os(prefix + ".cel");
-    writeHeader(os, "CELL");
+    OFstream os(starFileName(prefix, STARCDCore::CEL_FILE));
+    writeHeader(os, STARCDCore::HEADER_CEL);
 
     // this is what we seem to need
     // map foam cellModeller index -> star shape
     Map<label> shapeLookupIndex;
-    shapeLookupIndex.insert(hexModel->index(), 11);
-    shapeLookupIndex.insert(prismModel->index(), 12);
-    shapeLookupIndex.insert(tetModel->index(), 13);
-    shapeLookupIndex.insert(pyrModel->index(), 14);
+    shapeLookupIndex.insert
+    (
+        hexModel->index(),
+        STARCDCore::starcdHex
+    );
+    shapeLookupIndex.insert
+    (
+        prismModel->index(),
+        STARCDCore::starcdPrism
+    );
+    shapeLookupIndex.insert
+    (
+        tetModel->index(),
+        STARCDCore::starcdTet
+    );
+    shapeLookupIndex.insert
+    (
+        pyrModel->index(),
+        STARCDCore::starcdPyr
+    );
 
     const cellShapeList& shapes = mesh_.cellShapes();
     const cellList& cells  = mesh_.cells();
@@ -242,30 +199,30 @@ void Foam::fileFormats::STARCDMeshWriter::writeCells
 
     forAll(cells, cellId)
     {
-        label tableId = cellTableId_[cellId];
-        label materialType  = 1;        // 1(fluid)
+        const label tableId = cellTableId_[cellId];
+        label materialType = STARCDCore::starcdFluidType; // 1(fluid)
         if (cellTable_.found(tableId))
         {
             const dictionary& dict = cellTable_[tableId];
-            if (dict.found("MaterialType"))
-            {
-                word matType;
-                dict.lookup("MaterialType") >> matType;
-                if (matType == "solid")
-                {
-                    materialType = 2;
-                }
+            word matType;
 
+            if
+            (
+                dict.readIfPresent("MaterialType", matType)
+             && matType == "solid"
+            )
+            {
+                materialType = STARCDCore::starcdSolidType; // 2(solid)
             }
         }
 
         const cellShape& shape = shapes[cellId];
-        label mapIndex = shape.model().index();
+        const label mapIndex = shape.model().index();
 
         // a registered primitive type
         if (shapeLookupIndex.found(mapIndex))
         {
-            label shapeId = shapeLookupIndex[mapIndex];
+            const label shapeId = shapeLookupIndex[mapIndex];
             const labelList& vrtList = shapes[cellId];
 
             os  << cellId + 1
@@ -288,11 +245,11 @@ void Foam::fileFormats::STARCDMeshWriter::writeCells
                 count++;
             }
             os << endl;
-
         }
         else
         {
-            label shapeId = 255;        // treat as general polyhedral
+            // treat as general polyhedral
+            const label shapeId = STARCDCore::starcdPoly;
             const labelList& cFaces  = cells[cellId];
 
             // create (beg,end) indices
@@ -366,8 +323,8 @@ void Foam::fileFormats::STARCDMeshWriter::writeBoundary
     const fileName& prefix
 ) const
 {
-    OFstream os(prefix + ".bnd");
-    writeHeader(os, "BOUNDARY");
+    OFstream os(starFileName(prefix, STARCDCore::BND_FILE));
+    writeHeader(os, STARCDCore::HEADER_BND);
 
     const cellShapeList& shapes = mesh_.cellShapes();
     const cellList& cells  = mesh_.cells();
@@ -375,20 +332,23 @@ void Foam::fileFormats::STARCDMeshWriter::writeBoundary
     const labelList& owner = mesh_.faceOwner();
     const polyBoundaryMesh& patches = mesh_.boundaryMesh();
 
-    // this is what we seem to need
-    // these MUST correspond to foamToStarFaceAddr
     //
-    Map<label> faceLookupIndex;
-    faceLookupIndex.insert(hexModel->index(), 0);
-    faceLookupIndex.insert(prismModel->index(), 1);
-    faceLookupIndex.insert(tetModel->index(), 2);
-    faceLookupIndex.insert(pyrModel->index(), 3);
+    // Mapping between OpenFOAM and PROSTAR primitives
+    // - needed for face mapping
+    //
+    const Map<label> prostarShapeLookup =
+    {
+        { hexModel->index(),   STARCDCore::starcdHex },
+        { prismModel->index(), STARCDCore::starcdPrism },
+        { tetModel->index(),   STARCDCore::starcdTet },
+        { pyrModel->index(),   STARCDCore::starcdPyr }
+    };
 
     Info<< "Writing " << os.name() << " : "
         << (mesh_.nFaces() - patches[0].start()) << " boundaries" << endl;
 
 
-    label defaultId = findDefaultBoundary();
+    const label defaultId = findDefaultBoundary();
 
     //
     // write boundary faces - skip Default_Boundary_Region entirely
@@ -435,8 +395,8 @@ void Foam::fileFormats::STARCDMeshWriter::writeBoundary
 
             label mapIndex = shape.model().index();
 
-            // a registered primitive type
-            if (faceLookupIndex.found(mapIndex))
+            // A registered primitive type
+            if (prostarShapeLookup.found(mapIndex))
             {
                 const faceList sFaces = shape.faces();
                 forAll(sFaces, sFacei)
@@ -448,8 +408,8 @@ void Foam::fileFormats::STARCDMeshWriter::writeBoundary
                     }
                 }
 
-                mapIndex = faceLookupIndex[mapIndex];
-                cellFaceId = foamToStarFaceAddr[mapIndex][cellFaceId];
+                mapIndex = prostarShapeLookup[mapIndex];
+                cellFaceId = STARCDCore::foamToStarFaceAddr[mapIndex][cellFaceId];
             }
             // Info<< endl;
 
@@ -473,10 +433,12 @@ void Foam::fileFormats::STARCDMeshWriter::writeBoundary
 Foam::fileFormats::STARCDMeshWriter::STARCDMeshWriter
 (
     const polyMesh& mesh,
-    const scalar scaleFactor
+    const scalar scaleFactor,
+    const bool writeBndFile
 )
 :
-    meshWriter(mesh, scaleFactor)
+    meshWriter(mesh, scaleFactor),
+    writeBoundary_(writeBndFile)
 {
     boundaryRegion_.readDict(mesh_);
     cellTable_.readDict(mesh_);
@@ -492,15 +454,6 @@ Foam::fileFormats::STARCDMeshWriter::~STARCDMeshWriter()
 
 // * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
 
-void Foam::fileFormats::STARCDMeshWriter::rmFiles(const fileName& baseName) const
-{
-    rm(baseName + ".vrt");
-    rm(baseName + ".cel");
-    rm(baseName + ".bnd");
-    rm(baseName + ".inp");
-}
-
-
 bool Foam::fileFormats::STARCDMeshWriter::write(const fileName& meshName) const
 {
     fileName baseName(meshName);
@@ -519,10 +472,25 @@ bool Foam::fileFormats::STARCDMeshWriter::write(const fileName& meshName) const
         }
     }
 
-    rmFiles(baseName);
-    writePoints(baseName);
+    STARCDCore::removeFiles(baseName);
+
+    // points
+    {
+        OFstream os
+        (
+            starFileName(baseName, STARCDCore::VRT_FILE)
+        );
+
+        Info<< "Writing " << os.name() << " : "
+            << mesh_.nPoints() << " points" << endl;
+
+        writePoints(os, mesh_.points(), scaleFactor_);
+    }
+
+    // cells
     writeCells(baseName);
 
+    // boundaries
     if (writeBoundary_)
     {
         writeBoundary(baseName);
diff --git a/src/conversion/starcd/STARCDMeshWriter.H b/src/conversion/starcd/STARCDMeshWriter.H
index 9de8c60490d33ad19897f5a6fe7ced9b1f4cf9a9..2b33eaa20aca6ed4490a708f22de4e4c2aecabed 100644
--- a/src/conversion/starcd/STARCDMeshWriter.H
+++ b/src/conversion/starcd/STARCDMeshWriter.H
@@ -3,7 +3,7 @@
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
     \\  /    A nd           | Copyright (C) 2011 OpenFOAM Foundation
-     \\/     M anipulation  |
+     \\/     M anipulation  | Copyright (C) 2016 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -39,6 +39,7 @@ SourceFiles
 #define STARCDMeshWriter_H
 
 #include "meshWriter.H"
+#include "STARCDCore.H"
 #include "IOstream.H"
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
@@ -55,27 +56,17 @@ namespace fileFormats
 
 class STARCDMeshWriter
 :
-    public meshWriter
+    public meshWriter,
+    protected STARCDCore
 {
-    // Private Data
+    // Private data
 
-        static const char* defaultBoundaryName;
+        //- Write boundary (bnd) file - default true
+        bool writeBoundary_;
 
 
     // Private Member Functions
 
-        //- Disallow default bitwise copy construct
-        STARCDMeshWriter(const STARCDMeshWriter&) = delete;
-
-        //- Disallow default bitwise assignment
-        void operator=(const STARCDMeshWriter&) = delete;
-
-        //- Pro-STAR 4+ header format
-        static void writeHeader(Ostream&, const char* filetype);
-
-        //- Write points
-        void writePoints(const fileName& baseName) const;
-
         //- Write cells
         void writeCells(const fileName& baseName) const;
 
@@ -87,18 +78,24 @@ class STARCDMeshWriter
         label findDefaultBoundary() const;
 
 
-public:
+        //- Disallow default bitwise copy construct
+        STARCDMeshWriter(const STARCDMeshWriter&) = delete;
 
-    // Static data members
+        //- Disallow default bitwise assignment
+        void operator=(const STARCDMeshWriter&) = delete;
 
-        //- Face addressing from OpenFOAM faces -> pro-STAR faces
-        static const label foamToStarFaceAddr[4][6];
 
+public:
 
     // Constructors
 
         //- Write mesh files in PROSTAR format
-        STARCDMeshWriter(const polyMesh&, const scalar scaleFactor = 1.0);
+        STARCDMeshWriter
+        (
+            const polyMesh&,
+            const scalar scaleFactor = 1.0,
+            const bool writeBndFile = true
+        );
 
 
     //- Destructor
@@ -107,19 +104,13 @@ public:
 
     // Member Functions
 
-        // Edit
-
-            //- Remove STAR-CD files for the baseName
-            void rmFiles(const fileName& baseName) const;
-
-
-        // Write
+    // Write
 
-            //- Write volume mesh
-            virtual bool write
-            (
-                const fileName& meshName = fileName::null
-            ) const;
+        //- Write volume mesh
+        virtual bool write
+        (
+            const fileName& meshName = fileName::null
+        ) const;
 
 };
 
diff --git a/src/conversion/vtk/output/foamVtkOutput.C b/src/conversion/vtk/output/foamVtkOutput.C
new file mode 100644
index 0000000000000000000000000000000000000000..4ece681a85a8c8b8186ce328bfb528983c9c6205
--- /dev/null
+++ b/src/conversion/vtk/output/foamVtkOutput.C
@@ -0,0 +1,126 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2016 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 "foamVtkOutput.H"
+#include "foamVtkAsciiFormatter.H"
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+const Foam::word Foam::foamVtkOutput::legacy::EXT = "vtk";
+
+
+//! \cond fileScope
+static inline std::ostream& legacyDataHeader
+(
+    std::ostream& os,
+    const char* tag,
+    const Foam::label nItems,
+    const Foam::label nFields
+)
+{
+    os  << tag << ' ' << nItems << '\n'
+        << "FIELD attributes " << nFields << '\n';
+
+    return os;
+}
+//! \endcond
+
+
+// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
+
+Foam::label Foam::foamVtkOutput::writeVtmFile
+(
+    std::ostream& os,
+    const UList<fileName>& files
+)
+{
+    const word& content = "vtkMultiBlockDataSet";
+
+    foamVtkAsciiFormatter vtmFile(os);
+
+    vtmFile
+        .xmlHeader()
+        .openTag("VTKFile")
+        ( "type",        content )
+        ( "version",     "1.0" )
+        ( "byte_order",  foamVtkFormatter::byteOrder )
+        ( "header_type", foamVtkFormatter::headerType )
+        .closeTag();
+
+    vtmFile.tag(content);
+
+    forAll(files, i)
+    {
+        vtmFile
+            .openTag("DataSet")
+            ( "index", i )
+            ( "file", files[i] )
+            .closeTag(true);
+    }
+
+    vtmFile.endTag(content).endTag("VTKFile");
+
+    return files.size();
+}
+
+
+std::ostream& Foam::foamVtkOutput::legacy::writeHeader
+(
+    std::ostream& os,
+    const std::string& title,
+    const bool binary
+)
+{
+    os  << "# vtk DataFile Version 2.0" << nl
+        << title << nl
+        << (binary ? "BINARY" : "ASCII") << nl;
+
+    return os;
+}
+
+
+std::ostream& Foam::foamVtkOutput::legacy::writeCellDataHeader
+(
+    std::ostream& os,
+    const label nCells,
+    const label nFields
+)
+{
+    return legacyDataHeader(os, "CELL_DATA", nCells, nFields);
+}
+
+
+std::ostream& Foam::foamVtkOutput::legacy::writePointDataHeader
+(
+    std::ostream& os,
+    const label nPoints,
+    const label nFields
+)
+{
+    return legacyDataHeader(os, "POINT_DATA", nPoints, nFields);
+}
+
+
+// ************************************************************************* //
diff --git a/src/conversion/vtk/output/foamVtkOutput.H b/src/conversion/vtk/output/foamVtkOutput.H
new file mode 100644
index 0000000000000000000000000000000000000000..ad7964bfff7b638f1ff8837088d125feb10506d5
--- /dev/null
+++ b/src/conversion/vtk/output/foamVtkOutput.H
@@ -0,0 +1,216 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2016 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
+    foamVtkOutput
+
+Description
+    A collection of functions for writing vtk file content.
+
+SourceFiles
+    foamVtkOutput.C
+    foamVtkOutputTemplates.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef foamVtkOutput_H
+#define foamVtkOutput_H
+
+#include "floatScalar.H"
+#include "volFields.H"
+#include "foamVtkFormatter.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+/*---------------------------------------------------------------------------*\
+                        Class foamVtkOutput Declaration
+\*---------------------------------------------------------------------------*/
+
+class foamVtkOutput
+{
+    // Private Member Functions
+
+        //- Disallow construction
+        foamVtkOutput() = delete;
+
+public:
+
+    // Forward declarations
+    class legacy;
+
+
+    // Static Members
+
+        //- Write vtm datasets for specified files
+        static Foam::label writeVtmFile
+        (
+            std::ostream& os,
+            const UList<fileName>& files
+        );
+
+
+        //- Write a value component-wise.
+        template<class Type>
+        inline static void write(foamVtkFormatter&, const Type&);
+
+
+        //- Write a list of values.
+        //  The output does not include the payload size.
+        template<class Type>
+        static void writeList
+        (
+            foamVtkFormatter&,
+            const UList<Type>&
+        );
+
+
+        //- Write a list of values via indirect addressing.
+        //  The output does not include the payload size.
+        template<class Type>
+        static void writeList
+        (
+            foamVtkFormatter&,
+            const UList<Type>&,
+            const UList<label>& addressing
+        );
+
+
+        //- Write volField with cell values (including decomposed cells).
+        //  The output includes the payload size and flush.
+        template<class Type>
+        static void writeField
+        (
+            foamVtkFormatter&,
+            const GeometricField<Type, fvPatchField, volMesh>&,
+            const UList<label>& superCells
+        );
+
+};
+
+
+/*---------------------------------------------------------------------------*\
+                    Class foamVtkOutput::legacy Declaration
+\*---------------------------------------------------------------------------*/
+
+//- Basic support for legacy files
+class foamVtkOutput::legacy
+{
+    // Private Member Functions
+
+        //- Disallow construction
+        legacy() = delete;
+
+public:
+
+    // Static data members
+
+        //- file extension for legacy files (vtk)
+        static const Foam::word EXT;
+
+
+    // Static Members
+
+        //- Emit header for legacy file
+        static std::ostream& writeHeader
+        (
+            std::ostream&,
+            const std::string& title,
+            const bool binary = false
+        );
+
+
+        //- Emit header for legacy CELL_DATA
+        static std::ostream& writeCellDataHeader
+        (
+            std::ostream& os,
+            const label nCells,
+            const label nFields
+        );
+
+
+        //- Emit header for legacy POINT_DATA
+        static std::ostream& writePointDataHeader
+        (
+            std::ostream& os,
+            const label nPoints,
+            const label nFields
+        );
+
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+
+//- Template specialization for label
+template<>
+inline void Foam::foamVtkOutput::write<label>
+(
+    foamVtkFormatter& fmt,
+    const label& val
+)
+{
+    fmt.write(val);
+}
+
+
+//- Template specialization for float
+template<>
+inline void Foam::foamVtkOutput::write<float>
+(
+    foamVtkFormatter& fmt,
+    const float& val
+)
+{
+    fmt.write(val);
+}
+
+
+//- Template specialization for double
+template<>
+inline void Foam::foamVtkOutput::write<double>
+(
+    foamVtkFormatter& fmt,
+    const double& val
+)
+{
+    fmt.write(val);
+}
+
+} // End namespace Foam
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#ifdef NoRepository
+    #include "foamVtkOutputTemplates.C"
+#endif
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/conversion/vtk/output/foamVtkOutputTemplates.C b/src/conversion/vtk/output/foamVtkOutputTemplates.C
new file mode 100644
index 0000000000000000000000000000000000000000..03c2d56bd9d941d97e427b049914698fd179147a
--- /dev/null
+++ b/src/conversion/vtk/output/foamVtkOutputTemplates.C
@@ -0,0 +1,94 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2016 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/>.
+
+\*---------------------------------------------------------------------------*/
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+
+template<class Type>
+inline void Foam::foamVtkOutput::write
+(
+    foamVtkFormatter& fmt,
+    const Type& val
+)
+{
+    for (direction cmpt=0; cmpt < pTraits<Type>::nComponents; ++cmpt)
+    {
+        fmt.write(component(val, cmpt));
+    }
+}
+
+
+template<class Type>
+void Foam::foamVtkOutput::writeList
+(
+    foamVtkFormatter& fmt,
+    const UList<Type>& lst
+)
+{
+    forAll(lst, i)
+    {
+        write(fmt, lst[i]);
+    }
+}
+
+
+template<class Type>
+void Foam::foamVtkOutput::writeList
+(
+    foamVtkFormatter& fmt,
+    const UList<Type>& lst,
+    const UList<label>& addressing
+)
+{
+    forAll(addressing, i)
+    {
+        write(fmt, lst[addressing[i]]);
+    }
+}
+
+
+template<class Type>
+void Foam::foamVtkOutput::writeField
+(
+    foamVtkFormatter& fmt,
+    const GeometricField<Type, fvPatchField, volMesh>& vf,
+    const UList<label>& superCells
+)
+{
+    const uint64_t payLoad =
+    (
+        (vf.size() + superCells.size())
+      * pTraits<Type>::nComponents * sizeof(float)
+    );
+
+    fmt.writeSize(payLoad);
+    writeList(fmt, vf.internalField());
+    writeList(fmt, vf, superCells);
+
+    fmt.flush();
+}
+
+
+// ************************************************************************* //
diff --git a/src/conversion/vtk/part/foamVtkCells.C b/src/conversion/vtk/part/foamVtkCells.C
new file mode 100644
index 0000000000000000000000000000000000000000..b4c7beacde9d7523af4cd59f61edac03fc6c2590
--- /dev/null
+++ b/src/conversion/vtk/part/foamVtkCells.C
@@ -0,0 +1,649 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2011-2016 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 "foamVtkCells.H"
+#include "polyMesh.H"
+#include "cellShape.H"
+#include "cellModeller.H"
+
+// * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
+
+void Foam::foamVtkCells::correct()
+{
+    // Clear derived data
+    // clearGeom();
+
+    const cellModel& tet      = *(cellModeller::lookup("tet"));
+    const cellModel& pyr      = *(cellModeller::lookup("pyr"));
+    const cellModel& prism    = *(cellModeller::lookup("prism"));
+    const cellModel& wedge    = *(cellModeller::lookup("wedge"));
+    const cellModel& tetWedge = *(cellModeller::lookup("tetWedge"));
+    const cellModel& hex      = *(cellModeller::lookup("hex"));
+
+    const cellShapeList& cellShapes = mesh_.cellShapes();
+
+    // face owner is needed to determine the face orientation
+    const labelList& owner = mesh_.faceOwner();
+
+    // Unique vertex labels per polyhedral
+    HashSet<label> hashUniqId(2*256);
+
+    // =======================
+    // PASS 1: Determine sizes
+
+    label nVertLabels = 0;
+    label nFaceLabels = 0;
+    label nAddPoints  = 0;
+    label nAddCells   = 0;
+    label nAddVerts   = 0;
+
+    forAll(cellShapes, cellI)
+    {
+        const cellShape& shape = cellShapes[cellI];
+        const cellModel& model = shape.model();
+
+        if
+        (
+            model == tet
+         || model == pyr
+         || model == prism
+         || model == hex
+        )
+        {
+            // normal primitives
+            nVertLabels += shape.size();
+        }
+        else if (model == tetWedge && decompose_.requested())
+        {
+            // Treat as squeezed prism (VTK_WEDGE)
+            nVertLabels += 6;
+        }
+        else if (model == wedge && decompose_.requested())
+        {
+            // Treat as squeezed hex
+            nVertLabels += 8;
+        }
+        else if (decompose_.requested())
+        {
+            // Polyhedral: Decompose into tets + pyramids.
+
+            // Count vertices in first decomposed cell
+            bool first = true;
+
+            const cell& cFaces = mesh_.cells()[cellI];
+            forAll(cFaces, cFaceI)
+            {
+                const face& f = mesh_.faces()[cFaces[cFaceI]];
+
+                // Face decomposed into triangles and quads
+                // Tri -> Tet, Quad -> Pyr
+                label nTria = 0, nQuad = 0;
+                f.nTrianglesQuads(mesh_.points(), nTria, nQuad);
+
+                nAddCells  += nTria + nQuad;
+                nAddVerts  += (nTria * 4) + (nQuad * 5);
+
+                if (first)
+                {
+                    const label nvrt = (nQuad ? 5 : 4);
+                    nAddCells--;
+                    nAddVerts   -= nvrt;
+                    nVertLabels += nvrt;
+
+                    first = false;
+                }
+            }
+
+            ++nAddPoints;
+        }
+        else
+        {
+            // Polyhedral: Not decomposed.
+
+            const labelList& cFaces = mesh_.cells()[cellI];
+
+            // establish unique node ids used (only needed for XML)
+            hashUniqId.clear();
+
+            // determing sizing for face stream
+            // number of faces, size of each face, vertices per face
+            // [nFaces, nFace0Pts, id1, id2, ..., nFace1Pts, id1, id2, ...]
+
+            forAll(cFaces, cFaceI)
+            {
+                const face& f = mesh_.faces()[cFaces[cFaceI]];
+                nFaceLabels += f.size();
+
+                forAll(f, fp)
+                {
+                    hashUniqId.insert(f[fp]);
+                }
+            }
+
+            nVertLabels += hashUniqId.size();
+            nFaceLabels += 1 + cFaces.size();
+        }
+    }
+
+
+    //
+    // adjust/reserve sizes
+    //
+
+    // Cell types (including added cells) in vtk numbering
+    cellTypes_.setSize(cellShapes.size() + nAddCells);
+
+    // List of vertex labels in VTK ordering
+    vertLabels_.setSize(nVertLabels + nAddVerts);
+
+    vertOffset_.setSize(cellShapes.size() + nAddCells);
+
+    faceLabels_.clear();
+    faceOffset_.clear();
+    if (nFaceLabels)
+    {
+        faceLabels_.setSize(nFaceLabels);
+
+        // only need nCells (without nAddCells)
+        // set to -1 (primitive)
+        faceOffset_.setSize(cellShapes.size(), -1);
+    }
+
+    if (decompose_.requested())
+    {
+        decompose_.addPointCellLabels_.setSize(nAddPoints);
+        decompose_.superCells_.setSize(nAddCells);
+    }
+
+
+    // ======================
+    // PASS 2: Fill in arrays
+
+    // Need this offset later, but only for decomposed polys
+    const label offsetAddVerts = nVertLabels;
+
+    // Reset counters
+    nVertLabels = 0;
+    nFaceLabels = 0;
+    nAddPoints  = 0;
+    nAddCells   = 0;
+    nAddVerts   = 0;
+
+    forAll(cellShapes, cellI)
+    {
+        const cellShape& shape = cellShapes[cellI];
+        const cellModel& model = shape.model();
+
+        if (model == tet)
+        {
+            cellTypes_[cellI] = foamVtkCore::VTK_TETRA;
+            forAll(shape, i)
+            {
+                vertLabels_[nVertLabels++] = shape[i];
+            }
+            vertOffset_[cellI] = nVertLabels;
+        }
+        else if (model == pyr)
+        {
+            cellTypes_[cellI] = foamVtkCore::VTK_PYRAMID;
+            forAll(shape, i)
+            {
+                vertLabels_[nVertLabels++] = shape[i];
+            }
+            vertOffset_[cellI] = nVertLabels;
+        }
+        else if (model == hex)
+        {
+            cellTypes_[cellI] = foamVtkCore::VTK_HEXAHEDRON;
+            forAll(shape, i)
+            {
+                vertLabels_[nVertLabels++] = shape[i];
+            }
+            vertOffset_[cellI] = nVertLabels;
+        }
+        else if (model == prism)
+        {
+            cellTypes_[cellI] = foamVtkCore::VTK_WEDGE;
+
+            // VTK_WEDGE triangles point outwards (swap 1<->2, 4<->5)
+            vertLabels_[nVertLabels++] = shape[0];
+            vertLabels_[nVertLabels++] = shape[2];
+            vertLabels_[nVertLabels++] = shape[1];
+            vertLabels_[nVertLabels++] = shape[3];
+            vertLabels_[nVertLabels++] = shape[5];
+            vertLabels_[nVertLabels++] = shape[4];
+
+            vertOffset_[cellI] = nVertLabels;
+        }
+        else if (model == tetWedge && decompose_.requested())
+        {
+            // Treat as squeezed prism (VTK_WEDGE)
+            cellTypes_[cellI] = foamVtkCore::VTK_WEDGE;
+
+            vertLabels_[nVertLabels++] = shape[0];
+            vertLabels_[nVertLabels++] = shape[2];
+            vertLabels_[nVertLabels++] = shape[1];
+            vertLabels_[nVertLabels++] = shape[3];
+            vertLabels_[nVertLabels++] = shape[4];
+            vertLabels_[nVertLabels++] = shape[3];
+
+            vertOffset_[cellI] = nVertLabels;
+        }
+        else if (model == wedge && decompose_.requested())
+        {
+            // Treat as squeezed hex
+            cellTypes_[cellI] = foamVtkCore::VTK_HEXAHEDRON;
+
+            vertLabels_[nVertLabels++] = shape[0];
+            vertLabels_[nVertLabels++] = shape[1];
+            vertLabels_[nVertLabels++] = shape[2];
+            vertLabels_[nVertLabels++] = shape[2];
+            vertLabels_[nVertLabels++] = shape[3];
+            vertLabels_[nVertLabels++] = shape[4];
+            vertLabels_[nVertLabels++] = shape[5];
+            vertLabels_[nVertLabels++] = shape[6];
+
+            vertOffset_[cellI] = nVertLabels;
+        }
+        else if (decompose_.requested())
+        {
+            // Polyhedral cell - decompose into tet/pyr.
+
+            // Ensure we have the correct orientation for the base of the
+            // primitive cell shape.
+            // If the cell is face owner, the orientation needs to be flipped
+            // to avoid defining negative cells.
+            // VTK doesn't seem to care, but we'll do it anyhow for safety.
+
+            // The new vertex from the cell-centre
+            const label newVertexLabel = mesh_.nPoints() + nAddPoints;
+
+            // Mapping from additional point to cell
+            decompose_.addPointCellLabels_[nAddPoints++] = cellI;
+
+            // Whether to insert cell in place of original or not.
+            bool first = true;
+
+            const labelList& cFaces = mesh_.cells()[cellI];
+            forAll(cFaces, cFaceI)
+            {
+                const face& f = mesh_.faces()[cFaces[cFaceI]];
+                const bool isOwner = (owner[cFaces[cFaceI]] == cellI);
+
+                // Count triangles/quads in decomposition
+                label nTria = 0;
+                label nQuad = 0;
+                f.nTrianglesQuads(mesh_.points(), nTria, nQuad);
+
+                // Do actual decomposition
+                faceList faces3(nTria);
+                faceList faces4(nQuad);
+                nTria = 0, nQuad = 0;
+                f.trianglesQuads(mesh_.points(), nTria, nQuad, faces3, faces4);
+
+                forAll(faces4, fci)
+                {
+                    const face& quad = faces4[fci];
+
+                    label celLoc;
+                    label vrtLoc;
+
+                    if (first)
+                    {
+                        celLoc = cellI;
+                        vrtLoc = nVertLabels;
+                        nVertLabels += 5;
+
+                        vertOffset_[celLoc] = nVertLabels;
+                        first = false;
+                    }
+                    else
+                    {
+                        celLoc = mesh_.nCells() + nAddCells;
+                        vrtLoc = offsetAddVerts  + nAddVerts;
+                        nAddVerts += 5;
+
+                        vertOffset_[celLoc] = nAddVerts;
+                        decompose_.superCells_[nAddCells++] = celLoc;
+                    }
+
+                    cellTypes_[celLoc] = foamVtkCore::VTK_PYRAMID;
+
+                    // See note above about the orientation.
+                    if (isOwner)
+                    {
+                        vertLabels_[vrtLoc++] = quad[3];
+                        vertLabels_[vrtLoc++] = quad[2];
+                        vertLabels_[vrtLoc++] = quad[1];
+                        vertLabels_[vrtLoc++] = quad[0];
+                    }
+                    else
+                    {
+                        vertLabels_[vrtLoc++] = quad[0];
+                        vertLabels_[vrtLoc++] = quad[1];
+                        vertLabels_[vrtLoc++] = quad[2];
+                        vertLabels_[vrtLoc++] = quad[3];
+                    }
+
+                    vertLabels_[vrtLoc++] = newVertexLabel;
+                }
+
+                forAll(faces3, fci)
+                {
+                    const face& tria = faces3[fci];
+
+                    label celLoc;
+                    label vrtLoc;
+
+                    if (first)
+                    {
+                        celLoc = cellI;
+                        vrtLoc = nVertLabels;
+                        nVertLabels += 4;
+
+                        vertOffset_[celLoc] = nVertLabels;
+                        first = false;
+                    }
+                    else
+                    {
+                        celLoc = mesh_.nCells() + nAddCells;
+                        vrtLoc = offsetAddVerts + nAddVerts;
+                        nAddVerts += 4;
+
+                        vertOffset_[celLoc] = nAddVerts;
+                        decompose_.superCells_[nAddCells++] = celLoc;
+                    }
+
+                    cellTypes_[celLoc] = foamVtkCore::VTK_TETRA;
+
+                    // See note above about the orientation.
+                    if (isOwner)
+                    {
+                        vertLabels_[vrtLoc++] = tria[2];
+                        vertLabels_[vrtLoc++] = tria[1];
+                        vertLabels_[vrtLoc++] = tria[0];
+                    }
+                    else
+                    {
+                        vertLabels_[vrtLoc++] = tria[0];
+                        vertLabels_[vrtLoc++] = tria[1];
+                        vertLabels_[vrtLoc++] = tria[2];
+                    }
+                    vertLabels_[vrtLoc++] = newVertexLabel;
+                }
+            }
+        }
+        else
+        {
+            // Polyhedral cell - not decomposed
+
+            hashUniqId.clear();  // unique node ids used (only needed for XML)
+
+            // face-stream
+            //   [nFaces, nFace0Pts, id1, id2, ..., nFace1Pts, id1, id2, ...]
+
+            cellTypes_[cellI] = foamVtkCore::VTK_POLYHEDRON;
+            const labelList& cFaces = mesh_.cells()[cellI];
+
+            faceLabels_[nFaceLabels++] = cFaces.size();
+
+            forAll(cFaces, cFaceI)
+            {
+                const face& f = mesh_.faces()[cFaces[cFaceI]];
+                const bool isOwner = (owner[cFaces[cFaceI]] == cellI);
+
+                forAll(f, fp)
+                {
+                    hashUniqId.insert(f[fp]);
+                }
+
+                // number of labels for this face
+                faceLabels_[nFaceLabels++] = f.size();
+
+                if (isOwner)
+                {
+                    forAll(f, fp)
+                    {
+                        faceLabels_[nFaceLabels++] = f[fp];
+                    }
+                }
+                else
+                {
+                    // fairly immaterial if we reverse the list
+                    // or use face::reverseFace()
+                    forAllReverse(f, fp)
+                    {
+                        faceLabels_[nFaceLabels++] = f[fp];
+                    }
+                }
+            }
+
+            faceOffset_[cellI] = nFaceLabels;
+
+            const labelList uniq = hashUniqId.sortedToc();
+            forAll(uniq, i)
+            {
+                vertLabels_[nVertLabels++] = uniq[i];
+            }
+
+            vertOffset_[cellI] = nVertLabels;
+        }
+    }
+
+    // ===========================================
+    // PASS 3: Repair offsets for additional cells
+
+//     Info<<"vertOffset: " << vertOffset_.size() << " VS. " << (mesh_.nCells()) << endl;
+//     Info<<"nAddCells: "  << nAddCells << " VS. " << (mesh_.nCells()) << endl;
+
+    if (nAddCells)
+    {
+        const label beg = mesh_.nCells();
+        const label add = vertOffset_[beg-1];
+
+        for (label i = beg; i < vertOffset_.size(); ++i)
+        {
+            vertOffset_[i] += add;
+        }
+    }
+
+    // Some basic programming/sanity checks
+
+    if ((nVertLabels + nAddVerts) != vertOffset_[mesh_.nCells()-1 + nAddCells])
+    {
+        WarningInFunction
+            << "predicted offsets (" << nVertLabels << " + " << nAddVerts << ") != "
+            << vertOffset_[mesh_.nCells()-1 + nAddCells]
+            << endl;
+    }
+
+    if (offsetAddVerts != vertOffset_[mesh_.nCells()-1])
+    {
+        WarningInFunction
+            << "predicted regular offset " << offsetAddVerts
+            << " != " << vertOffset_[mesh_.nCells()]
+            << endl;
+    }
+
+    // nFaceLabels = 0;
+    // nAddPoints  = 0;
+    // nAddCells   = 0;
+
+    // Pout<<"vertLabels: " << vertLabels_.size() << " vs. " << (nVertLabels + nAddVerts) << endl;
+    // Pout<<"faceLabels: " << faceLabels_.size() << " vs. " << nFaceLabels << endl;
+#if 0
+    if (decompose_.requested())
+    {
+        Pout<< "    Original cells:" << mesh_.nCells()
+            << " points:" << mesh_.nPoints()
+            << " Additional cells:" << decompose_.superCells_.size()
+            << " additional points:" << decompose_.addPointCellLabels_.size()
+            << nl << endl;
+    }
+#endif
+}
+
+
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+Foam::foamVtkCells::decomp::decomp(const bool decomposePoly)
+:
+    addPointCellLabels_(),
+    superCells_(),
+    pointMap_(),
+    requested_(decomposePoly)
+{}
+
+
+Foam::foamVtkCells::foamVtkCells
+(
+    const polyMesh& mesh,
+    const bool decomposePoly,
+    const bool lazy
+)
+:
+    mesh_(mesh),
+    cellTypes_(),
+    vertLabels_(),
+    vertOffset_(),
+    faceLabels_(),
+    faceOffset_(),
+    decompose_(decomposePoly),
+    needsUpdate_(true)
+{
+    if (!lazy)
+    {
+        correct();
+    }
+}
+
+
+// * * * * * * * * * * * * * * * * Destructor  * * * * * * * * * * * * * * * //
+
+Foam::foamVtkCells::decomp::~decomp()
+{}
+
+
+Foam::foamVtkCells::~foamVtkCells()
+{}
+
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+
+void Foam::foamVtkCells::decomp::clear()
+{
+    superCells_.clear();
+    addPointCellLabels_.clear();
+    pointMap_.clear();
+}
+
+
+Foam::label Foam::foamVtkCells::nFieldPoints() const
+{
+    return mesh_.nPoints() + decompose_.addPointCellLabels_.size();
+}
+
+
+Foam::label Foam::foamVtkCells::legacyCellPayLoad() const
+{
+    label payLoad = cellTypes_.size();
+
+    if (faceOffset_.size())
+    {
+        // also has polys with face streams
+
+        label begVert = 0;
+        label begFace = 0;
+
+        forAll(faceOffset_, i)
+        {
+            label endFace = faceOffset_[i];
+            label endVert = vertOffset_[i];
+
+            if (endFace > 0)
+            {
+                // poly with face stream
+                payLoad += endFace - begFace;
+
+                begFace = endFace;
+            }
+            else
+            {
+                // primitive without face stream
+                payLoad += endVert - begVert;
+            }
+            begVert = endVert;
+        }
+    }
+    else if (vertOffset_.size())
+    {
+        // primitives only, trivial
+        payLoad += vertOffset_[vertOffset_.size()-1];
+    }
+
+    return payLoad;
+}
+
+
+bool Foam::foamVtkCells::needsUpdate() const
+{
+    return needsUpdate_;
+}
+
+
+bool Foam::foamVtkCells::expire()
+{
+    // Clear any stored topologies
+
+    // Clear derived data
+    // clearGeom();
+
+    // already marked as expired
+    if (needsUpdate_)
+    {
+        return false;
+    }
+
+    needsUpdate_ = true;
+    return true;
+}
+
+
+bool Foam::foamVtkCells::update()
+{
+    if (!needsUpdate_)
+    {
+        return false;
+    }
+
+    correct();
+
+    needsUpdate_ = false;
+    return true;
+}
+
+
+// ************************************************************************* //
diff --git a/src/conversion/vtk/part/foamVtkCells.H b/src/conversion/vtk/part/foamVtkCells.H
new file mode 100644
index 0000000000000000000000000000000000000000..692614a4b9b98e272062fb199751cde0c37980be
--- /dev/null
+++ b/src/conversion/vtk/part/foamVtkCells.H
@@ -0,0 +1,350 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2011-2016 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::foamVtkCells
+
+Description
+    The deep-copy description of an OpenFOAM volume mesh in data structures
+    corresponding to an VTK UnstructuredGrid, including the possiblity of
+    decomposing polyhedral cells into primitive cell types.
+
+    Knowledge of the vtkUnstructuredGrid and the corresponding \c .vtu
+    xml file-format aids in understanding this class.
+    For flexibilty, support for the legacy vtk file-format is also provided.
+
+    Primitive cell types are straighforward, polyhedral cells are represented
+    by a face stream:
+    \verbatim
+        [nFaces, nFace0Pts, id1, id2, ..., nFace1Pts, id1, id2, ...]
+    \endverbatim
+
+    For the legacy format, the face stream is simply passed as vertex labels
+    (connectivity).
+
+    For the xml format, the face stream is saved separately:
+    \verbatim
+        "connectivity"
+        == the unique vertex labels used by the cell (optionally sorted).
+
+        "offsets":
+        == offset + sizeof(connectivity)
+
+        "faces":
+        [nFaces, nFace0Pts, id1, id2, ..., nFace1Pts, id1, id2, ...]
+
+        "faceoffsets":
+        == faceoffsets + sizeof(faces)
+    \endverbatim
+
+    The storage of "connectivity" and "offsets" strongly resembles a
+    CompactListList, but the "offsets" point to the end of the respective
+    sub-lists.
+
+SourceFiles
+    foamVtkCells.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef foamVtkCells_H
+#define foamVtkCells_H
+
+#include "foamVtkCore.H"
+#include "DynamicList.H"
+#include "SubList.H"
+#include "labelList.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+// Forward declaration of classes
+class polyMesh;
+
+/*---------------------------------------------------------------------------*\
+                      Class foamVtkCells Declaration
+\*---------------------------------------------------------------------------*/
+
+class foamVtkCells
+:
+    public fileFormats::foamVtkCore
+{
+public:
+
+    //- Bookkeeping for polyhedral cell decomposition
+    class decomp
+    {
+    private:
+        friend foamVtkCells;
+
+        // Private data
+
+            //- Cell-centre labels for additional points of decomposed cells
+            DynamicList<label> addPointCellLabels_;
+
+            //- Label of original cell for decomposed cells
+            DynamicList<label> superCells_;
+
+            //- Point labels for subsetted meshes
+            DynamicList<label> pointMap_;
+
+            //- Track if decomposition was requested
+            const bool requested_;
+
+
+        // Private Member Functions
+
+            //- Disallow default bitwise copy construct
+            decomp(const decomp&) = delete;
+
+            //- Disallow default bitwise assignment
+            void operator=(const decomp&) = delete;
+
+    public:
+
+        // Constructors
+
+            //- Construct null
+            decomp(const bool decomposePoly = false);
+
+
+        //- Destructor
+        ~decomp();
+
+
+        // Member Functions
+
+        // Access
+
+            //- Polyhedral decomposition requested
+            inline bool requested() const;
+
+            //- Polyhedral decomposition used
+            inline bool used() const;
+
+            //- Label of original cell for decomposed cells
+            inline const labelList& superCells() const;
+
+            //- Cell-centre labels for additional points of decomposed cells
+            inline const labelList& addPointCellLabels() const;
+
+            //- Point labels for subsetted meshes
+            inline const labelList& pointMap() const;
+
+
+        // Edit
+
+            //- Clear
+            void clear();
+    };
+
+
+private:
+
+    // Private data
+
+        //- Reference to underlying mesh or mesh sub-set
+        const polyMesh& mesh_;
+
+        //- Cell types (including added cells) in vtk numbering
+        //  Range is 1-255
+        List<uint8_t> cellTypes_;
+
+        //- Vertices per cell (including added cells) in vtk ordering
+        DynamicList<label> vertLabels_;
+
+        //- Vertices per cell (including added cells) in vtk ordering
+        DynamicList<label> vertOffset_;
+
+        //- Face lists per polyhedral cell
+        DynamicList<label> faceLabels_;
+
+        //- Face label offsets
+        DynamicList<label> faceOffset_;
+
+        //- Bookkeeping for polyhedral cell decomposition
+        decomp decompose_;
+
+        //- Needs update
+        bool needsUpdate_;
+
+
+
+    // Private Member Functions
+
+        //- Create the geometry
+        void correct();
+
+        //- Disallow default bitwise copy construct
+        foamVtkCells(const foamVtkCells&) = delete;
+
+        //- Disallow default bitwise assignment
+        void operator=(const foamVtkCells&) = delete;
+
+
+public:
+
+    // Constructors
+
+        //- Construct from components.
+        //  Optionally with polyhedral decomposition and/or lazy evaluation.
+        //  A 'lazy' evaluation avoids fully creation within the constructor.
+        foamVtkCells
+        (
+            const polyMesh&,
+            const bool decomposePoly = false,
+            const bool lazy = false
+        );
+
+
+    //- Destructor
+    ~foamVtkCells();
+
+
+    // Member Functions
+
+        // Access
+
+            //- Query the poly decompose flag.
+            inline bool decomposeRequested() const;
+
+            //- Values for "connectivity" (XML) or basis for "CELLS" (legacy)
+            //  In the legacy format, the size (offset) must be prefixed.
+            inline const labelList& vertLabels() const;
+
+            //- Values for "offsets" (XML)
+            //  or sizes to prefix for for "CELLS" (legacy)
+            inline const labelList& vertOffsets() const;
+
+            //- Values for "types" (XML) and "CELL_TYPES" (legacy)
+            inline const List<uint8_t>& cellTypes() const;
+
+            //- Values for "faces" (XML)
+            inline const labelList& faceLabels() const;
+
+            //- Values for "faceoffsets" (XML)
+            inline const labelList& faceOffsets() const;
+
+            //- Additional point addressing (from added point to original cell)
+            inline const labelList& addPointCellLabels() const;
+
+            //- Additional cells mapping (from added cell to original cell)
+            inline const labelList& superCells() const;
+
+
+            //- Number of field cells
+            inline label nFieldCells() const;
+
+            //- Number of field points
+            label nFieldPoints() const;
+
+            //- The field size for legacy "CELLS".
+            //  In the legacy format, the size (offset) must be prefixed.
+            label legacyCellPayLoad() const;
+
+
+        //- Does the mapping need an update?
+        bool needsUpdate() const;
+
+        //- Mark as needing an update.
+        //  May also free up unneeded data.
+        //  Return false if it was already marked as expired.
+        bool expire();
+
+        //- Update the description (and decomposition) as required.
+        //  Do nothing (and return false) if no update was required
+        bool update();
+
+
+        //- The const_iterator for foamVtkCells
+        class const_iterator
+        {
+            friend class foamVtkCells;
+
+        protected:
+
+            // Protected Data
+
+                //- Reference to parent list
+                const foamVtkCells& parent_;
+
+                //- Element index
+                label index_;
+
+                //- Begin of connectivity sub-list
+                mutable label begVert_;
+
+                //- Begin of faces sub-list
+                mutable label begFace_;
+
+                //- On-demand legacy pointer
+                mutable autoPtr<SubList<label>> legacy_;
+
+
+            // Constructors
+
+                //- Construct begin/end iterator
+                inline const_iterator
+                (
+                    const foamVtkCells&,
+                    bool isEnd = false
+                );
+
+        public:
+
+            // Member operators
+
+                //- On-demand legacy cell labels (primitive or faces)
+                inline const labelUList& legacyCell() const;
+
+                //- Compare position
+                inline bool operator!=(const const_iterator&) const;
+
+                //- Pre-increment iterator
+                inline const_iterator& operator++();
+        };
+
+
+        //- const_iterator set to the beginning
+        inline const_iterator begin() const;
+
+        //- const_iterator set to beyond the end
+        inline const_iterator end() const;
+
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#include "foamVtkCellsI.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/conversion/vtk/part/foamVtkCellsI.H b/src/conversion/vtk/part/foamVtkCellsI.H
new file mode 100644
index 0000000000000000000000000000000000000000..5973f5113e8de89c3630fa095c201fed966401ca
--- /dev/null
+++ b/src/conversion/vtk/part/foamVtkCellsI.H
@@ -0,0 +1,239 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2016 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 "foamVtkCells.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+inline bool Foam::foamVtkCells::decomp::requested() const
+{
+    return requested_;
+}
+
+
+inline bool Foam::foamVtkCells::decomp::used() const
+{
+    return !superCells_.empty();
+}
+
+
+inline const Foam::labelList&
+Foam::foamVtkCells::decomp::superCells() const
+{
+    return superCells_;
+}
+
+
+inline const Foam::labelList&
+Foam::foamVtkCells::decomp::addPointCellLabels() const
+{
+    return addPointCellLabels_;
+}
+
+
+inline const Foam::labelList&
+Foam::foamVtkCells::decomp::pointMap() const
+{
+    return pointMap_;
+}
+
+
+inline bool Foam::foamVtkCells::decomposeRequested() const
+{
+    return decompose_.requested();
+}
+
+
+inline const Foam::labelList&
+Foam::foamVtkCells::vertLabels() const
+{
+    return vertLabels_;
+}
+
+
+inline const Foam::labelList&
+Foam::foamVtkCells::vertOffsets() const
+{
+    return vertOffset_;
+}
+
+
+inline const Foam::List<uint8_t>&
+Foam::foamVtkCells::cellTypes() const
+{
+    return cellTypes_;
+}
+
+
+inline const Foam::labelList&
+Foam::foamVtkCells::faceLabels() const
+{
+    return faceLabels_;
+}
+
+
+inline const Foam::labelList&
+Foam::foamVtkCells::faceOffsets() const
+{
+    return faceOffset_;
+}
+
+
+inline const Foam::labelList&
+Foam::foamVtkCells::addPointCellLabels() const
+{
+    return decompose_.addPointCellLabels();
+}
+
+
+inline const Foam::labelList&
+Foam::foamVtkCells::superCells() const
+{
+    return decompose_.superCells();
+}
+
+
+inline Foam::label
+Foam::foamVtkCells::nFieldCells() const
+{
+    return cellTypes_.size();
+}
+
+
+inline Foam::foamVtkCells::const_iterator
+Foam::foamVtkCells::begin() const
+{
+    return const_iterator(*this);
+}
+
+
+inline Foam::foamVtkCells::const_iterator
+Foam::foamVtkCells::end() const
+{
+    return const_iterator(*this, true);
+}
+
+
+// * * * * * * * * * * * * * * * * Iterators * * * * * * * * * * * * * * * * //
+
+inline Foam::foamVtkCells::const_iterator::const_iterator
+(
+    const foamVtkCells& cells,
+    bool isEnd
+)
+:
+    parent_(cells),
+    index_(0),
+    begVert_(0),
+    begFace_(0),
+    legacy_()
+{
+    if (isEnd)
+    {
+        index_ = parent_.vertOffsets().size();
+    }
+}
+
+
+inline
+Foam::foamVtkCells::const_iterator&
+Foam::foamVtkCells::const_iterator::operator++()
+{
+    ++index_;
+    legacy_.clear();
+
+    return *this;
+}
+
+
+inline
+const Foam::UList<Foam::label>&
+Foam::foamVtkCells::const_iterator::legacyCell() const
+{
+    if
+    (
+        legacy_.valid()
+     || index_ >= parent_.vertOffsets().size()
+    )
+    {
+        return legacy_();
+    }
+
+    const label endVert = parent_.vertOffsets()[index_];
+
+    const label endFace =
+    (
+        parent_.faceOffsets().size()
+      ? parent_.faceOffsets()[index_]
+      : -1
+    );
+
+    if (endFace > 0)
+    {
+        // poly with face stream
+
+        legacy_.reset
+        (
+            new SubList<label>
+            (
+                parent_.faceLabels(),
+                endFace - begFace_,
+                begFace_
+            )
+        );
+
+        begFace_ = endFace;
+    }
+    else
+    {
+        // primitive without face stream
+        legacy_.reset
+        (
+            new SubList<label>
+            (
+                parent_.vertLabels(),
+                endVert - begVert_,
+                begVert_
+            )
+        );
+    }
+
+    begVert_ = endVert;
+
+    return legacy_();
+}
+
+
+inline bool
+Foam::foamVtkCells::const_iterator::operator!=
+(
+    const const_iterator& rhs
+) const
+{
+    return (index_ != rhs.index_);
+}
+
+
+// ************************************************************************* //
diff --git a/src/dynamicMesh/Make/files b/src/dynamicMesh/Make/files
index 215d35048d1e33999822dc8be39d9589a241d232..de8f61296cd77d4c7d1a774f90da81716deffefc 100644
--- a/src/dynamicMesh/Make/files
+++ b/src/dynamicMesh/Make/files
@@ -90,6 +90,7 @@ polyMeshAdder/polyMeshAdder.C
 fvMeshTools/fvMeshTools.C
 
 fvMeshSubset/fvMeshSubset.C
+meshSubsetHelper/meshSubsetHelper.C
 
 motionSmoother/motionSmoother.C
 motionSmoother/motionSmootherAlgo.C
diff --git a/applications/utilities/postProcessing/dataConversion/foamToEnsight/meshSubsetHelper.C b/src/dynamicMesh/meshSubsetHelper/meshSubsetHelper.C
similarity index 88%
rename from applications/utilities/postProcessing/dataConversion/foamToEnsight/meshSubsetHelper.C
rename to src/dynamicMesh/meshSubsetHelper/meshSubsetHelper.C
index 05bdc9420bb74411a78277f13d425798a14bcec9..0f3760654cb11e43358124b3f66d71f1799f9bbb 100644
--- a/applications/utilities/postProcessing/dataConversion/foamToEnsight/meshSubsetHelper.C
+++ b/src/dynamicMesh/meshSubsetHelper/meshSubsetHelper.C
@@ -28,21 +28,34 @@ License
 #include "cellSet.H"
 #include "cellZone.H"
 #include "Time.H"
-#include "IOstreams.H"
 
 // * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
 
+Foam::meshSubsetHelper::meshSubsetHelper
+(
+    fvMesh& baseMesh
+)
+:
+    baseMesh_(baseMesh),
+    subsetter_(baseMesh),
+    type_(NONE),
+    name_()
+{
+    correct();
+}
+
+
 Foam::meshSubsetHelper::meshSubsetHelper
 (
     fvMesh& baseMesh,
-    const word& name,
-    const bool isCellSet
+    const subsetType type,
+    const word& name
 )
 :
     baseMesh_(baseMesh),
     subsetter_(baseMesh),
-    name_(name),
-    type_(name_.empty() ? 0 : isCellSet ? 1 : 2)
+    type_(name.empty() ? NONE : type),
+    name_(name)
 {
     correct();
 }
@@ -52,7 +65,7 @@ Foam::meshSubsetHelper::meshSubsetHelper
 
 void Foam::meshSubsetHelper::correct(bool verbose)
 {
-    if (type_ == 1)
+    if (type_ == SET)
     {
         if (verbose)
         {
@@ -62,7 +75,7 @@ void Foam::meshSubsetHelper::correct(bool verbose)
         cellSet subset(baseMesh_, name_);
         subsetter_.setLargeCellSubset(subset);
     }
-    else if (type_ == 2)
+    else if (type_ == ZONE)
     {
         if (verbose)
         {
diff --git a/applications/utilities/postProcessing/dataConversion/foamToEnsight/meshSubsetHelper.H b/src/dynamicMesh/meshSubsetHelper/meshSubsetHelper.H
similarity index 86%
rename from applications/utilities/postProcessing/dataConversion/foamToEnsight/meshSubsetHelper.H
rename to src/dynamicMesh/meshSubsetHelper/meshSubsetHelper.H
index 43112a5a18a80f250c9617a653ebe28585033bba..891e6d94a6ed625f6a4416203f7deebb9bdf80dc 100644
--- a/applications/utilities/postProcessing/dataConversion/foamToEnsight/meshSubsetHelper.H
+++ b/src/dynamicMesh/meshSubsetHelper/meshSubsetHelper.H
@@ -54,6 +54,17 @@ class Time;
 
 class meshSubsetHelper
 {
+public:
+
+        //- Internal book-keeping for subset type
+        enum subsetType
+        {
+            NONE,       //<! Not a subset
+            SET,        //<! Using a cellSet for the subset
+            ZONE        //<! Using a cellZone for the subset
+        };
+
+
     // Private data
 
         //- Reference to mesh
@@ -62,12 +73,12 @@ class meshSubsetHelper
         //- Subsetting engine + sub-fvMesh
         fvMeshSubset subsetter_;
 
+        //- Subset type if any.
+        const subsetType type_;
+
         //- Name of current cellSet/cellZone (or empty)
         const word name_;
 
-        //- Internal book-keeping. 0 = unused, 1 = set, 2 = zone
-        const int type_;
-
 
     // Private Member Functions
 
@@ -82,12 +93,15 @@ public:
 
     // Constructors
 
+        //- Construct from components
+        meshSubsetHelper(fvMesh& baseMesh);
+
         //- Construct from components
         meshSubsetHelper
         (
             fvMesh& baseMesh,
-            const word& name = word::null,
-            const bool isCellSet = false
+            const subsetType,
+            const word& name
         );
 
 
@@ -110,7 +124,7 @@ public:
         //- Check if running a sub-mesh is being used
         inline bool useSubMesh() const
         {
-            return type_;
+            return type_ != NONE;
         }
 
         //- Access either mesh or submesh
@@ -146,7 +160,7 @@ public:
                 Type,
                 fvPatchField,
                 volMesh
-            >::Internal& df
+            >::Internal&
         );
 
 
@@ -174,6 +188,11 @@ public:
             >::Internal&
         ) const;
 
+
+        //- Map volume field (does in fact do very little interpolation;
+        //  just copied from fvMeshSubset)
+        template<class GeoField>
+        tmp<GeoField> interpolate(const GeoField& fld) const;
 };
 
 
diff --git a/applications/utilities/postProcessing/dataConversion/foamToEnsight/meshSubsetHelperTemplates.C b/src/dynamicMesh/meshSubsetHelper/meshSubsetHelperTemplates.C
similarity index 89%
rename from applications/utilities/postProcessing/dataConversion/foamToEnsight/meshSubsetHelperTemplates.C
rename to src/dynamicMesh/meshSubsetHelper/meshSubsetHelperTemplates.C
index d588118d0f0fa6bcfdface68ea0013f9008645df..25a6d1301b7671324d1b63aacc9f0a5ecf0b98ff 100644
--- a/applications/utilities/postProcessing/dataConversion/foamToEnsight/meshSubsetHelperTemplates.C
+++ b/src/dynamicMesh/meshSubsetHelper/meshSubsetHelperTemplates.C
@@ -116,4 +116,25 @@ Foam::meshSubsetHelper::interpolate
 }
 
 
+template<class GeoField>
+Foam::tmp<GeoField>
+Foam::meshSubsetHelper::interpolate
+(
+    const GeoField& fld
+) const
+{
+    if (subsetter_.hasSubMesh())
+    {
+        tmp<GeoField> subFld = subsetter_.interpolate(fld);
+        subFld.ref().checkOut();
+        subFld.ref().rename(fld.name());
+        return subFld;
+    }
+    else
+    {
+        return fld;
+    }
+}
+
+
 // ************************************************************************* //
diff --git a/src/edgeMesh/edgeMeshFormats/starcd/STARCDedgeFormat.C b/src/edgeMesh/edgeMeshFormats/starcd/STARCDedgeFormat.C
index d58a67c562b0dfb2275384579f8ccc65055e6f26..9a002009a1c84f0bb88d4bf32134570edc983f5a 100644
--- a/src/edgeMesh/edgeMeshFormats/starcd/STARCDedgeFormat.C
+++ b/src/edgeMesh/edgeMeshFormats/starcd/STARCDedgeFormat.C
@@ -3,7 +3,7 @@
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
     \\  /    A nd           | Copyright (C) 2011-2016 OpenFOAM Foundation
-     \\/     M anipulation  |
+     \\/     M anipulation  | Copyright (C) 2016 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -37,7 +37,7 @@ inline void Foam::fileFormats::STARCDedgeFormat::writeLines
     const edgeList& edges
 )
 {
-    writeHeader(os, "CELL");
+    writeHeader(os, STARCDCore::HEADER_CEL);
 
     forAll(edges, edgeI)
     {
@@ -45,10 +45,10 @@ inline void Foam::fileFormats::STARCDedgeFormat::writeLines
         const label cellId = edgeI + 1;
 
         os  << cellId                    // includes 1 offset
-            << ' ' << starcdLineShape_   // 2(line) shape
+            << ' ' << starcdLine         // 2(line) shape
             << ' ' << e.size()
             << ' ' << 401                // arbitrary value
-            << ' ' << starcdLineType_;   // 5(line)
+            << ' ' << starcdLineType;    // 5(line)
 
         os  << nl << "  " << cellId << "  "
             << (e[0]+1) << "  " << (e[1]+1) << nl;
@@ -118,7 +118,7 @@ bool Foam::fileFormats::STARCDedgeFormat::read
     // read points from .vrt file
     readPoints
     (
-        IFstream(baseName + ".vrt")(),
+        IFstream(starFileName(baseName, STARCDCore::VRT_FILE))(),
         storedPoints(),
         pointId
     );
@@ -137,7 +137,7 @@ bool Foam::fileFormats::STARCDedgeFormat::read
     //
     // read .cel file
     // ~~~~~~~~~~~~~~
-    IFstream is(baseName + ".cel");
+    IFstream is(starFileName(baseName, STARCDCore::CEL_FILE));
     if (!is.good())
     {
         FatalErrorInFunction
@@ -145,7 +145,7 @@ bool Foam::fileFormats::STARCDedgeFormat::read
             << exit(FatalError);
     }
 
-    readHeader(is, "PROSTAR_CELL");
+    readHeader(is, STARCDCore::HEADER_CEL);
 
     DynamicList<edge>  dynEdges;
 
@@ -173,7 +173,7 @@ bool Foam::fileFormats::STARCDedgeFormat::read
             vertexLabels.append(mapPointId[vrtId]);
         }
 
-        if (typeId == starcdLineType_)
+        if (typeId == starcdLineType)
         {
             if (vertexLabels.size() >= 2)
             {
@@ -239,13 +239,21 @@ void Foam::fileFormats::STARCDedgeFormat::write
 
     fileName baseName = filename.lessExt();
 
-    writePoints(OFstream(baseName + ".vrt")(), pointLst);
-    writeLines(OFstream(baseName + ".cel")(), edgeLst);
+    writePoints
+    (
+        OFstream(starFileName(baseName, STARCDCore::VRT_FILE))(),
+        pointLst
+    );
+    writeLines
+    (
+        OFstream(starFileName(baseName, STARCDCore::CEL_FILE))(),
+        edgeLst
+    );
 
     // write a simple .inp file
     writeCase
     (
-        OFstream(baseName + ".inp")(),
+        OFstream(starFileName(baseName, STARCDCore::INP_FILE))(),
         pointLst,
         edgeLst.size()
     );
diff --git a/src/edgeMesh/edgeMeshFormats/starcd/STARCDedgeFormat.H b/src/edgeMesh/edgeMeshFormats/starcd/STARCDedgeFormat.H
index bc5a0ec1bbc96f4aefc533901580a4755443e698..d4c2206ff6c31e0c3dc16144d8ed149168d5edb9 100644
--- a/src/edgeMesh/edgeMeshFormats/starcd/STARCDedgeFormat.H
+++ b/src/edgeMesh/edgeMeshFormats/starcd/STARCDedgeFormat.H
@@ -3,7 +3,7 @@
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
     \\  /    A nd           | Copyright (C) 2011-2016 OpenFOAM Foundation
-     \\/     M anipulation  |
+     \\/     M anipulation  | Copyright (C) 2016 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -62,30 +62,18 @@ namespace fileFormats
 class STARCDedgeFormat
 :
     public edgeMesh,
-    public STARCDCore
+    protected STARCDCore
 {
-    // Private Data
-
-        //- STAR-CD identifier for line shapes (1d elements)
-        static const int starcdLineShape_ = 2;
-
-        //- STAR-CD identifier for line type
-        static const int starcdLineType_  = 5;
-
-
     // Private Member Functions
 
-        static inline void writeLines
-        (
-            Ostream&,
-            const edgeList&
-        );
+        static inline void writeLines(Ostream&, const edgeList&);
+
 
         //- Disallow default bitwise copy construct
-        STARCDedgeFormat(const STARCDedgeFormat&);
+        STARCDedgeFormat(const STARCDedgeFormat&) = delete;
 
         //- Disallow default bitwise assignment
-        void operator=(const STARCDedgeFormat&);
+        void operator=(const STARCDedgeFormat&) = delete;
 
 
 protected:
diff --git a/src/fileFormats/Make/files b/src/fileFormats/Make/files
index c972fde32e70d577dd7bc1f49fcdd51d1e61d8f0..8919e3392d620678737974d272ee3dd44f807754 100644
--- a/src/fileFormats/Make/files
+++ b/src/fileFormats/Make/files
@@ -7,10 +7,21 @@ ensight/part/ensightFaces.C
 ensight/read/ensightReadFile.C
 ensight/type/ensightPTraits.C
 
-vtk/vtkUnstructuredReader.C
 nas/NASCore.C
+fire/FIRECore.C
 starcd/STARCDCore.C
 
+vtk/foamVtkCore.C
+vtk/format/foamVtkAppendBase64Formatter.C
+vtk/format/foamVtkAppendRawFormatter.C
+vtk/format/foamVtkAsciiFormatter.C
+vtk/format/foamVtkBase64Formatter.C
+vtk/format/foamVtkLegacyFormatter.C
+vtk/format/foamVtkFormatter.C
+vtk/format/foamVtkOutputOptions.C
+vtk/read/vtkUnstructuredReader.C
+vtk/type/foamVtkPTraits.C
+
 coordSet/coordSet.C
 
 setWriters = sampledSetWriters
diff --git a/src/fileFormats/fire/FIRECore.C b/src/fileFormats/fire/FIRECore.C
new file mode 100644
index 0000000000000000000000000000000000000000..ab576116348cdc4d698b2966e16b4b1a7b69b051
--- /dev/null
+++ b/src/fileFormats/fire/FIRECore.C
@@ -0,0 +1,370 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2016 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 "FIRECore.H"
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+const Foam::NamedEnum<Foam::fileFormats::FIRECore::fileExt3d, 4>
+    Foam::fileFormats::FIRECore::file3dExtensions;
+
+namespace Foam
+{
+    template<>
+    const char* Foam::NamedEnum
+    <
+        Foam::fileFormats::FIRECore::fileExt3d,
+        4
+    >::names[] =
+    {
+        "fpma",
+        "fpmb",
+        "fpmaz",
+        "fpmbz"
+    };
+}
+
+
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+Foam::fileFormats::FIRECore::FIRECore()
+{}
+
+
+// * * * * * * * * * * *  Protected Member Functions * * * * * * * * * * * * //
+
+Foam::label Foam::fileFormats::FIRECore::readPoints
+(
+    ISstream& is,
+    pointField& points
+)
+{
+    const label n = getFireLabel(is);
+
+    if (n > 0)
+    {
+        points.setSize(n);
+
+        // read the coordinates
+        forAll(points, pointI)
+        {
+            points[pointI] = getFirePoint(is);
+        }
+    }
+    else
+    {
+        FatalErrorInFunction
+            << "no points in file " << is.name()
+            << abort(FatalError);
+    }
+
+    return n;
+}
+
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+Foam::fileName Foam::fileFormats::FIRECore::fireFileName
+(
+    const fileName& base,
+    const enum fileExt3d ext
+)
+{
+    return base + '.' + file3dExtensions[ext];
+}
+
+
+Foam::label Foam::fileFormats::FIRECore::getFireLabel(ISstream& is)
+{
+    if (is.format() == IOstream::BINARY)
+    {
+        fireInt_t ivalue;
+
+        is.stdStream().read
+        (
+            reinterpret_cast<char *>(&ivalue),
+            sizeof(ivalue)
+        );
+
+        return ivalue;
+    }
+    else
+    {
+        return readLabel(is);
+    }
+}
+
+
+Foam::point Foam::fileFormats::FIRECore::getFirePoint(ISstream& is)
+{
+    point pt;
+
+    if (is.format() == IOstream::BINARY)
+    {
+        fireReal_t coord[3];
+
+        is.stdStream().read
+        (
+            reinterpret_cast<char *>(&coord),
+            sizeof(coord)
+        );
+
+        pt.x() = coord[0];
+        pt.y() = coord[1];
+        pt.z() = coord[2];
+    }
+    else
+    {
+        pt.x() = readScalar(is);
+        pt.y() = readScalar(is);
+        pt.z() = readScalar(is);
+    }
+
+    return pt;
+}
+
+
+std::string Foam::fileFormats::FIRECore::getFireString(ISstream& is)
+{
+    std::string str;
+
+    if (is.format() == IOstream::BINARY)
+    {
+        long len;
+
+        is.stdStream().read
+        (
+            reinterpret_cast<char *>(&len),
+            sizeof(len)
+        );
+
+        str.resize(len);
+
+        for (std::size_t pos = 0; pos < str.size(); ++pos)
+        {
+            is.stdStream().read(&(str[pos]), sizeof(char));
+        }
+    }
+    else
+    {
+        const std::string whitespace(" \t\f\v\n\r");
+
+        string s;
+
+        // use a low-level getline, but this means we must handle
+        // blank lines manually
+        while (s.empty())
+        {
+            is.getLine(s);
+            if (!s.empty())
+            {
+                // remove prefix whitespace
+                size_t pos = s.find_first_not_of(whitespace);
+
+                if (pos != std::string::npos)
+                {
+                    s.erase(0, pos);
+
+                    // remove suffix whitespace
+                    pos = s.find_last_not_of(whitespace);
+                    if (pos != std::string::npos)
+                    {
+                        s.erase(pos + 1);
+                    }
+                }
+
+                if (pos == std::string::npos)
+                {
+                    s.clear();
+                }
+            }
+        }
+
+        str.swap(s);
+    }
+
+    return str;
+}
+
+
+void Foam::fileFormats::FIRECore::putFireLabel
+(
+    OSstream& os,
+    const label value
+)
+{
+    if (os.format() == Foam::IOstream::BINARY)
+    {
+        fireInt_t ivalue(value);
+
+        os.stdStream().write
+        (
+            reinterpret_cast<char const *>(&ivalue),
+            sizeof(ivalue)
+        );
+    }
+    else
+    {
+        os  << value;
+    }
+}
+
+
+void Foam::fileFormats::FIRECore::putFireLabels
+(
+    OSstream& os,
+    const labelUList& lst
+)
+{
+    if (os.format() == IOstream::BINARY)
+    {
+        fireInt_t ivalue(lst.size());
+
+        os.stdStream().write
+        (
+            reinterpret_cast<char const *>(&ivalue),
+            sizeof(ivalue)
+        );
+
+        forAll(lst, i)
+        {
+            ivalue = lst[i];
+
+            os.stdStream().write
+            (
+                reinterpret_cast<char const *>(&ivalue),
+                sizeof(ivalue)
+            );
+        }
+    }
+    else
+    {
+        os  << ' ' << lst.size();
+        forAll(lst, i)
+        {
+            os  << ' ' << lst[i];
+        }
+        os  << '\n';
+    }
+}
+
+
+void Foam::fileFormats::FIRECore::putFireLabels
+(
+    OSstream& os,
+    const label count,
+    const label start
+)
+{
+    if (os.format() == IOstream::BINARY)
+    {
+        fireInt_t ivalue(count);
+
+        os.stdStream().write
+        (
+            reinterpret_cast<char const *>(&ivalue),
+            sizeof(ivalue)
+        );
+
+        ivalue = start;
+        for (label i=0; i < count; ++i, ++ivalue)
+        {
+            os.stdStream().write
+            (
+                reinterpret_cast<char const *>(&ivalue),
+                sizeof(ivalue)
+            );
+        }
+    }
+    else
+    {
+        os  << ' ' << count;
+
+        label ivalue = start;
+        for (label i = 0; i < count; ++i, ++ivalue)
+        {
+            os  << ' ' << ivalue;
+        }
+        os  << '\n';
+    }
+}
+
+
+void Foam::fileFormats::FIRECore::putFirePoint
+(
+    OSstream& os,
+    const point& value
+)
+{
+    if (os.format() == IOstream::BINARY)
+    {
+        fireReal_t fvalue[3];
+        fvalue[0] = value.x();
+        fvalue[1] = value.y();
+        fvalue[2] = value.z();
+
+        os.stdStream().write
+        (
+            reinterpret_cast<char const *>(&fvalue),
+            sizeof(fvalue)
+        );
+    }
+    else
+    {
+        os  << ' '
+            << value.x() << ' '
+            << value.y() << ' '
+            << value.z() << '\n';
+    }
+}
+
+
+void Foam::fileFormats::FIRECore::putFireString
+(
+    OSstream& os,
+    const std::string& value
+)
+{
+    if (os.format() == IOstream::BINARY)
+    {
+        long len(value.size());
+
+        os.stdStream().write
+        (
+            reinterpret_cast<char const *>(&len),
+            sizeof(len)
+        );
+
+        os.stdStream().write(value.data(), len);
+    }
+    else
+    {
+        // output without surrounding quotes
+        os.stdStream() << value << '\n';
+    }
+}
+
+
+// ************************************************************************* //
diff --git a/src/fileFormats/fire/FIRECore.H b/src/fileFormats/fire/FIRECore.H
new file mode 100644
index 0000000000000000000000000000000000000000..3f9c654cc1ea1acdf0b5027a140d2014a3e44859
--- /dev/null
+++ b/src/fileFormats/fire/FIRECore.H
@@ -0,0 +1,179 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2016 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::fileFormats::FIRECore
+
+Description
+    Core routines used when reading/writing AVL/FIRE files.
+
+SourceFiles
+    FIRECore.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef FIRECore_H
+#define FIRECore_H
+
+#include "point.H"
+#include "string.H"
+#include "labelList.H"
+#include "pointField.H"
+#include "IOstreams.H"
+#include "NamedEnum.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+// forward declarations
+class polyMesh;
+
+namespace fileFormats
+{
+
+/*---------------------------------------------------------------------------*\
+                    Class fileFormats::FIRECore Declaration
+\*---------------------------------------------------------------------------*/
+
+class FIRECore
+{
+public:
+
+    // Public Data, Declarations
+
+        //- Selection Types
+        enum selectionType
+        {
+            cellSelection = 2,
+            faceSelection = 3
+        };
+
+        //- Shape-Type for FIRE (FLMA) files
+        enum shapeType
+        {
+            fireLine  = 1,
+            fireTri   = 2,
+            fireQuad  = 3,
+            fireTet   = 4,
+            fireHex   = 5,
+            firePyr   = 6,
+            firePrism = 8
+        };
+
+        //- Enumeration defining the file extensions for 3D types
+        enum fileExt3d
+        {
+            POLY_ASCII,
+            POLY_BINARY,
+            POLY_ASCII_COMPRESSED,
+            POLY_BINARY_COMPRESSED
+        };
+
+
+        //- Integer type (binary format)
+        typedef int32_t fireInt_t;
+
+        //- Float type (binary format)
+        typedef double  fireReal_t;
+
+protected:
+
+    // Protected Data
+
+        static const NamedEnum<fileExt3d, 4>  file3dExtensions;
+
+
+    // Protected Member Functions
+
+        //- Construct null
+        FIRECore();
+
+
+        //- Read points.
+        // This is the first thing to do when reading FPMA,FPMB,FLMA files.
+        // Return the number of points read.
+        //
+        // The file format is as follows:
+        // \verbatim
+        // NUMBER_OF_VERTICES
+        // x0 y0 z0 x1 y1 z1 ... xN-1 yN-1 zN-1
+        // \endverbatim
+        static label readPoints(ISstream&, pointField&);
+
+public:
+
+    // Public Member Functions
+
+        //- Resolve base file-name for the given file-type
+        static fileName fireFileName
+        (
+            const fileName& baseName,
+            const enum fileExt3d
+        );
+
+
+        //- Get an integer (ascii or binary)
+        static label getFireLabel(ISstream&);
+
+        //- Get an point x/y/z (ascii or binary)
+        static point getFirePoint(ISstream&);
+
+        //- Extract a string (ascii or binary)
+        static std::string getFireString(ISstream&);
+
+
+        //- Write an integer (ascii or binary)
+        static void putFireLabel(OSstream&, const label);
+
+        //- Write multiple integers (ascii or binary)
+        static void putFireLabels(OSstream&, const labelUList&);
+
+        //- Write an on-the-fly list of integers (ascii or binary)
+        static void putFireLabels
+        (
+            OSstream&,
+            const label count,
+            const label start
+        );
+
+
+        //- Write a point x/y/z (ascii or binary)
+        static void putFirePoint(OSstream&, const point&);
+
+        //- Write a string (ascii or binary)
+        static void putFireString(OSstream&, const std::string&);
+
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace fileFormats
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/fileFormats/fire/README b/src/fileFormats/fire/README
new file mode 100644
index 0000000000000000000000000000000000000000..1cf217d220506c415045a070a582653de5786cc8
--- /dev/null
+++ b/src/fileFormats/fire/README
@@ -0,0 +1,124 @@
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+                              AVL/FIRE fpma/fpmb
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+fpma/fpmb are polyhedral formats (ASCII and BINARY).
+The fpmaz/fpmbz are the same, but in compressed format.
+
+The fpma/fpmb format can exported directly from within the FIRE GUI.
+However, it is not recommended to import them directly.
+Instead use the utility fire_wm_convert_file, fire_wm_convert_mesh
+(or equivalents)
+are used to rewrite the fpma/fpmb into the internal FIRE format.
+
+
+~~~~~~
+FORMAT
+~~~~~~
+
+NUMBER_OF_VERTICES
+x0 y0 z0 x1 y1 z1 ... xN-1 yN-1 zN-1
+
+
+NUMBER_OF_FACES
+face-stream0
+...
+face-streamN-1
+
+
+NUMBER_OF_CELLS
+cell-face-stream0
+..
+cell-face-streamN-1
+
+
+NUMBER_OF_SELECTIONS
+selectionName[0]
+selectionType[0] (2 = cells, 3 = faces)
+numberOfIDs
+id0 .. indN-1
+<blank-line>
+
+
+
+The face-stream:
+    nvert vert0 .. vertN
+
+The cell-face-stream:
+    nface faceId0 .. faceIdN
+
+
+CAUTION:
+    The FIRE faces have inward-facing normals.
+    The OPENFOAM faces have outward-facing normals!
+
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+                                 AVL/FIRE flma
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The AVL/FIRE flma format is very simple, but can only be used for 1D/2D shapes
+and for 3D primitive shapes.
+
+it is fairly easy to write and to parse.
+For 3D polyhedra, the cell-types are non-obvious and not easily determined
+- the fpma/fpmb format is preferred for that.
+
+
+~~~~~~
+FORMAT
+~~~~~~
+NUMBER_OF_VERTICES
+x0 y0 z0 x1 y1 z1 ... xN-1 yN-1 zN-1
+
+NUMBER_OF_CELLS
+nv[0]
+verts[0]
+nv[N-1]
+...
+verts[N-1]
+<blank-line>
+NUMBER_OF_CELLS
+type0 type1 ... type2
+<blank-line>
+NUMBER_OF_SELECTIONS
+selectionName[0]
+selectionType[0] (2 = cells, 3 = faces)
+numberOfIDs
+id0 .. indN-1
+<blank-line>
+...
+selectionName[N-1]
+selectionType[N-1] (2 = cells, 3 = faces)
+numberOfIDs
+id0 .. indN-1
+<blank-line>
+
+
+-- NOTES (flma) --
+# vertex/cells are zero-based
+
+    cell-types:
+      1 = line
+      2 = tri
+      3 = quad
+      4 = tet
+      5 = hex
+      6 = pyr
+      8 = prism
+
+    +---------------+---------------+
+    |  shape        |  face-order   |
+    +---------------+---------------+
+    +  Tet(FIRE)    |  0 1 2 3      |
+    +  Tet(FOAM)    |  3 2 0 1      |
+    +---------------+---------------+
+    +  Hex(FIRE)    |  0 1 2 3 4 5  |
+    +  Hex(FOAM)    |  4 5 0 1 2 3  |
+    +---------------+---------------+
+    +  Pyr(FIRE)    |  0 1 2 3 4    |
+    +  Pyr(FOAM)    |  0 4 3 2 1    |
+    +---------------+---------------+
+    +  Prism(FIRE)  |  0 1 2 3 4    |
+    +  Prism(FOAM)  |  0 1 4 3 2    |
+    +---------------+---------------+
+
diff --git a/src/fileFormats/starcd/STARCDCore.C b/src/fileFormats/starcd/STARCDCore.C
index 5e1ef1fb08aba3ff745dda43e7e37ddf1aa9207e..612b9c30911fb2913314787a888cdcb0ee7ea1bf 100644
--- a/src/fileFormats/starcd/STARCDCore.C
+++ b/src/fileFormats/starcd/STARCDCore.C
@@ -3,7 +3,7 @@
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
     \\  /    A nd           | Copyright (C) 2011-2015 OpenFOAM Foundation
-     \\/     M anipulation  |
+     \\/     M anipulation  | Copyright (C) 2016 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -28,6 +28,72 @@ License
 #include "clock.H"
 #include "PackedBoolList.H"
 #include "IStringStream.H"
+#include "OSspecific.H"
+
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+const Foam::NamedEnum<Foam::fileFormats::STARCDCore::fileHeader, 3>
+    Foam::fileFormats::STARCDCore::fileHeaders_;
+
+const Foam::NamedEnum<Foam::fileFormats::STARCDCore::fileExt, 4>
+    Foam::fileFormats::STARCDCore::fileExtensions_;
+
+namespace Foam
+{
+    template<>
+    const char* Foam::NamedEnum
+    <
+        Foam::fileFormats::STARCDCore::fileHeader,
+        3
+    >::names[] =
+    {
+        "PROSTAR_CELL",
+        "PROSTAR_VERTEX",
+        "PROSTAR_BOUNDARY"
+    };
+
+    template<>
+    const char* Foam::NamedEnum
+    <
+        Foam::fileFormats::STARCDCore::fileExt,
+        4
+    >::names[] =
+    {
+        "cel",
+        "vrt",
+        "bnd",
+        "inp"
+    };
+}
+
+
+const char* const Foam::fileFormats::STARCDCore::defaultBoundaryName =
+    "Default_Boundary_Region";
+
+const char* const Foam::fileFormats::STARCDCore::defaultSolidBoundaryName =
+    "Default_Boundary_Solid";
+
+
+const Foam::Map<Foam::FixedList<int, 6>>
+Foam::fileFormats::STARCDCore::foamToStarFaceAddr =
+{
+    { starcdHex,   { 4, 5, 2, 3, 0, 1 } },
+    { starcdPrism, { 0, 1, 4, 5, 2, -1 } },
+    { starcdTet,   { 5, 4, 2, 0, -1, -1 } },
+    { starcdPyr,   { 0, 4, 3, 5, 2, -1 } }
+};
+
+
+const Foam::Map<Foam::FixedList<int, 6>>
+Foam::fileFormats::STARCDCore::starToFoamFaceAddr =
+{
+    { starcdHex,   { 4, 5, 2, 3, 0, 1 } },
+    { starcdPrism, { 0, 1, 4, -1, 2, 3 } },
+    { starcdTet,   { 3, -1, 2, -1, 1, 0 } },
+    { starcdPyr,   { 0, -1, 4, 2, 1, 3 } }
+};
+
 
 // * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
 
@@ -40,7 +106,7 @@ Foam::fileFormats::STARCDCore::STARCDCore()
 bool Foam::fileFormats::STARCDCore::readHeader
 (
     IFstream& is,
-    const word& signature
+    const enum fileHeader header
 )
 {
     if (!is.good())
@@ -49,22 +115,25 @@ bool Foam::fileFormats::STARCDCore::readHeader
             << abort(FatalError);
     }
 
-    word header;
+    word  magic;
     label majorVersion;
 
     string line;
 
     is.getLine(line);
-    IStringStream(line)() >> header;
+    IStringStream(line)() >> magic;
 
     is.getLine(line);
     IStringStream(line)() >> majorVersion;
 
     // add other checks ...
-    if (header != signature)
+    if (magic != fileHeaders_[header])
     {
-        Info<< "header mismatch " << signature << "  " << is.name()
+        Info<< "header mismatch " << fileHeaders_[header]
+            << "  " << is.name()
             << endl;
+
+        return false;
     }
 
     return true;
@@ -74,10 +143,10 @@ bool Foam::fileFormats::STARCDCore::readHeader
 void Foam::fileFormats::STARCDCore::writeHeader
 (
     Ostream& os,
-    const word& filetype
+    const enum fileHeader header
 )
 {
-    os  << "PROSTAR_" << filetype << nl
+    os  << fileHeaders_[header] << nl
         << 4000
         << " " << 0
         << " " << 0
@@ -92,13 +161,34 @@ void Foam::fileFormats::STARCDCore::writeHeader
 
 // * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
 
-bool Foam::fileFormats::STARCDCore::readPoints
+Foam::fileName Foam::fileFormats::STARCDCore::starFileName
+(
+    const fileName& base,
+    const enum fileExt ext
+)
+{
+    return base + '.' + fileExtensions_[ext];
+}
+
+
+void Foam::fileFormats::STARCDCore::removeFiles(const fileName& base)
+{
+    Foam::rm(starFileName(base, VRT_FILE));
+    Foam::rm(starFileName(base, CEL_FILE));
+    Foam::rm(starFileName(base, BND_FILE));
+    Foam::rm(starFileName(base, INP_FILE));
+}
+
+
+Foam::label Foam::fileFormats::STARCDCore::readPoints
 (
     IFstream& is,
     pointField& points,
     labelList& ids
 )
 {
+    label maxId = 0;
+
     if (!is.good())
     {
         FatalErrorInFunction
@@ -106,8 +196,7 @@ bool Foam::fileFormats::STARCDCore::readPoints
             << exit(FatalError);
     }
 
-    readHeader(is, "PROSTAR_VERTEX");
-
+    readHeader(is, HEADER_VRT);
 
     // reuse memory if possible
     DynamicList<point> dynPoints(points.xfer());
@@ -116,31 +205,35 @@ bool Foam::fileFormats::STARCDCore::readPoints
     dynPoints.clear();
     dynPointId.clear();
 
-    label lineLabel;
-    while ((is >> lineLabel).good())
     {
+        label lineLabel;
         scalar x, y, z;
 
-        is >> x >> y >> z;
+        while ((is >> lineLabel).good())
+        {
+            maxId = max(maxId, lineLabel);
+            is >> x >> y >> z;
 
-        dynPoints.append(point(x, y, z));
-        dynPointId.append(lineLabel);
+            dynPoints.append(point(x, y, z));
+            dynPointId.append(lineLabel);
+        }
     }
 
     points.transfer(dynPoints);
     ids.transfer(dynPointId);
 
-    return true;
+    return maxId;
 }
 
 
 void Foam::fileFormats::STARCDCore::writePoints
 (
     Ostream& os,
-    const pointField& pointLst
+    const pointField& points,
+    const double scaleFactor
 )
 {
-    writeHeader(os, "VERTEX");
+    writeHeader(os, HEADER_VRT);
 
     // Set the precision of the points data to 10
     os.precision(10);
@@ -148,18 +241,17 @@ void Foam::fileFormats::STARCDCore::writePoints
     // force decimal point for Fortran input
     os.setf(std::ios::showpoint);
 
-    forAll(pointLst, ptI)
+    forAll(points, ptI)
     {
+        // convert [m] -> [mm] etc
         os
-            << ptI + 1 << " "
-            << pointLst[ptI].x() << " "
-            << pointLst[ptI].y() << " "
-            << pointLst[ptI].z() << nl;
+            << ptI + 1 << ' '
+            << scaleFactor * points[ptI].x() << ' '
+            << scaleFactor * points[ptI].y() << ' '
+            << scaleFactor * points[ptI].z() << '\n';
     }
     os.flush();
 }
 
 
-
-
 // ************************************************************************* //
diff --git a/src/fileFormats/starcd/STARCDCore.H b/src/fileFormats/starcd/STARCDCore.H
index 25920cdda26ebebb9a5e2cd3d141c0c1519d5e7d..cae3d1e7c0de7a8e7a6a3e6d305f49642f354cff 100644
--- a/src/fileFormats/starcd/STARCDCore.H
+++ b/src/fileFormats/starcd/STARCDCore.H
@@ -3,7 +3,7 @@
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
     \\  /    A nd           | Copyright (C) 2011 OpenFOAM Foundation
-     \\/     M anipulation  |
+     \\/     M anipulation  | Copyright (C) 2016 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -36,7 +36,10 @@ SourceFiles
 #define STARCDCore_H
 
 #include "IFstream.H"
+#include "NamedEnum.H"
 #include "pointField.H"
+#include "Map.H"
+#include "FixedList.H"
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
@@ -52,20 +55,29 @@ namespace fileFormats
 
 class STARCDCore
 {
-protected:
-
-    // Protected Member Functions
-
-        //- Read header
-        static bool readHeader(IFstream&, const word& fileSignature);
+public:
 
-        //- Write header for fileType (CELL|VERTEX|BOUNDARY)
-        static void writeHeader(Ostream&, const word& fileType);
+    // Public Data, Declarations
 
+        //- Enumeration defining the file headers
+        enum fileHeader
+        {
+            HEADER_CEL,
+            HEADER_VRT,
+            HEADER_BND
+        };
 
-protected:
+        //- Enumeration defining the file extensions
+        enum fileExt
+        {
+            CEL_FILE,
+            VRT_FILE,
+            BND_FILE,
+            INP_FILE
+        };
 
-        enum cellType
+        //- Basic material type for STARCD/PROSTAR files
+        enum matlType
         {
             starcdFluidType   = 1,
             starcdSolidType   = 2,
@@ -75,6 +87,7 @@ protected:
             starcdPointType   = 6
         };
 
+        //- Shape-Type for STARCD/PROSTAR files
         enum shapeType
         {
             starcdPoint = 1,
@@ -88,11 +101,61 @@ protected:
         };
 
 
+        //- The name for default (unassigned) boundaries
+        static const char* const defaultBoundaryName;
+
+        //- The name we have chosen for default (unassigned) solid boundaries.
+        //  Slightly distinguished from the regular default name.
+        static const char* const defaultSolidBoundaryName;
+
+
+private:
+
+    // Private Data
+
+        static const NamedEnum<fileHeader, 3> fileHeaders_;
+        static const NamedEnum<fileExt, 4>    fileExtensions_;
+
+
+protected:
+
+    // Protected Member Functions
+
+        //- Face addressing from pro-STAR faces to OpenFOAM faces.
+        //  For hex, prism, tet, pyr primitive shapes.
+        static const Map<FixedList<int, 6>> starToFoamFaceAddr;
+
+        //- Face addressing from OpenFOAM faces to pro-STAR faces.
+        //  For hex, prism, tet, pyr primitive shapes.
+        static const Map<FixedList<int, 6>> foamToStarFaceAddr;
+
+
+    // Constructors
+
+        //- Construct null
+        STARCDCore();
+
+        //- Read header and check signature PROSTAR_(CELL|VERTEX|BOUNDARY)
+        static bool readHeader(IFstream&, const enum fileHeader);
+
+        //- Write header for fileType (CELL|VERTEX|BOUNDARY)
+        static void writeHeader(Ostream&, const enum fileHeader);
+
+
 public:
 
     // Public Member Functions
 
-        //- Read points from a (.vrt) file
+        //- Resolve base file-name for the given file-type
+        static fileName starFileName(const fileName& baseName, const enum fileExt);
+
+
+        //- Remove existing PROSTAR files for the given base file-name
+        static void removeFiles(const fileName& baseName);
+
+
+        //- Read points from a (.vrt) file, return the max prostar id used.
+        //
         // The file format is as follows:
         // \verbatim
         // Line 1:
@@ -104,16 +167,20 @@ public:
         // Body:
         //   {vertexId}  {x}  {y}  {z}  newline
         // \endverbatim
-        static bool readPoints(IFstream&, pointField&, labelList& ids);
-
-        //- Write header and points to (.vrt) file
-        static void writePoints(Ostream&, const pointField&);
-
-
-    // Constructors
-
-        //- Construct null
-        STARCDCore();
+        static label readPoints
+        (
+            IFstream&,
+            pointField&,
+            labelList& ids
+        );
+
+        //- Write header and points to (.vrt) file, optionally with scaling
+        static void writePoints
+        (
+            Ostream&,
+            const pointField&,
+            const scalar scaleFactor = 1.0
+        );
 
 };
 
diff --git a/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK/vtkMesh.C b/src/fileFormats/vtk/foamVtkCore.C
similarity index 53%
rename from applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK/vtkMesh.C
rename to src/fileFormats/vtk/foamVtkCore.C
index d13a96df3d4753375cdbfc514c4f6571ca82fa2c..2450c02b7b2d0c1c7acc86c9a1c7f6395410bbc0 100644
--- a/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK/vtkMesh.C
+++ b/src/fileFormats/vtk/foamVtkCore.C
@@ -2,7 +2,7 @@
   =========                 |
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2011-2016 OpenFOAM Foundation
+    \\  /    A nd           | Copyright (C) 2016 OpenCFD Ltd.
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
 License
@@ -23,60 +23,32 @@ License
 
 \*---------------------------------------------------------------------------*/
 
-#include "vtkMesh.H"
-#include "fvMeshSubset.H"
-#include "Time.H"
-#include "cellSet.H"
-
-// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
-
-Foam::vtkMesh::vtkMesh
-(
-    fvMesh& baseMesh,
-    const word& setName
-)
-:
-    baseMesh_(baseMesh),
-    subsetter_(baseMesh),
-    setName_(setName)
-{
-    if (setName.size())
-    {
-        // Read cellSet using whole mesh
-        cellSet currentSet(baseMesh_, setName_);
-
-        // Set current subset
-        subsetter_.setLargeCellSubset(currentSet);
-    }
-}
+#include "foamVtkCore.H"
 
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
 
-// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
 
-Foam::polyMesh::readUpdateState Foam::vtkMesh::readUpdate()
-{
-    polyMesh::readUpdateState meshState = baseMesh_.readUpdate();
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
 
-    if (meshState != polyMesh::UNCHANGED)
-    {
-        // Note: since fvMeshSubset has no movePoints() functionality,
-        // reconstruct the subset even if only movement.
+Foam::fileFormats::foamVtkCore::foamVtkCore()
+{}
 
-        topoPtr_.clear();
 
-        if (setName_.size())
-        {
-            Info<< "Subsetting mesh based on cellSet " << setName_ << endl;
+// * * * * * * * * * * *  Protected Member Functions * * * * * * * * * * * * //
 
-            // Read cellSet using whole mesh
-            cellSet currentSet(baseMesh_, setName_);
 
-            subsetter_.setLargeCellSubset(currentSet);
-        }
-    }
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
 
-    return meshState;
+/*
+Foam::fileName Foam::fileFormats::foamVtkCore::vtkFileName
+(
+    const fileName& base,
+    const enum fileExt ext
+)
+{
+    return base + '.' + fileExtensions_[ext];
 }
+*/
 
 
 // ************************************************************************* //
diff --git a/src/fileFormats/vtk/foamVtkCore.H b/src/fileFormats/vtk/foamVtkCore.H
new file mode 100644
index 0000000000000000000000000000000000000000..adcdc00871a5300fb0e5ddb4e12345473d40b1f8
--- /dev/null
+++ b/src/fileFormats/vtk/foamVtkCore.H
@@ -0,0 +1,109 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2016 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::foamVtkCore
+
+Description
+    Core routines for dealing with VTK files.
+
+SourceFiles
+    foamVtkCore.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef foamVtkCore_H
+#define foamVtkCore_H
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+namespace fileFormats
+{
+
+/*---------------------------------------------------------------------------*\
+                  Class fileFormats::foamVtkCore Declaration
+\*---------------------------------------------------------------------------*/
+
+class foamVtkCore
+{
+public:
+
+    // Public Data, Declarations
+
+        //- The context when outputting a VTK file (XML or legacy).
+        enum OutputContext
+        {
+            INLINE,     //<! Generate header and inline data
+            HEADER,     //<! Generate header only
+            APPEND      //<! Generate append-data
+        };
+
+
+        //- Equivalent to enumeration in "vtkCellType.h"
+        enum vtkTypes
+        {
+            VTK_EMPTY_CELL       = 0,
+            VTK_VERTEX           = 1,
+            VTK_POLY_VERTEX      = 2,
+            VTK_LINE             = 3,
+            VTK_POLY_LINE        = 4,
+            VTK_TRIANGLE         = 5,
+            VTK_TRIANGLE_STRIP   = 6,
+            VTK_POLYGON          = 7,
+            VTK_PIXEL            = 8,
+            VTK_QUAD             = 9,
+            VTK_TETRA            = 10,
+            VTK_VOXEL            = 11,
+            VTK_HEXAHEDRON       = 12,
+            VTK_WEDGE            = 13,
+            VTK_PYRAMID          = 14,
+            VTK_PENTAGONAL_PRISM = 15,
+            VTK_HEXAGONAL_PRISM  = 16,
+            VTK_POLYHEDRON       = 42
+        };
+
+
+protected:
+
+    // Constructors
+
+        //- Construct null
+        foamVtkCore();
+
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace fileFormats
+
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/fileFormats/vtk/format/foamVtkAppendBase64Formatter.C b/src/fileFormats/vtk/format/foamVtkAppendBase64Formatter.C
new file mode 100644
index 0000000000000000000000000000000000000000..ee8b84200f0aaa5b8d333d2e577b44c2d5289be3
--- /dev/null
+++ b/src/fileFormats/vtk/format/foamVtkAppendBase64Formatter.C
@@ -0,0 +1,65 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2016 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 "foamVtkAppendBase64Formatter.H"
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+const char* Foam::foamVtkAppendBase64Formatter::name_     = "append";
+const char* Foam::foamVtkAppendBase64Formatter::encoding_ = "base64";
+
+
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+Foam::foamVtkAppendBase64Formatter::foamVtkAppendBase64Formatter
+(
+    std::ostream& os
+)
+:
+    foamVtkBase64Formatter(os)
+{}
+
+
+// * * * * * * * * * * * * * * * * Destructor  * * * * * * * * * * * * * * * //
+
+Foam::foamVtkAppendBase64Formatter::~foamVtkAppendBase64Formatter()
+{}
+
+
+// * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * * //
+
+const char* Foam::foamVtkAppendBase64Formatter::name() const
+{
+    return name_;
+}
+
+
+const char* Foam::foamVtkAppendBase64Formatter::encoding() const
+{
+    return encoding_;
+}
+
+
+// ************************************************************************* //
diff --git a/src/fileFormats/vtk/format/foamVtkAppendBase64Formatter.H b/src/fileFormats/vtk/format/foamVtkAppendBase64Formatter.H
new file mode 100644
index 0000000000000000000000000000000000000000..b7239ee7c66ffc637441b4504eb69a9a74f52a24
--- /dev/null
+++ b/src/fileFormats/vtk/format/foamVtkAppendBase64Formatter.H
@@ -0,0 +1,99 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2016 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
+    foamVtkAppendBase64Formatter
+
+Description
+    Appended base-64 encoded binary output.
+    Uses an output filter layer to write base-64 encoded content.
+
+SourceFiles
+    foamVtkAppendBase64Formatter.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef foamVtkAppendBase64Formatter_H
+#define foamVtkAppendBase64Formatter_H
+
+#include "foamVtkBase64Formatter.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+/*---------------------------------------------------------------------------*\
+                Class foamVtkAppendBase64Formatter Declaration
+\*---------------------------------------------------------------------------*/
+
+class foamVtkAppendBase64Formatter
+:
+    public foamVtkBase64Formatter
+{
+    // Private Data Members
+
+        static const char* name_;
+        static const char* encoding_;
+
+
+    // Private Member Functions
+
+    //- Disallow default bitwise copy construct
+    foamVtkAppendBase64Formatter(const foamVtkAppendBase64Formatter&) = delete;
+
+    //- Disallow default bitwise assignment
+    void operator=(const foamVtkAppendBase64Formatter&) = delete;
+
+
+public:
+
+    // Constructors
+
+        //- Construct and attach to an output stream
+        foamVtkAppendBase64Formatter(std::ostream&);
+
+
+    //- Destructor
+    virtual ~foamVtkAppendBase64Formatter();
+
+
+    // Member Functions
+
+        //- Output name for XML type ("append")
+        virtual const char* name() const;
+
+        //- Name for the XML append encoding ("base64").
+        virtual const char* encoding() const;
+
+};
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/fileFormats/vtk/format/foamVtkAppendRawFormatter.C b/src/fileFormats/vtk/format/foamVtkAppendRawFormatter.C
new file mode 100644
index 0000000000000000000000000000000000000000..9933970495ea2e8a920659bf6f61edd680933914
--- /dev/null
+++ b/src/fileFormats/vtk/format/foamVtkAppendRawFormatter.C
@@ -0,0 +1,112 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2016 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 "foamVtkAppendRawFormatter.H"
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+const char* Foam::foamVtkAppendRawFormatter::name_     = "append";
+const char* Foam::foamVtkAppendRawFormatter::encoding_ = "raw";
+
+
+// * * * * * * * * * * * * Protected Member Functions  * * * * * * * * * * * //
+
+void Foam::foamVtkAppendRawFormatter::write
+(
+    const char* s,
+    std::streamsize n
+)
+{
+    os().write(s, n);
+}
+
+
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+Foam::foamVtkAppendRawFormatter::foamVtkAppendRawFormatter(std::ostream& os)
+:
+    foamVtkFormatter(os)
+{}
+
+
+// * * * * * * * * * * * * * * * * Destructor  * * * * * * * * * * * * * * * //
+
+Foam::foamVtkAppendRawFormatter::~foamVtkAppendRawFormatter()
+{}
+
+
+// * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * * //
+
+const char* Foam::foamVtkAppendRawFormatter::name() const
+{
+    return name_;
+}
+
+
+const char* Foam::foamVtkAppendRawFormatter::encoding() const
+{
+    return encoding_;
+}
+
+
+void Foam::foamVtkAppendRawFormatter::writeSize(const uint64_t val)
+{
+    write(reinterpret_cast<const char*>(&val), sizeof(uint64_t));
+}
+
+
+void Foam::foamVtkAppendRawFormatter::write(const uint8_t val)
+{
+    write(reinterpret_cast<const char*>(&val), sizeof(uint8_t));
+}
+
+
+void Foam::foamVtkAppendRawFormatter::write(const label val)
+{
+    // std::cerr<<"label is:" << sizeof(val) << '\n';
+    write(reinterpret_cast<const char*>(&val), sizeof(label));
+}
+
+
+void Foam::foamVtkAppendRawFormatter::write(const float val)
+{
+    // std::cerr<<"float is:" << sizeof(val) << '\n';
+    write(reinterpret_cast<const char*>(&val), sizeof(float));
+}
+
+
+void Foam::foamVtkAppendRawFormatter::write(const double val)
+{
+    // std::cerr<<"write double as float:" << val << '\n';
+    float copy(val);
+    write(copy);
+}
+
+
+void Foam::foamVtkAppendRawFormatter::flush()
+{}
+
+
+// ************************************************************************* //
diff --git a/src/fileFormats/vtk/format/foamVtkAppendRawFormatter.H b/src/fileFormats/vtk/format/foamVtkAppendRawFormatter.H
new file mode 100644
index 0000000000000000000000000000000000000000..abc3db09293f7baccc6c1796ef4ba3ffe40d2e1d
--- /dev/null
+++ b/src/fileFormats/vtk/format/foamVtkAppendRawFormatter.H
@@ -0,0 +1,115 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2016 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
+    foamVtkAppendRawFormatter
+
+Description
+    Appended raw binary output.
+
+SourceFiles
+    foamVtkAppendRawFormatter.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef foamVtkAppendRawFormatter_H
+#define foamVtkAppendRawFormatter_H
+
+#include "foamVtkFormatter.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+/*---------------------------------------------------------------------------*\
+                  Class foamVtkAppendRawFormatter Declaration
+\*---------------------------------------------------------------------------*/
+
+class foamVtkAppendRawFormatter
+:
+    public foamVtkFormatter
+{
+    // Private Data Members
+
+        static const char* name_;
+        static const char* encoding_;
+
+    // Private Member Functions
+
+        //- Disallow default bitwise copy construct
+        foamVtkAppendRawFormatter(const foamVtkAppendRawFormatter&) = delete;
+
+        //- Disallow default bitwise assignment
+        void operator=(const foamVtkAppendRawFormatter&) = delete;
+
+
+protected:
+
+    // Protected Member Functions
+
+        //- Write
+        void write(const char* s, std::streamsize n);
+
+
+public:
+
+    // Constructors
+
+        //- Construct and attach to an output stream
+        foamVtkAppendRawFormatter(std::ostream&);
+
+
+    //- Destructor
+    virtual ~foamVtkAppendRawFormatter();
+
+
+    // Member Functions
+
+        //- Output name for XML type ("append")
+        virtual const char* name() const;
+
+        //- Output name for append encoding type ("raw")
+        virtual const char* encoding() const;
+
+
+        //- Write leading size for binary output
+        virtual void writeSize(const uint64_t);
+
+        virtual void write(const uint8_t);
+        virtual void write(const label);
+        virtual void write(const float);
+        virtual void write(const double);
+        virtual void flush();
+
+};
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/fileFormats/vtk/format/foamVtkAsciiFormatter.C b/src/fileFormats/vtk/format/foamVtkAsciiFormatter.C
new file mode 100644
index 0000000000000000000000000000000000000000..6a78f73f53cbf19db657bda92e828c282329d4ab
--- /dev/null
+++ b/src/fileFormats/vtk/format/foamVtkAsciiFormatter.C
@@ -0,0 +1,136 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2016 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 "foamVtkAsciiFormatter.H"
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+const char* Foam::foamVtkAsciiFormatter::name_ = "ascii";
+
+
+// * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
+
+inline void Foam::foamVtkAsciiFormatter::next()
+{
+    if (pos_ == 6)
+    {
+        os()<< '\n';
+        pos_ = 0;
+    }
+    else if (pos_)
+    {
+        os()<< ' ';
+    }
+    ++pos_;
+}
+
+
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+Foam::foamVtkAsciiFormatter::foamVtkAsciiFormatter(std::ostream& os)
+:
+    foamVtkFormatter(os),
+    pos_(0)
+{}
+
+
+Foam::foamVtkAsciiFormatter::foamVtkAsciiFormatter
+(
+    std::ostream& os,
+    unsigned precision
+)
+:
+    foamVtkFormatter(os),
+    pos_(0)
+{
+    os.precision(precision);
+}
+
+
+// * * * * * * * * * * * * * * * * Destructor  * * * * * * * * * * * * * * * //
+
+Foam::foamVtkAsciiFormatter::~foamVtkAsciiFormatter()
+{
+    flush();
+}
+
+
+// * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * * //
+
+const char* Foam::foamVtkAsciiFormatter::name() const
+{
+    return name_;
+}
+
+
+const char* Foam::foamVtkAsciiFormatter::encoding() const
+{
+    return name_;
+}
+
+
+void Foam::foamVtkAsciiFormatter::writeSize(const uint64_t)
+{/*nop*/}
+
+
+void Foam::foamVtkAsciiFormatter::write(const uint8_t val)
+{
+    next();
+    os()<< int(val);
+}
+
+
+void Foam::foamVtkAsciiFormatter::write(const label val)
+{
+    next();
+    os()<< val;
+}
+
+
+void Foam::foamVtkAsciiFormatter::write(const float val)
+{
+    next();
+    os()<< val;
+}
+
+
+void Foam::foamVtkAsciiFormatter::write(const double val)
+{
+    next();
+    os()<< float(val);
+}
+
+
+void Foam::foamVtkAsciiFormatter::flush()
+{
+    if (pos_)
+    {
+        os()<< '\n';
+    }
+    pos_ = 0;
+}
+
+
+// ************************************************************************* //
diff --git a/src/fileFormats/vtk/format/foamVtkAsciiFormatter.H b/src/fileFormats/vtk/format/foamVtkAsciiFormatter.H
new file mode 100644
index 0000000000000000000000000000000000000000..009d776f89f1d85cf3cd5335e2feb5cde8695047
--- /dev/null
+++ b/src/fileFormats/vtk/format/foamVtkAsciiFormatter.H
@@ -0,0 +1,120 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2016 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
+    foamVtkAsciiFormatter
+
+Description
+    Inline ASCII binary output.
+    Adds spaces between entries and a newline every 6 items
+    (for consistency with what VTK itself outputs).
+
+SourceFiles
+    foamVtkAsciiFormatter.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef foamVtkAsciiFormatter_H
+#define foamVtkAsciiFormatter_H
+
+#include "foamVtkFormatter.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+/*---------------------------------------------------------------------------*\
+                    Class foamVtkAsciiFormatter Declaration
+\*---------------------------------------------------------------------------*/
+
+class foamVtkAsciiFormatter
+:
+    public foamVtkFormatter
+{
+    // Private Data Members
+
+        static const char* name_;
+
+        //- Track the current output position
+        unsigned short pos_;
+
+
+    // Private Member Functions
+
+        //- Advance to next position, adding space or newline as required
+        inline void next();
+
+
+        //- Disallow default bitwise copy construct
+        foamVtkAsciiFormatter(const foamVtkAsciiFormatter&) = delete;
+
+        //- Disallow default bitwise assignment
+        void operator=(const foamVtkAsciiFormatter&) = delete;
+
+
+public:
+
+    // Constructors
+
+        //- Construct and attach to an output stream, use default precision
+        foamVtkAsciiFormatter(std::ostream&);
+
+        //- Construct and attach to an output stream, use specified precision
+        foamVtkAsciiFormatter(std::ostream&, unsigned precision);
+
+
+    //- Destructor
+    virtual ~foamVtkAsciiFormatter();
+
+
+    // Member Functions
+
+        //- Name for the XML output type ("ascii")
+        //  The legacy output type is an uppercase version of this.
+        virtual const char* name() const;
+
+        //- Name for the XML append encoding - unused.
+        //  Currently simply "ASCII", but this should not be relied upon.
+        virtual const char* encoding() const;
+
+
+        //- Write leading size - this is a no-op for ascii output
+        virtual void writeSize(const uint64_t);
+
+        virtual void write(const uint8_t);
+        virtual void write(const label);
+        virtual void write(const float);
+        virtual void write(const double);
+        virtual void flush();
+};
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/fileFormats/vtk/format/foamVtkBase64Formatter.C b/src/fileFormats/vtk/format/foamVtkBase64Formatter.C
new file mode 100644
index 0000000000000000000000000000000000000000..a11d97bd49e573d318700e6e56f66802364daf51
--- /dev/null
+++ b/src/fileFormats/vtk/format/foamVtkBase64Formatter.C
@@ -0,0 +1,120 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2016 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 "foamVtkBase64Formatter.H"
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+const char* Foam::foamVtkBase64Formatter::name_     = "binary";
+const char* Foam::foamVtkBase64Formatter::encoding_ = "base64";
+
+
+// * * * * * * * * * * * * Protected Member Functions  * * * * * * * * * * * //
+
+void Foam::foamVtkBase64Formatter::write
+(
+    const char* s,
+    std::streamsize n
+)
+{
+    base64Layer::write(s, n);
+}
+
+
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+Foam::foamVtkBase64Formatter::foamVtkBase64Formatter(std::ostream& os)
+:
+    foamVtkFormatter(os),
+    base64Layer(os)
+{}
+
+
+// * * * * * * * * * * * * * * * * Destructor  * * * * * * * * * * * * * * * //
+
+Foam::foamVtkBase64Formatter::~foamVtkBase64Formatter()
+{
+    flush();
+}
+
+
+// * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * * //
+
+const char* Foam::foamVtkBase64Formatter::name() const
+{
+    return name_;
+}
+
+
+const char* Foam::foamVtkBase64Formatter::encoding() const
+{
+    return encoding_;
+}
+
+
+void Foam::foamVtkBase64Formatter::writeSize(const uint64_t val)
+{
+    write(reinterpret_cast<const char*>(&val), sizeof(uint64_t));
+}
+
+
+void Foam::foamVtkBase64Formatter::write(const uint8_t val)
+{
+    base64Layer::add(val);
+}
+
+
+void Foam::foamVtkBase64Formatter::write(const label val)
+{
+    // std::cerr<<"label is:" << sizeof(val) << '\n';
+    write(reinterpret_cast<const char*>(&val), sizeof(label));
+}
+
+
+void Foam::foamVtkBase64Formatter::write(const float val)
+{
+    // std::cerr<<"float is:" << sizeof(val) << '\n';
+    write(reinterpret_cast<const char*>(&val), sizeof(float));
+}
+
+
+void Foam::foamVtkBase64Formatter::write(const double val)
+{
+    // std::cerr<<"write double as float:" << val << '\n';
+    float copy(val);
+    write(copy);
+}
+
+
+void Foam::foamVtkBase64Formatter::flush()
+{
+    if (base64Layer::close())
+    {
+        os().put('\n');
+    }
+}
+
+
+// ************************************************************************* //
diff --git a/src/fileFormats/vtk/format/foamVtkBase64Formatter.H b/src/fileFormats/vtk/format/foamVtkBase64Formatter.H
new file mode 100644
index 0000000000000000000000000000000000000000..f01bec3d9aa343159749834e47cc0f7ff14e62fe
--- /dev/null
+++ b/src/fileFormats/vtk/format/foamVtkBase64Formatter.H
@@ -0,0 +1,116 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2016 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
+    foamVtkBase64Formatter
+
+Description
+    Inline base-64 encoded binary output.
+    Uses an output filter layer to write base-64 encoded content.
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef foamVtkBase64Formatter_H
+#define foamVtkBase64Formatter_H
+
+#include "foamVtkFormatter.H"
+#include "base64Layer.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+/*---------------------------------------------------------------------------*\
+                   Class foamVtkBase64Formatter Declaration
+\*---------------------------------------------------------------------------*/
+
+class foamVtkBase64Formatter
+:
+    public foamVtkFormatter,
+    private base64Layer
+{
+    // Private Data Members
+
+        static const char* name_;
+        static const char* encoding_;
+
+
+    // Private Member Functions
+
+        //- Disallow default bitwise copy construct
+        foamVtkBase64Formatter(const foamVtkBase64Formatter&) = delete;
+
+        //- Disallow default bitwise assignment
+        void operator=(const foamVtkBase64Formatter&) = delete;
+
+protected:
+
+    // Protected Member Functions
+
+        //- Write
+        void write(const char* s, std::streamsize n);
+
+
+public:
+
+    // Constructors
+
+        //- Construct and attach to an output stream
+        foamVtkBase64Formatter(std::ostream&);
+
+
+    //- Destructor
+    virtual ~foamVtkBase64Formatter();
+
+
+    // Member Functions
+
+        //- Name for the XML output type ("binary")
+        //  The lowercase version of the Legacy output type.
+        virtual const char* name() const;
+
+        //- Name for the XML append encoding.
+        virtual const char* encoding() const;
+
+
+        //- Write leading size for binary output
+        virtual void writeSize(const uint64_t);
+
+        virtual void write(const uint8_t);
+        virtual void write(const label);
+        virtual void write(const float);
+        virtual void write(const double);
+        virtual void flush();
+
+};
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/fileFormats/vtk/format/foamVtkFormatter.C b/src/fileFormats/vtk/format/foamVtkFormatter.C
new file mode 100644
index 0000000000000000000000000000000000000000..b204ad22795410165c657f553ea44b8b63b52cff
--- /dev/null
+++ b/src/fileFormats/vtk/format/foamVtkFormatter.C
@@ -0,0 +1,315 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2016 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 "foamVtkFormatter.H"
+#include "foamVtkPTraits.H"
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+const char* const Foam::foamVtkFormatter::byteOrder
+    = Foam::foamVtkPTraits<endian>::typeName;
+
+const char* const Foam::foamVtkFormatter::headerType =
+    Foam::foamVtkPTraits<uint64_t>::typeName;
+
+
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+Foam::foamVtkFormatter::foamVtkFormatter(std::ostream& os)
+:
+    os_(os),
+    xmlTags_(),
+    inTag_(false)
+{}
+
+
+// * * * * * * * * * * * * * * * * Destructor  * * * * * * * * * * * * * * * //
+
+Foam::foamVtkFormatter::~foamVtkFormatter()
+{}
+
+
+// * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * * //
+
+void Foam::foamVtkFormatter::indent()
+{
+    label n = xmlTags_.size() * 2;
+    while (n--)
+    {
+        os_ << ' ';
+    }
+}
+
+
+Foam::foamVtkFormatter&
+Foam::foamVtkFormatter::xmlHeader()
+{
+    if (inTag_)
+    {
+        WarningInFunction
+            << "xml header, but already within a tag!"
+            << endl;
+    }
+
+    os_ << "<?xml version='1.0'?>" << nl;
+
+    return *this;
+}
+
+
+Foam::foamVtkFormatter&
+Foam::foamVtkFormatter::comment(const std::string& text)
+{
+    if (inTag_)
+    {
+        WarningInFunction
+            << "adding xml comment inside a tag??"
+            << endl;
+    }
+
+    indent();
+    os_ << "<!-- " << text << " -->" << nl;
+
+    return *this;
+}
+
+
+Foam::foamVtkFormatter&
+Foam::foamVtkFormatter::openTag(const word& tag)
+{
+    if (inTag_)
+    {
+        WarningInFunction
+            << "open XML tag '" << tag << "', but already within a tag!"
+            << endl;
+    }
+
+    indent();
+    os_ << '<' << tag;
+
+    xmlTags_.push(tag);
+    inTag_ = true;
+
+    return *this;
+}
+
+
+Foam::foamVtkFormatter&
+Foam::foamVtkFormatter::closeTag(bool isEmpty)
+{
+    if (!inTag_)
+    {
+        WarningInFunction
+            << "close XML tag, but not within a tag!"
+            << endl;
+    }
+
+    if (isEmpty)
+    {
+        // eg, <tag ... />
+        xmlTags_.pop();
+        os_ << " /";
+    }
+    os_ << '>' << nl;
+
+    inTag_ = false;
+
+    return *this;
+}
+
+
+Foam::foamVtkFormatter&
+Foam::foamVtkFormatter::tag(const word& tag)
+{
+    openTag(tag);
+    closeTag();
+
+    return *this;
+}
+
+
+
+Foam::foamVtkFormatter&
+Foam::foamVtkFormatter::endTag(const word& tag)
+{
+    const word curr = xmlTags_.pop();
+    indent();
+
+    if (inTag_)
+    {
+        WarningInFunction
+            << "adding XML endTag '" << curr
+            << "' but already in another tag!"
+            << endl;
+    }
+
+    // verify inTag_
+    if (!tag.empty() && tag != curr)
+    {
+        WarningInFunction
+            << "expected to end xml-tag '" << tag
+            << "' but found '" << curr << "' instead"
+            << endl;
+    }
+
+    os_  << "</" << curr << '>' << nl;
+
+    inTag_ = false;
+
+    return *this;
+}
+
+
+
+Foam::foamVtkFormatter&
+Foam::foamVtkFormatter::xmlAttr
+(
+    const word& k,
+    const std::string& v,
+    const char quote
+)
+{
+    if (!inTag_)
+    {
+        WarningInFunction
+            << "xml attribute '" << k << "' but not within a tag!"
+            << endl;
+    }
+
+    os_ << ' ' << k << '=' << quote << v.c_str() << quote;
+
+    return *this;
+}
+
+
+Foam::foamVtkFormatter&
+Foam::foamVtkFormatter::xmlAttr
+(
+    const word& k,
+    const label v,
+    const char quote
+)
+{
+    if (!inTag_)
+    {
+        WarningInFunction
+            << "xml attribute '" << k << "' but not within a tag!"
+            << endl;
+    }
+
+    os_ << ' ' << k << '=' << quote << v << quote;
+
+    return *this;
+}
+
+
+Foam::foamVtkFormatter&
+Foam::foamVtkFormatter::xmlAttr
+(
+    const word& k,
+    const uint64_t v,
+    const char quote
+)
+{
+    if (!inTag_)
+    {
+        WarningInFunction
+            << "xml attribute '" << k << "' but not within a tag!"
+            << endl;
+    }
+
+    os_ << ' ' << k << '=' << quote << v << quote;
+
+    return *this;
+}
+
+
+Foam::foamVtkFormatter&
+Foam::foamVtkFormatter::xmlAttr
+(
+    const word& k,
+    const scalar v,
+    const char quote
+)
+{
+    if (!inTag_)
+    {
+        WarningInFunction
+            << "xml attribute '" << k << "' but not within a tag!"
+            << endl;
+    }
+
+    os_ << ' ' << k << '=' << quote << v << quote;
+
+    return *this;
+}
+
+
+// * * * * * * * * * * * * * * Member Operators * * * * * * * * * * * * * * //
+
+Foam::foamVtkFormatter&
+Foam::foamVtkFormatter::operator()
+(
+    const word& k,
+    const std::string& v
+)
+{
+    return xmlAttr(k, v);
+}
+
+
+Foam::foamVtkFormatter&
+Foam::foamVtkFormatter::operator()
+(
+    const word& k,
+    const label v
+)
+{
+    return xmlAttr(k, v);
+}
+
+
+Foam::foamVtkFormatter&
+Foam::foamVtkFormatter::operator()
+(
+    const word& k,
+    const uint64_t v
+)
+{
+    return xmlAttr(k, v);
+}
+
+
+Foam::foamVtkFormatter&
+Foam::foamVtkFormatter::operator()
+(
+    const word& k,
+    const scalar v
+)
+{
+    return xmlAttr(k, v);
+}
+
+
+// ************************************************************************* //
diff --git a/src/fileFormats/vtk/format/foamVtkFormatter.H b/src/fileFormats/vtk/format/foamVtkFormatter.H
new file mode 100644
index 0000000000000000000000000000000000000000..4fe0cb997773461de2a049b087af731126b359ee
--- /dev/null
+++ b/src/fileFormats/vtk/format/foamVtkFormatter.H
@@ -0,0 +1,233 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2016 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
+    foamVtkFormatter
+
+Description
+    Abstract class for a VTK output stream formatter.
+
+    Includes very simple support for writing XML tags.
+
+SourceFiles
+    foamVtkFormatter.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef foamVtkFormatter_H
+#define foamVtkFormatter_H
+
+#include "int.H"
+#include "uint64.H"
+#include "label.H"
+#include "word.H"
+#include "UList.H"
+#include "LIFOStack.H"
+#include "foamVtkPTraits.H"
+
+#include <iostream>
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+/*---------------------------------------------------------------------------*\
+                      Class foamVtkFormatter Declaration
+\*---------------------------------------------------------------------------*/
+
+class foamVtkFormatter
+{
+    // Private Data
+
+        //- The output stream for the formatter
+        std::ostream& os_;
+
+        //- Stack of current XML tags
+        LIFOStack<word> xmlTags_;
+
+        //- Tag open/closed/ended state
+        mutable bool inTag_;
+
+
+protected:
+
+    // Protected Member Functions
+
+        //- Construct and attach to an output stream
+        foamVtkFormatter(std::ostream& os);
+
+public:
+
+    // Static Data
+
+        //- VTK name for the 'byte_order' attribute
+        static const char* const byteOrder;
+
+        //- VTK name for the 'header_type' attribute (UInt64)
+        static const char* const headerType;
+
+
+    //- Destructor
+    virtual ~foamVtkFormatter();
+
+
+    // Member Functions
+
+        //- Access to the underlying output stream
+        inline std::ostream& os()
+        {
+            return os_;
+        }
+
+
+        //- Name for the XML output type.
+        //  Possibly the lowercase version of the Legacy output type
+        virtual const char* name() const = 0;
+
+        //- Name for the XML append encoding
+        virtual const char* encoding() const = 0;
+
+
+        //- Write leading size for binary output
+        virtual void writeSize(const uint64_t) = 0;
+
+        virtual void write(const uint8_t) = 0;
+        virtual void write(const label) = 0;
+        virtual void write(const float) = 0;
+        virtual void write(const double) = 0;
+        virtual void flush() = 0;
+
+
+    // Member Functions
+
+        //- Indent according to the currently nested XML tags
+        void indent();
+
+        //- Write XML header
+        foamVtkFormatter& xmlHeader();
+
+        //- Write XML comment (at the current indentation level)
+        foamVtkFormatter& comment(const std::string&);
+
+
+        //- Open XML tag
+        foamVtkFormatter& openTag(const word& tag);
+
+        //- Close XML tag, optional as an empty container.
+        //  Always adds a trailing newline.
+        foamVtkFormatter& closeTag(bool isEmpty = false);
+
+        //- End XML tag, optional with sanity check
+        //  Always adds a trailing newline.
+        foamVtkFormatter& endTag(const word& tag = word::null);
+
+        //- Write XML tag without any attributes. Combines openTag/closeTag.
+        foamVtkFormatter& tag(const word& tag);
+
+
+        //- Open "DataArray" XML tag
+        template<class Type, int nComp=0>
+        foamVtkFormatter& openDataArray(const word& dataName);
+
+
+        //- Insert a single "PDataArray" XML entry tag.
+        //  For some entries, the name is optional.
+        template<class Type, int nComp=0>
+        foamVtkFormatter& PDataArray(const word& dataName);
+
+
+        //- End "DataArray" XML tag
+        foamVtkFormatter& endDataArray()
+        {
+            return endTag("DataArray");
+        }
+
+
+
+        //- Write XML attribute
+        foamVtkFormatter& xmlAttr
+        (
+            const word&,
+            const std::string&,
+            const char quote='\''
+        );
+
+        //- Write XML attribute
+        foamVtkFormatter& xmlAttr
+        (
+            const word&,
+            const label,
+            const char quote='\''
+        );
+
+        //- Write XML attribute
+        foamVtkFormatter& xmlAttr
+        (
+            const word&,
+            const uint64_t,
+            const char quote='\''
+        );
+
+        //- Write XML attribute
+        foamVtkFormatter& xmlAttr
+        (
+            const word&,
+            const scalar,
+            const char quote='\''
+        );
+
+
+
+    // Member Operators
+
+        //- Write XML attribute
+        foamVtkFormatter& operator()(const word&, const std::string&);
+
+        //- Write XML attribute
+        foamVtkFormatter& operator()(const word&, const label);
+
+        //- Write XML attribute
+        foamVtkFormatter& operator()(const word&, const uint64_t);
+
+        //- Write XML attribute
+        foamVtkFormatter& operator()(const word&, const scalar);
+
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#ifdef NoRepository
+    #include "foamVtkFormatterTemplates.C"
+#endif
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/fileFormats/vtk/format/foamVtkFormatterTemplates.C b/src/fileFormats/vtk/format/foamVtkFormatterTemplates.C
new file mode 100644
index 0000000000000000000000000000000000000000..6e0830e2b1fab52f022a8ef6cfd81dbf15581d03
--- /dev/null
+++ b/src/fileFormats/vtk/format/foamVtkFormatterTemplates.C
@@ -0,0 +1,70 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2016 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 "foamVtkPTraits.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+template<class Type, int nComp>
+Foam::foamVtkFormatter& Foam::foamVtkFormatter::openDataArray
+(
+    const word& dataName
+)
+{
+    openTag("DataArray");
+    xmlAttr("type", foamVtkPTraits<Type>::typeName);
+    xmlAttr("Name", dataName);
+    if (nComp > 1)
+    {
+        xmlAttr("NumberOfComponents", nComp);
+    }
+    xmlAttr("format", name());
+
+    return *this;
+}
+
+
+template<class Type, int nComp>
+Foam::foamVtkFormatter& Foam::foamVtkFormatter::PDataArray
+(
+    const word& dataName
+)
+{
+    openTag("PDataArray");
+    xmlAttr("type", foamVtkPTraits<Type>::typeName);
+    if (dataName.size())
+    {
+        xmlAttr("Name", dataName);
+    }
+    if (nComp > 1)
+    {
+        xmlAttr("NumberOfComponents", nComp);
+    }
+
+    closeTag(true);
+
+    return *this;
+}
+
+// ************************************************************************* //
diff --git a/src/fileFormats/vtk/format/foamVtkLegacyFormatter.C b/src/fileFormats/vtk/format/foamVtkLegacyFormatter.C
new file mode 100644
index 0000000000000000000000000000000000000000..177249b306a84daf44531ffb39668beb836b9a0e
--- /dev/null
+++ b/src/fileFormats/vtk/format/foamVtkLegacyFormatter.C
@@ -0,0 +1,139 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2016 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 "foamVtkLegacyFormatter.H"
+#include "endian.H"
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+const char* Foam::foamVtkLegacyFormatter::name_ = "BINARY";
+
+
+// * * * * * * * * * * * * Protected Member Functions  * * * * * * * * * * * //
+
+void Foam::foamVtkLegacyFormatter::write
+(
+    const char* s,
+    std::streamsize n
+)
+{
+    os().write(s, n);
+}
+
+
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+Foam::foamVtkLegacyFormatter::foamVtkLegacyFormatter(std::ostream& os)
+:
+    foamVtkFormatter(os)
+{}
+
+
+// * * * * * * * * * * * * * * * * Destructor  * * * * * * * * * * * * * * * //
+
+Foam::foamVtkLegacyFormatter::~foamVtkLegacyFormatter()
+{}
+
+
+// * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * * //
+
+const char* Foam::foamVtkLegacyFormatter::name() const
+{
+    return name_;
+}
+
+
+const char* Foam::foamVtkLegacyFormatter::encoding() const
+{
+    return name_;
+}
+
+
+void Foam::foamVtkLegacyFormatter::writeSize(const uint64_t)
+{}
+
+
+void Foam::foamVtkLegacyFormatter::write(const uint8_t val)
+{
+    // Can only handle integers
+    int copy(val);
+    write(copy);
+}
+
+
+void Foam::foamVtkLegacyFormatter::write(const label val)
+{
+    // std::cerr<<"label is:" << sizeof(val) << '\n';
+
+    // Not entirely correct: the legacy format only supports 32-bit integers.
+    // Either limit size for 64-bit label, or simply do not support for 64-bit.
+#ifdef WM_LITTLE_ENDIAN
+# if WM_LABEL_SIZE == 32
+    uint32_t swapped = endian::swap32(val);
+    write(reinterpret_cast<const char*>(&swapped), sizeof(uint32_t));
+# elif WM_LABEL_SIZE == 64
+    uint64_t swapped = endian::swap64(val);
+    write(reinterpret_cast<const char*>(&swapped), sizeof(uint64_t));
+#endif
+#else
+    write(reinterpret_cast<const char*>(&val), sizeof(label));
+#endif
+}
+
+
+void Foam::foamVtkLegacyFormatter::write(const float val)
+{
+    // std::cerr<<"float is:" << sizeof(val) << '\n';
+
+#ifdef WM_LITTLE_ENDIAN
+    // De-reference in two stages to avoid the warning
+    //     dereferencing type-punned pointer will break strict-aliasing rules
+    //     [-Wstrict-aliasing]
+
+    const uint32_t* ptr = reinterpret_cast<const uint32_t*>(&val);
+    uint32_t swapped = endian::swap32(*ptr);
+    write(reinterpret_cast<const char*>(&swapped), sizeof(uint32_t));
+#else
+    write(reinterpret_cast<const char*>(&val), sizeof(float));
+#endif
+}
+
+
+void Foam::foamVtkLegacyFormatter::write(const double val)
+{
+    // Legacy cannot support Float64 anyhow.
+    // std::cerr<<"write double as float:" << val << '\n';
+    float copy(val);
+    write(copy);
+}
+
+
+void Foam::foamVtkLegacyFormatter::flush()
+{
+    os()<< '\n';
+}
+
+
+// ************************************************************************* //
diff --git a/src/fileFormats/vtk/format/foamVtkLegacyFormatter.H b/src/fileFormats/vtk/format/foamVtkLegacyFormatter.H
new file mode 100644
index 0000000000000000000000000000000000000000..fe2395add4d143085d854c987d040b6ed051caaa
--- /dev/null
+++ b/src/fileFormats/vtk/format/foamVtkLegacyFormatter.H
@@ -0,0 +1,119 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2016 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
+    foamVtkLegacyFormatter
+
+Description
+    Binary output for the VTK legacy format, always written as big-endian.
+
+    The legacy files are always written as big endian.
+    Since integers in the legacy format are limited to 32-bit,
+    this format should not be used for OpenFOAM with 64-bit label sizes.
+
+SourceFiles
+    foamVtkLegacyFormatter.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef foamVtkLegacyFormatter_H
+#define foamVtkLegacyFormatter_H
+
+#include "foamVtkFormatter.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+/*---------------------------------------------------------------------------*\
+                   Class foamVtkLegacyFormatter Declaration
+\*---------------------------------------------------------------------------*/
+
+class foamVtkLegacyFormatter
+:
+    public foamVtkFormatter
+{
+    // Private Data Members
+
+        static const char* name_;
+
+
+    // Private Member Functions
+
+        //- Disallow default bitwise copy construct
+        foamVtkLegacyFormatter(const foamVtkLegacyFormatter&) = delete;
+
+        //- Disallow default bitwise assignment
+        void operator=(const foamVtkLegacyFormatter&) = delete;
+
+
+protected:
+
+    // Protected Member Functions
+
+        //- Write
+        void write(const char* s, std::streamsize n);
+
+
+public:
+
+    // Constructors
+
+        //- Construct and attach to an output stream
+        foamVtkLegacyFormatter(std::ostream&);
+
+
+    //- Destructor
+    virtual ~foamVtkLegacyFormatter();
+
+
+    // Member Functions
+
+        //- Name for the Legacy output type ("BINARY")
+        virtual const char* name() const;
+
+        //- Name for the XML append encoding (unused)
+        //  Currently simply "BINARY", but this should not be relied upon.
+        virtual const char* encoding() const;
+
+
+        //- Write leading size - a no-op for legacy binary output
+        virtual void writeSize(const uint64_t);
+
+        virtual void write(const uint8_t);
+        virtual void write(const label);
+        virtual void write(const float);
+        virtual void write(const double);
+        virtual void flush();
+};
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/fileFormats/vtk/format/foamVtkOutputOptions.C b/src/fileFormats/vtk/format/foamVtkOutputOptions.C
new file mode 100644
index 0000000000000000000000000000000000000000..816cbe90b906762658ce94dac8db39f39c9d7e86
--- /dev/null
+++ b/src/fileFormats/vtk/format/foamVtkOutputOptions.C
@@ -0,0 +1,240 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2016 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 "foamVtkOutputOptions.H"
+
+#include "foamVtkAppendBase64Formatter.H"
+#include "foamVtkAppendRawFormatter.H"
+#include "foamVtkAsciiFormatter.H"
+#include "foamVtkBase64Formatter.H"
+#include "foamVtkLegacyFormatter.H"
+
+#include "IOstream.H"
+
+
+// * * * * * * * * * * * * * * Constructor * * * * * * * * * * * * * //
+
+Foam::foamVtkOutputOptions::foamVtkOutputOptions()
+:
+    type_(ASCII),
+    style_(NONE),
+    precision_(IOstream::defaultPrecision())
+{}
+
+
+// * * * * * * * * * * * * * * * * Selectors * * * * * * * * * * * * * * * * //
+
+Foam::autoPtr<Foam::foamVtkFormatter>
+Foam::foamVtkOutputOptions::newFormatter
+(
+    std::ostream& os
+) const
+{
+    switch (type_)
+    {
+        case (LEGACY | BINARY):
+            return autoPtr<foamVtkFormatter>
+            (
+                new foamVtkLegacyFormatter(os)
+            );
+
+        case BASE64:  // xml insitu
+            return autoPtr<foamVtkFormatter>
+            (
+                new foamVtkBase64Formatter(os)
+            );
+
+        case (APPEND | BASE64):
+            return autoPtr<foamVtkFormatter>
+            (
+                new foamVtkAppendBase64Formatter(os)
+            );
+
+        case (APPEND | BINARY):
+            return autoPtr<foamVtkFormatter>
+            (
+                new foamVtkAppendRawFormatter(os)
+            );
+
+        default:   // ASCII (legacy or xml) must always work
+            return autoPtr<foamVtkFormatter>
+            (
+                new foamVtkAsciiFormatter(os, precision_)
+            );
+    }
+}
+
+
+// * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * * //
+
+void Foam::foamVtkOutputOptions::ascii(bool b)
+{
+    if (b)
+    {
+        // Force ASCII:
+
+        if (type_ & APPEND)
+        {
+            // Append: ascii = base64 (vs raw binary)
+            type_ = (APPEND | BASE64);
+        }
+        else if (type_ & LEGACY)
+        {
+            // Legacy: ascii = ascii
+            type_ = (LEGACY | ASCII);
+        }
+        else
+        {
+            // XML: ascii = ascii
+            type_ = ASCII;
+        }
+    }
+    else
+    {
+        // Non-ASCII:
+
+        if (type_ & APPEND)
+        {
+            // Append: binary == (raw) binary
+            type_ = APPEND | BINARY;
+        }
+        else if (type_ & LEGACY)
+        {
+            // Legacy: binary = binary
+            type_ = LEGACY | BINARY;
+        }
+        else
+        {
+            // XML: binary == (inline) binary == base64
+            type_ = BASE64;
+        }
+    }
+}
+
+
+void Foam::foamVtkOutputOptions::append(bool b)
+{
+    if (b)
+    {
+        if (!(type_ & APPEND))
+        {
+            // XML:    base64 -> raw binary, ascii -> base64
+            // Legacy: binary -> raw binary, ascii -> base64
+            type_ = APPEND | ((type_ & (BASE64 | BINARY)) ? BINARY : BASE64);
+        }
+    }
+    else if (type_ & APPEND)
+    {
+        // Only revert back to inline XML base64 versions
+        // ASCII needs another step.
+
+        type_ = BASE64;
+    }
+}
+
+
+void Foam::foamVtkOutputOptions::legacy(bool b)
+{
+    if (b)
+    {
+        if (type_ & APPEND)
+        {
+            // Append: base64 -> ascii, binary -> binary
+            type_ = (LEGACY | ((type_ & BINARY) ? BINARY : ASCII));
+        }
+        else if (type_ & LEGACY)
+        {
+            // no-op
+        }
+        else
+        {
+            // XML: ascii -> ascii, base64 -> binary
+            type_ = (LEGACY | ((type_ & BASE64) ? BINARY : ASCII));
+        }
+    }
+    else if (type_ & LEGACY)
+    {
+        // Legacy: ascii -> xml ascii, binary -> xml base64
+        type_ = (type_ & BINARY) ? BASE64 : ASCII;
+    }
+}
+
+
+void Foam::foamVtkOutputOptions::precision(unsigned val) const
+{
+    precision_ = val;
+}
+
+
+Foam::Ostream&
+Foam::foamVtkOutputOptions::info(Ostream& os) const
+{
+    os << "type: " << type_;
+
+    switch (type_)
+    {
+        case (LEGACY | ASCII):
+            os << " legacy ascii";
+            break;
+
+        case (LEGACY | BINARY):
+            os << " legacy binary";
+            break;
+
+        case BASE64:
+            os << " xml insitu base64";
+            break;
+
+        case (APPEND | BASE64):
+            os << " xml-append base64";
+            break;
+
+        case (APPEND | BINARY):
+            os << " xml-append binary";
+            break;
+
+        case ASCII:
+            os << " xml insitu ascii";
+            break;
+
+        case BINARY:
+            os << " xml insitu binary - WRONG";
+            break;
+
+        default:
+            os << " unknown";
+            break;
+    }
+
+    if (legacy()) os << " legacy";
+    if (xml())    os << " xml";
+    if (append()) os << " append";
+    if (ascii())  os << " ascii";
+
+    return os;
+}
+
+
+// ************************************************************************* //
diff --git a/src/fileFormats/vtk/format/foamVtkOutputOptions.H b/src/fileFormats/vtk/format/foamVtkOutputOptions.H
new file mode 100644
index 0000000000000000000000000000000000000000..43a3ee0918e89350419ab3fda802fb18fb8c3acc
--- /dev/null
+++ b/src/fileFormats/vtk/format/foamVtkOutputOptions.H
@@ -0,0 +1,156 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2016 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
+    foamVtkOutputOptions
+
+Description
+    Encapsulate combinations of output format options.
+
+SourceFiles
+    foamVtkOutputOptions.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef foamVtkOutputOptions_H
+#define foamVtkOutputOptions_H
+
+#include "autoPtr.H"
+#include "foamVtkFormatter.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+class Ostream;
+
+/*---------------------------------------------------------------------------*\
+                    Class foamVtkOutputOptions Declaration
+\*---------------------------------------------------------------------------*/
+
+class foamVtkOutputOptions
+{
+    // Private data
+
+        //- The supported output/format types
+        enum foamVtkOptionTypes
+        {
+            ASCII  = 0x0000,    //!< ASCII formatting for data
+            BINARY = 0x0001,    //!< Raw binary formatting for data
+            BASE64 = 0x0002,    //!< Base64 encoding for data
+            LEGACY = 0x0100,    //!< Legacy vtk file format
+            APPEND = 0x0200     //!< XML append format
+        };
+
+        //- The output style tuning
+        enum foamVtkStyleOptions
+        {
+            NONE   = 0x0000,    //!< Normal
+            HEADER = 0x0001     //!< Emit xml header
+        };
+
+
+        //- The output format type
+        unsigned short type_;
+
+        //- The output style tuning
+        unsigned short style_;
+
+
+        //- ASCII write precision
+        mutable unsigned precision_;
+
+
+public:
+
+    // Constructors
+
+        //- Construct null - XML insitu ASCII format with default precision
+        foamVtkOutputOptions();
+
+
+    // Selectors
+
+        //- Return new data formatter based on the writer options
+        autoPtr<foamVtkFormatter> newFormatter(std::ostream&) const;
+
+
+    // Member Functions
+
+    // Access
+
+        //- True if writer uses legacy file format
+        inline bool legacy() const;
+
+        //- True if writer uses XML file format (non-legacy)
+        inline bool xml() const;
+
+        //- True if output format uses an append mode
+        inline bool append() const;
+
+        //- True if output format does not use an append mode
+        inline bool insitu() const;
+
+        //- True if output format is ASCII
+        inline bool ascii() const;
+
+
+    // Edit
+
+        //- Toggle ASCII mode on/off.
+        //  In append mode, this switches between base64 and raw binary.
+        //  In XML mode, this switches between ASCII and base64.
+        //  In legacy mode, this switches between ASCII and binary.
+        void ascii(bool);
+
+        //- Toggle append mode on/off.
+        void append(bool);
+
+        //- Toggle legacy mode on/off.
+        void legacy(bool);
+
+        //- Set the write precision to be used for new ASCII formatters
+        void precision(unsigned val) const;
+
+
+    // Other
+
+        //- Report information about the options
+        Ostream& info(Ostream&) const;
+
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#include "foamVtkOutputOptionsI.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/fileFormats/vtk/format/foamVtkOutputOptionsI.H b/src/fileFormats/vtk/format/foamVtkOutputOptionsI.H
new file mode 100644
index 0000000000000000000000000000000000000000..0205f2f98ec87c5dba88223afb604310eb13407b
--- /dev/null
+++ b/src/fileFormats/vtk/format/foamVtkOutputOptionsI.H
@@ -0,0 +1,56 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2016 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/>.
+
+\*---------------------------------------------------------------------------*/
+
+inline bool Foam::foamVtkOutputOptions::legacy() const
+{
+    return (type_ & LEGACY);
+}
+
+
+inline bool Foam::foamVtkOutputOptions::xml() const
+{
+    return !legacy();
+}
+
+
+inline bool Foam::foamVtkOutputOptions::append() const
+{
+    return (type_ & APPEND);
+}
+
+
+inline bool Foam::foamVtkOutputOptions::insitu() const
+{
+    return !(type_ & APPEND);
+}
+
+
+inline bool Foam::foamVtkOutputOptions::ascii() const
+{
+    return !(type_ & (BINARY | BASE64));
+}
+
+
+// ************************************************************************* //
diff --git a/src/fileFormats/vtk/vtkUnstructuredReader.C b/src/fileFormats/vtk/read/vtkUnstructuredReader.C
similarity index 98%
rename from src/fileFormats/vtk/vtkUnstructuredReader.C
rename to src/fileFormats/vtk/read/vtkUnstructuredReader.C
index 3edf53ee3d813f550a473f0da708ac73d0eba925..d834f4d5ebab183b171a2053f929833d1cec1106 100644
--- a/src/fileFormats/vtk/vtkUnstructuredReader.C
+++ b/src/fileFormats/vtk/read/vtkUnstructuredReader.C
@@ -136,7 +136,7 @@ void Foam::vtkUnstructuredReader::extractCells
     {
         switch (cellTypes[i])
         {
-            case VTK_VERTEX:
+            case foamVtkCore::VTK_VERTEX:
             {
                 warnUnhandledType(inFile, cellTypes[i], warningGiven);
                 label nRead = cellVertData[dataIndex++];
@@ -152,7 +152,7 @@ void Foam::vtkUnstructuredReader::extractCells
             }
             break;
 
-            case VTK_POLY_VERTEX:
+            case foamVtkCore::VTK_POLY_VERTEX:
             {
                 warnUnhandledType(inFile, cellTypes[i], warningGiven);
                 label nRead = cellVertData[dataIndex++];
@@ -160,7 +160,7 @@ void Foam::vtkUnstructuredReader::extractCells
             }
             break;
 
-            case VTK_LINE:
+            case foamVtkCore::VTK_LINE:
             {
                 //warnUnhandledType(inFile, cellTypes[i], warningGiven);
                 label nRead = cellVertData[dataIndex++];
@@ -180,7 +180,7 @@ void Foam::vtkUnstructuredReader::extractCells
             }
             break;
 
-            case VTK_POLY_LINE:
+            case foamVtkCore::VTK_POLY_LINE:
             {
                 //warnUnhandledType(inFile, cellTypes[i], warningGiven);
                 label nRead = cellVertData[dataIndex++];
@@ -194,7 +194,7 @@ void Foam::vtkUnstructuredReader::extractCells
             }
             break;
 
-            case VTK_TRIANGLE:
+            case foamVtkCore::VTK_TRIANGLE:
             {
                 faceMap_[facei] = i;
                 face& f = faces_[facei++];
@@ -214,7 +214,7 @@ void Foam::vtkUnstructuredReader::extractCells
             }
             break;
 
-            case VTK_QUAD:
+            case foamVtkCore::VTK_QUAD:
             {
                 faceMap_[facei] = i;
                 face& f = faces_[facei++];
@@ -235,7 +235,7 @@ void Foam::vtkUnstructuredReader::extractCells
             }
             break;
 
-            case VTK_POLYGON:
+            case foamVtkCore::VTK_POLYGON:
             {
                 faceMap_[facei] = i;
                 face& f = faces_[facei++];
@@ -248,7 +248,7 @@ void Foam::vtkUnstructuredReader::extractCells
             }
             break;
 
-            case VTK_TETRA:
+            case foamVtkCore::VTK_TETRA:
             {
                 label nRead = cellVertData[dataIndex++];
                 if (nRead != 4)
@@ -268,7 +268,7 @@ void Foam::vtkUnstructuredReader::extractCells
             }
             break;
 
-            case VTK_PYRAMID:
+            case foamVtkCore::VTK_PYRAMID:
             {
                 label nRead = cellVertData[dataIndex++];
                 if (nRead != 5)
@@ -289,7 +289,7 @@ void Foam::vtkUnstructuredReader::extractCells
             }
             break;
 
-            case VTK_WEDGE:
+            case foamVtkCore::VTK_WEDGE:
             {
                 label nRead = cellVertData[dataIndex++];
                 if (nRead != 6)
@@ -311,7 +311,7 @@ void Foam::vtkUnstructuredReader::extractCells
             }
             break;
 
-            case VTK_HEXAHEDRON:
+            case foamVtkCore::VTK_HEXAHEDRON:
             {
                 label nRead = cellVertData[dataIndex++];
                 if (nRead != 8)
diff --git a/src/fileFormats/vtk/vtkUnstructuredReader.H b/src/fileFormats/vtk/read/vtkUnstructuredReader.H
similarity index 90%
rename from src/fileFormats/vtk/vtkUnstructuredReader.H
rename to src/fileFormats/vtk/read/vtkUnstructuredReader.H
index db2391bc3bc92705af4461eeff8b6ad025783032..77bd71b4aac8f6ebcbaf9b90e1e1023871bae610 100644
--- a/src/fileFormats/vtk/vtkUnstructuredReader.H
+++ b/src/fileFormats/vtk/read/vtkUnstructuredReader.H
@@ -25,8 +25,8 @@ Class
     Foam::vtkUnstructuredReader
 
 Description
-    Reader for vtk unstructured_grid legacy files. Supports single CELLS, POINTS
-    etc. entry only.
+    Reader for vtk UNSTRUCTURED_GRID legacy files.
+    Supports single CELLS, POINTS etc. entry only.
 
     - all integer types (int, unsigned_int, long etc.) become Foam::label
     - all real types (float, double) become Foam::scalar
@@ -47,6 +47,7 @@ SourceFiles
 #ifndef vtkUnstructuredReader_H
 #define vtkUnstructuredReader_H
 
+#include "foamVtkCore.H"
 #include "objectRegistry.H"
 #include "cellShapeList.H"
 #include "HashSet.H"
@@ -62,6 +63,8 @@ namespace Foam
 \*---------------------------------------------------------------------------*/
 
 class vtkUnstructuredReader
+:
+    public fileFormats::foamVtkCore
 {
 public:
 
@@ -108,29 +111,6 @@ public:
         static const NamedEnum<parseMode, 5> parseModeNames;
 
 
-        //- Enumeration defining the cell types
-        enum vtkTypes
-        {
-            VTK_EMPTY_CELL       = 0,
-            VTK_VERTEX           = 1,
-            VTK_POLY_VERTEX      = 2,
-            VTK_LINE             = 3,
-            VTK_POLY_LINE        = 4,
-            VTK_TRIANGLE         = 5,
-            VTK_TRIANGLE_STRIP   = 6,
-            VTK_POLYGON          = 7,
-            VTK_PIXEL            = 8,
-            VTK_QUAD             = 9,
-            VTK_TETRA            = 10,
-            VTK_VOXEL            = 11,
-            VTK_HEXAHEDRON       = 12,
-            VTK_WEDGE            = 13,
-            VTK_PYRAMID          = 14,
-            VTK_PENTAGONAL_PRISM = 15,
-            VTK_HEXAGONAL_PRISM  = 16,
-        };
-
-
 private:
 
     //- Header
diff --git a/src/fileFormats/vtk/vtkUnstructuredReaderTemplates.C b/src/fileFormats/vtk/read/vtkUnstructuredReaderTemplates.C
similarity index 98%
rename from src/fileFormats/vtk/vtkUnstructuredReaderTemplates.C
rename to src/fileFormats/vtk/read/vtkUnstructuredReaderTemplates.C
index eb1e5f5273eeea792e32ee14aac739ade81a8026..86f6f6d89544c0a8f28e3f14aee6fe20ee127287 100644
--- a/src/fileFormats/vtk/vtkUnstructuredReaderTemplates.C
+++ b/src/fileFormats/vtk/read/vtkUnstructuredReaderTemplates.C
@@ -56,7 +56,7 @@ void Foam::vtkUnstructuredReader::printFieldStats
 {
     wordList fieldNames(obj.names(Type::typeName));
 
-    if (fieldNames.size() > 0)
+    if (fieldNames.size())
     {
         Info<< "Read " << fieldNames.size() << " " << Type::typeName
             << " fields:" << endl;
diff --git a/src/fileFormats/vtk/type/foamVtkPTraits.C b/src/fileFormats/vtk/type/foamVtkPTraits.C
new file mode 100644
index 0000000000000000000000000000000000000000..91bf1f3a33b647663c9077245b8252efa4d0c76a
--- /dev/null
+++ b/src/fileFormats/vtk/type/foamVtkPTraits.C
@@ -0,0 +1,70 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2016 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 "foamVtkPTraits.H"
+#include "endian.H"
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+template<>
+const char* const
+Foam::foamVtkPTraits<uint8_t>::typeName = "UInt8";
+
+template<>
+const char * const
+Foam::foamVtkPTraits<int32_t>::typeName = "Int32";
+
+template<>
+const char * const
+Foam::foamVtkPTraits<uint32_t>::typeName = "UInt32";
+
+template<>
+const char * const
+Foam::foamVtkPTraits<int64_t>::typeName = "Int64";
+
+template<>
+const char * const
+Foam::foamVtkPTraits<uint64_t>::typeName = "UInt64";
+
+template<>
+const char * const
+Foam::foamVtkPTraits<float>::typeName = "Float32";
+
+template<>
+const char * const
+Foam::foamVtkPTraits<double>::typeName = "Float64";
+
+#ifdef WM_LITTLE_ENDIAN
+template<>
+const char* const
+Foam::foamVtkPTraits<::Foam::endian>::typeName = "LittleEndian";
+#else
+template<>
+const char* const
+Foam::foamVtkPTraits<::Foam::endian>::typeName = "BigEndian";
+#endif
+
+
+// ************************************************************************* //
diff --git a/src/fileFormats/vtk/type/foamVtkPTraits.H b/src/fileFormats/vtk/type/foamVtkPTraits.H
new file mode 100644
index 0000000000000000000000000000000000000000..ea95c0820d1f61f8190336f0c7dd092013ffd98d
--- /dev/null
+++ b/src/fileFormats/vtk/type/foamVtkPTraits.H
@@ -0,0 +1,93 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2016 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::foamVtkPTraits
+
+Description
+    Names for VTK primitive types.
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef foamVtkPTraits_H
+#define foamVtkPTraits_H
+
+#include <cstdint>
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+// Forward declarations
+class endian;
+
+/*---------------------------------------------------------------------------*\
+                         Class foamVtkPTraits Declaration
+\*---------------------------------------------------------------------------*/
+
+template<class PrimitiveType>
+class foamVtkPTraits
+{
+public:
+
+    // Static data members
+
+        static const char* const typeName;
+};
+
+
+template<>
+const char* const foamVtkPTraits<uint8_t>::typeName;  // = UInt8
+
+template<>
+const char* const foamVtkPTraits<int32_t>::typeName;  // = Int32
+
+template<>
+const char* const foamVtkPTraits<int32_t>::typeName;  // = UInt32
+
+template<>
+const char* const foamVtkPTraits<int32_t>::typeName;  // = Int64
+
+template<>
+const char* const foamVtkPTraits<int64_t>::typeName;  // = UInt64
+
+template<>
+const char* const foamVtkPTraits<float>::typeName;    // = Float32
+
+template<>
+const char* const foamVtkPTraits<double>::typeName;   // = Float64
+
+template<>
+const char* const foamVtkPTraits<::Foam::endian>::typeName;
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/surfMesh/Make/files b/src/surfMesh/Make/files
index 5e19af861366c32dac7299078654198d27636264..f7beae2f7c0dcd154712c4ad53c80ff23b53d95c 100644
--- a/src/surfMesh/Make/files
+++ b/src/surfMesh/Make/files
@@ -24,6 +24,7 @@ $(surfaceFormats)/surfaceFormatsCore.C
 
 $(surfaceFormats)/ac3d/AC3DsurfaceFormatCore.C
 $(surfaceFormats)/ac3d/AC3DsurfaceFormatRunTime.C
+$(surfaceFormats)/fire/FLMAsurfaceFormatRunTime.C
 $(surfaceFormats)/gts/GTSsurfaceFormatRunTime.C
 $(surfaceFormats)/nas/NASsurfaceFormatRunTime.C
 $(surfaceFormats)/obj/OBJsurfaceFormatRunTime.C
diff --git a/src/surfMesh/surfaceFormats/fire/FLMAsurfaceFormat.C b/src/surfMesh/surfaceFormats/fire/FLMAsurfaceFormat.C
new file mode 100644
index 0000000000000000000000000000000000000000..4cbfb60d561c486b225e6d90d819cc72451bb768
--- /dev/null
+++ b/src/surfMesh/surfaceFormats/fire/FLMAsurfaceFormat.C
@@ -0,0 +1,390 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2016 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 "FLMAsurfaceFormat.H"
+
+
+// * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
+
+//! \cond fileScope
+//- Output newline in ascii mode, no-op in binary mode
+inline static void newline(Foam::OSstream& os)
+{
+    if (os.format() == Foam::IOstream::ASCII)
+    {
+        os  << Foam::endl;
+    }
+}
+
+
+template<class Face>
+inline static int countFaces(const Face& f)
+{
+    int n = (f.size() - 2); // number triangles can be determined directly
+    return n == 2 ? 1 : n;  // quads don't need triangulation
+}
+
+//! \endcond
+
+
+template<class Face>
+inline void Foam::fileFormats::FLMAsurfaceFormat<Face>::writeShell
+(
+    OSstream& os,
+    const Face& f
+)
+{
+    if (os.format() == IOstream::BINARY)
+    {
+        if (f.size() == 3 || f.size() == 4)
+        {
+            putFireLabel(os, f.size());
+            forAll(f, fp)
+            {
+                putFireLabel(os, f[fp]);
+            }
+        }
+        else
+        {
+            // simple triangulation about f[0].
+            // better triangulation should have been done before
+            for (label fp1 = 1; fp1 < f.size() - 1; ++fp1)
+            {
+                label fp2 = f.fcIndex(fp1);
+
+                putFireLabel(os, 3);
+                putFireLabel(os, f[0]);
+                putFireLabel(os, f[fp1]);
+                putFireLabel(os, f[fp2]);
+            }
+        }
+    }
+    else
+    {
+        // ASCII
+        if (f.size() == 3 || f.size() == 4)
+        {
+            os  << ' ' << f.size();
+            forAll(f, fp)
+            {
+                os  << ' ' << f[fp];
+            }
+            os  << nl;
+        }
+        else
+        {
+            for (label fp1 = 1; fp1 < f.size() - 1; ++fp1)
+            {
+                label fp2 = f.fcIndex(fp1);
+                os  << ' ' << 3 << ' '
+                    << f[0] << ' ' << f[fp1] << ' ' << f[fp2]
+                    << nl;
+            }
+        }
+    }
+}
+
+
+template<class Face>
+inline void Foam::fileFormats::FLMAsurfaceFormat<Face>::writeType
+(
+    OSstream& os,
+    const Face& f
+)
+{
+    if (os.format() == IOstream::BINARY)
+    {
+        if (f.size() == 4)
+        {
+            putFireLabel(os, fireQuad);
+        }
+        else
+        {
+            const label n = countFaces(f);
+            for (label i=0; i < n; ++i)
+            {
+                putFireLabel(os, fireTri);
+            }
+        }
+    }
+    else
+    {
+        // ASCII
+        if (f.size() == 4)
+        {
+            os  << ' ' << fireQuad;
+        }
+        else
+        {
+            const label n = countFaces(f);
+            for (label i=0; i < n; ++i)
+            {
+                os  << ' ' << fireTri;
+            }
+        }
+    }
+}
+
+
+// * * * * * * * * * * * * Protected Member Functions  * * * * * * * * * * * //
+
+template<class Face>
+void Foam::fileFormats::FLMAsurfaceFormat<Face>::write
+(
+    OSstream& os,
+    const MeshedSurfaceProxy<Face>& surf
+)
+{
+    if (!os.good())
+    {
+        FatalErrorIn
+        (
+            "fileFormats::FLMAsurfaceFormat::write"
+            "(OSstream&, const MeshedSurfaceProxy<Face>&)"
+        )
+            << "bad output state "
+            << exit(FatalError);
+    }
+
+    const pointField& pointLst = surf.points();
+    const List<Face>&  faceLst = surf.surfFaces();
+    const List<label>& faceMap = surf.faceMap();
+
+    // for no zones, suppress the group name
+    const List<surfZone>& zones =
+    (
+        surf.surfZones().empty()
+      ? surfaceFormatsCore::oneZone(faceLst, word::null)
+      : surf.surfZones()
+    );
+
+    const bool useFaceMap = (surf.useFaceMap() && zones.size() > 1);
+
+
+    // determine the number of faces by counting the
+    // tri/quads/triangulated) faces in each zone
+    label nFaces = 0;
+    List<label> zoneCount(zones.size());
+
+    {
+        label faceIndex = 0;
+        forAll(zones, zoneI)
+        {
+            const surfZone& zone = zones[zoneI];
+
+            label selCount = 0;
+            if (useFaceMap)
+            {
+                forAll(zone, localFaceI)
+                {
+                    selCount += countFaces(faceLst[faceMap[faceIndex++]]);
+                }
+            }
+            else
+            {
+                forAll(zone, localFaceI)
+                {
+                    selCount += countFaces(faceLst[faceIndex++]);
+                }
+            }
+
+            zoneCount[zoneI] = selCount;
+            nFaces += selCount;
+        }
+    }
+
+
+    // Points
+    // ~~~~~~
+
+    // Set the precision of the points data to 10
+    os.precision(10);
+
+    Info<< "points: " << pointLst.size() << endl;
+    putFireLabel(os, pointLst.size());
+    newline(os);
+
+    forAll(pointLst, ptI)
+    {
+        // scaling is normally 1
+        putFirePoint(os, pointLst[ptI]);
+    }
+    newline(os); // readability
+
+    // Faces indices
+    {
+        Info<< "faces:  " << nFaces << endl;
+        putFireLabel(os, nFaces);
+        newline(os);
+
+        label faceIndex = 0;
+        forAll(zones, zoneI)
+        {
+            const surfZone& zone = zones[zoneI];
+
+            if (useFaceMap)
+            {
+                forAll(zone, localFaceI)
+                {
+                    writeShell(os, faceLst[faceMap[faceIndex++]]);
+                }
+            }
+            else
+            {
+                forAll(zone, localFaceI)
+                {
+                    writeShell(os, faceLst[faceIndex++]);
+                }
+            }
+        }
+        newline(os);
+        newline(os); // readability
+    }
+
+
+    // Face types
+    {
+        putFireLabel(os, nFaces);
+        newline(os);
+
+        label faceIndex = 0;
+        forAll(zones, zoneI)
+        {
+            const surfZone& zone = zones[zoneI];
+
+            if (useFaceMap)
+            {
+                forAll(zone, localFaceI)
+                {
+                    writeType(os, faceLst[faceMap[faceIndex++]]);
+                }
+            }
+            else
+            {
+                forAll(zone, localFaceI)
+                {
+                    writeType(os, faceLst[faceIndex++]);
+                }
+            }
+        }
+        newline(os);
+        newline(os); // readability
+    }
+
+    // Selections (cell)
+    {
+        putFireLabel(os, zones.size());
+        newline(os);
+
+        label faceIndex = 0;
+        forAll(zones, zoneI)
+        {
+            const surfZone& zone = zones[zoneI];
+            const label selCount = zoneCount[zoneI];
+
+            putFireString(os, zone.name());
+            putFireLabel(os, static_cast<int>(FIRECore::cellSelection));
+            newline(os);
+
+            putFireLabels(os, selCount, faceIndex);
+            faceIndex += selCount;
+
+            newline(os); // readability
+        }
+    }
+}
+
+
+template<class Face>
+void Foam::fileFormats::FLMAsurfaceFormat<Face>::write
+(
+    const fileName& filename,
+    const MeshedSurfaceProxy<Face>& surf,
+    bool compress
+)
+{
+    autoPtr<OFstream> osPtr
+    (
+        compress
+      ? new OFstream
+        (
+            filename,
+            IOstream::ASCII,
+            IOstream::currentVersion,
+            IOstream::COMPRESSED
+        )
+      : new OFstream(filename)
+    );
+
+    if (osPtr->good())
+    {
+        FLMAsurfaceFormat<Face>::write(osPtr(), surf);
+        osPtr.clear();    // implicitly close the file
+
+        if (compress)
+        {
+            // rename .flmaz.gz -> .flmaz
+            // The '.gz' is automatically added by OFstream in compression mode
+            Foam::mv(filename + ".gz", filename);
+        }
+    }
+    else
+    {
+        FatalErrorIn
+        (
+            "fileFormats::FLMAsurfaceFormat::write"
+            "(const fileName&, const MeshedSurfaceProxy<Face>&)"
+        )
+            << "Cannot open file for writing " << filename
+            << exit(FatalError);
+    }
+}
+
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+template<class Face>
+void Foam::fileFormats::FLMAsurfaceFormat<Face>::write
+(
+    const fileName& filename,
+    const MeshedSurfaceProxy<Face>& surf
+)
+{
+    write(filename, surf, false);
+}
+
+
+template<class Face>
+void Foam::fileFormats::FLMAZsurfaceFormat<Face>::write
+(
+    const fileName& filename,
+    const MeshedSurfaceProxy<Face>& surf
+)
+{
+    FLMAsurfaceFormat<Face>::write(filename, surf, true);
+}
+
+
+// ************************************************************************* //
diff --git a/src/surfMesh/surfaceFormats/fire/FLMAsurfaceFormat.H b/src/surfMesh/surfaceFormats/fire/FLMAsurfaceFormat.H
new file mode 100644
index 0000000000000000000000000000000000000000..1af5a3f3972ac8aa1493013dde6dfb28829af47f
--- /dev/null
+++ b/src/surfMesh/surfaceFormats/fire/FLMAsurfaceFormat.H
@@ -0,0 +1,189 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2016 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::fileFormats::FLMAsurfaceFormat
+
+Description
+    Provide a means of writing AVL/FIRE FLMA format.
+
+SourceFiles
+    FLMAsurfaceFormat.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef FLMAsurfaceFormat_H
+#define FLMAsurfaceFormat_H
+
+#include "MeshedSurface.H"
+#include "MeshedSurfaceProxy.H"
+#include "FIRECore.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+namespace fileFormats
+{
+
+/*---------------------------------------------------------------------------*\
+                      Class FLMAsurfaceFormat Declaration
+\*---------------------------------------------------------------------------*/
+
+template<class Face>
+class FLMAsurfaceFormat
+:
+    public MeshedSurface<Face>,
+    public FIRECore
+{
+    // Private Member Functions
+
+        static inline void writeShell(OSstream&, const Face&);
+        static inline void writeType(OSstream&, const Face&);
+
+        //- Disallow default bitwise copy construct
+        FLMAsurfaceFormat(const FLMAsurfaceFormat<Face>&) = delete;
+
+        //- Disallow default bitwise assignment
+        void operator=(const FLMAsurfaceFormat<Face>&) = delete;
+
+
+protected:
+
+    // Protected Member Functions
+
+        //- Write surface mesh components by proxy
+        static void write
+        (
+            OSstream&,
+            const MeshedSurfaceProxy<Face>&
+        );
+
+
+        //- Write surface mesh components by proxy with/without compression
+        static void write
+        (
+            const fileName&,
+            const MeshedSurfaceProxy<Face>&,
+            bool compress
+        );
+
+
+public:
+
+    // Constructors
+
+        //- Construct null
+        FLMAsurfaceFormat()
+        {}
+
+
+    //- Destructor
+    virtual ~FLMAsurfaceFormat()
+    {}
+
+
+    // Member Functions
+
+        //- Write surface mesh components by proxy
+        static void write
+        (
+            const fileName&,
+            const MeshedSurfaceProxy<Face>&
+        );
+
+        //- Write flma file
+        virtual void write(const fileName& name) const
+        {
+            write(name, MeshedSurfaceProxy<Face>(*this));
+        }
+
+};
+
+
+/*---------------------------------------------------------------------------*\
+                     Class FLMAZsurfaceFormat Declaration
+\*---------------------------------------------------------------------------*/
+
+template<class Face>
+class FLMAZsurfaceFormat
+:
+    public FLMAsurfaceFormat<Face>
+{
+    // Private Member Functions
+
+        //- Disallow default bitwise copy construct
+        FLMAZsurfaceFormat(const FLMAZsurfaceFormat<Face>&) = delete;
+
+        //- Disallow default bitwise assignment
+        void operator=(const FLMAZsurfaceFormat<Face>&) = delete;
+
+
+public:
+
+    // Constructors
+
+        //- Construct null
+        FLMAZsurfaceFormat()
+        {}
+
+
+    //- Destructor
+    virtual ~FLMAZsurfaceFormat()
+    {}
+
+
+    // Member Functions
+
+        //- Write surface mesh components by proxy
+        static void write
+        (
+            const fileName&,
+            const MeshedSurfaceProxy<Face>&
+        );
+
+        //- Write flmaz file
+        virtual void write(const fileName& name) const
+        {
+            write(name, MeshedSurfaceProxy<Face>(*this));
+        }
+
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace fileFormats
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#ifdef NoRepository
+#   include "FLMAsurfaceFormat.C"
+#endif
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/surfMesh/surfaceFormats/fire/FLMAsurfaceFormatRunTime.C b/src/surfMesh/surfaceFormats/fire/FLMAsurfaceFormatRunTime.C
new file mode 100644
index 0000000000000000000000000000000000000000..a4b00b40f0ccdb24606a2f380bb4aff92fbe379e
--- /dev/null
+++ b/src/surfMesh/surfaceFormats/fire/FLMAsurfaceFormatRunTime.C
@@ -0,0 +1,82 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2016 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 "FLMAsurfaceFormat.H"
+
+#include "addToRunTimeSelectionTable.H"
+#include "addToMemberFunctionSelectionTable.H"
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+namespace Foam
+{
+namespace fileFormats
+{
+
+// write MeshedSurfaceProxy
+addNamedTemplatedToMemberFunctionSelectionTable
+(
+    MeshedSurfaceProxy,
+    FLMAsurfaceFormat,
+    face,
+    write,
+    fileExtension,
+    flma
+);
+addNamedTemplatedToMemberFunctionSelectionTable
+(
+    MeshedSurfaceProxy,
+    FLMAsurfaceFormat,
+    triFace,
+    write,
+    fileExtension,
+    flma
+);
+
+
+// write MeshedSurfaceProxy (comnpressed versions of above)
+addNamedTemplatedToMemberFunctionSelectionTable
+(
+    MeshedSurfaceProxy,
+    FLMAZsurfaceFormat,
+    face,
+    write,
+    fileExtension,
+    flmaz
+);
+addNamedTemplatedToMemberFunctionSelectionTable
+(
+    MeshedSurfaceProxy,
+    FLMAZsurfaceFormat,
+    triFace,
+    write,
+    fileExtension,
+    flmaz
+);
+
+}
+}
+
+// ************************************************************************* //
diff --git a/src/surfMesh/surfaceFormats/starcd/STARCDsurfaceFormat.C b/src/surfMesh/surfaceFormats/starcd/STARCDsurfaceFormat.C
index c4ef94cb383e040490dd58c0b91f8231a2fe2b91..219448967cfb40b8c481d016e8ecf62452ff4318 100644
--- a/src/surfMesh/surfaceFormats/starcd/STARCDsurfaceFormat.C
+++ b/src/surfMesh/surfaceFormats/starcd/STARCDsurfaceFormat.C
@@ -38,10 +38,10 @@ inline void Foam::fileFormats::STARCDsurfaceFormat<Face>::writeShell
 )
 {
     os  << cellId                    // includes 1 offset
-        << ' ' << starcdShellShape_  // 3(shell) shape
+        << ' ' << starcdShell        // 3(shell) shape
         << ' ' << f.size()
         << ' ' << cellTableId
-        << ' ' << starcdShellType_;  // 4(shell)
+        << ' ' << starcdShellType;   // 4(shell)
 
     // primitives have <= 8 vertices, but prevent overrun anyhow
     // indent following lines for ease of reading
@@ -85,15 +85,10 @@ bool Foam::fileFormats::STARCDsurfaceFormat<Face>::read
     fileName baseName = filename.lessExt();
 
     // read cellTable names (if possible)
-    Map<word> cellTableLookup;
-
-    {
-        IFstream is(baseName + ".inp");
-        if (is.good())
-        {
-            cellTableLookup = readInpCellTable(is);
-        }
-    }
+    Map<word> cellTableLookup = readInpCellTable
+    (
+        IFstream(starFileName(baseName, STARCDCore::INP_FILE))()
+    );
 
 
     // STAR-CD index of points
@@ -102,7 +97,7 @@ bool Foam::fileFormats::STARCDsurfaceFormat<Face>::read
     // read points from .vrt file
     readPoints
     (
-        IFstream(baseName + ".vrt")(),
+        IFstream(starFileName(baseName, STARCDCore::VRT_FILE))(),
         this->storedPoints(),
         pointId
     );
@@ -115,10 +110,10 @@ bool Foam::fileFormats::STARCDsurfaceFormat<Face>::read
     }
     pointId.clear();
 
-    //
+
     // read .cel file
     // ~~~~~~~~~~~~~~
-    IFstream is(baseName + ".cel");
+    IFstream is(starFileName(baseName, STARCDCore::CEL_FILE));
     if (!is.good())
     {
         FatalErrorInFunction
@@ -126,7 +121,7 @@ bool Foam::fileFormats::STARCDsurfaceFormat<Face>::read
             << exit(FatalError);
     }
 
-    readHeader(is, "PROSTAR_CELL");
+    readHeader(is, STARCDCore::HEADER_CEL);
 
     DynamicList<Face>  dynFaces;
     DynamicList<label> dynZones;
@@ -162,7 +157,7 @@ bool Foam::fileFormats::STARCDsurfaceFormat<Face>::read
             vertexLabels.append(mapPointId[vrtId]);
         }
 
-        if (typeId == starcdShellType_)
+        if (typeId == starcdShellType)
         {
             // Convert groupID into zoneID
             Map<label>::const_iterator fnd = lookup.find(cellTableId);
@@ -262,9 +257,13 @@ void Foam::fileFormats::STARCDsurfaceFormat<Face>::write
 
     fileName baseName = filename.lessExt();
 
-    writePoints(OFstream(baseName + ".vrt")(), pointLst);
-    OFstream os(baseName + ".cel");
-    writeHeader(os, "CELL");
+    writePoints
+    (
+        OFstream(starFileName(baseName, STARCDCore::VRT_FILE))(),
+        pointLst
+    );
+    OFstream os(starFileName(baseName, STARCDCore::CEL_FILE));
+    writeHeader(os, STARCDCore::HEADER_CEL);
 
     label faceIndex = 0;
     forAll(zones, zoneI)
@@ -292,7 +291,7 @@ void Foam::fileFormats::STARCDsurfaceFormat<Face>::write
     // write simple .inp file
     writeCase
     (
-        OFstream(baseName + ".inp")(),
+        OFstream(starFileName(baseName, STARCDCore::INP_FILE))(),
         pointLst,
         faceLst.size(),
         zones
diff --git a/src/surfMesh/surfaceFormats/starcd/STARCDsurfaceFormat.H b/src/surfMesh/surfaceFormats/starcd/STARCDsurfaceFormat.H
index 8e4c7e09413afe588229743af16744939d523f66..8d21f2cd870514f4fbd43fd1547206bcf786aa65 100644
--- a/src/surfMesh/surfaceFormats/starcd/STARCDsurfaceFormat.H
+++ b/src/surfMesh/surfaceFormats/starcd/STARCDsurfaceFormat.H
@@ -3,7 +3,7 @@
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
     \\  /    A nd           | Copyright (C) 2011-2016 OpenFOAM Foundation
-     \\/     M anipulation  |
+     \\/     M anipulation  | Copyright (C) 2016 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -63,15 +63,6 @@ class STARCDsurfaceFormat
     public MeshedSurface<Face>,
     public STARCDsurfaceFormatCore
 {
-    // Private Data
-
-        //- STAR-CD identifier for shell shapes (2d elements)
-        static const int starcdShellShape_ = 3;
-
-        //- STAR-CD identifier for shell type (shells vs. baffles)
-        static const int starcdShellType_  = 4;
-
-
     // Private Member Functions
 
         static inline void writeShell
@@ -83,10 +74,10 @@ class STARCDsurfaceFormat
         );
 
         //- Disallow default bitwise copy construct
-        STARCDsurfaceFormat(const STARCDsurfaceFormat<Face>&);
+        STARCDsurfaceFormat(const STARCDsurfaceFormat<Face>&) = delete;
 
         //- Disallow default bitwise assignment
-        void operator=(const STARCDsurfaceFormat<Face>&);
+        void operator=(const STARCDsurfaceFormat<Face>&) = delete;
 
 
 public:
diff --git a/src/surfMesh/surfaceFormats/starcd/STARCDsurfaceFormatCore.C b/src/surfMesh/surfaceFormats/starcd/STARCDsurfaceFormatCore.C
index c73db97ddb5c625a6a02b6843dd9d72f93ab823d..8200a5b58be44a9e66b7d1c07e0dcf00bbf1fac6 100644
--- a/src/surfMesh/surfaceFormats/starcd/STARCDsurfaceFormatCore.C
+++ b/src/surfMesh/surfaceFormats/starcd/STARCDsurfaceFormatCore.C
@@ -3,7 +3,7 @@
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
     \\  /    A nd           | Copyright (C) 2011 OpenFOAM Foundation
-     \\/     M anipulation  |
+     \\/     M anipulation  | Copyright (C) 2016 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -42,6 +42,11 @@ Foam::fileFormats::STARCDsurfaceFormatCore::readInpCellTable
 {
     Map<word> lookup;
 
+    if (!is.good())
+    {
+        return lookup;
+    }
+
     regExp ctnameRE
     (
         " *CTNA[^ ]*"        // keyword - min 4 chars