diff --git a/src/fileFormats/ensight/file/ensightCase.C b/src/fileFormats/ensight/file/ensightCase.C
index 7e6e6be5f18a25f8e1b111d7c83ce6c83a53a86c..9c0e3595377293e88212c87ae5305ba48018e28f 100644
--- a/src/fileFormats/ensight/file/ensightCase.C
+++ b/src/fileFormats/ensight/file/ensightCase.C
@@ -5,7 +5,7 @@
     \\  /    A nd           | www.openfoam.com
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-    Copyright (C) 2016-2018 OpenCFD Ltd.
+    Copyright (C) 2016-2020 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -26,15 +26,10 @@ License
 \*---------------------------------------------------------------------------*/
 
 #include "ensightCase.H"
-#include "stringListOps.H"
+#include "ensightGeoFile.H"
 #include "Time.H"
 #include "cloud.H"
 #include "IOmanip.H"
-#include "globalIndex.H"
-
-#include "ensightFile.H"
-#include "ensightGeoFile.H"
-#include "demandDrivenData.H"
 
 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
 
@@ -69,7 +64,7 @@ void Foam::ensightCase::initialize()
             else
             {
                 DetailInfo
-                    <<"Warning: re-using existing directory" << nl
+                    << "Warning: re-using existing directory" << nl
                     << "    " << ensightDir_ << endl;
             }
         }
@@ -78,7 +73,7 @@ void Foam::ensightCase::initialize()
         mkDir(dataDir());
 
         // The case file is always ASCII
-        os_ = new OFstream(ensightDir_/caseName_, IOstream::ASCII);
+        os_.reset(new OFstream(ensightDir_/caseName_, IOstream::ASCII));
 
         // Format options
         os_->setf(ios_base::left);
@@ -126,7 +121,7 @@ Foam::label Foam::ensightCase::checkTimeset(const labelHashSet& lookup) const
 
 void Foam::ensightCase::writeHeader() const
 {
-    if (os_)  // master only
+    if (os_)  // True on master only
     {
         this->rewind();
         *os_
@@ -218,7 +213,7 @@ void Foam::ensightCase::writeTimeset
     const scalar timeCorrection
 ) const
 {
-    // make a copy
+    // Make a copy
     labelHashSet hashed(lookup);
     hashed.erase(-1);
 
@@ -282,9 +277,10 @@ void Foam::ensightCase::noteGeometry(const bool moving) const
 
 void Foam::ensightCase::noteCloud(const word& cloudName) const
 {
+    // Force into existence
     if (!cloudVars_.found(cloudName))
     {
-        cloudVars_.insert(cloudName, HashTable<string>());
+        cloudVars_.emplace(cloudName);
     }
     cloudTimes_.insert(timeIndex_);
 
@@ -388,9 +384,9 @@ Foam::ensightCase::ensightCase
 )
 :
     options_(new options(opts)),
+    os_(nullptr),
     ensightDir_(ensightDir),
     caseName_(caseName + ".case"),
-    os_(nullptr),
     changed_(false),
     timeIndex_(0),
     timeValue_(0),
@@ -398,6 +394,7 @@ Foam::ensightCase::ensightCase
     geomTimes_(),
     cloudTimes_(),
     variables_(),
+    nodeVariables_(),
     cloudVars_()
 {
     initialize();
@@ -412,9 +409,9 @@ Foam::ensightCase::ensightCase
 )
 :
     options_(new options(format)),
+    os_(nullptr),
     ensightDir_(ensightDir),
     caseName_(caseName + ".case"),
-    os_(nullptr),
     changed_(false),
     timeIndex_(0),
     timeValue_(0),
@@ -422,21 +419,13 @@ Foam::ensightCase::ensightCase
     geomTimes_(),
     cloudTimes_(),
     variables_(),
+    nodeVariables_(),
     cloudVars_()
 {
     initialize();
 }
 
 
-// * * * * * * * * * * * * * * * * Destructor  * * * * * * * * * * * * * * * //
-
-Foam::ensightCase::~ensightCase()
-{
-    deleteDemandDrivenData(options_);
-    deleteDemandDrivenData(os_);
-}
-
-
 // * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
 
 void Foam::ensightCase::nextTime(const scalar value)
@@ -580,7 +569,7 @@ void Foam::ensightCase::write() const
     //
     if (variables_.size() || cloudVars_.size())
     {
-        // start of variables
+        // Start of variables
         *os_
             << nl
             << "VARIABLE" << nl;
@@ -598,7 +587,7 @@ void Foam::ensightCase::write() const
             << ensType.c_str()
             <<
             (
-                nodeValues()
+                (nodeVariables_.found(varName) || nodeValues())
               ? " per node:    1  "  // time-set 1
               : " per element: 1  "  // time-set 1
             )
@@ -678,7 +667,7 @@ void Foam::ensightCase::write() const
 Foam::autoPtr<Foam::ensightGeoFile>
 Foam::ensightCase::newGeometry
 (
-    const bool moving
+    bool moving
 ) const
 {
     autoPtr<Foam::ensightGeoFile> output;
@@ -749,8 +738,12 @@ Foam::Ostream& Foam::ensightCase::printInfo(Ostream& os) const
     os  << "Ensight case:" << nl
         << "   path: "   << ensightDir_ << nl
         << "   name: "   << caseName_   << nl
-        << "   format: " << format()    << nl
-        << "   values per " << (nodeValues() ? "node" : "element") << nl;
+        << "   format: " << format()    << nl;
+
+    if (nodeValues())
+    {
+        os << "   values per node" << nl;
+    }
 
     return os;
 }
diff --git a/src/fileFormats/ensight/file/ensightCase.H b/src/fileFormats/ensight/file/ensightCase.H
index 84791279397178605e3dc8da6224ec5076986ce7..2765522c4e3936516bb902216b743ccb8d47e524 100644
--- a/src/fileFormats/ensight/file/ensightCase.H
+++ b/src/fileFormats/ensight/file/ensightCase.H
@@ -5,7 +5,7 @@
     \\  /    A nd           | www.openfoam.com
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-    Copyright (C) 2016 OpenCFD Ltd.
+    Copyright (C) 2016-2020 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -32,6 +32,9 @@ Description
 
 SourceFiles
     ensightCase.C
+    ensightCaseI.H
+    ensightCaseOptions.C
+    ensightCaseTemplates.C
 
 \*---------------------------------------------------------------------------*/
 
@@ -42,17 +45,18 @@ SourceFiles
 #include "HashSet.H"
 #include "InfoProxy.H"
 #include "Map.H"
+#include "HashSet.H"
 #include "OSspecific.H"
 #include "Pstream.H"
-
 #include "ensightGeoFile.H"
+#include <memory>
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
 namespace Foam
 {
 
-// Forward declarations
+// Forward Declarations
 class ensightCase;
 class instant;
 class Time;
@@ -65,7 +69,7 @@ class ensightCase
 {
 public:
 
-    // Forward declarations
+    // Forward Declarations
     class options;
 
     // Public Data
@@ -76,12 +80,16 @@ public:
         //- The name for geometry files
         static const char* geometryName;
 
+
 private:
 
-    // Private data
+    // Private Data
 
         //- Case writing options
-        const options* options_;
+        const std::unique_ptr<options> options_;
+
+        //- Output stream (master only)
+        mutable std::unique_ptr<OFstream> os_;
 
         //- Output path (absolute)
         fileName ensightDir_;
@@ -89,9 +97,6 @@ private:
         //- Case name (with ".case" ending)
         word caseName_;
 
-        //- Output stream (master only)
-        mutable OFstream* os_;
-
         //- Track state changes since last write
         mutable bool changed_;
 
@@ -119,6 +124,9 @@ private:
         //- Fields/Variables with the ensight type
         mutable HashTable<string> variables_;
 
+        //- Remember fields that are to be treated as point data
+        mutable HashSet<string> nodeVariables_;
+
         //- Cloud names and variables
         mutable HashTable<HashTable<string>> cloudVars_;
 
@@ -214,7 +222,7 @@ public:
 
 
     //- Destructor
-    ~ensightCase();
+    ~ensightCase() = default;
 
 
     // Member Functions
@@ -236,7 +244,7 @@ public:
         //- Consistent zero-padded integer value
         inline word padded(const label i) const;
 
-        //- Use values per nodes instead of per element
+        //- Force use of values per node instead of per element
         inline bool nodeValues() const;
 
         //- Write clouds into their own directory instead in "data" directory
@@ -269,8 +277,7 @@ public:
     // Addition of entries to case file
 
         //- Open stream for new geometry file (on master).
-        autoPtr<ensightGeoFile> newGeometry(const bool moving = false) const;
-
+        autoPtr<ensightGeoFile> newGeometry(bool moving = false) const;
 
         //- Open stream for new cloud positions (on master).
         //  Note the use of ensightFile, not ensightGeoFile.
@@ -279,11 +286,19 @@ public:
             const word& cloudName
         ) const;
 
-
         //- Open stream for new data file (on master), with current index.
+        //  Optionally marking as containing POINT_DATA
         template<class Type>
-        autoPtr<ensightFile> newData(const word& varName) const;
+        autoPtr<ensightFile> newData
+        (
+            const word& varName,
+            const bool isPointData = false
+        ) const;
 
+        //- Open stream for new data file (on master), with current index
+        //- and marking as containing POINT_DATA
+        template<class Type>
+        autoPtr<ensightFile> newPointData(const word& varName) const;
 
         //- Open stream for new cloud data file (on master), with current index.
         template<class Type>
@@ -313,28 +328,29 @@ public:
 //- Configuration options for the ensightCase
 class ensightCase::options
 {
-private:
+    // Private Data
+
+        //- Ascii/Binary file output
+        IOstream::streamFormat format_;
 
-    //- Ascii/Binary file output
-    IOstream::streamFormat format_;
+        //- Remove existing directory and sub-directories on creation
+        bool overwrite_;
 
-    //- Width of mask for subdirectories
-    label width_;
+        //- Force use of values per node instead of per element
+        bool nodeValues_;
 
-    //- The '*' mask appropriate for subdirectories
-    word mask_;
+        //- Write clouds into their own directory
+        bool separateCloud_;
 
-    //- The printf format for zero-padded subdirectory numbers
-    string printf_;
+        //- Width of mask for subdirectories
+        label width_;
 
-    //- Remove existing directory and sub-directories on creation
-    bool overwrite_;
+        //- The '*' mask appropriate for subdirectories
+        word mask_;
 
-    //- Write values at nodes
-    bool nodeValues_;
+        //- The printf format for zero-padded subdirectory numbers
+        string printf_;
 
-    //- Write clouds into their own directory
-    bool separateCloud_;
 
 public:
 
@@ -363,9 +379,6 @@ public:
         //- Remove existing directory and sub-directories on creation
         bool overwrite() const;
 
-        //- Use values per nodes instead of per element
-        bool nodeValues() const;
-
         //- Write clouds into their own directory instead in "data" directory
         bool separateCloud() const;
 
@@ -379,12 +392,20 @@ public:
         //- Remove existing directory and sub-directories on creation
         void overwrite(bool);
 
-        //- Use values per nodes instead of per element
-        void nodeValues(bool);
-
         //- Write clouds into their own directory instead in "data" directory
         void separateCloud(bool);
 
+
+    // Housekeeping
+
+        //- Force use of values per node instead of per element
+        bool nodeValues() const;
+
+        //- Force use of values per node instead of per element
+        //  Deprecated(2020-02) - The newData() method with a second parameter
+        //  is more flexible.
+        //  \deprecated(2020-02) - newData() with second parameter
+        void nodeValues(bool);
 };
 
 
diff --git a/src/fileFormats/ensight/file/ensightCaseOptions.C b/src/fileFormats/ensight/file/ensightCaseOptions.C
index f74cc5a7e8565aefc6e60606d79f223c0fd70760..f3a175638512b6735ccf5d919facc5d6207c98b8 100644
--- a/src/fileFormats/ensight/file/ensightCaseOptions.C
+++ b/src/fileFormats/ensight/file/ensightCaseOptions.C
@@ -32,14 +32,14 @@ License
 Foam::ensightCase::options::options(IOstream::streamFormat format)
 :
     format_(format),
-    width_(0),
-    mask_(),
-    printf_(),
     overwrite_(false),
     nodeValues_(false),
-    separateCloud_(false)
+    separateCloud_(false),
+    width_(0),
+    mask_(),
+    printf_()
 {
-    width(8); // Ensures that mask and printf-format are properly resized
+    width(8);  // Fill mask and setup printf-format
 }
 
 
diff --git a/src/fileFormats/ensight/file/ensightCaseTemplates.C b/src/fileFormats/ensight/file/ensightCaseTemplates.C
index ae496c95c256bb8491c33ce277f42ee0a33ca284..5cbadf5388c14807fac794027af8e405e2ad5d38 100644
--- a/src/fileFormats/ensight/file/ensightCaseTemplates.C
+++ b/src/fileFormats/ensight/file/ensightCaseTemplates.C
@@ -5,7 +5,7 @@
     \\  /    A nd           | www.openfoam.com
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-    Copyright (C) 2016 OpenCFD Ltd.
+    Copyright (C) 2016-2020 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -34,7 +34,8 @@ template<class Type>
 Foam::autoPtr<Foam::ensightFile>
 Foam::ensightCase::newData
 (
-    const word& name
+    const word& name,
+    const bool isPointData
 ) const
 {
     autoPtr<ensightFile> output;
@@ -42,9 +43,10 @@ Foam::ensightCase::newData
     if (Pstream::master())
     {
         const ensight::VarName varName(name);
+
         output = createDataFile(varName);
 
-        // description
+        // Description
         output().write
         (
             string
@@ -55,14 +57,31 @@ Foam::ensightCase::newData
         );
         output().newline();
 
-        // note field variable for later use
+        // Remember the field variable for later use
         noteVariable(varName, ensightPTraits<Type>::typeName);
+
+        // Could warn about existing variables that changed representation
+        if (isPointData)
+        {
+            nodeVariables_.set(varName);
+        }
     }
 
     return output;
 }
 
 
+template<class Type>
+Foam::autoPtr<Foam::ensightFile>
+Foam::ensightCase::newPointData
+(
+    const word& name
+) const
+{
+    return newData<Type>(name, true);  // POINT_DATA
+}
+
+
 template<class Type>
 Foam::autoPtr<Foam::ensightFile>
 Foam::ensightCase::newCloudData
@@ -78,7 +97,7 @@ Foam::ensightCase::newCloudData
         const ensight::VarName varName(name);
         output = createCloudFile(cloudName, varName);
 
-        // description
+        // Description
         output().write
         (
             string
@@ -89,7 +108,7 @@ Foam::ensightCase::newCloudData
         );
         output().newline();
 
-        // note cloud variable for later use
+        // Remember the cloud variable for later use
         noteCloud(cloudName, varName, ensightPTraits<Type>::typeName);
     }
 
diff --git a/src/fileFormats/ensight/file/ensightFile.C b/src/fileFormats/ensight/file/ensightFile.C
index 4cdb931fffdec98dd269af401d3e560ec5c46e2d..f9d041c89b769813f5a6cb7ba7fec0c796720f15 100644
--- a/src/fileFormats/ensight/file/ensightFile.C
+++ b/src/fileFormats/ensight/file/ensightFile.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2015 OpenFOAM Foundation
-    Copyright (C) 2016-2019 OpenCFD Ltd.
+    Copyright (C) 2016-2020 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -29,7 +29,6 @@ License
 #include "ensightFile.H"
 #include "error.H"
 #include "UList.H"
-
 #include <cstring>
 #include <sstream>
 
@@ -39,10 +38,12 @@ bool Foam::ensightFile::allowUndef_ = false;
 
 Foam::scalar Foam::ensightFile::undefValue_ = Foam::floatScalarVGREAT;
 
-// default is width 8
+// Default is width 8
 Foam::string Foam::ensightFile::mask_   = "********";
 Foam::string Foam::ensightFile::dirFmt_ = "%08d";
 
+const char* const Foam::ensightFile::coordinates = "coordinates";
+
 
 // * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
 
@@ -85,6 +86,20 @@ Foam::label Foam::ensightFile::subDirWidth()
 }
 
 
+bool Foam::ensightFile::isUndef(const UList<scalar>& field)
+{
+    for (const scalar& val : field)
+    {
+        if (std::isnan(val))
+        {
+            return true;
+        }
+    }
+
+    return true;
+}
+
+
 // * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
 
 void Foam::ensightFile::initialize()
@@ -153,24 +168,13 @@ Foam::scalar Foam::ensightFile::undefValue(const scalar value)
 }
 
 
-Foam::Ostream& Foam::ensightFile::write
-(
-    const char* buf,
-    std::streamsize count
-)
-{
-    stdStream().write(buf, count);
-    return *this;
-}
-
-
-Foam::Ostream& Foam::ensightFile::write(const char* value)
+Foam::Ostream& Foam::ensightFile::writeString(const char* str)
 {
     // Output 80 chars, but allocate for trailing nul character
     // to avoid -Wstringop-truncation warnings/errors.
 
     char buf[80+1];
-    strncpy(buf, value, 80); // max 80 chars or padded with nul if smaller
+    strncpy(buf, str, 80); // max 80 chars or padded with nul if smaller
 
     if (format() == IOstream::BINARY)
     {
@@ -186,9 +190,38 @@ Foam::Ostream& Foam::ensightFile::write(const char* value)
 }
 
 
-Foam::Ostream& Foam::ensightFile::write(const string& value)
+Foam::Ostream& Foam::ensightFile::writeString(const std::string& str)
+{
+    return writeString(str.c_str());
+}
+
+
+Foam::Ostream& Foam::ensightFile::write(const char* str)
 {
-    return write(value.c_str());
+    return writeString(str);
+}
+
+
+Foam::Ostream& Foam::ensightFile::write(const word& str)
+{
+    return writeString(str);
+}
+
+
+Foam::Ostream& Foam::ensightFile::write(const string& str)
+{
+    return writeString(str);
+}
+
+
+Foam::Ostream& Foam::ensightFile::write
+(
+    const char* buf,
+    std::streamsize count
+)
+{
+    stdStream().write(buf, count);
+    return *this;
 }
 
 
@@ -244,6 +277,14 @@ Foam::Ostream& Foam::ensightFile::write(const scalar value)
 {
     float fvalue(value);
 
+    // TBD: limit range?
+    // #if defined(WM_DP)
+    // if (mag(value) < scalar(floatScalarVSMALL))
+    // {
+    //     fvalue = 0;
+    // }
+    // #endif
+
     if (format() == IOstream::BINARY)
     {
         write
@@ -282,17 +323,17 @@ Foam::Ostream& Foam::ensightFile::writeKeyword(const keyType& key)
 {
     if (allowUndef_)
     {
-        write(string(static_cast<const string&>(key) + " undef"));
+        writeString(key + " undef");
         newline();
         write(undefValue_);
         newline();
     }
     else
     {
-        // ensure we get ensightFile::write(const string&)
-        write(static_cast<const string&>(key));
+        writeString(key);
         newline();
     }
+
     return *this;
 }
 
@@ -301,7 +342,7 @@ Foam::Ostream& Foam::ensightFile::writeBinaryHeader()
 {
     if (format() == IOstream::BINARY)
     {
-        write("C Binary");
+        writeString("C Binary");
     }
 
     return *this;
@@ -314,7 +355,7 @@ Foam::Ostream& Foam::ensightFile::writeBinaryHeader()
 
 void Foam::ensightFile::beginPart(const label index)
 {
-    write("part");
+    writeString("part");
     newline();
     write(index+1); // Ensight starts with 1
     newline();
@@ -323,59 +364,45 @@ void Foam::ensightFile::beginPart(const label index)
 
 void Foam::ensightFile::beginParticleCoordinates(const label nparticles)
 {
-    write("particle coordinates");
+    writeString("particle coordinates");
     newline();
     write(nparticles, 8); // unusual width
     newline();
 }
 
 
-void Foam::ensightFile::writeList(const UList<label>& field)
+void Foam::ensightFile::writeLabels(const UList<label>& list)
 {
-    for (const label val : field)
+    for (const label val : list)
     {
-        write(scalar(val));
+        write(val);
         newline();
     }
 }
 
 
-void Foam::ensightFile::writeList(const UList<scalar>& field)
+void Foam::ensightFile::writeList(const UList<label>& field)
 {
-    for (const scalar& val : field)
+    for (const label val : field)
     {
-        if (std::isnan(val))
-        {
-            writeUndef();
-        }
-        else
-        {
-            write(val);
-        }
-
+        write(scalar(val));
         newline();
     }
 }
 
 
-
-void Foam::ensightFile::writeList
-(
-    const UList<scalar>& field,
-    const labelUList& addr
-)
+void Foam::ensightFile::writeList(const UList<scalar>& field)
 {
-    for (const label id : addr)
+    for (const scalar val : field)
     {
-        if (id < 0 || id >= field.size() || std::isnan(field[id]))
+        if (std::isnan(val))
         {
             writeUndef();
         }
         else
         {
-            write(field[id]);
+            write(val);
         }
-
         newline();
     }
 }
diff --git a/src/fileFormats/ensight/file/ensightFile.H b/src/fileFormats/ensight/file/ensightFile.H
index 6f78b573321099c7f2e06398b43dc6d0a88584f4..2ebaa1e9816749d720fde94816012dbc96693358 100644
--- a/src/fileFormats/ensight/file/ensightFile.H
+++ b/src/fileFormats/ensight/file/ensightFile.H
@@ -37,11 +37,9 @@ Description
 #define ensightFile_H
 
 #include "OFstream.H"
-#include "IOstream.H"
-
 #include "ensightFileName.H"
 #include "ensightVarName.H"
-#include "UList.H"
+#include "IndirectListBase.H"
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
@@ -61,7 +59,7 @@ class ensightFile
         //- Allow undef in results
         static bool allowUndef_;
 
-        //- Value to represent undef in results
+        //- Value to represent undef in results (default: 1e+37, floatVGREAT)
         static scalar undefValue_;
 
         //- The '*' mask appropriate for subDir
@@ -85,6 +83,12 @@ class ensightFile
 
 public:
 
+    // Static Data Members
+
+        //- The keyword "coordinates"
+        static const char* const coordinates;
+
+
     // Static Functions
 
         //- Return a null ensightFile
@@ -118,6 +122,8 @@ public:
     ~ensightFile() = default;
 
 
+    // Member Functions
+
     // Access
 
         //- Return setting for whether 'undef' values are allowed in results
@@ -150,35 +156,42 @@ public:
 
     // Output
 
-        //- Inherit write from Ostream
-        using Ostream::write;
-
-        //- Binary write
-        virtual Ostream& write(const char* buf, std::streamsize count);
-
-        //- Write element keyword with trailing newline, optionally with undef
+        //- Write element keyword with trailing newline,
+        //- optionally with undef and the value for undefined
         virtual Ostream& writeKeyword(const keyType& key);
 
-        //- Write "C Binary" for binary files (eg, geometry/measured)
+        //- Write "C Binary" string for binary files (eg, geometry/measured)
         Ostream& writeBinaryHeader();
 
+        //- Write C-string as "%79s" or as binary (max 80 chars)
+        Ostream& writeString(const char* str);
+
+        //- Write string as "%79s" or as binary (max 80 chars)
+        Ostream& writeString(const std::string& str);
+
         //- Write undef value
         Ostream& writeUndef();
 
-        //- Write C-string as "%79s" or as binary (max 80 chars)
-        Ostream& write(const char*);
+        //- Binary write
+        virtual Ostream& write(const char* buf, std::streamsize count);
 
-        //- Write string as "%79s" or as binary (max 80 chars)
-        Ostream& write(const string&);
+        //- Write C-string, uses writeString()
+        virtual Ostream& write(const char* str);
+
+        //- Write word, uses writeString()
+        virtual Ostream& write(const word& str);
+
+        //- Write string, uses writeString()
+        virtual Ostream& write(const string& str);
 
         //- Write integer as "%10d" or as binary
-        Ostream& write(const label);
+        Ostream& write(const label value);
 
         //- Write integer with specified width or as binary
-        Ostream& write(const label, const label fieldWidth);
+        Ostream& write(const label value, const label fieldWidth);
 
         //- Write float as "%12.5e" or as binary
-        Ostream& write(const scalar);
+        Ostream& write(const scalar value);
 
         //- Add carriage return to ascii stream
         void newline();
@@ -192,7 +205,17 @@ public:
         //- Begin a "particle coordinates" block (measured data)
         void beginParticleCoordinates(const label nparticles);
 
+        //- Write a list of integers
+        //  With carriage return after each value (ascii stream)
+        void writeLabels(const UList<label>& list);
+
+        //- Write a list of integers
+        //  With carriage return after each value (ascii stream)
+        template<class Addr>
+        void writeLabels(const IndirectListBase<label, Addr>& list);
+
         //- Write a list of integers as float values
+        //  With carriage return after each value (ascii stream)
         void writeList(const UList<label>& field);
 
         //- Write a list of floats as "%12.5e" or as binary
@@ -201,7 +224,18 @@ public:
 
         //- Write an indirect list of scalars as "%12.5e" or as binary
         //  With carriage return after each value (ascii stream)
-        void writeList(const UList<scalar>& field, const labelUList& addr);
+        template<class Addr>
+        void writeList(const IndirectListBase<scalar, Addr>& field);
+
+
+    // Other Methods
+
+        //- Check for any NaN in the field
+        static bool isUndef(const UList<scalar>& field);
+
+        //- Check for any NaN in the field
+        template<class Addr>
+        static bool isUndef(const IndirectListBase<scalar, Addr>& field);
 };
 
 
@@ -211,6 +245,12 @@ public:
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
+#ifdef NoRepository
+    #include "ensightFileTemplates.C"
+#endif
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
 #endif
 
 // ************************************************************************* //
diff --git a/src/fileFormats/ensight/file/ensightFileTemplates.C b/src/fileFormats/ensight/file/ensightFileTemplates.C
new file mode 100644
index 0000000000000000000000000000000000000000..97b00009a30f400dc80e62b768484dd5f5bb9e18
--- /dev/null
+++ b/src/fileFormats/ensight/file/ensightFileTemplates.C
@@ -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/>.
+
+\*---------------------------------------------------------------------------*/
+
+// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
+
+template<class Addr>
+bool Foam::ensightFile::isUndef(const IndirectListBase<scalar, Addr>& field)
+{
+    for (const scalar val : field)
+    {
+        if (std::isnan(val))
+        {
+            return true;
+        }
+    }
+
+    return true;
+}
+
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+template<class Addr>
+void Foam::ensightFile::writeLabels(const IndirectListBase<label, Addr>& list)
+{
+    for (const scalar val : list)
+    {
+        write(val);
+        newline();
+    }
+}
+
+
+template<class Addr>
+void Foam::ensightFile::writeList(const IndirectListBase<scalar, Addr>& field)
+{
+    for (const scalar val : field)
+    {
+        if (std::isnan(val))
+        {
+            writeUndef();
+        }
+        else
+        {
+            write(val);
+        }
+        newline();
+    }
+}
+
+
+// ************************************************************************* //
diff --git a/src/fileFormats/ensight/file/ensightGeoFile.C b/src/fileFormats/ensight/file/ensightGeoFile.C
index 7b6006f3b38e527f6f80bf407d351404d7dfbd94..8a4f21a7ee863069872b104395a0869ea1e3c435 100644
--- a/src/fileFormats/ensight/file/ensightGeoFile.C
+++ b/src/fileFormats/ensight/file/ensightGeoFile.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011 OpenFOAM Foundation
-    Copyright (C) 2016-2019 OpenCFD Ltd.
+    Copyright (C) 2016-2020 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -82,8 +82,7 @@ Foam::ensightGeoFile::ensightGeoFile
 
 Foam::Ostream& Foam::ensightGeoFile::writeKeyword(const keyType& key)
 {
-    // Ensure we get ensightFile::write(const string&)
-    write(static_cast<const string&>(key));
+    writeString(key);
     newline();
 
     return *this;
@@ -101,15 +100,16 @@ void Foam::ensightGeoFile::beginPart
 )
 {
     beginPart(index);
-    write(description);
+    writeString(description);
     newline();
 }
 
 
 void Foam::ensightGeoFile::beginCoordinates(const label npoints)
 {
-    write("coordinates");
+    writeString(ensightFile::coordinates);
     newline();
+
     write(npoints);
     newline();
 }
diff --git a/src/fileFormats/ensight/file/ensightGeoFile.H b/src/fileFormats/ensight/file/ensightGeoFile.H
index 1415ecf69f82a37b5c98c6daa0bea1d3eccf36e7..77a735a9c8510025bb2a81337ca69861e752c6c4 100644
--- a/src/fileFormats/ensight/file/ensightGeoFile.H
+++ b/src/fileFormats/ensight/file/ensightGeoFile.H
@@ -97,6 +97,8 @@ public:
     ~ensightGeoFile() = default;
 
 
+    // Member Functions
+
     // Output
 
         //- Write keyword with trailing newline
diff --git a/src/fileFormats/ensight/part/ensightPart.C b/src/fileFormats/ensight/part/ensightPart.C
index 1a2ec97c985117b40c25db4573af8792e51ec3d2..2092a33f396390c9c046412c2ce6a3cac6ea843c 100644
--- a/src/fileFormats/ensight/part/ensightPart.C
+++ b/src/fileFormats/ensight/part/ensightPart.C
@@ -36,30 +36,6 @@ namespace Foam
 }
 
 
-// * * * * * * * * * * * * Protected Member Functions  * * * * * * * * * * * //
-
-// TODO - move elsewhere
-#if 0
-bool Foam::ensightPart::isFieldDefined
-(
-    const List<scalar>& field
-    // const labelUList& addr = cellIds() or faceIds()
-) const
-{
-    forAll(addr, elemI)
-    {
-        const label id = addr[i];
-
-        if (id >= field.size() || std::isnan(field[id]))
-        {
-            return false;
-        }
-    }
-    return true;
-}
-#endif
-
-
 // * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
 
 Foam::ensightPart::ensightPart(const string& description)