diff --git a/src/lagrangian/intermediate/Make/files b/src/lagrangian/intermediate/Make/files
index 4d6e12f6dc..445cbc2a20 100644
--- a/src/lagrangian/intermediate/Make/files
+++ b/src/lagrangian/intermediate/Make/files
@@ -120,7 +120,7 @@ phaseProperties/phasePropertiesList/phasePropertiesList.C
/* additional helper classes */
clouds/Templates/KinematicCloud/cloudSolution/cloudSolution.C
-
+submodels/CloudFunctionObjects/CloudFunctionObject/cloudFunctionObjectTools.C
/* averaging methods */
submodels/MPPIC/AveragingMethods/makeAveragingMethods.C
diff --git a/src/lagrangian/intermediate/parcels/include/makeParcelCloudFunctionObjects.H b/src/lagrangian/intermediate/parcels/include/makeParcelCloudFunctionObjects.H
index 6b0d78ac38..f2fc385cb6 100644
--- a/src/lagrangian/intermediate/parcels/include/makeParcelCloudFunctionObjects.H
+++ b/src/lagrangian/intermediate/parcels/include/makeParcelCloudFunctionObjects.H
@@ -6,7 +6,7 @@
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2011-2018 OpenFOAM Foundation
- Copyright (C) 2020-2022 OpenCFD Ltd.
+ Copyright (C) 2020-2023 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@@ -38,10 +38,10 @@ License
#include "ParticleTracks.H"
#include "ParticleTrap.H"
#include "ParticleZoneInfo.H"
+#include "ParticleHistogram.H"
#include "PatchCollisionDensity.H"
#include "PatchInteractionFields.H"
#include "PatchPostProcessing.H"
-#include "PatchParticleHistogram.H"
#include "RemoveParcels.H"
#include "VoidFraction.H"
#include "KinematicReynoldsNumber.H"
@@ -60,10 +60,10 @@ License
makeCloudFunctionObjectType(ParticleTracks, CloudType); \
makeCloudFunctionObjectType(ParticleTrap, CloudType); \
makeCloudFunctionObjectType(ParticleZoneInfo, CloudType); \
+ makeCloudFunctionObjectType(ParticleHistogram, CloudType); \
makeCloudFunctionObjectType(PatchCollisionDensity, CloudType); \
makeCloudFunctionObjectType(PatchInteractionFields, CloudType); \
makeCloudFunctionObjectType(PatchPostProcessing, CloudType); \
- makeCloudFunctionObjectType(PatchParticleHistogram, CloudType); \
makeCloudFunctionObjectType(RemoveParcels, CloudType); \
makeCloudFunctionObjectType(VoidFraction, CloudType); \
makeCloudFunctionObjectType(KinematicReynoldsNumber, CloudType); \
diff --git a/src/lagrangian/intermediate/parcels/include/makeReactingParcelCloudFunctionObjects.H b/src/lagrangian/intermediate/parcels/include/makeReactingParcelCloudFunctionObjects.H
index e8bed51a3e..e620ceb4c1 100644
--- a/src/lagrangian/intermediate/parcels/include/makeReactingParcelCloudFunctionObjects.H
+++ b/src/lagrangian/intermediate/parcels/include/makeReactingParcelCloudFunctionObjects.H
@@ -6,7 +6,7 @@
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2011-2018 OpenFOAM Foundation
- Copyright (C) 2020-2022 OpenCFD Ltd.
+ Copyright (C) 2020-2023 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@@ -38,10 +38,10 @@ License
#include "ParticleTracks.H"
#include "ParticleTrap.H"
#include "ParticleZoneInfo.H"
+#include "ParticleHistogram.H"
#include "PatchCollisionDensity.H"
#include "PatchInteractionFields.H"
#include "PatchPostProcessing.H"
-#include "PatchParticleHistogram.H"
#include "RemoveParcels.H"
#include "VoidFraction.H"
#include "NusseltNumber.H"
@@ -63,10 +63,10 @@ License
makeCloudFunctionObjectType(ParticleTracks, CloudType); \
makeCloudFunctionObjectType(ParticleTrap, CloudType); \
makeCloudFunctionObjectType(ParticleZoneInfo, CloudType); \
+ makeCloudFunctionObjectType(ParticleHistogram, CloudType); \
makeCloudFunctionObjectType(PatchCollisionDensity, CloudType); \
makeCloudFunctionObjectType(PatchInteractionFields, CloudType); \
makeCloudFunctionObjectType(PatchPostProcessing, CloudType); \
- makeCloudFunctionObjectType(PatchParticleHistogram, CloudType); \
makeCloudFunctionObjectType(RemoveParcels, CloudType); \
makeCloudFunctionObjectType(VoidFraction, CloudType); \
makeCloudFunctionObjectType(NusseltNumber, CloudType); \
diff --git a/src/lagrangian/intermediate/parcels/include/makeThermoParcelCloudFunctionObjects.H b/src/lagrangian/intermediate/parcels/include/makeThermoParcelCloudFunctionObjects.H
index 0e0a312b8f..4a8a37f4fc 100644
--- a/src/lagrangian/intermediate/parcels/include/makeThermoParcelCloudFunctionObjects.H
+++ b/src/lagrangian/intermediate/parcels/include/makeThermoParcelCloudFunctionObjects.H
@@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
- Copyright (C) 2021-2022 OpenCFD Ltd.
+ Copyright (C) 2021-2023 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@@ -37,10 +37,10 @@ License
#include "ParticleTracks.H"
#include "ParticleTrap.H"
#include "ParticleZoneInfo.H"
+#include "ParticleHistogram.H"
#include "PatchCollisionDensity.H"
#include "PatchInteractionFields.H"
#include "PatchPostProcessing.H"
-#include "PatchParticleHistogram.H"
#include "RemoveParcels.H"
#include "VoidFraction.H"
#include "NusseltNumber.H"
@@ -61,10 +61,10 @@ License
makeCloudFunctionObjectType(ParticleTracks, CloudType); \
makeCloudFunctionObjectType(ParticleTrap, CloudType); \
makeCloudFunctionObjectType(ParticleZoneInfo, CloudType); \
+ makeCloudFunctionObjectType(ParticleHistogram, CloudType); \
makeCloudFunctionObjectType(PatchCollisionDensity, CloudType); \
makeCloudFunctionObjectType(PatchInteractionFields, CloudType); \
makeCloudFunctionObjectType(PatchPostProcessing, CloudType); \
- makeCloudFunctionObjectType(PatchParticleHistogram, CloudType); \
makeCloudFunctionObjectType(RemoveParcels, CloudType); \
makeCloudFunctionObjectType(VoidFraction, CloudType); \
makeCloudFunctionObjectType(NusseltNumber, CloudType); \
diff --git a/src/lagrangian/intermediate/submodels/CloudFunctionObjects/CloudFunctionObject/cloudFunctionObjectTools.C b/src/lagrangian/intermediate/submodels/CloudFunctionObjects/CloudFunctionObject/cloudFunctionObjectTools.C
new file mode 100644
index 0000000000..1f58610711
--- /dev/null
+++ b/src/lagrangian/intermediate/submodels/CloudFunctionObjects/CloudFunctionObject/cloudFunctionObjectTools.C
@@ -0,0 +1,113 @@
+/*---------------------------------------------------------------------------*\
+ ========= |
+ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
+ \\ / O peration |
+ \\ / A nd | www.openfoam.com
+ \\/ M anipulation |
+-------------------------------------------------------------------------------
+ Copyright (C) 2023 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 .
+
+\*---------------------------------------------------------------------------*/
+
+#include "cloudFunctionObjectTools.H"
+
+// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
+
+Foam::cloudFunctionObjectTools::collector::collector
+(
+ const dictionary& dict,
+ const polyMesh& mesh
+)
+:
+ isPatch_(false),
+ IDs_(),
+ names_(),
+ BBs_()
+{
+ wordRes matcher;
+
+ if (dict.readIfPresent("patches", matcher) && !matcher.empty())
+ {
+ isPatch_ = true;
+
+ IDs_ = mesh.boundaryMesh().indices(matcher);
+
+ names_.resize(IDs_.size());
+
+ label count = 0;
+ for (const label patchi : IDs_)
+ {
+ names_[count] = mesh.boundaryMesh()[patchi].name();
+ ++count;
+ }
+ }
+ else if (dict.readIfPresent("faceZones", matcher))
+ {
+ const faceZoneMesh& fzm = mesh.faceZones();
+
+ IDs_ = fzm.indices(matcher);
+
+ BBs_.resize(IDs_.size());
+ names_.resize(IDs_.size());
+
+ label count = 0;
+ for (const label zonei : IDs_)
+ {
+ const faceZone& fz = fzm[zonei];
+ names_[count] = fz.name();
+
+ auto& bb = BBs_[count];
+ ++count;
+
+ bb.reset();
+
+ const auto& faces = mesh.faces();
+ const auto& points = mesh.points();
+ for (const label facei : fz)
+ {
+ bb.add(points, faces[facei]);
+ }
+ bb.reduce();
+ bb.inflate(0.05);
+ }
+ }
+
+ if (matcher.empty() || IDs_.size() < 1)
+ {
+ FatalIOErrorInFunction(dict)
+ << "No matching patches or face zones found: "
+ << flatOutput(matcher) << nl
+ << exit(FatalIOError);
+ }
+}
+
+
+Foam::cloudFunctionObjectTools::collector::collector
+(
+ const collector& phc
+)
+:
+ isPatch_(phc.isPatch_),
+ IDs_(phc.IDs_),
+ names_(phc.names_),
+ BBs_(phc.BBs_)
+{}
+
+
+// ************************************************************************* //
diff --git a/src/lagrangian/intermediate/submodels/CloudFunctionObjects/CloudFunctionObject/cloudFunctionObjectTools.H b/src/lagrangian/intermediate/submodels/CloudFunctionObjects/CloudFunctionObject/cloudFunctionObjectTools.H
new file mode 100644
index 0000000000..e5fe1fee3e
--- /dev/null
+++ b/src/lagrangian/intermediate/submodels/CloudFunctionObjects/CloudFunctionObject/cloudFunctionObjectTools.H
@@ -0,0 +1,129 @@
+/*---------------------------------------------------------------------------*\
+ ========= |
+ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
+ \\ / O peration |
+ \\ / A nd | www.openfoam.com
+ \\/ M anipulation |
+-------------------------------------------------------------------------------
+ Copyright (C) 2023 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 .
+
+Class
+ Foam::cloudFunctionObjectTools::collector
+
+Description
+ Implementation of template-invariant tools for
+ various function objects such as Foam::ParticleHistogram.
+
+SourceFiles
+ cloudFunctionObjectTools.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef cloudFunctionObjectTools_collector_H
+#define cloudFunctionObjectTools_collector_H
+
+#include "polyMesh.H"
+#include "boundBox.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+/*---------------------------------------------------------------------------*\
+ Namespace cloudFunctionObjectTools Declaration
+\*---------------------------------------------------------------------------*/
+
+namespace cloudFunctionObjectTools
+{
+
+/*---------------------------------------------------------------------------*\
+ Class collector Declaration
+\*---------------------------------------------------------------------------*/
+
+class collector
+{
+ // Private Data
+
+ //- Flag to decide if the collector is patch based
+ bool isPatch_;
+
+ //- List of indices of collectors (zones or patches)
+ labelList IDs_;
+
+ //- List of names of collectors
+ wordList names_;
+
+ //- List of bounding-boxes of collectors
+ List BBs_;
+
+
+public:
+
+ // Constructors
+
+ //- Construct from dictionary
+ collector
+ (
+ const dictionary& dict,
+ const polyMesh& mesh
+ );
+
+ //- Copy construct
+ collector(const collector& phc);
+
+ //- No copy assignment
+ void operator=(const collector&) = delete;
+
+
+ //- Destructor
+ virtual ~collector() = default;
+
+
+ // Member Functions
+
+ // Access
+
+ //- Return the flag if the collector is patch based
+ bool isPatch() const noexcept { return isPatch_; }
+
+ //- Return number of collectors (zones or patches)
+ label size() const noexcept { return IDs_.size(); }
+
+ //- Return const reference to the indices of collectors
+ const labelList& IDs() const noexcept { return IDs_; }
+
+ //- Return const reference to the names of collectors
+ const wordList& names() const noexcept { return names_; }
+
+ //- Return const reference to the bounding-boxes of collectors
+ const List& BBs() const noexcept { return BBs_; }
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace cloudFunctionObjectTools
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/lagrangian/intermediate/submodels/CloudFunctionObjects/ParticleHistogram/ParticleHistogram.C b/src/lagrangian/intermediate/submodels/CloudFunctionObjects/ParticleHistogram/ParticleHistogram.C
new file mode 100644
index 0000000000..e0010d085b
--- /dev/null
+++ b/src/lagrangian/intermediate/submodels/CloudFunctionObjects/ParticleHistogram/ParticleHistogram.C
@@ -0,0 +1,278 @@
+/*---------------------------------------------------------------------------*\
+ ========= |
+ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
+ \\ / O peration |
+ \\ / A nd | www.openfoam.com
+ \\/ M anipulation |
+-------------------------------------------------------------------------------
+ Copyright (C) 2020-2023 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 .
+
+\*---------------------------------------------------------------------------*/
+
+#include "ParticleHistogram.H"
+#include "Pstream.H"
+#include "stringListOps.H"
+#include "ListOps.H"
+#include "ListListOps.H"
+
+// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
+
+template
+void Foam::ParticleHistogram::writeFileHeader(Ostream& os) const
+{
+ this->writeHeaderValue(os, "nBin", nBins_);
+ this->writeHeaderValue(os, "min", range_.min());
+ this->writeHeaderValue(os, "max", range_.max());
+ this->writeHeader(os, "");
+ this->writeCommented(os, "dEdge1");
+ os << tab << "dEdge2"
+ << tab << "nParticles"
+ << tab << "nParticlesCumulative"
+ << endl;
+}
+
+
+// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
+
+template
+Foam::ParticleHistogram::ParticleHistogram
+(
+ const dictionary& dict,
+ CloudType& owner,
+ const word& modelName
+)
+:
+ CloudFunctionObject(dict, owner, modelName, typeName),
+ functionObjects::writeFile
+ (
+ owner,
+ this->localPath(),
+ typeName
+ ),
+ collector_(this->coeffDict(), owner.mesh()),
+ nBins_
+ (
+ this->coeffDict().template getCheck