diff --git a/src/functionObjects/field/Make/files b/src/functionObjects/field/Make/files
index 59fabd85cd..b7c2550efb 100644
--- a/src/functionObjects/field/Make/files
+++ b/src/functionObjects/field/Make/files
@@ -1,5 +1,11 @@
AMIWeights/AMIWeights.C
+binField/binField.C
+binField/binModels/binModel/binModel.C
+binField/binModels/binModel/binModelNew.C
+binField/binModels/uniformBin/uniformBin.C
+binField/binModels/singleDirectionUniformBin/singleDirectionUniformBin.C
+
columnAverage/columnAverage.C
continuityError/continuityError.C
diff --git a/src/functionObjects/field/binField/binField.C b/src/functionObjects/field/binField/binField.C
new file mode 100644
index 0000000000..b805afcd9a
--- /dev/null
+++ b/src/functionObjects/field/binField/binField.C
@@ -0,0 +1,128 @@
+/*---------------------------------------------------------------------------*\
+ ========= |
+ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
+ \\ / O peration |
+ \\ / A nd | www.openfoam.com
+ \\/ M anipulation |
+-------------------------------------------------------------------------------
+ Copyright (C) 2022 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 "binField.H"
+#include "binModel.H"
+#include "addToRunTimeSelectionTable.H"
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+namespace Foam
+{
+namespace functionObjects
+{
+ defineTypeNameAndDebug(binField, 0);
+ addToRunTimeSelectionTable(functionObject, binField, dictionary);
+}
+}
+
+
+// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
+
+Foam::functionObjects::binField::binField
+(
+ const word& name,
+ const Time& runTime,
+ const dictionary& dict,
+ bool readFields
+)
+:
+ fvMeshFunctionObject(name, runTime, dict),
+ binModelPtr_(nullptr)
+{
+ if (readFields)
+ {
+ read(dict);
+ }
+}
+
+
+Foam::functionObjects::binField::binField
+(
+ const word& name,
+ const objectRegistry& obr,
+ const dictionary& dict,
+ bool readFields
+)
+:
+ fvMeshFunctionObject(name, obr, dict),
+ binModelPtr_(nullptr)
+{
+ if (readFields)
+ {
+ read(dict);
+ }
+}
+
+
+// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
+
+bool Foam::functionObjects::binField::read(const dictionary& dict)
+{
+ if (!fvMeshFunctionObject::read(dict))
+ {
+ return false;
+ }
+
+ Info<< type() << " " << name() << ":" << endl;
+
+ binModelPtr_.reset(binModel::New(dict, mesh_, name()));
+
+ return true;
+}
+
+
+bool Foam::functionObjects::binField::execute()
+{
+ Log << type() << " " << name() << ":" << nl
+ << " Calculating bins" << nl << endl;
+
+ binModelPtr_->apply();
+
+ return true;
+}
+
+
+bool Foam::functionObjects::binField::write()
+{
+ return true;
+}
+
+
+void Foam::functionObjects::binField::updateMesh(const mapPolyMesh& mpm)
+{
+ binModelPtr_->updateMesh(mpm);
+}
+
+
+void Foam::functionObjects::binField::movePoints(const polyMesh& mesh)
+{
+ binModelPtr_->movePoints(mesh);
+}
+
+
+// ************************************************************************* //
diff --git a/src/functionObjects/field/binField/binField.H b/src/functionObjects/field/binField/binField.H
new file mode 100644
index 0000000000..1fd57a1aed
--- /dev/null
+++ b/src/functionObjects/field/binField/binField.H
@@ -0,0 +1,221 @@
+/*---------------------------------------------------------------------------*\
+ ========= |
+ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
+ \\ / O peration |
+ \\ / A nd | www.openfoam.com
+ \\/ M anipulation |
+-------------------------------------------------------------------------------
+ Copyright (C) 2022 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::functionObjects::binField
+
+Group
+ grpFieldFunctionObjects
+
+Description
+ Calculates binned data, where specified patches are divided into
+ segments according to various input bin characteristics, so that
+ spatially-localised information can be output for each segment.
+
+ Operands:
+ \table
+ Operand | Type | Location
+ input | vol\Field(s) | \/\s
+ output file | dat | postProcessing/\/\/\
+ output field | - | -
+ \endtable
+
+ where \c \=Scalar/Vector/SphericalTensor/SymmTensor/Tensor.
+
+Usage
+ Minimal example by using \c system/controlDict.functions:
+ \verbatim
+ binField1
+ {
+ // Mandatory entries
+ type binField;
+ libs (fieldFunctionObjects);
+ binModel ;
+ fields ();
+ patches ();
+ binData
+ {
+ // Entries of the chosen binModel
+ }
+
+ // Optional entries
+ cellZones ();
+ decomposePatchValues ;
+
+ // Conditional optional entries
+
+ // Option-1, i.e. general coordinate system specification
+ coordinateSystem
+ {
+ type cartesian;
+ origin (0 0 0);
+ rotation
+ {
+ type axes;
+ e3 (0 0 1);
+ e1 (1 0 0); // (e1, e2) or (e2, e3) or (e3, e1)
+ }
+ }
+
+ // Option-2, i.e. the centre of rotation
+ // by inherently using e3=(0 0 1) and e1=(1 0 0)
+ CofR (0 0 0);
+
+ // Inherited entries
+ ...
+ }
+ \endverbatim
+
+ where the entries mean:
+ \table
+ Property | Description | Type | Reqd | Deflt
+ type | Type name: binField | word | yes | -
+ libs | Library name: fieldFunctionObjects | word | yes | -
+ binModel | Name of the bin model | word | yes | -
+ fields | Names of operand fields | wordHasSet | yes | -
+ patches | Names of operand patches | wordRes | yes | -
+ binData | Entries of the chosen bin model | dict | yes | -
+ decomposePatchValues | Flag to output normal and tangential components | bool | no | false
+ cellZones | Names of operand cell zones | wordRes | no | -
+ coordinateSystem | Coordinate system specifier | dict | cndtnl | -
+ CofR | Centre of rotation | vector | cndtnl | -
+ \endtable
+
+ Options for the \c binModel entry:
+ \verbatim
+ singleDirectionUniformBin | Segments in a single direction
+ uniformBin | Segments in multiple directions
+ \endverbatim
+
+ The inherited entries are elaborated in:
+ - \link fvMeshFunctionObject.H \endlink
+ - \link writeFile.H \endlink
+ - \link coordinateSystem.H \endlink
+
+SourceFiles
+ binField.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef Foam_functionObjects_binField_H
+#define Foam_functionObjects_binField_H
+
+#include "fvMeshFunctionObject.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+class binModel;
+
+namespace functionObjects
+{
+
+/*---------------------------------------------------------------------------*\
+ Class binField Declaration
+\*---------------------------------------------------------------------------*/
+
+class binField
+:
+ public fvMeshFunctionObject
+{
+protected:
+
+ // Protected Data
+
+ //- Runtime-selectable bin model
+ autoPtr binModelPtr_;
+
+
+public:
+
+ //- Runtime type information
+ TypeName("binField");
+
+
+ // Constructors
+
+ //- Construct from Time and dictionary
+ binField
+ (
+ const word& name,
+ const Time& runTime,
+ const dictionary& dict,
+ const bool readFields = true
+ );
+
+ //- Construct from objectRegistry and dictionary
+ binField
+ (
+ const word& name,
+ const objectRegistry& obr,
+ const dictionary& dict,
+ const bool readFields = true
+ );
+
+ //- No copy construct
+ binField(const binField&) = delete;
+
+ //- No copy assignment
+ void operator=(const binField&) = delete;
+
+
+ //- Destructor
+ virtual ~binField() = default;
+
+
+ // Member Functions
+
+ //- Read the dictionary
+ virtual bool read(const dictionary& dict);
+
+ //- Execute the function object
+ virtual bool execute();
+
+ //- Write to data files/fields and to streams
+ virtual bool write();
+
+ //- Update for changes of mesh
+ virtual void updateMesh(const mapPolyMesh& mpm);
+
+ //- Update for changes of mesh
+ virtual void movePoints(const polyMesh& mesh);
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace functionObjects
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/functionObjects/field/binField/binModels/binModel/binModel.C b/src/functionObjects/field/binField/binModels/binModel/binModel.C
new file mode 100644
index 0000000000..5e351b0783
--- /dev/null
+++ b/src/functionObjects/field/binField/binModels/binModel/binModel.C
@@ -0,0 +1,205 @@
+/*---------------------------------------------------------------------------*\
+ ========= |
+ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
+ \\ / O peration |
+ \\ / A nd | www.openfoam.com
+ \\/ M anipulation |
+-------------------------------------------------------------------------------
+ Copyright (C) 2021-2022 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 "binModel.H"
+#include "fvMesh.H"
+#include "cartesianCS.H"
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+namespace Foam
+{
+ defineTypeNameAndDebug(binModel, 0);
+ defineRunTimeSelectionTable(binModel, dictionary);
+}
+
+
+// * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
+
+template<>
+bool Foam::binModel::decomposePatchValues
+(
+ List>& data,
+ const label bini,
+ const vector& v,
+ const vector& n
+) const
+{
+ if (!decomposePatchValues_)
+ {
+ return false;
+ }
+
+ #ifdef FULLDEBUG
+ if (data.size() != 3)
+ {
+ FatalErrorInFunction
+ << "Inconsistent data list size - expect size 3"
+ << abort(FatalError);
+ }
+ #endif
+
+ data[1][bini] += n*(v & n);
+ data[2][bini] += v - n*(v & n);
+
+ return true;
+}
+
+
+void Foam::binModel::setCoordinateSystem
+(
+ const dictionary& dict,
+ const word& e3Name,
+ const word& e1Name
+)
+{
+ coordSysPtr_.clear();
+
+ if (dict.found(coordinateSystem::typeName_()))
+ {
+ coordSysPtr_ =
+ coordinateSystem::New
+ (
+ mesh_,
+ dict,
+ coordinateSystem::typeName_()
+ );
+
+ Info<< "Setting co-ordinate system:" << nl
+ << " - type : " << coordSysPtr_->name() << nl
+ << " - origin : " << coordSysPtr_->origin() << nl
+ << " - e3 : " << coordSysPtr_->e3() << nl
+ << " - e1 : " << coordSysPtr_->e1() << endl;
+ }
+ else if (dict.found("CofR"))
+ {
+ const vector origin(dict.get("CofR"));
+
+ const vector e3
+ (
+ e3Name == word::null
+ ? vector(0, 0, 1)
+ : dict.get(e3Name)
+ );
+
+ const vector e1
+ (
+ e1Name == word::null
+ ? vector(1, 0, 0)
+ : dict.get(e1Name)
+ );
+
+ coordSysPtr_.reset(new coordSystem::cartesian(origin, e3, e1));
+ }
+ else
+ {
+ coordSysPtr_.reset(new coordSystem::cartesian(dict));
+ }
+}
+
+
+// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
+
+Foam::binModel::binModel
+(
+ const dictionary& dict,
+ const fvMesh& mesh,
+ const word& outputPrefix
+)
+:
+ writeFile(mesh, outputPrefix),
+ mesh_(mesh),
+ decomposePatchValues_(false),
+ cumulative_(false),
+ coordSysPtr_(),
+ nBin_(1),
+ patchSet_(),
+ fieldNames_(),
+ cellZoneIDs_(),
+ filePtrs_()
+{}
+
+
+// * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * * //
+
+bool Foam::binModel::read(const dictionary& dict)
+{
+ patchSet_ = mesh_.boundaryMesh().patchSet(dict.get("patches"));
+ fieldNames_ = dict.get("fields").sortedToc();
+
+ if (dict.found("cellZones"))
+ {
+ DynamicList zoneIDs;
+ DynamicList czUnmatched;
+ for (const auto& cz : dict.get("cellZones"))
+ {
+ const labelList czi(mesh_.cellZones().indices(cz));
+
+ if (czi.empty())
+ {
+ czUnmatched.append(cz);
+ }
+ else
+ {
+ zoneIDs.append(czi);
+ }
+ }
+
+ if (czUnmatched.size())
+ {
+ WarningInFunction
+ << "Unable to find zone(s): " << czUnmatched << nl
+ << "Valid cellZones are : " << mesh_.cellZones().sortedNames()
+ << endl;
+ }
+
+ cellZoneIDs_.transfer(zoneIDs);
+ }
+
+ decomposePatchValues_ = dict.get("decomposePatchValues");
+
+ filePtrs_.setSize(fieldNames_.size());
+ forAll(filePtrs_, i)
+ {
+ filePtrs_.set(i, createFile(fieldNames_[i] + "Bin"));
+ }
+
+ setCoordinateSystem(dict);
+
+ return true;
+}
+
+
+void Foam::binModel::updateMesh(const mapPolyMesh& mpm)
+{}
+
+
+void Foam::binModel::movePoints(const polyMesh& mesh)
+{}
+
+
+// ************************************************************************* //
diff --git a/src/functionObjects/field/binField/binModels/binModel/binModel.H b/src/functionObjects/field/binField/binModels/binModel/binModel.H
new file mode 100644
index 0000000000..3583a0a973
--- /dev/null
+++ b/src/functionObjects/field/binField/binModels/binModel/binModel.H
@@ -0,0 +1,243 @@
+/*---------------------------------------------------------------------------*\
+ ========= |
+ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
+ \\ / O peration |
+ \\ / A nd | www.openfoam.com
+ \\/ M anipulation |
+-------------------------------------------------------------------------------
+ Copyright (C) 2021-2022 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::binModel
+
+Description
+ Base class for bin models to handle general bin characteristics.
+
+SourceFiles
+ binModel.C
+ binModelNew.C
+ binModelTemplates.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef Foam_binModel_H
+#define Foam_binModel_H
+
+#include "dictionary.H"
+#include "HashSet.H"
+#include "volFields.H"
+#include "runTimeSelectionTables.H"
+#include "OFstream.H"
+#include "coordinateSystem.H"
+#include "writeFile.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+// Forward Declarations
+class fvMesh;
+
+/*---------------------------------------------------------------------------*\
+ Class binModel Declaration
+\*---------------------------------------------------------------------------*/
+
+class binModel
+:
+ public functionObjects::writeFile
+{
+protected:
+
+ // Protected Data
+
+ //- Reference to the mesh
+ const fvMesh& mesh_;
+
+ //- Decompose patch values into normal and tangential components
+ bool decomposePatchValues_;
+
+ //- Flag to accumulate bin data with
+ //- increasing distance in binning direction
+ bool cumulative_;
+
+ //- Local coordinate system of bins
+ autoPtr coordSysPtr_;
+
+ //- Total number of bins
+ label nBin_;
+
+ //- Indices of operand patches
+ labelHashSet patchSet_;
+
+ //- Names of operand fields
+ wordList fieldNames_;
+
+ //- Indices of operand cell zones
+ labelList cellZoneIDs_;
+
+ //- List of file pointers; 1 file per field
+ PtrList filePtrs_;
+
+
+ // Protected Member Functions
+
+ //- Set the co-ordinate system from dictionary and axes names
+ void setCoordinateSystem
+ (
+ const dictionary& dict,
+ const word& e3Name = word::null,
+ const word& e1Name = word::null
+ );
+
+ //- Helper function to decompose patch values
+ //- into normal and tangential components
+ template
+ bool decomposePatchValues
+ (
+ List>& data,
+ const label bini,
+ const Type& v,
+ const vector& n
+ ) const;
+
+ //- Helper function to construct a string description for a given type
+ template
+ string writeComponents(const word& stem) const;
+
+ //- Write binned data to stream
+ template
+ void writeBinnedData
+ (
+ List>& data,
+ Ostream& os
+ ) const;
+
+
+public:
+
+ //- Runtime type information
+ TypeName("binModel");
+
+
+ // Declare runtime constructor selection table
+
+ declareRunTimeSelectionTable
+ (
+ autoPtr,
+ binModel,
+ dictionary,
+ (
+ const dictionary& dict,
+ const fvMesh& mesh,
+ const word& outputPrefix
+ ),
+ (dict, mesh, outputPrefix)
+ );
+
+
+ // Selectors
+
+ //- Return a reference to the selected bin model
+ static autoPtr New
+ (
+ const dictionary& dict,
+ const fvMesh& mesh,
+ const word& outputPrefix
+ );
+
+
+ // Constructors
+
+ //- Construct from components
+ binModel
+ (
+ const dictionary& dict,
+ const fvMesh& mesh,
+ const word& outputPrefix
+ );
+
+ //- No copy construct
+ binModel(const binModel&) = delete;
+
+ //- No copy assignment
+ void operator=(const binModel&) = delete;
+
+
+ //- Destructor
+ virtual ~binModel() = default;
+
+
+ // Member Functions
+
+ //- Read the dictionary
+ virtual bool read(const dictionary& dict);
+
+
+ // Access
+
+ //- Return the total number of bins
+ label nBin() const noexcept
+ {
+ return nBin_;
+ };
+
+
+ // Evaluation
+
+ //- Initialise bin properties
+ virtual void initialise() = 0;
+
+ //- Apply bins
+ virtual void apply() = 0;
+
+ //- Update for changes of mesh
+ virtual void updateMesh(const mapPolyMesh& mpm);
+
+ //- Update for changes of mesh
+ virtual void movePoints(const polyMesh& mesh);
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+template<>
+bool binModel::decomposePatchValues
+(
+ List>& data,
+ const label bini,
+ const vector& v,
+ const vector& n
+) const;
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#ifdef NoRepository
+ #include "binModelTemplates.C"
+#endif
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/functionObjects/field/binField/binModels/binModel/binModelNew.C b/src/functionObjects/field/binField/binModels/binModel/binModelNew.C
new file mode 100644
index 0000000000..2447b09919
--- /dev/null
+++ b/src/functionObjects/field/binField/binModels/binModel/binModelNew.C
@@ -0,0 +1,59 @@
+/*---------------------------------------------------------------------------*\
+ ========= |
+ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
+ \\ / O peration |
+ \\ / A nd | www.openfoam.com
+ \\/ M anipulation |
+-------------------------------------------------------------------------------
+ Copyright (C) 2021-2022 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 "binModel.H"
+#include "fvMesh.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+Foam::autoPtr Foam::binModel::New
+(
+ const dictionary& dict,
+ const fvMesh& mesh,
+ const word& outputPrefix
+)
+{
+ word modelType(dict.get("binModel"));
+
+ auto cstrIter = dictionaryConstructorTablePtr_->cfind(modelType);
+
+ if (!cstrIter.found())
+ {
+ FatalIOErrorInLookup
+ (
+ dict,
+ "binModel",
+ modelType,
+ *dictionaryConstructorTablePtr_
+ ) << exit(FatalIOError);
+ }
+
+ return autoPtr(cstrIter()(dict, mesh, outputPrefix));
+}
+
+
+// ************************************************************************* //
diff --git a/src/functionObjects/field/binField/binModels/binModel/binModelTemplates.C b/src/functionObjects/field/binField/binModels/binModel/binModelTemplates.C
new file mode 100644
index 0000000000..fdd86ffaa4
--- /dev/null
+++ b/src/functionObjects/field/binField/binModels/binModel/binModelTemplates.C
@@ -0,0 +1,102 @@
+/*---------------------------------------------------------------------------*\
+ ========= |
+ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
+ \\ / O peration |
+ \\ / A nd | www.openfoam.com
+ \\/ M anipulation |
+-------------------------------------------------------------------------------
+ Copyright (C) 2021-2022 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 .
+
+\*---------------------------------------------------------------------------*/
+
+// * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
+
+template
+bool Foam::binModel::decomposePatchValues
+(
+ List>& data,
+ const label bini,
+ const Type& v,
+ const vector& n
+) const
+{
+ return decomposePatchValues_;
+}
+
+
+template
+Foam::string Foam::binModel::writeComponents(const word& stem) const
+{
+ if (pTraits::nComponents == 1)
+ {
+ return stem;
+ }
+
+ string result = "";
+ for (label cmpt = 0; cmpt < pTraits::nComponents; ++cmpt)
+ {
+ if (cmpt) result += " ";
+ result += stem + "_" + word(pTraits::componentNames[cmpt]);
+ }
+ return result;
+};
+
+
+template
+void Foam::binModel::writeBinnedData
+(
+ List>& data,
+ Ostream& os
+) const
+{
+ if (cumulative_)
+ {
+ for (auto& datai : data)
+ {
+ for (label bini = 1; bini < nBin_; ++bini)
+ {
+ datai[bini] += datai[bini-1];
+ }
+ }
+ }
+
+ writeCurrentTime(os);
+
+ for (label bini = 0; bini < nBin_; ++bini)
+ {
+ Type total = Zero;
+
+ for (label i = 0; i < data.size(); ++i)
+ {
+ total += data[i][bini];
+ }
+
+ writeValue(os, total);
+
+ for (label i = 0; i < data.size(); ++i)
+ {
+ writeValue(os, data[i][bini]);
+ }
+ }
+
+ os << endl;
+}
+
+
+// ************************************************************************* //
diff --git a/src/functionObjects/field/binField/binModels/singleDirectionUniformBin/singleDirectionUniformBin.C b/src/functionObjects/field/binField/binModels/singleDirectionUniformBin/singleDirectionUniformBin.C
new file mode 100644
index 0000000000..2140504cc0
--- /dev/null
+++ b/src/functionObjects/field/binField/binModels/singleDirectionUniformBin/singleDirectionUniformBin.C
@@ -0,0 +1,186 @@
+/*---------------------------------------------------------------------------*\
+ ========= |
+ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
+ \\ / O peration |
+ \\ / A nd | www.openfoam.com
+ \\/ M anipulation |
+-------------------------------------------------------------------------------
+ Copyright (C) 2021-2022 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 "singleDirectionUniformBin.H"
+#include "addToRunTimeSelectionTable.H"
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+namespace Foam
+{
+namespace binModels
+{
+ defineTypeNameAndDebug(singleDirectionUniformBin, 0);
+ addToRunTimeSelectionTable(binModel, singleDirectionUniformBin, dictionary);
+}
+}
+
+
+// * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
+
+void Foam::binModels::singleDirectionUniformBin::initialise()
+{
+ const polyBoundaryMesh& pbm = mesh_.boundaryMesh();
+
+ // Determine extents of patches in a given direction
+ scalar geomMin = GREAT;
+ scalar geomMax = -GREAT;
+ for (const label patchi : patchSet_)
+ {
+ const polyPatch& pp = pbm[patchi];
+ const scalarField d(pp.faceCentres() & binDir_);
+ geomMin = min(min(d), geomMin);
+ geomMax = max(max(d), geomMax);
+ }
+
+ for (const label zonei : cellZoneIDs_)
+ {
+ const cellZone& cZone = mesh_.cellZones()[zonei];
+ const vectorField cz(mesh_.C(), cZone);
+ const scalarField d(cz & binDir_);
+
+ geomMin = min(min(d), geomMin);
+ geomMax = max(max(d), geomMax);
+ }
+
+ reduce(geomMin, minOp());
+ reduce(geomMax, maxOp());
+
+ // Slightly boost max so that region of interest is fully within bounds
+ geomMax = 1.0001*(geomMax - geomMin) + geomMin;
+
+ // Use geometry limits if not specified by the user
+ if (binMin_ == GREAT) binMin_ = geomMin;
+ if (binMax_ == GREAT) binMax_ = geomMax;
+
+ binDx_ = (binMax_ - binMin_)/scalar(nBin_);
+
+ if (binDx_ <= 0)
+ {
+ FatalErrorInFunction
+ << "Max bound must be greater than min bound" << nl
+ << " d = " << binDx_ << nl
+ << " min = " << binMin_ << nl
+ << " max = " << binMax_ << nl
+ << exit(FatalError);
+ }
+}
+
+
+// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
+
+Foam::binModels::singleDirectionUniformBin::singleDirectionUniformBin
+(
+ const dictionary& dict,
+ const fvMesh& mesh,
+ const word& outputPrefix
+)
+:
+ binModel(dict, mesh, outputPrefix),
+ binDx_(0),
+ binMin_(GREAT),
+ binMax_(GREAT),
+ binDir_(Zero)
+{
+ read(dict);
+}
+
+
+// * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * * //
+
+bool Foam::binModels::singleDirectionUniformBin::read(const dictionary& dict)
+{
+ if (!binModel::read(dict))
+ {
+ return false;
+ }
+
+ Info<< " Activating a set of single-direction bins" << endl;
+
+ const dictionary& binDict = dict.subDict("binData");
+
+ nBin_ = binDict.getCheck("nBin", labelMinMax::ge(1));
+
+ Info<< " Employing " << nBin_ << " bins" << endl;
+ if (binDict.readIfPresent("min", binMin_))
+ {
+ Info<< " - min : " << binMin_ << endl;
+ }
+ if (binDict.readIfPresent("max", binMax_))
+ {
+ Info<< " - max : " << binMax_ << endl;
+ }
+
+ cumulative_ = binDict.getOrDefault("cumulative", false);
+ Info<< " - cumulative : " << cumulative_ << endl;
+ Info<< " - decomposePatchValues : " << decomposePatchValues_ << endl;
+
+ binDir_ = binDict.get("direction");
+ binDir_.normalise();
+
+ if (mag(binDir_) == 0)
+ {
+ FatalIOErrorInFunction(dict)
+ << "Input direction should not be zero valued" << nl
+ << " direction = " << binDir_ << nl
+ << exit(FatalIOError);
+ }
+
+ Info<< " - direction : " << binDir_ << nl << endl;
+
+ initialise();
+
+ return true;
+}
+
+
+void Foam::binModels::singleDirectionUniformBin::apply()
+{
+ forAll(fieldNames_, i)
+ {
+ const bool ok =
+ processField(i)
+ || processField(i)
+ || processField(i)
+ || processField(i)
+ || processField(i);
+
+ if (!ok)
+ {
+ WarningInFunction
+ << "Unable to find field " << fieldNames_[i]
+ << ". Avaliable objects are "
+ << mesh_.objectRegistry::sortedToc()
+ << endl;
+ }
+ }
+
+ writtenHeader_ = true;
+}
+
+
+// ************************************************************************* //
diff --git a/src/functionObjects/field/binField/binModels/singleDirectionUniformBin/singleDirectionUniformBin.H b/src/functionObjects/field/binField/binModels/singleDirectionUniformBin/singleDirectionUniformBin.H
new file mode 100644
index 0000000000..1e2a631b11
--- /dev/null
+++ b/src/functionObjects/field/binField/binModels/singleDirectionUniformBin/singleDirectionUniformBin.H
@@ -0,0 +1,186 @@
+/*---------------------------------------------------------------------------*\
+ ========= |
+ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
+ \\ / O peration |
+ \\ / A nd | www.openfoam.com
+ \\/ M anipulation |
+-------------------------------------------------------------------------------
+ Copyright (C) 2021-2022 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::binModels::singleDirectionUniformBin
+
+Description
+ Calculates binned data in a specified direction.
+
+ For example, a 10cm-long patch extending only in the x-direction
+ can be binned into 5 bins in the same direction, so that
+ local information can be output for each 2cm-long segment.
+
+Usage
+ Minimal example by using \c system/controlDict.functions:
+ \verbatim
+ binField1
+ {
+ // Other binField entries
+ ...
+
+ // Mandatory entries
+ binModel singleDirectionUniformBin;
+
+ binData
+ {
+ // Mandatory entries
+ nBin ;
+ direction ;
+
+ // Optional entries
+ cumulative ;
+ min ;
+ max ;
+ }
+ }
+ \endverbatim
+
+ where the entries mean:
+ \table
+ Property | Description | Type | Reqd | Deflt
+ binModel | Type name: singleDirectionUniformBin | word | yes | -
+ binData | Entries of the chosen bin model | dict | yes | -
+ nBin | Number of bins in binning direction | label | yes | -
+ direction | Binning direction | vector | yes | -
+ cumulative | Flag to bin data accumulated with increasing distance in binning direction | bool | no | false
+ min | Min-bound in the binning direction with respect to the global coordinate system's origin | scalar | no | GREAT
+ max | Max-bound in the binning direction with respect to the global coordinate system's origin | scalar | no | GREAT
+ \endtable
+
+SourceFiles
+ singleDirectionUniformBin.C
+ singleDirectionUniformBinTemplates.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef Foam_binModels_singleDirectionUniformBin_H
+#define Foam_binModels_singleDirectionUniformBin_H
+
+#include "binModel.H"
+#include "writeFile.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+namespace binModels
+{
+
+/*---------------------------------------------------------------------------*\
+ Class singleDirectionUniformBin Declaration
+\*---------------------------------------------------------------------------*/
+
+class singleDirectionUniformBin
+:
+ public binModel
+{
+protected:
+
+ // Protected Data
+
+ //- Distance between bin divisions
+ scalar binDx_;
+
+ //- Minimum bin bound
+ scalar binMin_;
+
+ //- Maximum bin bound
+ scalar binMax_;
+
+ //- Binning direction
+ vector binDir_;
+
+
+ // Protected Member Functions
+
+ //- Write header for a binned-data file
+ template
+ void writeFileHeader(OFstream& os) const;
+
+ //- Initialise bin properties
+ virtual void initialise();
+
+ //- Apply the binning to field fieldi
+ template
+ bool processField(const label fieldi);
+
+
+public:
+
+ //- Runtime type information
+ TypeName("singleDirectionUniformBin");
+
+
+ // Constructors
+
+ //- Construct from components
+ singleDirectionUniformBin
+ (
+ const dictionary& dict,
+ const fvMesh& mesh,
+ const word& outputPrefix
+ );
+
+ //- No copy construct
+ singleDirectionUniformBin(const singleDirectionUniformBin&) = delete;
+
+ //- No copy assignment
+ void operator=(const singleDirectionUniformBin&) = delete;
+
+
+ //- Destructor
+ virtual ~singleDirectionUniformBin() = default;
+
+
+ // Member Functions
+
+ //- Read the dictionary
+ virtual bool read(const dictionary& dict);
+
+ //- Apply bins
+ virtual void apply();
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace binModels
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#ifdef NoRepository
+ #include "singleDirectionUniformBinTemplates.C"
+#endif
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/functionObjects/field/binField/binModels/singleDirectionUniformBin/singleDirectionUniformBinTemplates.C b/src/functionObjects/field/binField/binModels/singleDirectionUniformBin/singleDirectionUniformBinTemplates.C
new file mode 100644
index 0000000000..5fedd25a11
--- /dev/null
+++ b/src/functionObjects/field/binField/binModels/singleDirectionUniformBin/singleDirectionUniformBinTemplates.C
@@ -0,0 +1,195 @@
+/*---------------------------------------------------------------------------*\
+ ========= |
+ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
+ \\ / O peration |
+ \\ / A nd | www.openfoam.com
+ \\/ M anipulation |
+-------------------------------------------------------------------------------
+ Copyright (C) 2021-2022 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 .
+
+\*---------------------------------------------------------------------------*/
+
+
+template
+void Foam::binModels::singleDirectionUniformBin::writeFileHeader
+(
+ OFstream& os
+) const
+{
+ writeHeaderValue(os, "bins", nBin_);
+ writeHeaderValue(os, "start", binMin_);
+ writeHeaderValue(os, "end", binMax_);
+ writeHeaderValue(os, "delta", binDx_);
+ writeHeaderValue(os, "direction", binDir_);
+
+ // Compute and print bin end points in the binning direction
+ vectorField binPoints(nBin_);
+ writeCommented(os, "x co-ords :");
+ forAll(binPoints, pointi)
+ {
+ binPoints[pointi] = (binMin_ + (pointi + 1)*binDx_)*binDir_;
+ os << tab << binPoints[pointi].x();
+ }
+ os << nl;
+
+ writeCommented(os, "y co-ords :");
+ forAll(binPoints, pointi)
+ {
+ os << tab << binPoints[pointi].y();
+ }
+ os << nl;
+
+ writeCommented(os, "z co-ords :");
+ forAll(binPoints, pointi)
+ {
+ os << tab << binPoints[pointi].z();
+ }
+ os << nl;
+
+ writeHeader(os, "");
+ writeCommented(os, "Time");
+
+ for (label i = 0; i < nBin_; ++i)
+ {
+ const word ibin("_" + Foam::name(i));
+ writeTabbed(os, writeComponents("total" + ibin));
+ writeTabbed(os, writeComponents("internal" + ibin));
+
+ if (decomposePatchValues_)
+ {
+ writeTabbed(os, writeComponents("normal" + ibin));
+ writeTabbed(os, writeComponents("tangenial" + ibin));
+ }
+ else
+ {
+ writeTabbed(os, writeComponents("patch" + ibin));
+ }
+
+ }
+
+ os << endl;
+}
+
+
+template
+bool Foam::binModels::singleDirectionUniformBin::processField
+(
+ const label fieldi
+)
+{
+ const word& fieldName = fieldNames_[fieldi];
+
+ typedef GeometricField VolFieldType;
+
+ const VolFieldType* fieldPtr = mesh_.findObject(fieldName);
+
+ if (!fieldPtr)
+ {
+ return false;
+ }
+
+ if (Pstream::master() && !writtenHeader_)
+ {
+ writeFileHeader(filePtrs_[fieldi]);
+ }
+
+ const VolFieldType& fld = *fieldPtr;
+
+ // Total number of fields
+ //
+ // 0: internal
+ // 1: patch total
+ //
+ // OR
+ //
+ // 0: internal
+ // 1: patch normal
+ // 2: patch tangential
+ label nField = 2;
+ if (decomposePatchValues_)
+ {
+ nField += 1;
+ }
+
+ List> data(nField);
+ for (auto& binList : data)
+ {
+ binList.setSize(nBin_, Zero);
+ }
+
+ for (const label zonei : cellZoneIDs_)
+ {
+ const cellZone& cZone = mesh_.cellZones()[zonei];
+
+ for (const label celli : cZone)
+ {
+ const scalar dd = mesh_.C()[celli] & binDir_;
+
+ if (dd < binMin_ || dd > binMax_)
+ {
+ continue;
+ }
+
+ // Find the bin division corresponding to the cell
+ const label bini =
+ min(max(floor((dd - binMin_)/binDx_), 0), nBin_ - 1);
+
+ data[0][bini] += fld[celli];
+ }
+ }
+
+ forAllIters(patchSet_, iter)
+ {
+ const label patchi = iter();
+ const polyPatch& pp = mesh_.boundaryMesh()[patchi];
+ const vectorField np(mesh_.boundary()[patchi].nf());
+
+ const scalarField dd(pp.faceCentres() & binDir_);
+
+ forAll(dd, facei)
+ {
+ // Avoid faces outside of the bin
+ if (dd[facei] < binMin_ || dd[facei] > binMax_)
+ {
+ continue;
+ }
+
+ // Find the bin division corresponding to the face
+ const label bini =
+ min(max(floor((dd[facei] - binMin_)/binDx_), 0), nBin_ - 1);
+
+ const Type& v = fld.boundaryField()[patchi][facei];
+
+ if (!decomposePatchValues(data, bini, v, np[facei]))
+ {
+ data[1][bini] += v;
+ }
+ }
+ }
+
+ if (Pstream::master())
+ {
+ writeBinnedData(data, filePtrs_[fieldi]);
+ }
+
+ return true;
+}
+
+
+// ************************************************************************* //
diff --git a/src/functionObjects/field/binField/binModels/uniformBin/uniformBin.C b/src/functionObjects/field/binField/binModels/uniformBin/uniformBin.C
new file mode 100644
index 0000000000..77836bf690
--- /dev/null
+++ b/src/functionObjects/field/binField/binModels/uniformBin/uniformBin.C
@@ -0,0 +1,315 @@
+/*---------------------------------------------------------------------------*\
+ ========= |
+ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
+ \\ / O peration |
+ \\ / A nd | www.openfoam.com
+ \\/ M anipulation |
+-------------------------------------------------------------------------------
+ Copyright (C) 2021-2022 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 "uniformBin.H"
+#include "addToRunTimeSelectionTable.H"
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+namespace Foam
+{
+namespace binModels
+{
+ defineTypeNameAndDebug(uniformBin, 0);
+ addToRunTimeSelectionTable(binModel, uniformBin, dictionary);
+}
+}
+
+// * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
+
+void Foam::binModels::uniformBin::initialise()
+{
+ const polyBoundaryMesh& pbm = mesh_.boundaryMesh();
+
+ // Determine extents of patches in a given coordinate system
+ vector geomMin(GREAT, GREAT, GREAT);
+ vector geomMax(-GREAT, -GREAT, -GREAT);
+
+ for (const label patchi : patchSet_)
+ {
+ const polyPatch& pp = pbm[patchi];
+ const vectorField ppcs(coordSysPtr_->localPosition(pp.faceCentres()));
+
+ for (direction i = 0; i < vector::nComponents; ++i)
+ {
+ geomMin[i] = min(min(ppcs.component(i)), geomMin[i]);
+ geomMax[i] = max(max(ppcs.component(i)), geomMax[i]);
+ }
+ }
+
+ for (const label zonei : cellZoneIDs_)
+ {
+ const cellZone& cZone = mesh_.cellZones()[zonei];
+ const vectorField d
+ (
+ coordSysPtr_->localPosition(vectorField(mesh_.C(), cZone))
+ );
+
+ for (direction i = 0; i < vector::nComponents; ++i)
+ {
+ geomMin[i] = min(min(d.component(i)), geomMin[i]);
+ geomMax[i] = max(max(d.component(i)), geomMax[i]);
+ }
+ }
+
+ reduce(geomMin, minOp());
+ reduce(geomMax, maxOp());
+
+ for (direction i = 0; i < vector::nComponents; ++i)
+ {
+ // Slightly boost max so that region of interest is fully within bounds
+ geomMax[i] = 1.0001*(geomMax[i] - geomMin[i]) + geomMin[i];
+
+ // Use geometry limits if not specified by the user
+ if (binMinMax_[i][0] == GREAT) binMinMax_[i][0] = geomMin[i];
+ if (binMinMax_[i][1] == GREAT) binMinMax_[i][1] = geomMax[i];
+
+ if (binMinMax_[i][0] > binMinMax_[i][1])
+ {
+ FatalErrorInFunction
+ << "Max bounds must be greater than min bounds" << nl
+ << " direction = " << i << nl
+ << " min = " << binMinMax_[i][0] << nl
+ << " max = " << binMinMax_[i][1] << nl
+ << exit(FatalError);
+ }
+
+ //- Compute bin widths in binning directions
+ binW_[i] = (binMinMax_[i][1] - binMinMax_[i][0])/scalar(nBins_[i]);
+
+ if (binW_[i] <= 0)
+ {
+ FatalErrorInFunction
+ << "Bin widths must be greater than zero" << nl
+ << " direction = " << i << nl
+ << " min bound = " << binMinMax_[i][0] << nl
+ << " max bound = " << binMinMax_[i][1] << nl
+ << " bin width = " << binW_[i]
+ << exit(FatalError);
+ }
+ }
+
+ setBinsAddressing();
+}
+
+
+Foam::labelList Foam::binModels::uniformBin::binAddr(const vectorField& d) const
+{
+ labelList binIndices(d.size(), -1);
+
+ forAll(d, i)
+ {
+ // Avoid elements outside of the bin
+ bool faceInside = true;
+ for (direction j = 0; j < vector::nComponents; ++j)
+ {
+ if (d[i][j] < binMinMax_[j][0] || d[i][j] > binMinMax_[j][1])
+ {
+ faceInside = false;
+ break;
+ }
+ }
+
+ if (faceInside)
+ {
+ // Find the bin division corresponding to the element
+ Vector n(Zero);
+ for (direction j = 0; j < vector::nComponents; ++j)
+ {
+ n[j] = floor((d[i][j] - binMinMax_[j][0])/binW_[j]);
+ n[j] = min(max(n[j], 0), nBins_[j] - 1);
+ }
+
+ // Order: (e1, e2, e3), the first varies the fastest
+ binIndices[i] = n[0] + nBins_[0]*n[1] + nBins_[0]*nBins_[1]*n[2];
+ }
+ else
+ {
+ binIndices[i] = -1;
+ }
+ }
+
+ return binIndices;
+}
+
+
+void Foam::binModels::uniformBin::setBinsAddressing()
+{
+ faceToBin_.setSize(mesh_.nBoundaryFaces());
+ faceToBin_ = -1;
+
+ forAllIters(patchSet_, iter)
+ {
+ const polyPatch& pp = mesh_.boundaryMesh()[iter()];
+ const label i0 = pp.start() - mesh_.nInternalFaces();
+
+ SubList(faceToBin_, pp.size(), i0) =
+ binAddr(coordSysPtr_->localPosition(pp.faceCentres()));
+ }
+
+ cellToBin_.setSize(mesh_.nCells());
+ cellToBin_ = -1;
+
+ for (const label zonei : cellZoneIDs_)
+ {
+ const cellZone& cZone = mesh_.cellZones()[zonei];
+ labelList bins
+ (
+ binAddr(coordSysPtr_->localPosition(vectorField(mesh_.C(), cZone)))
+ );
+
+ forAll(cZone, i)
+ {
+ const label celli = cZone[i];
+ cellToBin_[celli] = bins[i];
+ }
+ }
+}
+
+
+// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
+
+Foam::binModels::uniformBin::uniformBin
+(
+ const dictionary& dict,
+ const fvMesh& mesh,
+ const word& outputPrefix
+)
+:
+ binModel(dict, mesh, outputPrefix),
+ nBins_(Zero),
+ binW_(Zero),
+ binMinMax_
+ (
+ vector2D(GREAT, GREAT),
+ vector2D(GREAT, GREAT),
+ vector2D(GREAT, GREAT)
+ )
+{
+ read(dict);
+}
+
+
+// * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * * //
+
+bool Foam::binModels::uniformBin::read(const dictionary& dict)
+{
+ if (!binModel::read(dict))
+ {
+ return false;
+ }
+
+ Info<< " Activating a set of uniform bins" << endl;
+
+ const dictionary& binDict = dict.subDict("binData");
+
+ nBins_ = binDict.get>("nBin");
+
+ for (const label n : nBins_)
+ {
+ nBin_ *= n;
+ }
+
+ if (nBin_ <= 0)
+ {
+ FatalIOErrorInFunction(binDict)
+ << "Number of bins must be greater than zero" << nl
+ << " e1 bins = " << nBins_[0] << nl
+ << " e2 bins = " << nBins_[1] << nl
+ << " e3 bins = " << nBins_[2]
+ << exit(FatalIOError);
+ }
+
+ Info<< " - Employing:" << nl
+ << " " << nBins_[0] << " e1 bins," << nl
+ << " " << nBins_[1] << " e2 bins," << nl
+ << " " << nBins_[2] << " e3 bins"
+ << endl;
+
+ cumulative_ = binDict.getOrDefault("cumulative", false);
+ Info<< " - cumulative : " << cumulative_ << endl;
+ Info<< " - decomposePatchValues : " << decomposePatchValues_ << endl;
+
+ if (binDict.found("minMax"))
+ {
+ const dictionary& minMaxDict = binDict.subDict("minMax");
+
+ for (direction i = 0; i < vector::nComponents; ++i)
+ {
+ const word ei("e" + Foam::name(i));
+
+ if (minMaxDict.readIfPresent(ei, binMinMax_[i]))
+ {
+ Info<< " - " << ei << " min : "
+ << binMinMax_[i][0] << nl
+ << " - " << ei << " max : "
+ << binMinMax_[i][1] << endl;
+ }
+ }
+ }
+ Info<< endl;
+
+ initialise();
+
+ return true;
+}
+
+
+void Foam::binModels::uniformBin::apply()
+{
+ forAll(fieldNames_, i)
+ {
+ const bool ok =
+ processField(i)
+ || processField(i)
+ || processField(i)
+ || processField(i)
+ || processField(i);
+
+ if (!ok)
+ {
+ WarningInFunction
+ << "Unable to find field " << fieldNames_[i]
+ << endl;
+ }
+ }
+
+ writtenHeader_ = true;
+}
+
+
+void Foam::binModels::uniformBin::updateMesh(const mapPolyMesh& mpm)
+{}
+
+
+void Foam::binModels::uniformBin::movePoints(const polyMesh& mesh)
+{
+ setBinsAddressing();
+}
+
+
+// ************************************************************************* //
diff --git a/src/functionObjects/field/binField/binModels/uniformBin/uniformBin.H b/src/functionObjects/field/binField/binModels/uniformBin/uniformBin.H
new file mode 100644
index 0000000000..bbc043b6da
--- /dev/null
+++ b/src/functionObjects/field/binField/binModels/uniformBin/uniformBin.H
@@ -0,0 +1,222 @@
+/*---------------------------------------------------------------------------*\
+ ========= |
+ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
+ \\ / O peration |
+ \\ / A nd | www.openfoam.com
+ \\/ M anipulation |
+-------------------------------------------------------------------------------
+ Copyright (C) 2021-2022 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::binModels::uniformBin
+
+Description
+ Calculates binned data in multiple segments according to
+ a specified Cartesian or cylindrical coordinate system.
+
+Usage
+ Minimal example by using \c system/controlDict.functions:
+ \verbatim
+ binField1
+ {
+ // Other binField entries
+ ...
+
+ // Mandatory entries
+ binModel uniformBin;
+
+ binData
+ {
+ // Mandatory entries
+ nBin >;
+
+ // Optional entries
+ cumulative ;
+ minMax
+ {
+ e1 ( ); // (min max);
+ e2 ( );
+ e3 ( );
+ }
+ }
+ }
+ \endverbatim
+
+ where the entries mean:
+ \table
+ Property | Description | Type | Reqd | Deflt
+ binModel | Type name: uniformBin | word | yes | -
+ binData | Entries of the chosen bin model | dict | yes | -
+ nBin | Numbers of bins in specified directions | Vector\ | yes | -
+ cumulative | Flag to bin data accumulated with increasing distance in binning direction | bool | no | false
+ minMax | Min-max bounds in binning directions with respect to the coordinateSystem's origin | dict | no | -
+ \endtable
+
+Note
+ - The order of bin numbering is (e1, e2, e3), where the first
+ varies the fastest. For example, for a cylindrical bin with
+ \f$ nBin = (radial, azimuth, height) = (2, 4, 2) \f$, the bin indices
+ may look like as follows - note the counterclockwise increments:
+ \verbatim
+ |-------------------|
+ | 12 | | 14 |
+ | 11 | 13 |
+ | 9 | 15 |
+ | 10 | | 16 |
+ |-------------------|
+ / / / /
+ / / / /
+ |-------------------|
+ | 4 | | 6 |
+ | 3 | 5 |
+ | 1 | 7 |
+ | 2 | | 8 |
+ |-------------------|
+ \endverbatim
+
+SourceFiles
+ uniformBin.C
+ uniformBinTemplates.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef Foam_binModels_uniformBin_H
+#define Foam_binModels_uniformBin_H
+
+#include "binModel.H"
+#include "writeFile.H"
+#include "coordinateSystem.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+namespace binModels
+{
+
+/*---------------------------------------------------------------------------*\
+ Class uniformBin Declaration
+\*---------------------------------------------------------------------------*/
+
+class uniformBin
+:
+ public binModel
+{
+protected:
+
+ // Protected Data
+
+ //- Numbers of bins in binning directions
+ Vector nBins_;
+
+ //- Equidistant bin widths in binning directions
+ vector binW_;
+
+ //- Min-max bounds of bins in binning directions
+ Vector binMinMax_;
+
+ //- Face index to bin index addressing
+ labelList faceToBin_;
+
+ //- Cell index to bin index addressing
+ labelList cellToBin_;
+
+
+ // Protected Member Functions
+
+ //- Write header for an binned-data file
+ template
+ void writeFileHeader(OFstream& os) const;
+
+ //- Initialise bin properties
+ virtual void initialise();
+
+ //- Return list of bin indices corresponding to positions given by d
+ virtual labelList binAddr(const vectorField& d) const;
+
+ //- Set/cache the bin addressing
+ virtual void setBinsAddressing();
+
+ //- Apply the binning to field fieldi
+ template
+ bool processField(const label fieldi);
+
+
+public:
+
+ //- Runtime type information
+ TypeName("uniformBin");
+
+
+ // Constructors
+
+ //- Construct from components
+ uniformBin
+ (
+ const dictionary& dict,
+ const fvMesh& mesh,
+ const word& outputPrefix
+ );
+
+ //- No copy construct
+ uniformBin(const uniformBin&) = delete;
+
+ //- No copy assignment
+ void operator=(const uniformBin&) = delete;
+
+
+ //- Destructor
+ virtual ~uniformBin() = default;
+
+
+ // Member Functions
+
+ //- Read the dictionary
+ virtual bool read(const dictionary& dict);
+
+ //- Apply bins
+ virtual void apply();
+
+ //- Update for changes of mesh
+ virtual void updateMesh(const mapPolyMesh& mpm);
+
+ //- Update for changes of mesh
+ virtual void movePoints(const polyMesh& mesh);
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#ifdef NoRepository
+ #include "uniformBinTemplates.C"
+#endif
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace binModels
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/functionObjects/field/binField/binModels/uniformBin/uniformBinTemplates.C b/src/functionObjects/field/binField/binModels/uniformBin/uniformBinTemplates.C
new file mode 100644
index 0000000000..8f7c1c103e
--- /dev/null
+++ b/src/functionObjects/field/binField/binModels/uniformBin/uniformBinTemplates.C
@@ -0,0 +1,178 @@
+/*---------------------------------------------------------------------------*\
+ ========= |
+ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
+ \\ / O peration |
+ \\ / A nd | www.openfoam.com
+ \\/ M anipulation |
+-------------------------------------------------------------------------------
+ Copyright (C) 2021-2022 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 .
+
+\*---------------------------------------------------------------------------*/
+
+// * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
+
+template
+void Foam::binModels::uniformBin::writeFileHeader
+(
+ OFstream& os
+) const
+{
+ writeHeader(os, "bins");
+
+ const tensor& R = coordSysPtr_->R();
+ for (direction i = 0; i < vector::nComponents; ++i)
+ {
+ writeHeaderValue(os, "e" + Foam::name(i) + " bins", nBins_[i]);
+ writeHeaderValue(os, " start", binMinMax_[i][0]);
+ writeHeaderValue(os, " end", binMinMax_[i][1]);
+ writeHeaderValue(os, " delta", binW_[i]);
+ writeHeaderValue(os, " direction", R.col(i));
+ }
+ writeCommented(os, "bin end co-ordinates:");
+ os << nl;
+
+ // Compute and print bin end points in binning directions
+ for (direction i = 0; i < vector::nComponents; ++i)
+ {
+ scalar binEnd = binMinMax_[i][0];
+
+ writeCommented(os, "e"+Foam::name(i)+" co-ords :");
+ for (label j = 0; j < nBins_[i]; ++j)
+ {
+ binEnd += binW_[i];
+ os << tab << binEnd;
+ }
+ os << nl;
+ }
+
+ writeHeader(os, "");
+ writeCommented(os, "Time");
+
+ for (label i = 0; i < nBin_; ++i)
+ {
+ const word ibin(Foam::name(i) + ':');
+ writeTabbed(os, writeComponents("total" + ibin));
+ writeTabbed(os, writeComponents("internal" + ibin));
+
+ if (decomposePatchValues_)
+ {
+ writeTabbed(os, writeComponents("normal" + ibin));
+ writeTabbed(os, writeComponents("tangential" + ibin));
+ }
+ else
+ {
+ writeTabbed(os, writeComponents("patch" + ibin));
+ }
+ }
+
+ os << endl;
+}
+
+template
+bool Foam::binModels::uniformBin::processField(const label fieldi)
+{
+ const word& fieldName = fieldNames_[fieldi];
+
+ typedef GeometricField VolFieldType;
+
+ const VolFieldType* fieldPtr = mesh_.findObject(fieldName);
+
+ if (!fieldPtr)
+ {
+ return false;
+ }
+
+ if (Pstream::master() && !writtenHeader_)
+ {
+ writeFileHeader(filePtrs_[fieldi]);
+ }
+
+ const VolFieldType& fld = *fieldPtr;
+
+ // Total number of fields
+ //
+ // 0: internal
+ // 1: patch total
+ //
+ // OR
+ //
+ // 0: internal
+ // 1: patch normal
+ // 2: patch tangential
+ label nField = 2;
+ if (decomposePatchValues_)
+ {
+ nField += 1;
+ }
+
+ List> data(nField);
+ for (auto& binList : data)
+ {
+ binList.setSize(nBin_, Zero);
+ }
+
+ for (const label zonei : cellZoneIDs_)
+ {
+ const cellZone& cZone = mesh_.cellZones()[zonei];
+
+ for (const label celli : cZone)
+ {
+ const label bini = cellToBin_[celli];
+
+ if (bini != -1)
+ {
+ data[0][bini] += fld[celli];
+ }
+ }
+ }
+
+ forAllIters(patchSet_, iter)
+ {
+ const label patchi = iter();
+ const polyPatch& pp = mesh_.boundaryMesh()[patchi];
+ const vectorField np(mesh_.boundary()[patchi].nf());
+
+ forAll(pp, facei)
+ {
+ const label localFacei =
+ pp.start() - mesh_.nInternalFaces() + facei;
+ const label bini = faceToBin_[localFacei];
+
+ if (bini != -1)
+ {
+ const Type& v = fld.boundaryField()[patchi][facei];
+
+ if (!decomposePatchValues(data, bini, v, np[facei]))
+ {
+ data[1][bini] += v;
+ }
+ }
+ }
+ }
+
+ if (Pstream::master())
+ {
+ writeBinnedData(data, filePtrs_[fieldi]);
+ }
+
+ return true;
+}
+
+
+// ************************************************************************* //