From e90eafcf18ab373e20b53275ff558c2270fc4367 Mon Sep 17 00:00:00 2001
From: Mark Olesen <Mark.Olesen@esi-group.com>
Date: Fri, 2 Aug 2019 17:20:18 +0200
Subject: [PATCH] ENH: rationalize cloud method inheritance

- a top-level cloud::nParcels() virtual, which is overloaded by the
  first level of Cloud inheritance. This permits quick determination of
  cloud sizes, even when retrieved from registry with the base level.
  Eg,
      cloud* cldPtr = mesh.cfindObject<cloud>("myCloud");
      label nParcels = (cldPtr ? cldPtr->nParcels() : 0);

- make writeLagrangianPositions on by default unless explicitly
  disabled in the InfoSwitches.

  Flag output errors (where neither coordinates nor positions are
  written) with Fatal.

- additional IOField helper functions in cloud

STYLE: simplify iterator inheritance
---
 src/OpenFOAM/fields/cloud/cloud.C             | 15 ++-
 src/OpenFOAM/fields/cloud/cloud.H             | 54 ++++++++++-
 .../runTimePostProcessing/geometryCloud.C     |  2 +-
 .../geometryCloudGather.C                     |  2 +-
 .../lagrangian/common/parcelSelectionDetail.C |  5 +-
 .../lagrangian/dataCloud/dataCloud.C          |  2 +-
 .../lagrangian/dataCloud/dataCloudTemplates.C |  5 +-
 .../lagrangian/vtkCloud/vtkCloud.C            |  2 +-
 .../clouds/Templates/DSMCCloud/DSMCCloud.H    |  2 +-
 src/lagrangian/basic/Cloud/Cloud.H            | 92 ++++++-------------
 src/lagrangian/basic/Cloud/CloudIO.C          | 16 ++--
 .../injectedParticle/injectedParticleIO.C     | 12 +--
 src/lagrangian/basic/particle/particle.C      |  2 +-
 src/lagrangian/basic/particle/particle.H      | 23 ++---
 .../basic/particle/particleTemplates.C        |  6 ++
 .../Templates/KinematicCloud/KinematicCloud.H |  7 +-
 .../KinematicCloud/KinematicCloudI.H          |  7 --
 17 files changed, 133 insertions(+), 121 deletions(-)

diff --git a/src/OpenFOAM/fields/cloud/cloud.C b/src/OpenFOAM/fields/cloud/cloud.C
index 1f9cfdc37af..3eb76c0a286 100644
--- a/src/OpenFOAM/fields/cloud/cloud.C
+++ b/src/OpenFOAM/fields/cloud/cloud.C
@@ -2,7 +2,7 @@
   =========                 |
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2016-2018 OpenCFD Ltd.
+    \\  /    A nd           | Copyright (C) 2016-2019 OpenCFD Ltd.
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
                             | Copyright (C) 2011-2016 OpenFOAM Foundation
@@ -48,6 +48,12 @@ Foam::cloud::geometryTypeNames
 
 // * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
 
+Foam::cloud::cloud(const objectRegistry& obr)
+:
+    cloud(obr, defaultName)
+{}
+
+
 Foam::cloud::cloud(const objectRegistry& obr, const word& cloudName)
 :
     objectRegistry
@@ -67,6 +73,13 @@ Foam::cloud::cloud(const objectRegistry& obr, const word& cloudName)
 
 // * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
 
+Foam::label Foam::cloud::nParcels() const
+{
+    NotImplemented;
+    return 0;
+}
+
+
 void Foam::cloud::autoMap(const mapPolyMesh&)
 {
     NotImplemented;
diff --git a/src/OpenFOAM/fields/cloud/cloud.H b/src/OpenFOAM/fields/cloud/cloud.H
index d113e103408..3eab3f511dd 100644
--- a/src/OpenFOAM/fields/cloud/cloud.H
+++ b/src/OpenFOAM/fields/cloud/cloud.H
@@ -2,7 +2,7 @@
   =========                 |
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2016-2018 OpenCFD Ltd.
+    \\  /    A nd           | Copyright (C) 2016-2019 OpenCFD Ltd.
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
                             | Copyright (C) 2011-2016 OpenFOAM Foundation
@@ -39,6 +39,7 @@ SourceFiles
 
 #include "objectRegistry.H"
 #include "Enum.H"
+#include "point.H"
 #include "IOField.H"
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
@@ -46,7 +47,7 @@ SourceFiles
 namespace Foam
 {
 
-// Forward declaration of classes
+// Forward Declarations
 class mapPolyMesh;
 
 /*---------------------------------------------------------------------------*\
@@ -91,8 +92,11 @@ public:
 
     // Constructors
 
-        //- Construct for the given objectRegistry and named cloud instance
-        cloud(const objectRegistry&, const word& cloudName = defaultName);
+        //- Construct for given objectRegistry and default cloud name
+        explicit cloud(const objectRegistry& obr);
+
+        //- Construct for given objectRegistry and named cloud instance
+        cloud(const objectRegistry& obr, const word& cloudName);
 
 
     //- Destructor
@@ -101,10 +105,16 @@ public:
 
     // Member Functions
 
+        // Sizes
+
+            //- Number of parcels for the hosting cloud
+            virtual label nParcels() const;
+
+
         // Edit
 
             //- Remap the cells of particles corresponding to the
-            //  mesh topology change
+            //- mesh topology change
             virtual void autoMap(const mapPolyMesh&);
 
 
@@ -124,6 +134,40 @@ public:
                 const label nParticle,
                 objectRegistry& obr
             );
+
+            //- Locate an IOField within object registry
+            //  \return nullptr if not found or wrong type
+            template<class Type>
+            inline static const IOField<Type>* findIOField
+            (
+                const word& fieldName,
+                const objectRegistry& obr
+            )
+            {
+                return obr.cfindObject<IOField<Type>>(fieldName);
+            }
+
+            //- Locate the "position" IOField within object registry
+            //  \return nullptr if not found or wrong type
+            inline static const IOField<point>* findIOPosition
+            (
+                const objectRegistry& obr
+            )
+            {
+                return obr.cfindObject<IOField<point>>("position");
+            }
+
+            //- Lookup an IOField within object registry
+            //  Fatal if not found or wrong type
+            template<class Type>
+            inline static const IOField<Type>& lookupIOField
+            (
+                const word& fieldName,
+                const objectRegistry& obr
+            )
+            {
+                return obr.lookupObject<IOField<Type>>(fieldName);
+            }
 };
 
 
diff --git a/src/functionObjects/graphics/runTimePostProcessing/geometryCloud.C b/src/functionObjects/graphics/runTimePostProcessing/geometryCloud.C
index c60ac2fa9be..7a0e37f8017 100644
--- a/src/functionObjects/graphics/runTimePostProcessing/geometryCloud.C
+++ b/src/functionObjects/graphics/runTimePostProcessing/geometryCloud.C
@@ -159,7 +159,7 @@ addGeometry
 
     objPtr->writeObjects(obrTmp);
 
-    const auto* pointsPtr = obrTmp.findObject<vectorField>("position");
+    const auto* pointsPtr = cloud::findIOPosition(obrTmp);
 
     if (!pointsPtr)
     {
diff --git a/src/functionObjects/graphics/runTimePostProcessing/geometryCloudGather.C b/src/functionObjects/graphics/runTimePostProcessing/geometryCloudGather.C
index c8eb4e0d475..9abb7108464 100644
--- a/src/functionObjects/graphics/runTimePostProcessing/geometryCloudGather.C
+++ b/src/functionObjects/graphics/runTimePostProcessing/geometryCloudGather.C
@@ -46,7 +46,7 @@ Foam::functionObjects::runTimePostPro::geometryCloud::gatherCloud
     auto multiPiece = vtkSmartPointer<vtkMultiPieceDataSet>::New();
     multiPiece->SetNumberOfPieces(Pstream::nProcs());
 
-    const auto* pointsPtr = obrTmp.findObject<vectorField>("position");
+    const auto* pointsPtr = cloud::findIOPosition(obrTmp);
 
     if (!needsCollective())
     {
diff --git a/src/functionObjects/lagrangian/common/parcelSelectionDetail.C b/src/functionObjects/lagrangian/common/parcelSelectionDetail.C
index 2eecb674ca0..a6c17b93390 100644
--- a/src/functionObjects/lagrangian/common/parcelSelectionDetail.C
+++ b/src/functionObjects/lagrangian/common/parcelSelectionDetail.C
@@ -2,7 +2,7 @@
   =========                 |
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2018 OpenCFD Ltd.
+    \\  /    A nd           | Copyright (C) 2018-2019 OpenCFD Ltd.
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
 License
@@ -23,6 +23,7 @@ License
 
 \*---------------------------------------------------------------------------*/
 
+#include "cloud.H"
 #include "parcelSelectionDetail.H"
 #include "scalarPredicates.H"
 #include "labelField.H"
@@ -167,7 +168,7 @@ bool Foam::Detail::parcelSelection::calculateFilter
     // Start with all parcels unselected
 
     // Number of parcels (locally)
-    const auto* pointsPtr = obrTmp.findObject<vectorField>("position");
+    const auto* pointsPtr = cloud::findIOPosition(obrTmp);
     label nParcels = pointsPtr->size();
 
     parcelAddr_.reset();
diff --git a/src/functionObjects/lagrangian/dataCloud/dataCloud.C b/src/functionObjects/lagrangian/dataCloud/dataCloud.C
index 005c5d0889a..cb93e664fd3 100644
--- a/src/functionObjects/lagrangian/dataCloud/dataCloud.C
+++ b/src/functionObjects/lagrangian/dataCloud/dataCloud.C
@@ -71,7 +71,7 @@ bool Foam::functionObjects::dataCloud::writeCloud
 
     objPtr->writeObjects(obrTmp);
 
-    const auto* pointsPtr = obrTmp.findObject<vectorField>("position");
+    const auto* pointsPtr = cloud::findIOPosition(obrTmp);
 
     if (!pointsPtr)
     {
diff --git a/src/functionObjects/lagrangian/dataCloud/dataCloudTemplates.C b/src/functionObjects/lagrangian/dataCloud/dataCloudTemplates.C
index 27412033208..93b2a55c53e 100644
--- a/src/functionObjects/lagrangian/dataCloud/dataCloudTemplates.C
+++ b/src/functionObjects/lagrangian/dataCloud/dataCloudTemplates.C
@@ -2,7 +2,7 @@
   =========                 |
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2018 OpenCFD Ltd.
+    \\  /    A nd           | Copyright (C) 2018-2019 OpenCFD Ltd.
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
 License
@@ -23,6 +23,7 @@ License
 
 \*---------------------------------------------------------------------------*/
 
+#include "cloud.H"
 #include "IOField.H"
 #include "OFstream.H"
 #include "ListOps.H"
@@ -171,7 +172,7 @@ bool Foam::functionObjects::dataCloud::writeField
     const objectRegistry& obrTmp
 ) const
 {
-    const auto* pointsPtr = obrTmp.findObject<vectorField>("position");
+    const auto* pointsPtr = cloud::findIOPosition(obrTmp);
 
     if (!pointsPtr)
     {
diff --git a/src/functionObjects/lagrangian/vtkCloud/vtkCloud.C b/src/functionObjects/lagrangian/vtkCloud/vtkCloud.C
index 9abd8f09269..655e22a9d47 100644
--- a/src/functionObjects/lagrangian/vtkCloud/vtkCloud.C
+++ b/src/functionObjects/lagrangian/vtkCloud/vtkCloud.C
@@ -120,7 +120,7 @@ bool Foam::functionObjects::vtkCloud::writeCloud
 
     objPtr->writeObjects(obrTmp);
 
-    const auto* pointsPtr = obrTmp.findObject<vectorField>("position");
+    const auto* pointsPtr = cloud::findIOPosition(obrTmp);
 
     if (!pointsPtr)
     {
diff --git a/src/lagrangian/DSMC/clouds/Templates/DSMCCloud/DSMCCloud.H b/src/lagrangian/DSMC/clouds/Templates/DSMCCloud/DSMCCloud.H
index 98b0c9dab87..b0c5547f3e5 100644
--- a/src/lagrangian/DSMC/clouds/Templates/DSMCCloud/DSMCCloud.H
+++ b/src/lagrangian/DSMC/clouds/Templates/DSMCCloud/DSMCCloud.H
@@ -140,7 +140,7 @@ class DSMCCloud
         Random rndGen_;
 
 
-        // boundary value fields
+        // Boundary value fields
 
             //- Boundary temperature
             volScalarField boundaryT_;
diff --git a/src/lagrangian/basic/Cloud/Cloud.H b/src/lagrangian/basic/Cloud/Cloud.H
index 906e7d7f419..f8f51656a37 100644
--- a/src/lagrangian/basic/Cloud/Cloud.H
+++ b/src/lagrangian/basic/Cloud/Cloud.H
@@ -2,7 +2,7 @@
   =========                 |
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2017 OpenCFD Ltd.
+    \\  /    A nd           | Copyright (C) 2017-2019 OpenCFD Ltd.
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
                             | Copyright (C) 2011-2017 OpenFOAM Foundation
@@ -50,19 +50,9 @@ SourceFiles
 namespace Foam
 {
 
-// Forward declaration of functions
-template<class ParticleType>
-class Cloud;
-
-template<class ParticleType>
-class IOPosition;
-
-template<class ParticleType>
-Ostream& operator<<
-(
-    Ostream&,
-    const Cloud<ParticleType>&
-);
+// Forward Declarations
+template<class ParticleType> class Cloud;
+template<class ParticleType> class IOPosition;
 
 
 /*---------------------------------------------------------------------------*\
@@ -122,14 +112,15 @@ public:
 
     typedef ParticleType particleType;
 
-    typedef typename IDLList<ParticleType>::iterator iterator;
-    typedef typename IDLList<ParticleType>::const_iterator const_iterator;
+    //- Parcels are just particles
+    typedef ParticleType parcelType;
 
-    //-Runtime type information
+
+    //- Runtime type information
     TypeName("Cloud");
 
 
-    // Static data
+    // Static Data
 
         //- Name of cloud properties dictionary
         static word cloudPropertiesName;
@@ -166,7 +157,10 @@ public:
             }
 
             //- Return the number of particles in the cloud
-            label size() const
+            using IDLList<ParticleType>::size;
+
+            //- Return the number of particles in the cloud
+            virtual label nParcels() const
             {
                 return IDLList<ParticleType>::size();
             };
@@ -178,51 +172,27 @@ public:
             }
 
 
-            // Iterators
-
-                const const_iterator begin() const
-                {
-                    return IDLList<ParticleType>::begin();
-                };
-
-                const const_iterator cbegin() const
-                {
-                    return IDLList<ParticleType>::cbegin();
-                };
+    // Iterators
 
-                const const_iterator end() const
-                {
-                    return IDLList<ParticleType>::end();
-                };
+        using typename IDLList<ParticleType>::iterator;
+        using typename IDLList<ParticleType>::const_iterator;
 
-                const const_iterator cend() const
-                {
-                    return IDLList<ParticleType>::cend();
-                };
-
-                iterator begin()
-                {
-                    return IDLList<ParticleType>::begin();
-                };
-
-                iterator end()
-                {
-                    return IDLList<ParticleType>::end();
-                };
+        using IDLList<ParticleType>::begin;
+        using IDLList<ParticleType>::cbegin;
+        using IDLList<ParticleType>::end;
+        using IDLList<ParticleType>::cend;
 
 
         // Edit
 
-            void clear()
-            {
-                IDLList<ParticleType>::clear();
-            };
+            //- Clear the particle list
+            using IDLList<ParticleType>::clear;
 
             //- Transfer particle to cloud
             void addParticle(ParticleType* pPtr);
 
             //- Remove particle from cloud and delete
-            void deleteParticle(ParticleType&);
+            void deleteParticle(ParticleType& p);
 
             //- Remove lost particles from cloud and delete
             void deleteLostParticles();
@@ -289,19 +259,17 @@ public:
             //- Write positions to \<cloudName\>_positions.obj file
             void writePositions() const;
 
-            //- Call this before a topology change. Stores the particles global
-            //  positions in the database for use during mapping.
+            //- Call this before a topology change.
+            //  Stores the particles global positions in the database
+            //  for use during mapping.
             void storeGlobalPositions() const;
+};
 
 
-    // Ostream Operator
+// Ostream Operator
 
-        friend Ostream& operator<< <ParticleType>
-        (
-            Ostream&,
-            const Cloud<ParticleType>&
-        );
-};
+template<class ParticleType>
+Ostream& operator<<(Ostream& os, const Cloud<ParticleType>& c);
 
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
diff --git a/src/lagrangian/basic/Cloud/CloudIO.C b/src/lagrangian/basic/Cloud/CloudIO.C
index 2eb068c570b..5f8d300b5b2 100644
--- a/src/lagrangian/basic/Cloud/CloudIO.C
+++ b/src/lagrangian/basic/Cloud/CloudIO.C
@@ -2,7 +2,7 @@
   =========                 |
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2017 OpenCFD Ltd.
+    \\  /    A nd           | Copyright (C) 2017-2019 OpenCFD Ltd.
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
                             | Copyright (C) 2011-2017 OpenFOAM Foundation
@@ -67,10 +67,12 @@ void Foam::Cloud<ParticleType>::readCloudUniformProperties()
             );
 
         const word procName("processor" + Foam::name(Pstream::myProcNo()));
-        if (uniformPropsDict.found(procName))
+
+        const dictionary* dictptr = uniformPropsDict.findDict(procName);
+
+        if (dictptr)
         {
-            uniformPropsDict.subDict(procName).lookup("particleCount")
-                >> ParticleType::particleCount_;
+            dictptr->readEntry("particleCount", ParticleType::particleCount_);
         }
     }
     else
@@ -133,7 +135,7 @@ void Foam::Cloud<ParticleType>::initCloud(const bool checkClass)
 
     IOPosition<Cloud<ParticleType>> ioP(*this, geometryType_);
 
-    bool valid = ioP.headerOk();
+    const bool valid = ioP.headerOk();
     Istream& is = ioP.readStream(checkClass ? typeName : "", valid);
     if (valid)
     {
@@ -265,9 +267,9 @@ bool Foam::Cloud<ParticleType>::writeObject
 // * * * * * * * * * * * * * * * Ostream Operators * * * * * * * * * * * * * //
 
 template<class ParticleType>
-Foam::Ostream& Foam::operator<<(Ostream& os, const Cloud<ParticleType>& pc)
+Foam::Ostream& Foam::operator<<(Ostream& os, const Cloud<ParticleType>& c)
 {
-    pc.writeData(os);
+    c.writeData(os);
 
     os.check(FUNCTION_NAME);
     return os;
diff --git a/src/lagrangian/basic/injectedParticle/injectedParticleIO.C b/src/lagrangian/basic/injectedParticle/injectedParticleIO.C
index 4eb9f940d77..fc7332314bf 100644
--- a/src/lagrangian/basic/injectedParticle/injectedParticleIO.C
+++ b/src/lagrangian/basic/injectedParticle/injectedParticleIO.C
@@ -164,13 +164,7 @@ void Foam::injectedParticle::writeObjects
     objectRegistry& obr
 )
 {
-    // Force writing positions instead of coordinates
-    const bool oldWriteCoordinates = particle::writeLagrangianCoordinates;
-    const bool oldWritePositions = particle::writeLagrangianPositions;
-
-    particle::writeLagrangianCoordinates = false;
-    particle::writeLagrangianPositions = true;
-
+    // Always writes "position", not "coordinates"
     particle::writeObjects(c, obr);
 
     label np = c.size();
@@ -191,10 +185,6 @@ void Foam::injectedParticle::writeObjects
 
         ++i;
     }
-
-    // Restore
-    particle::writeLagrangianCoordinates = oldWriteCoordinates;
-    particle::writeLagrangianPositions = oldWritePositions;
 }
 
 
diff --git a/src/lagrangian/basic/particle/particle.C b/src/lagrangian/basic/particle/particle.C
index 66ab510d4d8..f460e4327e9 100644
--- a/src/lagrangian/basic/particle/particle.C
+++ b/src/lagrangian/basic/particle/particle.C
@@ -46,7 +46,7 @@ bool Foam::particle::writeLagrangianCoordinates = true;
 
 bool Foam::particle::writeLagrangianPositions
 (
-    Foam::debug::infoSwitch("writeLagrangianPositions", 0)
+    Foam::debug::infoSwitch("writeLagrangianPositions", 1)
 );
 
 registerInfoSwitch
diff --git a/src/lagrangian/basic/particle/particle.H b/src/lagrangian/basic/particle/particle.H
index ced1efa076f..d28f566df31 100644
--- a/src/lagrangian/basic/particle/particle.H
+++ b/src/lagrangian/basic/particle/particle.H
@@ -53,11 +53,9 @@ Description
 namespace Foam
 {
 
-// Forward declaration of classes
+// Forward Declarations
 class particle;
-
 class polyPatch;
-
 class cyclicPolyPatch;
 class cyclicAMIPolyPatch;
 class cyclicACMIPolyPatch;
@@ -67,16 +65,8 @@ class symmetryPolyPatch;
 class wallPolyPatch;
 class wedgePolyPatch;
 
-// Forward declaration of friend functions and operators
-
-Ostream& operator<<
-(
-    Ostream&,
-    const particle&
-);
-
+Ostream& operator<<(Ostream&, const particle&);
 bool operator==(const particle&, const particle&);
-
 bool operator!=(const particle&, const particle&);
 
 /*---------------------------------------------------------------------------*\
@@ -360,7 +350,7 @@ public:
         static bool writeLagrangianCoordinates;
 
         //- Write particle positions file (v1706 format and earlier)
-        //- Default is false
+        //- Default is true (disable in etc/controlDict)
         static bool writeLagrangianPositions;
 
 
@@ -685,14 +675,15 @@ public:
         static void writeFields(const TrackCloudType& c);
 
         //- Write particle fields as objects into the obr registry
+        //  Always writes "position", not "coordinate"
         template<class CloudType>
         static void writeObjects(const CloudType& c, objectRegistry& obr);
 
         //- Write the particle barycentric coordinates and cell info
-        void writeCoordinates(Ostream&) const;
+        void writeCoordinates(Ostream& os) const;
 
-        //- Write the particle position and cell
-        virtual void writePosition(Ostream&) const;
+        //- Write the particle position and cell id
+        virtual void writePosition(Ostream& os) const;
 
 
     // Friend Operators
diff --git a/src/lagrangian/basic/particle/particleTemplates.C b/src/lagrangian/basic/particle/particleTemplates.C
index 0c0163c7e6f..a78dd660877 100644
--- a/src/lagrangian/basic/particle/particleTemplates.C
+++ b/src/lagrangian/basic/particle/particleTemplates.C
@@ -78,6 +78,12 @@ void Foam::particle::writeFields(const TrackCloudType& c)
         IOPosition<TrackCloudType> ioP(c);
         ioP.write(np > 0);
     }
+    else if (!writeLagrangianPositions)
+    {
+        FatalErrorInFunction
+            << "Must select coordinates and/or positions" << nl
+            << exit(FatalError);
+    }
 
     // Optionally write positions file in v1706 format and earlier
     if (writeLagrangianPositions)
diff --git a/src/lagrangian/intermediate/clouds/Templates/KinematicCloud/KinematicCloud.H b/src/lagrangian/intermediate/clouds/Templates/KinematicCloud/KinematicCloud.H
index 35af3acf592..e76c63c7e1b 100644
--- a/src/lagrangian/intermediate/clouds/Templates/KinematicCloud/KinematicCloud.H
+++ b/src/lagrangian/intermediate/clouds/Templates/KinematicCloud/KinematicCloud.H
@@ -2,7 +2,7 @@
   =========                 |
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
-    \\  /    A nd           | Copyright (C) 2016 OpenCFD Ltd.
+    \\  /    A nd           | Copyright (C) 2016-2019 OpenCFD Ltd.
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
                             | Copyright (C) 2011-2017 OpenFOAM Foundation
@@ -494,7 +494,10 @@ public:
         // Check
 
             //- Total number of parcels
-            inline label nParcels() const;
+            virtual label nParcels() const
+            {
+                return CloudType::nParcels();
+            }
 
             //- Total mass in system
             inline scalar massInSystem() const;
diff --git a/src/lagrangian/intermediate/clouds/Templates/KinematicCloud/KinematicCloudI.H b/src/lagrangian/intermediate/clouds/Templates/KinematicCloud/KinematicCloudI.H
index 095eabd9c66..01085af53d8 100644
--- a/src/lagrangian/intermediate/clouds/Templates/KinematicCloud/KinematicCloudI.H
+++ b/src/lagrangian/intermediate/clouds/Templates/KinematicCloud/KinematicCloudI.H
@@ -262,13 +262,6 @@ Foam::KinematicCloud<CloudType>::UIntegrator() const
 }
 
 
-template<class CloudType>
-inline Foam::label Foam::KinematicCloud<CloudType>::nParcels() const
-{
-    return this->size();
-}
-
-
 template<class CloudType>
 inline Foam::scalar Foam::KinematicCloud<CloudType>::massInSystem() const
 {
-- 
GitLab