diff --git a/src/fileFormats/Make/files b/src/fileFormats/Make/files
index cf37026392c8c3978a521d7e1634638287604a26..928615cb5beca0050b91edc26dff5496e8c32c82 100644
--- a/src/fileFormats/Make/files
+++ b/src/fileFormats/Make/files
@@ -25,6 +25,7 @@ $(part)/surface/ensightOutputSurface.C
 ensight/read/ensightReadFile.C
 ensight/type/ensightPTraits.C
 
+abaqus/ABAQUSCore.C
 nastran/NASCore.C
 obj/OBJstream.C
 fire/FIRECore.C
diff --git a/src/fileFormats/abaqus/ABAQUSCore.C b/src/fileFormats/abaqus/ABAQUSCore.C
new file mode 100644
index 0000000000000000000000000000000000000000..356bffa3c1d99211cbca07db08f5ba4e25198f47
--- /dev/null
+++ b/src/fileFormats/abaqus/ABAQUSCore.C
@@ -0,0 +1,695 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | www.openfoam.com
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+    Copyright (C) 2020 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 "ABAQUSCore.H"
+#include "IFstream.H"
+#include "ListOps.H"
+#include "stringOps.H"
+#include "UIListStream.H"
+
+#undef Foam_readAbaqusSurface
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+static Foam::Map<Foam::labelList> abaqusToFoamFaceAddr_;
+
+
+// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+// Use peek(), get(), unget() to detect and skip lines that appear to be
+// "** comment" lines.
+//
+// Uses a mix of std::istream and ISstream methods
+// since we need the low-level get()/unget()
+
+static void skipComments(ISstream& iss)
+{
+    #if OPENFOAM < 2002
+    string line;
+    #endif
+
+    auto& is = iss.stdStream();
+
+    bool isComment = true;
+    while (isComment)
+    {
+        isComment = ('*' == is.peek());
+
+        if (isComment)
+        {
+            // Get and check the next one
+            (void) is.get();
+
+            isComment = ('*' == is.peek());
+            if (isComment)
+            {
+                // Found "** ..." (a comment) - read/discard
+                // ISstream::getLine to keep track of the line numbers
+
+                #if OPENFOAM >= 2002
+                iss.getLine(nullptr);
+                #else
+                iss.getLine(line);
+                #endif
+            }
+            else
+            {
+                // Not a comment
+                // - unget the '*', implicitly break out of loop
+                is.unget();
+            }
+        }
+    }
+}
+
+
+// Get an identifier of the form "NSET=..." (case-insensitive)
+// Return the string on success, an empty string on failure
+
+static string getIdentifier(const word& keyword, string& inputLine)
+{
+    // Strip out whitespace (not a valid Abaqus identifier anyhow)
+    // - makes parsing easier, avoids tab/carriage-returns etc.
+
+    stringOps::inplaceRemoveSpace(inputLine);
+
+    // Do string comparisons in upper-case
+
+    const auto key(stringOps::upper(keyword));
+    const auto line(stringOps::upper(inputLine));
+
+    // Extract "..,key=value,key2=value,"
+
+    // Not sure if we need the additional ',' prefix
+    // in search to avoid similar keys.
+
+    auto beg = line.find("," + key + "=");
+
+    if (beg != std::string::npos)
+    {
+        // Skip past the '='
+        beg += key.size() + 2;
+
+        // The closing comma
+        auto len = line.find(',', beg);
+        if (len != std::string::npos)
+        {
+            len -= beg;
+        }
+
+        // Substring from inputLine (not uppercase!)
+        return inputLine.substr(beg, len);
+    }
+
+    // Not found
+    return string();
+}
+
+
+// Walk the string content (CSV format) to append integer labels
+// until the line is exhausted or the list is full.
+//
+// Return false on read error or if the line exhausted while getting
+// element.
+
+static bool appendCsvLabels
+(
+    const std::string& line,
+    labelUList& elemNodes,
+    label& nodei
+)
+{
+    const label nNodes = elemNodes.size();
+
+    std::size_t pos = 0;
+
+    while (nodei < nNodes && pos != std::string::npos)
+    {
+        auto beg = pos;
+        auto len = line.find(',', pos);
+
+        if (len == std::string::npos)
+        {
+            pos = len;
+        }
+        else
+        {
+            pos = len + 1;
+            len -= beg;
+        }
+
+        if (readLabel(line.substr(beg, len), elemNodes[nodei]))
+        {
+            ++nodei;
+        }
+        else
+        {
+            // Read error, or need another line
+            return false;
+        }
+    }
+
+    return (nodei >= nNodes);
+}
+
+
+} // End namespace Foam
+
+
+// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
+
+const Foam::Map<Foam::labelList>&
+Foam::fileFormats::ABAQUSCore::abaqusToFoamFaceAddr()
+{
+    if (abaqusToFoamFaceAddr_.empty())
+    {
+        abaqusToFoamFaceAddr_.emplace(abaqusTet, labelList({3, 2, 0, 1}));
+        abaqusToFoamFaceAddr_.emplace(abaqusPrism, labelList({0, 1, 4, 3, 2}));
+        abaqusToFoamFaceAddr_.emplace(abaqusHex, labelList({4, 5, 2, 1, 3, 0}));
+    }
+
+    return abaqusToFoamFaceAddr_;
+}
+
+
+Foam::fileFormats::ABAQUSCore::shapeType
+Foam::fileFormats::ABAQUSCore::getElementType(const std::string& elemTypeName)
+{
+    // Check for element-type
+    #undef checkElemType
+    #define checkElemType(test) (elemTypeName.find(test) != std::string::npos)
+
+    if
+    (
+        checkElemType("S3")
+     || checkElemType("CPE3")
+     || checkElemType("2D3")
+    )
+    {
+        return shapeType::abaqusTria;
+    }
+    else if
+    (
+        checkElemType("S4")
+     || checkElemType("CPE4")
+     || checkElemType("2D4")
+     || checkElemType("CPEG4")
+    )
+    {
+        return shapeType::abaqusQuad;
+    }
+    else if
+    (
+        checkElemType("3D4")    // C3D4*, Q3D4, ...
+    )
+    {
+        return shapeType::abaqusTet;
+    }
+    else if
+    (
+        checkElemType("3D5")    // C3D5*
+    )
+    {
+        return shapeType::abaqusPyr;
+    }
+    else if
+    (
+        checkElemType("3D6")    // C3D6*
+    )
+    {
+        return shapeType::abaqusPrism;
+    }
+    else if
+    (
+        checkElemType("3D8")    // C3D8*
+    )
+    {
+        return shapeType::abaqusHex;
+    }
+
+    #undef checkElemType
+
+    return shapeType::abaqusUnknownShape;
+}
+
+
+// * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
+
+Foam::label
+Foam::fileFormats::ABAQUSCore::readHelper::addNewElset
+(
+    const std::string& setName
+)
+{
+    if (elsetMap_.empty())
+    {
+        // Always have a lookup for empty string
+        elsetMap_.set(string::null, 0);
+    }
+
+    if (setName.empty())
+    {
+        return 0;
+    }
+
+    // Direct case-sensitive lookup - it might be there
+
+    label setId = elsetMap_.lookup(setName, -1);
+    if (setId >= 0)
+    {
+        return setId;
+    }
+
+
+    // Case-insensitive search, use upper-case
+
+    const auto needle(stringOps::upper(setName));
+
+    forAllConstIters(elsetMap_, iter)
+    {
+        const auto haystack(stringOps::upper(iter.key()));
+
+        if (needle == haystack)
+        {
+            return iter.val();
+        }
+    }
+
+    // Not there. Save at the next location
+    setId = elsetMap_.size();
+    elsetMap_.set(setName, setId);
+
+    return setId;
+}
+
+
+Foam::label
+Foam::fileFormats::ABAQUSCore::readHelper::readPoints
+(
+    ISstream& is
+)
+{
+    const label initialCount = points_.size();
+
+    char sep; // Comma separator (dummy)
+    label id;
+    point p;
+
+    string line;
+
+    // Read nodes (points) until next "*Section"
+    while (is.peek() != '*' && is.peek() != EOF)
+    {
+        // Grab the line and wrap as string-stream
+        is.getLine(line);
+        UIListStream ss(line.data(), line.length());
+
+        if (line.empty())
+        {
+            // Not sure if we should terminate on blank lines?
+            continue;
+        }
+
+        // Parse line for ID, X, Y, Z
+        ss >> id >> sep >> p.x() >> sep >> p.y() >> sep >> p.z();
+
+        nodeIds_.append(id);
+        points_.append(p);
+    }
+
+    return (points_.size() - initialCount);
+}
+
+
+Foam::label
+Foam::fileFormats::ABAQUSCore::readHelper::readElements
+(
+    ISstream& is,
+    const ABAQUSCore::shapeType shape,
+    const label setId
+)
+{
+    // Info<< "*Element" << nl;
+
+    const label nNodes = ABAQUSCore::nPoints(shape);
+
+    if (!nNodes)
+    {
+        return 0;
+    }
+
+    const label initialCount = elemTypes_.size();
+
+    char sep; // Comma separator (dummy)
+    label id;
+    labelList elemNodes(nNodes, Zero);
+
+    string line;
+
+    // Read element connectivity until next "*Section"
+
+    // Parse for ID, node1, node2, ...
+    while (is.peek() != '*' && is.peek() != EOF)
+    {
+        // elemNodes = Zero;   for sanity checks?
+
+        is >> id >> sep;
+
+        label nodei = 0;
+        while (nodei < nNodes)
+        {
+            // Grab the rest of the line, walk through CSV fields
+            is.getLine(line);
+
+            appendCsvLabels(line, elemNodes, nodei);
+        }
+
+        // Checks?
+
+        connectivity_.append(elemNodes);
+        elemTypes_.append(shape);
+        elemIds_.append(id);
+        elsetIds_.append(setId);
+    }
+
+    return (elemTypes_.size() - initialCount);
+}
+
+
+void Foam::fileFormats::ABAQUSCore::readHelper::read
+(
+    ISstream& is
+)
+{
+    clear();
+
+    label nread;
+    string line;
+
+    while (is.good())
+    {
+        is.getLine(line);
+
+        // Start processing on "*Section-Name",
+        // but skip "** comments" etc
+        if (line[0] != '*' || !std::isalpha(line[1]))
+        {
+            continue;
+        }
+
+        // Some abaqus files use upper-case or mixed-case for section names,
+        // convert all to upper-case for ease.
+
+        string upperLine(stringOps::upper(line));
+
+        // "*Nodes" section
+        if (upperLine.starts_with("*NODE"))
+        {
+            // Ignore "NSET=...", we cannot do anything useful with it
+
+            skipComments(is);
+
+            nread = readPoints(is);
+
+            if (verbose_)
+            {
+                InfoErr
+                    << "Read " << nread << " *NODE entries" << nl;
+            }
+            continue;
+        }
+
+        // "*Element" section
+        if (upperLine.starts_with("*ELEMENT,"))
+        {
+            // Must have "TYPE=..."
+            auto elemTypeName = getIdentifier("TYPE", line);
+
+            // May have "ELSET=..." on the same line
+            string elsetName(getIdentifier("ELSET", line));
+
+            const shapeType shape(getElementType(elemTypeName));
+
+            if (!ABAQUSCore::nPoints(shape))
+            {
+                // Unknown/unsupported
+                if (verbose_)
+                {
+                    InfoErr
+                        << "Ignore abaqus element type: "
+                        << elemTypeName << nl;
+                }
+                continue;
+            }
+
+            const label elsetId = addNewElset(elsetName);
+
+            skipComments(is);
+
+            nread = readElements(is, shape, elsetId);
+
+            if (verbose_)
+            {
+                InfoErr
+                    << "Read " << nread << " *ELEMENT entries ("
+                    << elemTypeName << ") elset="
+                    << elsetName << nl;
+            }
+            continue;
+        }
+
+
+        // "*Surface" section
+        if (upperLine.starts_with("*SURFACE,"))
+        {
+            #ifdef Foam_readAbaqusSurface
+
+            skipComments(is);
+
+            #else
+            Info<< "Reading of abaqus surfaces not implemented" << nl;
+            #endif
+
+            continue;
+        }
+    }
+}
+
+
+void Foam::fileFormats::ABAQUSCore::readHelper::purge_solids()
+{
+    // Negative set
+    bitSet select(elemTypes_.size(), false);
+
+    forAll(elemTypes_, i)
+    {
+        if (!isValidType(elemTypes_[i]) || isSolidType(elemTypes_[i]))
+        {
+            select.set(i);
+        }
+    }
+
+    if (select.any())
+    {
+        select.flip();
+
+        inplaceSubset(select, connectivity_);
+        inplaceSubset(select, elemTypes_);
+
+        inplaceSubset(select, elemIds_);
+        inplaceSubset(select, elsetIds_);
+    }
+}
+
+
+void Foam::fileFormats::ABAQUSCore::readHelper::compact_nodes()
+{
+    if (!nodeIds_.empty())
+    {
+        // Has original 1-based ids
+        //
+        // Need to convert to local (0-based) points
+        // in the order in which we read them
+        // and compact unused values
+
+        // Could construct a sort order to preserve the original
+        // point order, but that is not likely relevant for anyone.
+
+
+        // Which original node ids actually being used by elements?
+
+        // We may have many ids, but speculate that they are sparse
+        // and have high element numbers.
+
+        // Use a Map instead of labelList.
+
+        Map<label> nodeIdRemapping(2*points_.size());
+
+        // Pass 1: which nodes are being used?
+
+        for (const labelList& elem : connectivity_)
+        {
+            for (const label origId : elem)
+            {
+                nodeIdRemapping(origId) = 0; // any value
+            }
+        }
+
+        // Define compact local points, finalize the node id remapping
+
+        label nPoints = 0;
+        labelList oldToNewLocal(nodeIds_.size(), -1);
+
+        forAll(nodeIds_, i)
+        {
+            const label origId = nodeIds_[i];
+
+            if (nodeIdRemapping.found(origId))
+            {
+                oldToNewLocal[i] = nPoints;
+                nodeIdRemapping(origId) = nPoints;
+                ++nPoints;
+            }
+        }
+
+        // Prune out -1 values (shrinks list)
+        inplaceReorder(oldToNewLocal, points_, true);
+
+        // Relabel the elements
+
+        for (labelList& elem : connectivity_)
+        {
+            for (label& id : elem)
+            {
+                id = nodeIdRemapping[id];
+            }
+        }
+
+        // Done!
+        nodeIds_.clear();
+    }
+    else
+    {
+        // Already numbered (0-based), but perhaps not compacted
+
+        // Which node ids actually being used by elements?
+        bitSet usedNodeIds(points_.size());
+
+        for (const labelList& elem : connectivity_)
+        {
+            usedNodeIds.set(elem);
+        }
+
+        // Compact the numbers
+        labelList oldToNewLocal = invert(points_.size(), usedNodeIds);
+
+        // Prune out -1 values (shrinks list)
+        inplaceReorder(oldToNewLocal, points_, true);
+
+
+        // Renumber non-compact to compact
+        for (labelList& elem : connectivity_)
+        {
+            inplaceRenumber(oldToNewLocal, elem);
+        }
+    }
+}
+
+
+void Foam::fileFormats::ABAQUSCore::writePoints
+(
+    Ostream& os,
+    const UList<point>& points,
+    const scalar scaleFactor
+)
+{
+    if (points.empty())
+    {
+        return;
+    }
+
+    // Set the precision of the points data to 10
+    os.precision(10);
+
+    // Force decimal point for Fortran input
+    os.setf(std::ios::showpoint);
+
+    label vertId = 1;  // 1-based vertex labels
+
+    os << "*NODE" << nl;
+    for (const point& p : points)
+    {
+        // Convert [m] -> [mm] etc
+        os  << "  "
+            << vertId << ", "
+            << (scaleFactor * p.x()) << ','
+            << (scaleFactor * p.y()) << ','
+            << (scaleFactor * p.z()) << nl;
+
+        ++vertId;
+    }
+}
+
+
+Foam::label Foam::fileFormats::ABAQUSCore::faceDecomposition
+(
+    const UList<point>& points,
+    const UList<face>& faces,
+    labelList& decompOffsets,
+    DynamicList<face>& decompFaces
+)
+{
+    // On-demand face decomposition (triangulation)
+
+    decompOffsets.resize(faces.size()+1);
+    decompFaces.clear();
+
+    auto offsetIter = decompOffsets.begin();
+    *offsetIter = 0; // The first offset is always zero
+
+    for (const face& f : faces)
+    {
+        const label n = f.size();
+
+        if (n != 3 && n != 4)
+        {
+            // Decompose non-tri/quad into tris
+            f.triangles(points, decompFaces);
+        }
+
+        // The end offset, which is the next begin offset
+        *(++offsetIter) = decompFaces.size();
+    }
+
+    return decompFaces.size();
+}
+
+
+// ************************************************************************* //
diff --git a/src/fileFormats/abaqus/ABAQUSCore.H b/src/fileFormats/abaqus/ABAQUSCore.H
new file mode 100644
index 0000000000000000000000000000000000000000..6360952ea35c134480ec53d5ff4188a86bd36905
--- /dev/null
+++ b/src/fileFormats/abaqus/ABAQUSCore.H
@@ -0,0 +1,306 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | www.openfoam.com
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+    Copyright (C) 2020 OpenCFD Ltd.
+-------------------------------------------------------------------------------
+License
+    This file is part of OpenFOAM.
+
+    OpenFOAM is free software: you can redistribute it and/or modify it
+    under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
+
+Class
+    Foam::fileFormats::ABAQUSCore
+
+Description
+    Core routines used when reading/writing ABAQUS files.
+
+    Face mappings for abaqus deduced from libmesh internals
+
+    Tet4 cells
+    \table
+        Face       | OpenFOAM | libmesh | abaqus | starcd
+        (1 2 3)    | 0        | 2       | 2      | 5
+        (0 3 2)    | 1        | 3       | 3      | 4
+        (0 1 3)    | 2        | 1       | 1      | 2
+        (0 2 1)    | 3        | 0       | 0      | 0
+    \endtable
+
+    Pyr5 cells
+    \table
+        Face       | OpenFOAM | libmesh | abaqus | starcd
+        (0 3 2 1)  | 0        | 4       | n/a    | 0
+        (0 4 3)    | 1        | 3       | n/a    | 4
+        (3 4 2)    | 2        | 2       | n/a    | 3
+        (1 2 4)    | 3        | 1       | n/a    | 5
+        (0 1 4)    | 4        | 0       | n/a    | 2
+    \endtable
+
+    Prism6 cells
+    \table
+        Face       | OpenFOAM | libmesh | abaqus | starcd
+        (0 2 1)    | 0        | 0       | 0      | 0
+        (3 4 5)    | 1        | 4       | 1      | 1
+        (0 3 5 2)  | 2        | 3       | 4      | 4
+        (1 2 5 4)  | 3        | 2       | 3      | 5
+        (0 1 4 3)  | 4        | 1       | 2      | 2
+    \endtable
+
+    Hex8 cells
+    \table
+        Face       | OpenFOAM | libmesh | abaqus | starcd
+        (0 4 7 3)  | 0        | 4       | 5      | 4
+        (1 2 6 5)  | 1        | 2       | 3      | 5
+        (0 1 5 4)  | 2        | 1       | 2      | 2
+        (3 7 6 2)  | 3        | 3       | 4      | 3
+        (0 3 2 1)  | 4        | 0       | 0      | 0
+        (4 5 6 7)  | 5        | 5       | 1      | 1
+    \endtable
+
+SourceFiles
+    ABAQUSCore.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef ABAQUSCore_H
+#define ABAQUSCore_H
+
+#include "Fstream.H"
+#include "Enum.H"
+#include "Map.H"
+#include "face.H"
+#include "point.H"
+#include "DynamicList.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+namespace fileFormats
+{
+
+/*---------------------------------------------------------------------------*\
+                   Class fileFormats::ABAQUSCore Declaration
+\*---------------------------------------------------------------------------*/
+
+class ABAQUSCore
+{
+public:
+
+    // Public Data, Declarations
+
+        //- Shape-Type - the values are for internal use only!
+        enum shapeType : uint8_t
+        {
+            abaqusUnknownShape = 0,
+            abaqusTria  = 0x03,
+            abaqusQuad  = 0x04,
+            abaqusTet   = 0x84,
+            abaqusPyr   = 0x85,
+            abaqusPrism = 0x86,
+            abaqusHex   = 0x88
+        };
+
+
+    // Public Functions
+
+        //- Classify named element type (eg, S4R) to known/supported
+        //- element types.
+        //  The input string must be Uppercase!
+        static shapeType getElementType(const std::string& elemTypeName);
+
+        //- The number of points associated with the element type
+        inline static int nPoints(shapeType tag)
+        {
+            return (tag & 0x3F);
+        }
+
+        //- True if element type is not unknown/invalid
+        inline static bool isValidType(shapeType tag)
+        {
+            return tag;
+        }
+
+        //- True if element type is a 2D shell
+        inline static bool isShellType(shapeType tag)
+        {
+            return (tag & 0x07) && !(tag & 0x80);
+        }
+
+        //- True if element type is a 3D element
+        inline static bool isSolidType(shapeType tag)
+        {
+            return (tag & 0x80);
+        }
+
+
+protected:
+
+    // Protected Member Functions
+
+        //- Face addressing from ABAQUS faces to OpenFOAM faces.
+        //  For hex, prism, tet primitive shapes.
+        static const Map<labelList>& abaqusToFoamFaceAddr();
+
+
+    // Protected Classes
+
+        //- Raw reader structure
+        struct readHelper
+        {
+            // General
+
+                //- Additional verbosity
+                bool verbose_;
+
+
+            // Point Handling
+
+                //- Locations of the points (nodes)
+                DynamicList<point> points_;
+
+                //- The 1-based abaqus Id for the point (node)
+                DynamicList<label> nodeIds_;
+
+
+            // Element Handling
+
+                //- The element connectivity.
+                //  Initially uses the abaqus node Id (1-based)
+                //  but remapped to 0-based compact form later.
+                DynamicList<labelList> connectivity_;
+
+                //- The 1-based abaqus Id for the element
+                DynamicList<label> elemIds_;
+
+                //- The element types
+                DynamicList<ABAQUSCore::shapeType> elemTypes_;
+
+                //- The element set ids
+                DynamicList<label> elsetIds_;
+
+                //- Mapping of elem set names
+                HashTable<label, string> elsetMap_;
+
+
+            // Constructos
+
+                //- Default construct
+                explicit readHelper(const bool verbosity=false)
+                :
+                    verbose_(verbosity)
+                {}
+
+
+            // Member Functions
+
+                //- Clear out contents.
+                void clear()
+                {
+                    points_.clear();
+                    nodeIds_.clear();
+
+                    connectivity_.clear();
+                    elemTypes_.clear();
+
+                    elemIds_.clear();
+                    elsetIds_.clear();
+                    elsetMap_.clear();
+                }
+
+                //- Add a new element set name or return an existing one.
+                //  Case-insensitive.
+                label addNewElset(const std::string& setName);
+
+
+                //- Read an abaqus input file
+                void read(ISstream& is);
+
+                //- Read entries within a "*Nodes" section.
+                //  Appends to points and nodeIds lists.
+                //
+                //  \return the number of points read
+                label readPoints(ISstream& is);
+
+                //- Read entries within an "*Element" section.
+                //  If the shape is known/supported, appends to
+                //  connectivity, elemType, elemIds lists.
+                //
+                //  \return the number of elements read
+                label readElements
+                (
+                    ISstream& is,
+                    const ABAQUSCore::shapeType shape,
+                    const label setId = 0
+                );
+
+
+                //- Remove non-shell elements and compact the points
+                void purge_solids();
+
+                //- Compact unused points and relabel connectivity
+                void compact_nodes();
+        };
+
+
+    // Constructors
+
+        //- Default construct
+        ABAQUSCore() = default;
+
+public:
+
+    // Public Member Functions
+
+        //- Write '*NODE' header and entries to file, optionally with scaling
+        //  This is a no-op for an empty list
+        static void writePoints
+        (
+            Ostream& os,
+            const UList<point>& points,
+            const scalar scaleFactor = 1.0
+        );
+
+        //- Calculate face decomposition for non tri/quad faces
+        //
+        //  \param points the surface points
+        //  \param faces  the surface faces
+        //  \param decompOffsets begin/end offsets (size+1) into decompFaces
+        //  \param decompFaces  List of non-tri/quad decomposed into triangles
+        //
+        //  \return number of decomposed faces
+        static label faceDecomposition
+        (
+            const UList<point>& points,
+            const UList<face>& faces,
+            labelList& decompOffsets,
+            DynamicList<face>& decompFaces
+        );
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace fileFormats
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/surfMesh/Make/files b/src/surfMesh/Make/files
index 155153d07e8226d901068a8b69868d6094f2e7f5..05c1609192630e2f78fe1ae360a1bb0cac45e9ab 100644
--- a/src/surfMesh/Make/files
+++ b/src/surfMesh/Make/files
@@ -25,6 +25,8 @@ surfZone/surfZoneIOList.C
 surfaceFormats = surfaceFormats
 $(surfaceFormats)/surfaceFormatsCore.C
 
+$(surfaceFormats)/abaqus/ABAQUSsurfaceFormatCore.C
+$(surfaceFormats)/abaqus/ABAQUSsurfaceFormatRunTime.C
 $(surfaceFormats)/ac3d/AC3DsurfaceFormatCore.C
 $(surfaceFormats)/ac3d/AC3DsurfaceFormatRunTime.C
 $(surfaceFormats)/fire/FLMAsurfaceFormatRunTime.C
@@ -59,6 +61,7 @@ triSurface/patches/surfacePatch.C
 writers = writers
 
 $(writers)/surfaceWriter.C
+$(writers)/abaqus/abaqusSurfaceWriter.C
 $(writers)/boundaryData/boundaryDataSurfaceWriter.C
 $(writers)/ensight/ensightSurfaceWriter.C
 $(writers)/foam/foamSurfaceWriter.C
diff --git a/src/surfMesh/surfaceFormats/abaqus/ABAQUSsurfaceFormat.C b/src/surfMesh/surfaceFormats/abaqus/ABAQUSsurfaceFormat.C
new file mode 100644
index 0000000000000000000000000000000000000000..a9677707bb541dcd3ae62fc9e953e00d159ae4f9
--- /dev/null
+++ b/src/surfMesh/surfaceFormats/abaqus/ABAQUSsurfaceFormat.C
@@ -0,0 +1,351 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | www.openfoam.com
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+    Copyright (C) 2020 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 "ABAQUSsurfaceFormat.H"
+#include "IFstream.H"
+#include "IOmanip.H"
+#include "faceTraits.H"
+#include "stringOps.H"
+
+// * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
+
+template<class Face>
+inline Foam::label Foam::fileFormats::ABAQUSsurfaceFormat<Face>::writeShell
+(
+    Ostream& os,
+    const Face& f,
+    label elemId,
+    const std::string& elsetName,
+    bool header
+)
+{
+    const label n = f.size();
+
+    if (n == 4)
+    {
+        if (header)
+        {
+            os  << "*ELEMENT, TYPE=S4";
+
+            if (!elsetName.empty())
+            {
+                os  << ", ELSET=" << elsetName;
+            }
+            os  << nl;
+        }
+
+        os  << "  "
+            << (++elemId) << ','
+            << (f[0] + 1) << ','
+            << (f[1] + 1) << ','
+            << (f[2] + 1) << ','
+            << (f[3] + 1) << nl;
+    }
+    else
+    {
+        if (header)
+        {
+            os  << "*ELEMENT, TYPE=S3";
+
+            if (!elsetName.empty())
+            {
+                os  << ", ELSET=" << elsetName;
+            }
+            os  << nl;
+        }
+
+        if (n == 3)
+        {
+            os  << "  "
+                << (++elemId) << ','
+                << (f[0] + 1) << ','
+                << (f[1] + 1) << ','
+                << (f[2] + 1) << nl;
+        }
+        else
+        {
+            // simple triangulation about f[0].
+            // better triangulation should have been done before
+
+            for (label fp1 = 1; fp1 < f.size() - 1; ++fp1)
+            {
+                const label fp2 = f.fcIndex(fp1);
+
+                os  << "  "
+                    << (++elemId) << ','
+                    << (f[0] + 1) << ','
+                    << (f[fp1] + 1) << ','
+                    << (f[fp2] + 1) << nl;
+            }
+        }
+    }
+
+    return elemId;
+}
+
+
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+template<class Face>
+Foam::fileFormats::ABAQUSsurfaceFormat<Face>::ABAQUSsurfaceFormat
+(
+    const fileName& filename
+)
+{
+    read(filename);
+}
+
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+template<class Face>
+bool Foam::fileFormats::ABAQUSsurfaceFormat<Face>::read
+(
+    const fileName& filename
+)
+{
+    // Clear everything
+    this->clear();
+
+    IFstream is(filename);
+    if (!is.good())
+    {
+        FatalErrorInFunction
+            << "Cannot read file " << filename << nl
+            << exit(FatalError);
+    }
+
+
+    ABAQUSCore::readHelper reader(true);
+
+    reader.read(is);
+
+    // This needs more work
+
+    // No solids
+    reader.purge_solids();
+    reader.compact_nodes();
+
+
+    // Convert connectivity to faces
+
+    DynamicList<Face> dynFaces(reader.connectivity_.size());
+
+    for (labelList& conn : reader.connectivity_)
+    {
+        dynFaces.append(Face(std::move(conn)));
+    }
+
+
+    // Rationalize the zones (elset)
+
+    // Only retain element sets that are actually used
+    labelHashSet elsetUsed(reader.elsetIds_);
+
+    labelList newToOldZone(elsetUsed.sortedToc());
+
+    // Extra safety
+    if (newToOldZone.empty())
+    {
+        newToOldZone.resize(1, Zero);
+    }
+
+    Map<label> oldToNewZone(2*newToOldZone.size());
+
+    forAll(newToOldZone, zonei)
+    {
+        oldToNewZone.set(newToOldZone[zonei], zonei);
+    }
+
+    wordList  zoneNames(newToOldZone.size());
+    labelList zoneSizes(newToOldZone.size(), Zero);
+
+    forAllConstIters(reader.elsetMap_, iter)
+    {
+        const label zonei = oldToNewZone.lookup(iter.val(), -1);
+
+        if (zonei >= 0)
+        {
+            zoneNames[zonei] = word::validate(iter.key());
+        }
+    }
+
+    // No empty strings
+    forAll(zoneNames, zonei)
+    {
+        if (zoneNames[zonei].empty())
+        {
+            zoneNames[zonei] = surfZoneIdentifier::defaultName(zonei);
+        }
+    }
+
+    // Steal the elset Ids for our zones
+    DynamicList<label> dynZones(std::move(reader.elsetIds_));
+
+    // Renumber elset -> zoneId and increment the count
+    for (label& zonei : dynZones)
+    {
+        zonei = oldToNewZone.lookup(zonei, 0);
+
+        ++zoneSizes[zonei];
+    }
+
+
+    // Transfer to normal lists
+    this->storedPoints().transfer(reader.points_);
+
+    this->sortFacesAndStore
+    (
+        dynFaces,
+        dynZones,
+        reader.elemIds_,
+        true // sorted
+    );
+
+    // Add zones (retaining empty ones)
+    this->addZones(zoneSizes, zoneNames);
+    this->addZonesToFaces(); // for labelledTri
+
+    return true;
+}
+
+
+template<class Face>
+void Foam::fileFormats::ABAQUSsurfaceFormat<Face>::write
+(
+    const fileName& filename,
+    const MeshedSurfaceProxy<Face>& surf,
+    IOstreamOption streamOpt,
+    const dictionary&
+)
+{
+    // ASCII only, allow output compression
+    streamOpt.format(IOstream::ASCII);
+
+    const UList<point>& pointLst = surf.points();
+    const UList<Face>&  faceLst  = surf.surfFaces();
+    const UList<label>& faceMap  = surf.faceMap();
+    const UList<label>& elemIds  = surf.faceIds();
+
+    // for no zones, suppress the group name
+    const surfZoneList zones =
+    (
+        surf.surfZones().empty()
+      ? surfaceFormatsCore::oneZone(faceLst, "")
+      : surf.surfZones()
+    );
+
+    const bool useFaceMap = (surf.useFaceMap() && zones.size() > 1);
+
+    // Possible to use faceIds?
+    bool useOrigFaceIds =
+        (!useFaceMap && elemIds.size() == faceLst.size());
+
+    if (useOrigFaceIds)
+    {
+        // Not possible with on-the-fly face decomposition
+
+        for (const auto& f : faceLst)
+        {
+            if (f.size() > 4)
+            {
+                useOrigFaceIds = false;
+                break;
+            }
+        }
+    }
+
+
+    OFstream os(filename, streamOpt);
+    if (!os.good())
+    {
+        FatalErrorInFunction
+            << "Cannot write file " << filename << nl
+            << exit(FatalError);
+    }
+
+
+    os  << "*HEADING" << nl;
+
+    os  << nl
+        << "**" << nl
+        << "** Points" << nl
+        << "**" << nl;
+
+    writePoints(os, pointLst);
+
+    os  << "**" << nl
+        << "** Faces" << nl
+        << "**" << nl
+        << nl;
+
+
+    // Simple tracking for change of element type/set
+    labelPair prevOutput(-1, -1);
+
+    label faceIndex = 0;
+    label zoneIndex = 0;
+    label elemId = 0;
+
+    for (const surfZone& zone : zones)
+    {
+        for (label nLocal = zone.size(); nLocal--; ++faceIndex)
+        {
+            const label facei =
+                (useFaceMap ? faceMap[faceIndex] : faceIndex);
+
+            const Face& f = faceLst[facei];
+
+            if (useOrigFaceIds)
+            {
+                elemId = elemIds[facei];
+            }
+
+            const label n = f.size();
+
+            bool header =
+                (prevOutput.first() != n || prevOutput.second() != zoneIndex);
+
+            if (header)
+            {
+                // Update values
+                prevOutput.first() = n;
+                prevOutput.second() = zoneIndex;
+            }
+
+            elemId = writeShell(os, f, elemId, zone.name(), header);
+        }
+
+        ++zoneIndex;
+    }
+
+    os  << "**" << nl
+        << "**" << nl;
+}
+
+
+// ************************************************************************* //
diff --git a/src/surfMesh/surfaceFormats/abaqus/ABAQUSsurfaceFormat.H b/src/surfMesh/surfaceFormats/abaqus/ABAQUSsurfaceFormat.H
new file mode 100644
index 0000000000000000000000000000000000000000..57b1ff4d41e23b891cbd3a7c4cd0baa435e66549
--- /dev/null
+++ b/src/surfMesh/surfaceFormats/abaqus/ABAQUSsurfaceFormat.H
@@ -0,0 +1,144 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | www.openfoam.com
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+    Copyright (C) 2020 OpenCFD Ltd.
+-------------------------------------------------------------------------------
+License
+    This file is part of OpenFOAM.
+
+    OpenFOAM is free software: you can redistribute it and/or modify it
+    under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
+
+Class
+    Foam::fileFormats::ABAQUSsurfaceFormat
+
+Description
+    Abaqus surface reader.
+
+    Output stream options:
+       - ASCII only
+       - compression on/off
+
+    Output dictionary options: ignored
+
+SourceFiles
+    ABAQUSsurfaceFormat.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef ABAQUSsurfaceFormat_H
+#define ABAQUSsurfaceFormat_H
+
+#include "MeshedSurface.H"
+#include "MeshedSurfaceProxy.H"
+#include "UnsortedMeshedSurface.H"
+#include "ABAQUSsurfaceFormatCore.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+namespace fileFormats
+{
+
+/*---------------------------------------------------------------------------*\
+              Class fileFormats::ABAQUSsurfaceFormat Declaration
+\*---------------------------------------------------------------------------*/
+
+template<class Face>
+class ABAQUSsurfaceFormat
+:
+    public MeshedSurface<Face>,
+    public ABAQUSsurfaceFormatCore
+{
+    // Private Member Functions
+
+        //- Output S3 or S4
+        inline static label writeShell
+        (
+            Ostream& os,
+            const Face& f,
+            label elemId,       //!< 0-based element Id
+            const std::string& elsetName,
+            bool header = true
+        );
+
+
+public:
+
+    // Constructors
+
+        //- Default construct
+        ABAQUSsurfaceFormat() = default;
+
+        //- Read construct from file name
+        ABAQUSsurfaceFormat(const fileName& filename);
+
+
+    //- Destructor
+    virtual ~ABAQUSsurfaceFormat() = default;
+
+
+    // Static Member Functions
+
+        //- Write surface mesh components by proxy
+        static void write
+        (
+            const fileName& filename,
+            const MeshedSurfaceProxy<Face>& surf,
+            IOstreamOption streamOpt = IOstreamOption(),
+            const dictionary& /*unused*/ = dictionary::null
+        );
+
+
+    // Member Functions
+
+        //- Read from file
+        virtual bool read
+        (
+            const fileName& filename
+        );
+
+        //- Write surface mesh to file
+        virtual void write
+        (
+            const fileName& name,
+            IOstreamOption streamOpt = IOstreamOption(),
+            const dictionary& options = dictionary::null
+        ) const
+        {
+            write(name, MeshedSurfaceProxy<Face>(*this), streamOpt, options);
+        }
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace fileFormats
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#ifdef NoRepository
+    #include "ABAQUSsurfaceFormat.C"
+#endif
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/surfMesh/surfaceFormats/abaqus/ABAQUSsurfaceFormatCore.C b/src/surfMesh/surfaceFormats/abaqus/ABAQUSsurfaceFormatCore.C
new file mode 100644
index 0000000000000000000000000000000000000000..4e2192dd6a5f51b1c3627ab0160326370080cf3a
--- /dev/null
+++ b/src/surfMesh/surfaceFormats/abaqus/ABAQUSsurfaceFormatCore.C
@@ -0,0 +1,39 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | www.openfoam.com
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+    Copyright (C) 2020 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 "ABAQUSsurfaceFormatCore.H"
+#include "clock.H"
+#include "regExp.H"
+#include "IFstream.H"
+#include "ListOps.H"
+#include "stringOps.H"
+#include "UIListStream.H"
+
+// * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
+
+
+// ************************************************************************* //
diff --git a/src/surfMesh/surfaceFormats/abaqus/ABAQUSsurfaceFormatCore.H b/src/surfMesh/surfaceFormats/abaqus/ABAQUSsurfaceFormatCore.H
new file mode 100644
index 0000000000000000000000000000000000000000..742fb706fa990fc31a580794f931018f9bfe30c3
--- /dev/null
+++ b/src/surfMesh/surfaceFormats/abaqus/ABAQUSsurfaceFormatCore.H
@@ -0,0 +1,76 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | www.openfoam.com
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+    Copyright (C) 2020 OpenCFD Ltd.
+-------------------------------------------------------------------------------
+License
+    This file is part of OpenFOAM.
+
+    OpenFOAM is free software: you can redistribute it and/or modify it
+    under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
+
+Class
+    Foam::fileFormats::ABAQUSsurfaceFormatCore
+
+Description
+    Internal class used by the ABAQUSsurfaceFormat
+
+SourceFiles
+    ABAQUSsurfaceFormatCore.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef ABAQUSsurfaceFormatCore_H
+#define ABAQUSsurfaceFormatCore_H
+
+#include "Fstream.H"
+#include "Ostream.H"
+#include "MeshedSurface.H"
+#include "DynamicList.H"
+#include "ABAQUSCore.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+namespace fileFormats
+{
+
+/*---------------------------------------------------------------------------*\
+            Class fileFormats::ABAQUSsurfaceFormatCore Declaration
+\*---------------------------------------------------------------------------*/
+
+class ABAQUSsurfaceFormatCore
+:
+    public ABAQUSCore
+{
+protected:
+
+    // Protected Static Member Functions
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace fileFormats
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/surfMesh/surfaceFormats/abaqus/ABAQUSsurfaceFormatRunTime.C b/src/surfMesh/surfaceFormats/abaqus/ABAQUSsurfaceFormatRunTime.C
new file mode 100644
index 0000000000000000000000000000000000000000..6b429b36923711df774648e59fa978fd6df71e2b
--- /dev/null
+++ b/src/surfMesh/surfaceFormats/abaqus/ABAQUSsurfaceFormatRunTime.C
@@ -0,0 +1,82 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | www.openfoam.com
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+    Copyright (C) 2020 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 "ABAQUSsurfaceFormat.H"
+
+#include "addToRunTimeSelectionTable.H"
+#include "addToMemberFunctionSelectionTable.H"
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+namespace Foam
+{
+namespace fileFormats
+{
+
+// Read MeshedSurface - tag as .abq for now (.inp name conflict with starcd)
+addNamedTemplatedToRunTimeSelectionTable
+(
+    MeshedSurface,
+    ABAQUSsurfaceFormat,
+    face,
+    fileExtension,
+    abq
+);
+
+// Write with MeshedSurfaceProxy
+addNamedTemplatedToMemberFunctionSelectionTable
+(
+    MeshedSurfaceProxy,
+    ABAQUSsurfaceFormat,
+    face,
+    write,
+    fileExtension,
+    abq
+);
+addNamedTemplatedToMemberFunctionSelectionTable
+(
+    MeshedSurfaceProxy,
+    ABAQUSsurfaceFormat,
+    triFace,
+    write,
+    fileExtension,
+    abq
+);
+addNamedTemplatedToMemberFunctionSelectionTable
+(
+    MeshedSurfaceProxy,
+    ABAQUSsurfaceFormat,
+    labelledTri,
+    write,
+    fileExtension,
+    abq
+);
+
+}
+}
+
+// ************************************************************************* //
diff --git a/src/surfMesh/writers/abaqus/abaqusSurfaceWriter.C b/src/surfMesh/writers/abaqus/abaqusSurfaceWriter.C
new file mode 100644
index 0000000000000000000000000000000000000000..2ad3dc5f416e74af0747603ca884f2d4f5bff6ab
--- /dev/null
+++ b/src/surfMesh/writers/abaqus/abaqusSurfaceWriter.C
@@ -0,0 +1,341 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | www.openfoam.com
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+    Copyright (C) 2020 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 "abaqusSurfaceWriter.H"
+#include "ABAQUSCore.H"
+#include "Pair.H"
+#include "IOmanip.H"
+#include "OSspecific.H"
+#include "surfaceWriterMethods.H"
+#include "addToRunTimeSelectionTable.H"
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+namespace Foam
+{
+namespace surfaceWriters
+{
+    defineTypeName(abaqusWriter);
+    addToRunTimeSelectionTable(surfaceWriter, abaqusWriter, word);
+    addToRunTimeSelectionTable(surfaceWriter, abaqusWriter, wordDict);
+}
+}
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+// Field writing implementation
+#include "abaqusSurfaceWriterImpl.C"
+
+// Field writing methods
+defineSurfaceWriterWriteFields(Foam::surfaceWriters::abaqusWriter);
+
+
+// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+// Write connectivity as CSV list
+inline static void writeConnectivity
+(
+    Ostream& os,
+    const label elemId,
+    const labelUList& elem
+)
+{
+    os  << "  " << elemId;
+
+    for (const label vert : elem)
+    {
+        os << ", " << (vert + 1);
+    }
+
+    os << nl;
+}
+
+} // End namespace Foam
+
+
+// * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
+
+void Foam::surfaceWriters::abaqusWriter::writeFace
+(
+    Ostream& os,
+    const labelUList& f,
+    const label elemId,
+    const label propId,
+    bool header
+) const
+{
+    // Only called with 3 or 4 points!
+
+    if (header)
+    {
+        os  << "*ELEMENT, TYPE=S" << f.size();
+
+        if (propId >= 0)
+        {
+            os  << ", ELSET=_" << propId;
+        }
+
+        os  << nl;
+    }
+
+    writeConnectivity(os, elemId, f);
+}
+
+
+void Foam::surfaceWriters::abaqusWriter::writeGeometry
+(
+    Ostream& os,
+    const meshedSurf& surf,
+    labelList& decompOffsets,
+    DynamicList<face>& decompFaces
+) const
+{
+    const pointField& points = surf.points();
+    const faceList&    faces = surf.faces();
+    const labelList&   zones = surf.zoneIds();
+    const labelList& elemIds = surf.faceIds();
+
+    // Possible to use faceIds?
+    bool useOrigFaceIds = (elemIds.size() == faces.size());
+
+    if (useOrigFaceIds)
+    {
+        // Not possible with on-the-fly face decomposition
+        for (const auto& f : faces)
+        {
+            if (f.size() > 4)
+            {
+                useOrigFaceIds = false;
+                break;
+            }
+        }
+    }
+
+
+    os  << "*HEADING" << nl;
+
+    os  << nl
+        << "**" << nl
+        << "** Points" << nl
+        << "**" << nl;
+
+    fileFormats::ABAQUSCore::writePoints(os, points, geometryScale_);
+
+
+    // Write faces, with on-the-fly decomposition (triangulation)
+    decompOffsets.resize(faces.size()+1);
+    decompFaces.clear();
+
+    decompOffsets[0] = 0; // The first offset is always zero
+
+    os  << "**" << nl
+        << "** Faces" << nl
+        << "**" << nl
+        << nl;
+
+
+    // Simple tracking for change of element type/set
+    labelPair prevOutput(-1, -1);
+
+    label elemId = 0;  // The element-id
+    forAll(faces, facei)
+    {
+        const face& f = faces[facei];
+
+        if (useOrigFaceIds)
+        {
+            // When available and not decomposed
+            elemId = elemIds[facei];
+        }
+
+        // 1-offset for PID
+        const label propId = 1 + (facei < zones.size() ? zones[facei] : 0);
+
+        const label n = f.size();
+
+        bool header =
+            (prevOutput.first() != n || prevOutput.second() != propId);
+
+        if (header)
+        {
+            // Update values
+            prevOutput.first() = n;
+            prevOutput.second() = propId;
+        }
+
+        if (n == 3 || n == 4)
+        {
+            writeFace(os, f, ++elemId, propId, header);
+        }
+        else
+        {
+            // Decompose into tris
+            prevOutput.first() = 3;
+
+            f.triangles(points, decompFaces);
+
+            for
+            (
+                label decompi = decompOffsets[facei];
+                decompi < decompFaces.size();
+                ++decompi
+            )
+            {
+                writeFace
+                (
+                    os,
+                    decompFaces[decompi],
+                    ++elemId,
+                    propId,
+                    header
+                );
+
+                header = false;
+            }
+        }
+
+        // The end offset, which is the next begin offset
+        decompOffsets[facei+1] = decompFaces.size();
+    }
+
+    os  << "**" << nl
+        << "**" << nl;
+}
+
+
+Foam::Ostream& Foam::surfaceWriters::abaqusWriter::writeFooter
+(
+    Ostream& os,
+    const meshedSurf& surf
+) const
+{
+    return os;
+}
+
+
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+Foam::surfaceWriters::abaqusWriter::abaqusWriter()
+:
+    surfaceWriter(),
+    geometryScale_(1),
+    fieldScale_(),
+    noGeometry_(false)
+{}
+
+
+Foam::surfaceWriters::abaqusWriter::abaqusWriter
+(
+    const dictionary& options
+)
+:
+    surfaceWriter(options),
+    geometryScale_(options.getOrDefault<scalar>("scale", 1)),
+    fieldScale_(options.subOrEmptyDict("fieldScale")),
+    noGeometry_(options.getOrDefault("noGeometry", false))
+{}
+
+
+Foam::surfaceWriters::abaqusWriter::abaqusWriter
+(
+    const meshedSurf& surf,
+    const fileName& outputPath,
+    bool parallel,
+    const dictionary& options
+)
+:
+    abaqusWriter(options)
+{
+    open(surf, outputPath, parallel);
+}
+
+
+Foam::surfaceWriters::abaqusWriter::abaqusWriter
+(
+    const pointField& points,
+    const faceList& faces,
+    const fileName& outputPath,
+    bool parallel,
+    const dictionary& options
+)
+:
+    abaqusWriter(options)
+{
+    open(points, faces, outputPath, parallel);
+}
+
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+Foam::fileName Foam::surfaceWriters::abaqusWriter::write()
+{
+    checkOpen();
+
+    // Geometry:  rootdir/<TIME>/surfaceName.abq
+
+    fileName outputFile = outputPath_;
+    if (useTimeDir() && !timeName().empty())
+    {
+        // Splice in time-directory
+        outputFile = outputPath_.path() / timeName() / outputPath_.name();
+    }
+    outputFile.ext("abq");
+
+    if (verbose_)
+    {
+        Info<< "Writing abaqus geometry to " << outputFile << endl;
+    }
+
+
+    const meshedSurf& surf = surface();
+
+    if (Pstream::master() || !parallel_)
+    {
+        if (!isDir(outputFile.path()))
+        {
+            mkDir(outputFile.path());
+        }
+
+        OFstream os(outputFile);
+
+        labelList decompOffsets;
+        DynamicList<face> decompFaces;
+
+        writeGeometry(os, surf, decompOffsets, decompFaces);
+
+        writeFooter(os, surf) << nl;
+    }
+
+    wroteGeom_ = true;
+    return outputFile;
+}
+
+
+// ************************************************************************* //
diff --git a/src/surfMesh/writers/abaqus/abaqusSurfaceWriter.H b/src/surfMesh/writers/abaqus/abaqusSurfaceWriter.H
new file mode 100644
index 0000000000000000000000000000000000000000..e9fafc8d2dd286d764909c1dfab79dc210975e1d
--- /dev/null
+++ b/src/surfMesh/writers/abaqus/abaqusSurfaceWriter.H
@@ -0,0 +1,208 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | www.openfoam.com
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+    Copyright (C) 2012-2016 OpenFOAM Foundation
+    Copyright (C) 2015-2020 OpenCFD Ltd.
+-------------------------------------------------------------------------------
+License
+    This file is part of OpenFOAM.
+
+    OpenFOAM is free software: you can redistribute it and/or modify it
+    under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
+
+Class
+    Foam::surfaceWriters::abaqusWriter
+
+Description
+    A surface writer for the ABAQUS file format - both surface mesh and fields
+
+    The formatOptions for abaqus:
+    \table
+        Property   | Description                            | Required | Default
+        scale      | output geometry scaling                | no    | 1
+        fieldScale | output field scaling (dictionary)      | no    | empty
+        noGeometry | BETA (suppress geometry output)        | no    | false
+    \endtable
+
+    For example,
+    \verbatim
+    formatOptions
+    {
+        abaqus
+        {
+            scale   1000;     // [m] -> [mm]
+            fieldScale
+            {
+               "p.*"   0.01;  // [Pa] -> [mbar]
+            }
+        }
+    }
+    \endverbatim
+
+    \heading Output file locations
+
+     Needs to be adjusted. Currently writes output in raw format.
+
+Note
+    Output variable scaling does not apply to integer types such as Ids.
+
+SourceFiles
+    abaqusSurfaceWriter.C
+    abaqusSurfaceWriterImpl.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef abaqusSurfaceWriter_H
+#define abaqusSurfaceWriter_H
+
+#include "surfaceWriter.H"
+#include "ABAQUSCore.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+namespace surfaceWriters
+{
+
+/*---------------------------------------------------------------------------*\
+                        Class abaqusWriter Declaration
+\*---------------------------------------------------------------------------*/
+
+class abaqusWriter
+:
+    public surfaceWriter
+{
+    // Private Data
+
+        //- Output geometry scaling
+        const scalar geometryScale_;
+
+        //- Output field scaling
+        const dictionary fieldScale_;
+
+        //- BETA feature
+        bool noGeometry_;
+
+
+    // Private Member Functions
+
+        //- Write face element (tri/quad)
+        void writeFace
+        (
+            Ostream& os,
+            const labelUList& f,
+            const label elemId,     //!< 1-based Element Id
+            const label propId,     //!< 1-based Property Id
+            const bool header = true
+        ) const;
+
+        //- Write the surface mesh geometry, tracking face decomposition
+        //
+        //  \param decompOffsets  begin/end offsets (size+1) into decompFaces
+        //  \param decompFaces    Non tri/quad decomposed into triangles
+        void writeGeometry
+        (
+            Ostream& os,
+            const meshedSurf& surf,
+            labelList& decompOffsets,
+            DynamicList<face>& decompFaces
+        ) const;
+
+        //- Write the footer information
+        Ostream& writeFooter(Ostream& os, const meshedSurf& surf) const;
+
+        //- Write a face-based value
+        template<class Type>
+        Ostream& writeFaceValue
+        (
+            Ostream& os,
+            const Type& value,
+            const label elemId      //!< 1-based Element Id
+        ) const;
+
+        //- Templated write operation
+        template<class Type>
+        fileName writeTemplate
+        (
+            const word& fieldName,          //!< Name of field
+            const Field<Type>& localValues  //!< Local field values to write
+        );
+
+
+public:
+
+    //- Declare type-name, virtual type (with debug switch)
+    TypeNameNoDebug("abaqus");
+
+
+    // Constructors
+
+        //- Default construct
+        abaqusWriter();
+
+        //- Construct with some output options
+        explicit abaqusWriter(const dictionary& options);
+
+        //- Construct from components
+        abaqusWriter
+        (
+            const meshedSurf& surf,
+            const fileName& outputPath,
+            bool parallel = Pstream::parRun(),
+            const dictionary& options = dictionary()
+        );
+
+        //- Construct from components
+        abaqusWriter
+        (
+            const pointField& points,
+            const faceList& faces,
+            const fileName& outputPath,
+            bool parallel = Pstream::parRun(),
+            const dictionary& options = dictionary()
+        );
+
+
+    //- Destructor
+    virtual ~abaqusWriter() = default;
+
+
+    // Member Functions
+
+        //- Write surface geometry to file.
+        virtual fileName write(); // override
+
+        declareSurfaceWriterWriteMethod(label);
+        declareSurfaceWriterWriteMethod(scalar);
+        declareSurfaceWriterWriteMethod(vector);
+        declareSurfaceWriterWriteMethod(sphericalTensor);
+        declareSurfaceWriterWriteMethod(symmTensor);
+        declareSurfaceWriterWriteMethod(tensor);
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace surfaceWriters
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/surfMesh/writers/abaqus/abaqusSurfaceWriterImpl.C b/src/surfMesh/writers/abaqus/abaqusSurfaceWriterImpl.C
new file mode 100644
index 0000000000000000000000000000000000000000..9258bc424600687867e2c087ba2fa5e40aa71366
--- /dev/null
+++ b/src/surfMesh/writers/abaqus/abaqusSurfaceWriterImpl.C
@@ -0,0 +1,261 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | www.openfoam.com
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+    Copyright (C) 2020 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 "OFstream.H"
+#include "IOmanip.H"
+#include "OSspecific.H"
+
+// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+    // Emit each component
+    template<class Type>
+    static inline void writeData(Ostream& os, const Type& val)
+    {
+        for (direction cmpt=0; cmpt <  pTraits<Type>::nComponents; ++cmpt)
+        {
+            os  << ' ' << component(val, cmpt);
+        }
+        os  << nl;
+    }
+
+} // End namespace Foam
+
+
+// * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
+
+template<class Type>
+Foam::Ostream& Foam::surfaceWriters::abaqusWriter::writeFaceValue
+(
+    Ostream& os,
+    const Type& value,
+    const label elemId
+) const
+{
+    os << elemId;
+    writeData(os, value);
+
+    return os;
+}
+
+
+template<class Type>
+Foam::fileName Foam::surfaceWriters::abaqusWriter::writeTemplate
+(
+    const word& fieldName,
+    const Field<Type>& localValues
+)
+{
+    checkOpen();
+
+    // Field:  rootdir/<TIME>/field/surfaceName.abq
+
+    fileName outputFile = outputPath_.path();
+    if (useTimeDir() && !timeName().empty())
+    {
+        // Splice in time-directory
+        outputFile /= timeName();
+    }
+    outputFile /= fieldName / outputPath_.name();
+    outputFile.ext("raw");  // stop-gap
+
+
+    // Output scaling for the variable, but not for integer types.
+    // could also solve with clever templating
+
+    const scalar varScale =
+    (
+        std::is_integral<Type>::value
+      ? scalar(1)
+      : fieldScale_.getOrDefault<scalar>(fieldName, 1)
+    );
+
+    if (verbose_)
+    {
+        Info<< "Writing field " << fieldName;
+        if (!equal(varScale, 1))
+        {
+            Info<< " (scaling " << varScale << ')';
+        }
+        Info<< " to " << outputFile << endl;
+    }
+
+
+    // geometry merge() implicit
+    tmp<Field<Type>> tfield = mergeField(localValues);
+
+    const meshedSurf& surf = surface();
+
+    if (Pstream::master() || !parallel_)
+    {
+        const auto& values = tfield();
+
+        if (!isDir(outputFile.path()))
+        {
+            mkDir(outputFile.path());
+        }
+
+        // const scalar timeValue(0);
+
+
+        // Additional bookkeeping for decomposing non tri/quad
+        labelList decompOffsets;
+        DynamicList<face> decompFaces;
+
+        // Separate geometry
+
+        if (noGeometry_)
+        {
+            fileFormats::ABAQUSCore::faceDecomposition
+            (
+                surf.points(),
+                surf.faces(),
+                decompOffsets,
+                decompFaces
+            );
+        }
+        else
+        {
+            OFstream osGeom(outputFile.lessExt().ext("abq"));
+            writeGeometry(osGeom, surf, decompOffsets, decompFaces);
+        }
+
+        // Raw output values. To be improved
+
+        OFstream os(outputFile);
+
+        if (verbose_)
+        {
+            Info<< "Writing raw (abaqus) file to " << os.name() << endl;
+            // Info<< "Writing raw abaqus file to " << os.name() << endl;
+        }
+
+
+        // Regular (undecomposed) faces
+        const faceList&    faces = surf.faces();
+        const labelList& elemIds = surf.faceIds();
+
+
+        // Possible to use faceIds?
+        // Not possible with on-the-fly face decomposition
+        const bool useOrigFaceIds =
+        (
+            elemIds.size() == faces.size()
+         && decompFaces.empty()
+        );
+
+
+        label elemId = 0;
+
+        if (this->isPointData())
+        {
+            forAll(faces, facei)
+            {
+                if (useOrigFaceIds)
+                {
+                    // When available and not decomposed
+                    elemId = elemIds[facei];
+                }
+
+                const label beginElemId = elemId;
+
+                // Any face decomposition
+                for
+                (
+                    label decompi = decompOffsets[facei];
+                    decompi < decompOffsets[facei+1];
+                    ++decompi
+                )
+                {
+                    const face& f = decompFaces[decompi];
+
+                    Type v = Zero;
+                    for (const label verti : f)
+                    {
+                        v += values[verti];
+                    }
+                    v *= (varScale / f.size());
+
+                    writeFaceValue(os, v, ++elemId);
+                }
+
+
+                // Face not decomposed
+                if (beginElemId == elemId)
+                {
+                    const face& f = faces[facei];
+
+                    Type v = Zero;
+                    for (const label verti : f)
+                    {
+                        v += values[verti];
+                    }
+                    v *= (varScale / f.size());
+
+                    writeFaceValue(os, v, ++elemId);
+                }
+            }
+        }
+        else
+        {
+            auto valIter = values.cbegin();
+
+            forAll(faces, facei)
+            {
+                if (useOrigFaceIds)
+                {
+                    // When available and not decomposed
+                    elemId = elemIds[facei];
+                }
+
+                const Type v(varScale * *valIter);
+                ++valIter;
+
+                label nValues =
+                    max
+                    (
+                        label(1),
+                        (decompOffsets[facei+1] - decompOffsets[facei])
+                    );
+
+                while (nValues--)
+                {
+                    writeFaceValue(os, v, ++elemId);
+                }
+            }
+        }
+
+        writeFooter(os, surf);
+    }
+
+    wroteGeom_ = true;
+    return outputFile;
+}
+
+
+// ************************************************************************* //