diff --git a/src/functionObjects/field/Make/files b/src/functionObjects/field/Make/files
index 3fd56b2f79a6486e5e459ece5d42cb6e2ec3e419..bc8fe9634f4c0c728d0944603f58972e63a82142 100644
--- a/src/functionObjects/field/Make/files
+++ b/src/functionObjects/field/Make/files
@@ -66,6 +66,10 @@ surfaceInterpolate/surfaceInterpolate.C
 
 regionSizeDistribution/regionSizeDistribution.C
 histogram/histogram.C
+histogram/histogramModels/histogramModel/histogramModel.C
+histogram/histogramModels/histogramModel/histogramModelNew.C
+histogram/histogramModels/equalBinWidth/equalBinWidth.C
+histogram/histogramModels/unequalBinWidth/unequalBinWidth.C
 
 fieldExpression/fieldExpression.C
 components/components.C
diff --git a/src/functionObjects/field/histogram/histogram.C b/src/functionObjects/field/histogram/histogram.C
index 90e2289cc108a16163ed8201015e656fdae6d502..4bafa9521c4ef58543ceff0078e4a7e643947fa5 100644
--- a/src/functionObjects/field/histogram/histogram.C
+++ b/src/functionObjects/field/histogram/histogram.C
@@ -27,8 +27,7 @@ License
 \*---------------------------------------------------------------------------*/
 
 #include "histogram.H"
-#include "volFields.H"
-#include "ListOps.H"
+#include "histogramModel.H"
 #include "addToRunTimeSelectionTable.H"
 
 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
@@ -52,11 +51,8 @@ Foam::functionObjects::histogram::histogram
     const dictionary& dict
 )
 :
-    functionObjects::fvMeshFunctionObject(name, runTime, dict),
-    functionObjects::writeFile(obr_, name),
-    max_(-GREAT),
-    min_(GREAT),
-    setWriterPtr_(nullptr)
+    fvMeshFunctionObject(name, runTime, dict),
+    histogramModelPtr_(nullptr)
 {
     read(dict);
 }
@@ -66,30 +62,14 @@ Foam::functionObjects::histogram::histogram
 
 bool Foam::functionObjects::histogram::read(const dictionary& dict)
 {
-    fvMeshFunctionObject::read(dict);
-    writeFile::read(dict);
-
-    dict.readEntry("field", fieldName_);
-
-    max_ = dict.getOrDefault<scalar>("max", -GREAT);
-    min_ = dict.getOrDefault<scalar>("min", GREAT);
-    dict.readEntry("nBins", nBins_);
-
-    if (nBins_ < 1)
+    if (!fvMeshFunctionObject::read(dict))
     {
-        FatalErrorInFunction
-            << "Number of histogram bins = " << nBins_
-            << " cannot be negative or zero."
-            << abort(FatalError);
+        return false;
     }
 
-    const word writeType(dict.get<word>("setFormat"));
+    Info<< type() << " " << name() << ":" << endl;
 
-    setWriterPtr_ = coordSetWriter::New
-    (
-        writeType,
-        dict.subOrEmptyDict("formatOptions").optionalSubDict(writeType)
-    );
+    histogramModelPtr_.reset(histogramModel::New(name(), mesh_, dict));
 
     return true;
 }
@@ -103,115 +83,13 @@ bool Foam::functionObjects::histogram::execute()
 
 bool Foam::functionObjects::histogram::write()
 {
-    Log << type() << " " << name() << " write:" << nl;
-
-    tmp<volScalarField> tfield;
-    tfield.cref(obr_.cfindObject<volScalarField>(fieldName_));
-
-    if (tfield)
-    {
-        Log << "    Looking up field " << fieldName_ << endl;
-    }
-    else
-    {
-        Log << "    Reading field " << fieldName_ << endl;
-        tfield = tmp<volScalarField>::New
-        (
-            IOobject
-            (
-                fieldName_,
-                mesh_.time().timeName(),
-                mesh_,
-                IOobject::MUST_READ,
-                IOobject::NO_WRITE
-            ),
-            mesh_
-        );
-    }
-    const auto& field = tfield();
-
-    scalar histMax = max_;
-    scalar histMin = min_;
-
-    if (max_ == -GREAT)
-    {
-        // Determine current min and max
-        histMax = max(field).value();
-
-        if (min_ == GREAT)
-        {
-            histMin = min(field).value();
-        }
-        Log << "    Determined histogram bounds from field"
-            << " min/max(" << fieldName_ << ") = "
-            << histMin << ' ' << histMax << endl;
-    }
-    else if (min_ == GREAT)
-    {
-        histMin = 0;
-    }
-
-    // Calculate the mid-points of bins for the graph axis
-    pointField xBin(nBins_, Zero);
-    const scalar delta = (histMax - histMin)/nBins_;
+    Log << type() << " " << name() << " write:" << endl;
 
+    if (!histogramModelPtr_->write(log))
     {
-        scalar x = histMin + 0.5*delta;
-        for (point& p : xBin)
-        {
-            p.x() = x;
-            x += delta;
-        }
-    }
-
-    scalarField dataNormalized(nBins_, Zero);
-    labelField dataCount(nBins_, Zero);
-    const scalarField& V = mesh_.V();
-
-    forAll(field, celli)
-    {
-        const label bini = (field[celli] - histMin)/delta;
-        if (bini >= 0 && bini < nBins_)
-        {
-            dataNormalized[bini] += V[celli];
-            dataCount[bini]++;
-        }
-    }
-
-    Pstream::listCombineGather(dataNormalized, plusEqOp<scalar>());
-    Pstream::listCombineGather(dataCount, plusEqOp<label>());
-
-    if (Pstream::master())
-    {
-        const scalar sumData = sum(dataNormalized);
-
-        if (sumData > SMALL)
-        {
-            dataNormalized /= sumData;
-
-            const coordSet coords(fieldName_, "x", xBin, mag(xBin));
-
-            auto& writer = *setWriterPtr_;
-
-            writer.open
-            (
-                coords,
-                (
-                    writeFile::baseTimeDir()
-                  / (coords.name() + coordSetWriter::suffix(fieldName_))
-                )
-            );
-
-            Log << "    Writing histogram of " << fieldName_
-                << " to " << writer.path() << endl;
-
-            writer.nFields(2);
-            writer.write(fieldName_, dataNormalized);
-            writer.write(fieldName_ + "Count", dataCount);
-
-            writer.close(true);
-        }
+        return false;
     }
+    Log << endl;
 
     return true;
 }
diff --git a/src/functionObjects/field/histogram/histogram.H b/src/functionObjects/field/histogram/histogram.H
index e5e7781f443338942202318e69490f05e1fe8689..304c197539933b342c16f18a784e6776c777c7a8 100644
--- a/src/functionObjects/field/histogram/histogram.H
+++ b/src/functionObjects/field/histogram/histogram.H
@@ -35,65 +35,58 @@ Description
 
     Operands:
     \table
-      Operand        | Type              | Location
-      input          | volScalarField    | $FOAM_CASE/\<time\>/\<inpField\>
-      output file    | dat  | $FOAM_CASE/postProcessing/\<FO\>/\<time\>/\<file\>
-      output field   | -                 | -
+      Operand        | Type           | Location
+      input          | volScalarField | \<time\>/\<inpField\>
+      output file    | dat            | postProcessing/\<FO\>/\<time\>/histogram
+      output field   | -              | -
     \endtable
 
-    The set written contains two columns, the first the volume averaged values,
-    the second the raw bin count.
+    The data written contains four columns (from left to right):
+    - time
+    - mid-point of histogram bin
+    - histogram counts - number of samples in each bin
+    - volume-weighted histogram values
 
 Usage
     Minimal example by using \c system/controlDict.functions:
     \verbatim
     histogram1
     {
-        // Mandatory entries (unmodifiable)
-        type        histogram;
-        libs        (fieldFunctionObjects);
+        // Mandatory entries
+        type            histogram;
+        libs            (fieldFunctionObjects);
+        field           <word>;
+        model           <word>;
 
-        // Mandatory (inherited) entries (runtime modifiable)
-        field       p;
-        nBins       100;
-        setFormat   gnuplot;
+        // Conditional entries
 
-        // Optional entries (runtime modifiable)
-        max         5;
-        min        -5;
+            // Option-1: when model == equalBinWidth
 
-        // Optional (inherited) entries
+            // Option-2: when model == unequalBinWidth
+
+        // Inherited entries
         ...
     }
     \endverbatim
 
     where the entries mean:
     \table
-      Property     | Description                        | Type | Req'd | Dflt
-      type         | Type name: histogram               | word |  yes  | -
-      libs         | Library name: fieldFunctionObjects | word |  yes  | -
-      field        | Name of operand field              | word |  yes  | -
-      nBins        | Number of histogram bins           | label | yes  | -
-      setFormat    | Output format                      | word |  yes  | -
-      max          | Maximum value sampled       | scalar | no  | fieldMax
-      min          | minimum value sampled       | scalar | no  | 0.0
+      Property     | Description                        | Type | Reqd | Deflt
+      type         | Type name: histogram               | word | yes  | -
+      libs         | Library name: fieldFunctionObjects | word | yes  | -
+      field        | Name of operand field              | word | yes  | -
+      model        | Name of the histogram model        | word | yes  | -
     \endtable
 
-    The inherited entries are elaborated in:
-     - \link functionObject.H \endlink
-     - \link writeFile.H \endlink
-
-    Usage by the \c postProcess utility is not available.
-
-Note
-    If \c max is not provided it will use the field's min and max as the bin
-    extremes. If \c max is provided but not \c min it will use 0.
+    Options for the \c model entry:
+    \verbatim
+      equalBinWidth    | Use equal-bin width
+      unequalBinWidth  | Use unequal-bin widths
+    \endverbatim
 
-See also
-    - Foam::functionObject
-    - Foam::functionObjects::fvMeshFunctionObject
-    - Foam::functionObjects::writeFile
-    - ExtendedCodeGuide::functionObjects::field::histogram
+    The inherited entries are elaborated in:
+      - \link functionObject.H \endlink
+      - \link writeFile.H \endlink
 
 SourceFiles
     histogram.C
@@ -104,41 +97,30 @@ SourceFiles
 #define Foam_functionObjects_histogram_H
 
 #include "fvMeshFunctionObject.H"
-#include "writeFile.H"
-#include "coordSetWriter.H"
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 
 namespace Foam
 {
+
+// Forward Declarations
+class histogramModel;
+
 namespace functionObjects
 {
 
 /*---------------------------------------------------------------------------*\
-                         Class histogram Declaration
+                        Class histogram Declaration
 \*---------------------------------------------------------------------------*/
 
 class histogram
 :
-    public functionObjects::fvMeshFunctionObject,
-    public functionObjects::writeFile
+    public fvMeshFunctionObject
 {
     // Private Data
 
-        //- Number of bins
-        label nBins_;
-
-        //- Name of field
-        word fieldName_;
-
-        //- Maximum value
-        scalar max_;
-
-        //- Minimum value
-        scalar min_;
-
-        //- Output formatter to write
-        mutable autoPtr<coordSetWriter> setWriterPtr_;
+        //- Histogram model
+        autoPtr<histogramModel> histogramModelPtr_;
 
 
 public:
@@ -170,8 +152,8 @@ public:
 
     // Member Functions
 
-        //- Read the histogram data
-        virtual bool read(const dictionary&);
+        //- Read the top-level dictionary
+        virtual bool read(const dictionary& dict);
 
         //- Execute (effectively no-op)
         virtual bool execute();
diff --git a/src/functionObjects/field/histogram/histogramModels/equalBinWidth/equalBinWidth.C b/src/functionObjects/field/histogram/histogramModels/equalBinWidth/equalBinWidth.C
new file mode 100644
index 0000000000000000000000000000000000000000..01bec912e9bed1e506108adbf4bc8826577b8454
--- /dev/null
+++ b/src/functionObjects/field/histogram/histogramModels/equalBinWidth/equalBinWidth.C
@@ -0,0 +1,167 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | www.openfoam.com
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+    Copyright (C) 2016 OpenFOAM Foundation
+    Copyright (C) 2016-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 <http://www.gnu.org/licenses/>.
+
+\*---------------------------------------------------------------------------*/
+
+#include "equalBinWidth.H"
+#include "histogramModel.H"
+#include "addToRunTimeSelectionTable.H"
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+namespace Foam
+{
+namespace histogramModels
+{
+    defineTypeNameAndDebug(equalBinWidth, 0);
+    addToRunTimeSelectionTable(histogramModel, equalBinWidth, dictionary);
+}
+}
+
+
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+Foam::histogramModels::equalBinWidth::equalBinWidth
+(
+    const word& name,
+    const fvMesh& mesh,
+    const dictionary& dict
+)
+:
+    histogramModel(name, mesh, dict),
+    nBins_(0),
+    min_(GREAT),
+    max_(-GREAT)
+{
+    read(dict);
+}
+
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+bool Foam::histogramModels::equalBinWidth::read(const dictionary& dict)
+{
+    if (!histogramModel::read(dict))
+    {
+        return false;
+    }
+
+    nBins_ = dict.getScalar("nBins");
+
+    if (nBins_ < 1)
+    {
+        FatalIOErrorInFunction(dict)
+            << "Number of histogram bins = " << nBins_
+            << " cannot be negative or zero."
+            << abort(FatalIOError);
+    }
+
+    min_ = dict.getOrDefault<scalar>("min", GREAT);
+    max_ = dict.getOrDefault<scalar>("max", -GREAT);
+
+    return true;
+}
+
+
+bool Foam::histogramModels::equalBinWidth::write(const bool log)
+{
+    // Retrieve operand field
+    const volScalarField& field = histogramModel::getOrReadField(fieldName());
+
+    // Determine min and max from the operand field
+    // if the user did not provide any min or max
+    scalar histMax = max_;
+    scalar histMin = min_;
+
+    if (max_ == -GREAT)
+    {
+        histMax = max(field).value();
+
+        if (min_ == GREAT)
+        {
+            histMin = min(field).value();
+        }
+        if (log)
+        {
+            Info<< "    Determined histogram bounds from field"
+                << " min/max(" << fieldName() << ") = "
+                << histMin << ' ' << histMax << endl;
+        }
+    }
+    else if (min_ == GREAT)
+    {
+        histMin = 0;
+    }
+
+    if (histMax < histMin)
+    {
+        FatalErrorInFunction
+            << "Histogram minimum = " << histMin
+            << ", cannot be larger than histogram maximum = " << histMax
+            << exit(FatalError);
+    }
+
+
+    // Calculate the mid-points of bins for the graph axis
+    pointField binMidPoints(nBins_, Zero);
+    const scalar delta = (histMax - histMin)/nBins_;
+
+    {
+        scalar x = histMin + 0.5*delta;
+        for (point& p : binMidPoints)
+        {
+            p.x() = x;
+            x += delta;
+        }
+    }
+
+
+    // Calculate the histogram data
+    scalarField dataNormalised(nBins_, Zero);
+    labelField dataCount(nBins_, Zero);
+    const scalarField& V = mesh().V();
+
+    forAll(field, celli)
+    {
+        const label bini = (field[celli] - histMin)/delta;
+        if (bini >= 0 && bini < nBins_)
+        {
+            dataNormalised[bini] += V[celli];
+            dataCount[bini]++;
+        }
+    }
+    Pstream::listCombineGather(dataNormalised, plusEqOp<scalar>());
+    Pstream::listCombineGather(dataCount, plusEqOp<label>());
+
+
+    // Write histogram data
+    histogramModel::write(dataNormalised, dataCount, mag(binMidPoints));
+
+    return true;
+}
+
+
+// ************************************************************************* //
diff --git a/src/functionObjects/field/histogram/histogramModels/equalBinWidth/equalBinWidth.H b/src/functionObjects/field/histogram/histogramModels/equalBinWidth/equalBinWidth.H
new file mode 100644
index 0000000000000000000000000000000000000000..e61b76f30d894570843612aa31f62397e53e12c9
--- /dev/null
+++ b/src/functionObjects/field/histogram/histogramModels/equalBinWidth/equalBinWidth.H
@@ -0,0 +1,146 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  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 <http://www.gnu.org/licenses/>.
+
+Class
+    Foam::histogramModels::equalBinWidth
+
+Description
+    Histogram model which groups data into bins of equal width.
+
+Usage
+    Minimal example by using \c system/controlDict.functions:
+    \verbatim
+    histogram1
+    {
+        // Inherited entries
+        ...
+
+        // Mandatory entries
+        nBins       <label>;
+
+        // Optional entries
+        min         <scalar>;
+        max         <scalar>;
+    }
+    \endverbatim
+
+    where the entries mean:
+    \table
+      Property  | Description                       | Type   | Reqd | Deflt
+      nBins     | Number of histogram bins          | label  | yes  | -
+      min       | Minimum value of histogram data   | scalar | no   | -
+      max       | Maximum value of histogram data   | scalar | no   | -
+    \endtable
+
+Note
+  - If \c max is not provided, \c histogram will use operand
+    field's min and max as the bin extremes.
+  - If \c max is provided without \c min,
+    \c histogram will use 0 for the \c min.
+
+SourceFiles
+    equalBinWidth.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef Foam_histogramModels_equalBinWidth_H
+#define Foam_histogramModels_equalBinWidth_H
+
+#include "histogramModel.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+namespace histogramModels
+{
+
+/*---------------------------------------------------------------------------*\
+                        Class equalBinWidth Declaration
+\*---------------------------------------------------------------------------*/
+
+class equalBinWidth
+:
+    public histogramModel
+{
+    // Private Data
+
+        //- Number of bins
+        label nBins_;
+
+        //- Minimum value of histogram data
+        scalar min_;
+
+        //- Maximum value of histogram data
+        scalar max_;
+
+
+public:
+
+    //- Runtime type information
+    TypeName("equalBinWidth");
+
+
+    // Constructors
+
+        //- Construct from components
+        equalBinWidth
+        (
+            const word& name,
+            const fvMesh& mesh,
+            const dictionary& dict
+        );
+
+        //- No copy construct
+        equalBinWidth(const equalBinWidth&) = delete;
+
+        //- No copy assignment
+        void operator=(const equalBinWidth&) = delete;
+
+
+    // Destructor
+    virtual ~equalBinWidth() = default;
+
+
+    // Member Functions
+
+        //- Read top-level dictionary
+        virtual bool read(const dictionary& dict);
+
+        //- Write data to stream and files
+        virtual bool write(const bool log);
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace histogramModels
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/functionObjects/field/histogram/histogramModels/histogramModel/histogramModel.C b/src/functionObjects/field/histogram/histogramModels/histogramModel/histogramModel.C
new file mode 100644
index 0000000000000000000000000000000000000000..8bfb64ce1d59b1989fbd863eda90f987dd1dc86e
--- /dev/null
+++ b/src/functionObjects/field/histogram/histogramModels/histogramModel/histogramModel.C
@@ -0,0 +1,152 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  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 <http://www.gnu.org/licenses/>.
+
+\*---------------------------------------------------------------------------*/
+
+#include "histogramModel.H"
+#include "fvMesh.H"
+#include "ListOps.H"
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+namespace Foam
+{
+    defineTypeNameAndDebug(histogramModel, 0);
+    defineRunTimeSelectionTable(histogramModel, dictionary);
+}
+
+
+// * * * * * * * * * * * * Protected Member Functions  * * * * * * * * * * * //
+
+void Foam::histogramModel::writeFileHeader(Ostream& os)
+{
+    writeHeader(os, "Histogram");
+    writeCommented(os, "Time");
+    writeTabbed(os, "binMidPoints");
+    writeTabbed(os, "dataCounts");
+    writeTabbed(os, "dataValues");
+    os  << endl;
+}
+
+
+Foam::volScalarField& Foam::histogramModel::getOrReadField
+(
+    const word& fieldName
+) const
+{
+    auto* ptr = mesh_.getObjectPtr<volScalarField>(fieldName);
+
+    if (!ptr)
+    {
+        ptr = new volScalarField
+        (
+            IOobject
+            (
+                fieldName,
+                mesh_.time().timeName(),
+                mesh_,
+                IOobject::MUST_READ,
+                IOobject::AUTO_WRITE
+            ),
+            mesh_
+        );
+        mesh_.objectRegistry::store(ptr);
+    }
+
+    return *ptr;
+}
+
+
+void Foam::histogramModel::write
+(
+    scalarField& dataNormalised,
+    const labelField& dataCount,
+    const scalarField& magBinMidPoint
+)
+{
+    if (!Pstream::master())
+    {
+        return;
+    }
+
+    const scalar sumData = sum(dataNormalised);
+
+    if (sumData < SMALL)
+    {
+        return;
+    }
+
+    dataNormalised /= sumData;
+
+    const auto time = mesh().time().value();
+
+    forAll(dataNormalised, i)
+    {
+        file()
+            << time << tab
+            << magBinMidPoint[i] << tab
+            << dataCount[i] << tab
+            << dataNormalised[i]
+            << endl;
+    }
+}
+
+
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+Foam::histogramModel::histogramModel
+(
+    const word& name,
+    const fvMesh& mesh,
+    const dictionary& dict
+)
+:
+    writeFile(mesh, name, "histogram", dict),
+    mesh_(mesh),
+    fieldName_(word::null)
+{}
+
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+bool Foam::histogramModel::read(const dictionary& dict)
+{
+    if (!functionObjects::writeFile::read(dict))
+    {
+        return false;
+    }
+
+    fieldName_ = dict.get<word>("field");
+
+    if (writeToFile() && !writtenHeader_)
+    {
+        writeFileHeader(file());
+    }
+
+    return true;
+}
+
+
+// ************************************************************************* //
diff --git a/src/functionObjects/field/histogram/histogramModels/histogramModel/histogramModel.H b/src/functionObjects/field/histogram/histogramModels/histogramModel/histogramModel.H
new file mode 100644
index 0000000000000000000000000000000000000000..2543633908f4be7f186e81c45fa64cb38d9590c9
--- /dev/null
+++ b/src/functionObjects/field/histogram/histogramModels/histogramModel/histogramModel.H
@@ -0,0 +1,184 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  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 <http://www.gnu.org/licenses/>.
+
+Namespace
+    Foam::histogramModels
+
+Description
+    A namespace for various histogram model implementations.
+
+Class
+    Foam::histogramModel
+
+Description
+    A base class for histogram models.
+
+SourceFiles
+    histogramModel.C
+    histogramModelNew.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef Foam_histogramModel_H
+#define Foam_histogramModel_H
+
+#include "writeFile.H"
+#include "volFields.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+// Forward Declarations
+class fvMesh;
+
+/*---------------------------------------------------------------------------*\
+                        Class histogramModel Declaration
+\*---------------------------------------------------------------------------*/
+
+class histogramModel
+:
+    public functionObjects::writeFile
+{
+    // Private Data
+
+        //- Const reference to the mesh
+        const fvMesh& mesh_;
+
+        //- Name of operand field
+        word fieldName_;
+
+
+protected:
+
+    // Protected Member Functions
+
+        //- Output file header information
+        virtual void writeFileHeader(Ostream& os);
+
+        //- Return requested field from the object registry
+        //- or read+register the field to the object registry
+        volScalarField& getOrReadField(const word& fieldName) const;
+
+        //- Write histogram data
+        void write
+        (
+            scalarField& dataNormalised,
+            const labelField& dataCount,
+            const scalarField& magMidBin
+        );
+
+
+public:
+
+    //- Runtime type information
+    TypeName("histogramModel");
+
+
+    // Declare runtime constructor selection table
+
+        declareRunTimeSelectionTable
+        (
+            autoPtr,
+            histogramModel,
+            dictionary,
+            (
+                const word& name,
+                const fvMesh& mesh,
+                const dictionary& dict
+            ),
+            (name, mesh, dict)
+        );
+
+
+    // Selectors
+
+        //- Return a reference to the selected histogram model
+        static autoPtr<histogramModel> New
+        (
+            const word& name,
+            const fvMesh& mesh,
+            const dictionary& dict
+        );
+
+
+    // Constructors
+
+        //- Construct from components
+        histogramModel
+        (
+            const word& name,
+            const fvMesh& mesh,
+            const dictionary& dict
+        );
+
+        //- No copy construct
+        histogramModel(const histogramModel&) = delete;
+
+        //- No copy assignment
+        void operator=(const histogramModel&) = delete;
+
+
+    //- Destructor
+    virtual ~histogramModel() = default;
+
+
+    // Member Functions
+
+    // Access
+
+        //- Return const reference to the mesh
+        const fvMesh& mesh() const noexcept
+        {
+            return mesh_;
+        }
+
+        //- Return const reference to the operand field name
+        const word& fieldName() const noexcept
+        {
+            return fieldName_;
+        }
+
+
+    // I-O
+
+        //- Read top-level dictionary
+        virtual bool read(const dictionary& dict);
+
+        //- Write data to stream and files
+        virtual bool write(const bool log) = 0;
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/functionObjects/field/histogram/histogramModels/histogramModel/histogramModelNew.C b/src/functionObjects/field/histogram/histogramModels/histogramModel/histogramModelNew.C
new file mode 100644
index 0000000000000000000000000000000000000000..cc98cf026339f1cd2b4144b1a79b0304ed8521ed
--- /dev/null
+++ b/src/functionObjects/field/histogram/histogramModels/histogramModel/histogramModelNew.C
@@ -0,0 +1,62 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  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 <http://www.gnu.org/licenses/>.
+
+
+\*---------------------------------------------------------------------------*/
+
+#include "histogramModel.H"
+#include "fvMesh.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+Foam::autoPtr<Foam::histogramModel> Foam::histogramModel::New
+(
+    const word& name,
+    const fvMesh& mesh,
+    const dictionary& dict
+)
+{
+    const word modelType(dict.get<word>("model"));
+
+    Info<< "    Selecting model: " << modelType << nl << endl;
+
+    auto* ctorPtr = dictionaryConstructorTable(modelType);
+
+    if (!ctorPtr)
+    {
+        FatalIOErrorInLookup
+        (
+            dict,
+            "histogramModel",
+            modelType,
+            *dictionaryConstructorTablePtr_
+        ) << exit(FatalIOError);
+    }
+
+    return autoPtr<histogramModel>(ctorPtr(name, mesh, dict));
+}
+
+
+// ************************************************************************* //
diff --git a/src/functionObjects/field/histogram/histogramModels/unequalBinWidth/unequalBinWidth.C b/src/functionObjects/field/histogram/histogramModels/unequalBinWidth/unequalBinWidth.C
new file mode 100644
index 0000000000000000000000000000000000000000..b4b94c4337f33ff227e4807df470a8ea22de567f
--- /dev/null
+++ b/src/functionObjects/field/histogram/histogramModels/unequalBinWidth/unequalBinWidth.C
@@ -0,0 +1,155 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  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 <http://www.gnu.org/licenses/>.
+
+\*---------------------------------------------------------------------------*/
+
+#include "unequalBinWidth.H"
+#include "histogramModel.H"
+#include "addToRunTimeSelectionTable.H"
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+namespace Foam
+{
+namespace histogramModels
+{
+    defineTypeNameAndDebug(unequalBinWidth, 0);
+    addToRunTimeSelectionTable(histogramModel, unequalBinWidth, dictionary);
+}
+}
+
+
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+Foam::histogramModels::unequalBinWidth::unequalBinWidth
+(
+    const word& name,
+    const fvMesh& mesh,
+    const dictionary& dict
+)
+:
+    histogramModel(name, mesh, dict),
+    nBins_(-1),
+    ranges_(Zero)
+{
+    read(dict);
+}
+
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+bool Foam::histogramModels::unequalBinWidth::read(const dictionary& dict)
+{
+    if (!histogramModel::read(dict))
+    {
+        return false;
+    }
+
+    ranges_ = dict.get<List<Pair<scalar>>>("ranges");
+
+    forAll(ranges_, bini)
+    {
+        const auto& range = ranges_[bini];
+        const scalar min = range.first();
+        const scalar max = range.second();
+
+        if (max < min)
+        {
+            FatalIOErrorInFunction(dict)
+                << "For bin-" << bini
+                << ", min is larger than max."
+                << " min = " << min
+                << " max = " << max
+                << abort(FatalIOError);
+        }
+    }
+
+    nBins_ = ranges_.size();
+
+    if (nBins_ < 1)
+    {
+        FatalIOErrorInFunction(dict)
+            << "Number of histogram bins = " << nBins_
+            << " cannot be negative or zero."
+            << abort(FatalIOError);
+    }
+
+    return true;
+}
+
+
+bool Foam::histogramModels::unequalBinWidth::write(const bool log)
+{
+    // Retrieve operand field
+    const volScalarField& field = histogramModel::getOrReadField(fieldName());
+
+    // Calculate the mid-points of bins for the graph axis
+    pointField midBin(nBins_, Zero);
+
+    forAll(ranges_, bini)
+    {
+        point& p = midBin[bini];
+        const auto& range = ranges_[bini];
+        const scalar min = range.first();
+        const scalar max = range.second();
+
+        const scalar delta = max - min;
+        p.x() = min + 0.5*delta;
+    }
+
+
+    // Calculate the histogram data
+    scalarField dataNormalised(nBins_, Zero);
+    labelField dataCount(nBins_, Zero);
+    const scalarField& V = mesh().V();
+
+    forAll(field, celli)
+    {
+        forAll(ranges_, bini)
+        {
+            const auto& range = ranges_[bini];
+            const scalar min = range.first();
+            const scalar max = range.second();
+
+            if (field[celli] >= min && field[celli] < max)
+            {
+                dataNormalised[bini] += V[celli];
+                dataCount[bini]++;
+                break;
+            }
+        }
+    }
+    Pstream::listCombineGather(dataNormalised, plusEqOp<scalar>());
+    Pstream::listCombineGather(dataCount, plusEqOp<label>());
+
+
+    // Write histogram data
+    histogramModel::write(dataNormalised, dataCount, mag(midBin));
+
+    return true;
+}
+
+
+// ************************************************************************* //
diff --git a/src/functionObjects/field/histogram/histogramModels/unequalBinWidth/unequalBinWidth.H b/src/functionObjects/field/histogram/histogramModels/unequalBinWidth/unequalBinWidth.H
new file mode 100644
index 0000000000000000000000000000000000000000..c9c9815d83b86aedc839fbf7fd485bfc16a548e9
--- /dev/null
+++ b/src/functionObjects/field/histogram/histogramModels/unequalBinWidth/unequalBinWidth.H
@@ -0,0 +1,144 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  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 <http://www.gnu.org/licenses/>.
+
+Class
+    Foam::histogramModels::unequalBinWidth
+
+Description
+    Histogram model which groups data into bins of unequal widths.
+
+Usage
+    Minimal example by using \c system/controlDict.functions:
+    \verbatim
+    histogram1
+    {
+        // Inherited entries
+        ...
+
+        // Mandatory entries
+        ranges
+        (
+            // min       max
+            (<scalar>    <scalar>)    // bin-0
+            (<scalar>    <scalar>)    // bin-1
+            ...
+        );
+    }
+    \endverbatim
+
+    where the entries mean:
+    \table
+      Property  | Description                       | Type   | Reqd | Deflt
+      ranges    | Min-max values of histogram data <!--
+                -->                 | List\<Pair\<scalar\>\> | no   | -
+    \endtable
+
+Note
+  - All bins are half-open, that is [min, max).
+  - Bins should be specified as consecutive, non-overlapping
+    and adjacent intervals of the field variable. No warning
+    or runtime error will be emitted, otherwise.
+
+SourceFiles
+    unequalBinWidth.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef Foam_histogramModels_unequalBinWidth_H
+#define Foam_histogramModels_unequalBinWidth_H
+
+#include "histogramModel.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+namespace histogramModels
+{
+
+/*---------------------------------------------------------------------------*\
+                        Class unequalBinWidth Declaration
+\*---------------------------------------------------------------------------*/
+
+class unequalBinWidth
+:
+    public histogramModel
+{
+    // Private Data
+
+        //- Number of bins
+        label nBins_;
+
+        //- Lower and upper ranges of operand bins
+        List<Pair<scalar>> ranges_;
+
+
+public:
+
+    //- Runtime type information
+    TypeName("unequalBinWidth");
+
+
+    // Constructors
+
+        //- Construct from components
+        unequalBinWidth
+        (
+            const word& name,
+            const fvMesh& mesh,
+            const dictionary& dict
+        );
+
+        //- No copy construct
+        unequalBinWidth(const unequalBinWidth&) = delete;
+
+        //- No copy assignment
+        void operator=(const unequalBinWidth&) = delete;
+
+
+    // Destructor
+    virtual ~unequalBinWidth() = default;
+
+
+    // Member Functions
+
+        //- Read top-level dictionary
+        virtual bool read(const dictionary& dict);
+
+        //- Write data to stream and files
+        virtual bool write(const bool log);
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace histogramModels
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/tutorials/incompressible/pisoFoam/RAS/cavity/system/FOs/FOhistogram b/tutorials/incompressible/pisoFoam/RAS/cavity/system/FOs/FOhistogram
index 34a589c74df8fa7d03667f748b16fd7ce9cb921b..80cf773783f8c81a8c011a0bbbd337343908a486 100644
--- a/tutorials/incompressible/pisoFoam/RAS/cavity/system/FOs/FOhistogram
+++ b/tutorials/incompressible/pisoFoam/RAS/cavity/system/FOs/FOhistogram
@@ -12,15 +12,54 @@ histogram1
     type            histogram;
     libs            (fieldFunctionObjects);
     field           p;
-    nBins           100;
-    setFormat       raw;
+    model           equalBinWidth;
 
-    // Optional entries
+    // Conditional entries
+    nBins           100;
     max             10;
     min            -10;
 
-    // Optional (inherited) entries
-    writePrecision   16;
+    // Inherited entries
+    writePrecision   6;
+    writeToFile      true;
+    useUserTime      true;
+
+    region          region0;
+    enabled         true;
+    log             true;
+    timeStart       0;
+    timeEnd         1000;
+    executeControl  timeStep;
+    executeInterval 1;
+    writeControl    writeTime;
+    writeInterval   -1;
+}
+
+
+histogram2
+{
+    // Mandatory entries
+    type            histogram;
+    libs            (fieldFunctionObjects);
+    field           p;
+    model           unequalBinWidth;
+
+    // Conditional entries
+    ranges
+    (
+        // min  max
+        (-10    -9)    // bin-0
+        (-9     -8)    // bin-1
+        (-8     -7)
+        (-7     -6)
+        (-6     -2)
+        (-2      0)
+        (0       5)
+        (5       10)
+    );
+
+    // Inherited entries
+    writePrecision   6;
     writeToFile      true;
     useUserTime      true;