diff --git a/src/regionFaModels/Make/files b/src/regionFaModels/Make/files
index cd0717dbd83b2fc4ffd6e125939af84db33f3251..375a0c49d386c1d214c7ba7a06b6e03acb005c41 100644
--- a/src/regionFaModels/Make/files
+++ b/src/regionFaModels/Make/files
@@ -42,5 +42,6 @@ liquidFilm/kinematicThinFilm/kinematicThinFilm.C
 derivedFvPatchFields/filmShell/velocityFilmShellFvPatchVectorField.C
 
 functionObjects/setTimeStep/setTimeStepFaRegionsFunctionObject.C
+functionObjects/surfaceCourantNumber/surfaceCourantNumber.C
 
 LIB = $(FOAM_LIBBIN)/libregionFaModels
diff --git a/src/regionFaModels/functionObjects/surfaceCourantNumber/surfaceCourantNumber.C b/src/regionFaModels/functionObjects/surfaceCourantNumber/surfaceCourantNumber.C
new file mode 100644
index 0000000000000000000000000000000000000000..49c7dc76c485b5b79409755a88af6d688856595f
--- /dev/null
+++ b/src/regionFaModels/functionObjects/surfaceCourantNumber/surfaceCourantNumber.C
@@ -0,0 +1,227 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | www.openfoam.com
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+    Copyright (C) 2024 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 "surfaceCourantNumber.H"
+#include "fvMesh.H"
+#include "faMesh.H"
+#include "areaFields.H"
+#include "edgeFields.H"
+#include "facEdgeIntegrate.H"
+#include "zeroGradientFaPatchFields.H"
+#include "addToRunTimeSelectionTable.H"
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+namespace Foam
+{
+namespace functionObjects
+{
+    defineTypeNameAndDebug(surfaceCourantNumber, 0);
+    addToRunTimeSelectionTable(functionObject, surfaceCourantNumber, dictionary);
+}
+}
+
+
+// * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
+
+void Foam::functionObjects::surfaceCourantNumber::writeFileHeader(Ostream& os)
+{
+    writeHeader(os, "Surface Courant Number");
+
+    writeCommented(os, "Time");
+    writeTabbed(os, "min");
+    writeTabbed(os, "max");
+    writeTabbed(os, "mean");
+    os  << endl;
+
+    writtenHeader_ = true;
+}
+
+
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+Foam::functionObjects::surfaceCourantNumber::surfaceCourantNumber
+(
+    const word& name,
+    const Time& runTime,
+    const dictionary& dict
+)
+:
+    fvMeshFunctionObject(name, runTime, dict),
+    writeFile(mesh_, name, typeName, dict),
+    resultName_("surfaceCo"),
+    phisName_("phis"),
+    rhoName_("rho")
+{
+    read(dict);
+}
+
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+bool Foam::functionObjects::surfaceCourantNumber::read(const dictionary& dict)
+{
+    if (!fvMeshFunctionObject::read(dict) || !writeFile::read(dict))
+    {
+        return false;
+    }
+
+    dict.readIfPresent("result", resultName_);
+    dict.readIfPresent("phis", phisName_);
+    dict.readIfPresent("rho", rhoName_);
+
+    // Registry containing all finite-area meshes on the polyMesh
+    const auto* faRegistryPtr = faMesh::registry(mesh_);
+
+    if (!faRegistryPtr)
+    {
+        FatalIOErrorInFunction(dict)
+            << "No finite-area object registry is available."
+            << abort(FatalIOError);
+    }
+
+    word areaName;
+
+    if (!dict.readIfPresent("area", areaName))
+    {
+        wordList available = faRegistryPtr->sortedNames<faMesh>();
+        if (!available.empty())
+        {
+            areaName = available.front();
+        }
+    }
+
+    if (areaName.empty())
+    {
+        FatalIOErrorInFunction(dict)
+            << "No name for finite-area mesh is available."
+            << abort(FatalIOError);
+    }
+
+    faMeshPtr_ = std::shared_ptr<const faMesh>
+    (
+        faRegistryPtr->cfindObject<faMesh>(areaName),
+        [](const faMesh*) { /* no-op deleter to avoid double deletion */ }
+    );
+
+    return true;
+}
+
+
+bool Foam::functionObjects::surfaceCourantNumber::execute()
+{
+    if (!faMeshPtr_->foundObject<edgeScalarField>(phisName_))
+    {
+        WarningInFunction
+            << "No edge flux field is available. "
+            << "Name of provided edge flux field (phi): " << phisName_
+            << endl;
+
+        return false;
+    }
+
+    const auto& phis = faMeshPtr_->lookupObject<edgeScalarField>(phisName_);
+
+    tmp<areaScalarField::Internal> tCo =
+        (0.5*faMeshPtr_->time().deltaT())
+       *fac::edgeSum(mag(phis))()()
+       /faMeshPtr_->S();
+
+    areaScalarField::Internal Co = tCo.ref();
+
+    if (Co.dimensions() == dimDensity)
+    {
+        Co /= faMeshPtr_->lookupObject<areaScalarField>(rhoName_);
+    }
+
+    auto* resultPtr = faMeshPtr_->getObjectPtr<areaScalarField>(resultName_);
+
+    if (!resultPtr)
+    {
+        resultPtr = new areaScalarField
+        (
+            IOobject
+            (
+                resultName_,
+                faMeshPtr_->time().timeName(),
+                *faMeshPtr_,
+                IOobjectOption()
+            ),
+            *faMeshPtr_,
+            dimensionedScalar(dimless, Zero),
+            faPatchFieldBase::zeroGradientType()
+        );
+        regIOobject::store(resultPtr);
+    }
+    auto& result = *resultPtr;
+
+    result.internalFieldRef() = tCo;
+    result.correctBoundaryConditions();
+
+
+    const scalarMinMax limits(gMinMax(result));
+    const scalar mean = gAverage(result);
+
+    Log << "Surface Courant number: "
+        << "mean: " << mean
+        << " max: " << limits.max()
+        << endl;
+
+    if (writeToFile())
+    {
+        if (!writtenHeader_) writeFileHeader(file());
+
+        writeCurrentTime(file());
+        file()
+            << token::TAB << limits.min()
+            << token::TAB << limits.max()
+            << token::TAB << mean
+            << endl;
+    }
+
+    return true;
+}
+
+
+bool Foam::functionObjects::surfaceCourantNumber::write()
+{
+    const auto* result = faMeshPtr_->cfindObject<areaScalarField>(resultName_);
+
+    if (!result)
+    {
+        return false;
+    }
+
+    Log << type() << " " << name() << " write: " << result->name() << endl;
+
+    result->write();
+
+    return true;
+}
+
+
+// ************************************************************************* //
diff --git a/src/regionFaModels/functionObjects/surfaceCourantNumber/surfaceCourantNumber.H b/src/regionFaModels/functionObjects/surfaceCourantNumber/surfaceCourantNumber.H
new file mode 100644
index 0000000000000000000000000000000000000000..8c64a01b627b1684ecb21fb6a55b65705d14b1c5
--- /dev/null
+++ b/src/regionFaModels/functionObjects/surfaceCourantNumber/surfaceCourantNumber.H
@@ -0,0 +1,175 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | www.openfoam.com
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+    Copyright (C) 2024 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::functionObjects::surfaceCourantNumber
+
+Description
+    Computes the surface Courant number field at finite-area face centres.
+
+    Operands:
+    \table
+      Operand           | Type            | Location
+      input             | -               | -
+      output file       | dat            <!--
+                    --> | postProcessing/\<FO\>/\<time\>/\<file\>
+      output field      | areaScalarField | \<time\>/\<outField\>
+    \endtable
+
+Usage
+    Minimal example by using \c system/controlDict.functions:
+    \verbatim
+    surfaceCourantNumber1
+    {
+        // Mandatory entries
+        type        surfaceCourantNumber;
+        libs        (regionFaModels);
+
+        // Optional entries
+        area        <word>;
+        result      <word>;
+        phis        <word>;
+        rho         <word>;
+
+        // Inherited entries
+        ...
+    }
+    \endverbatim
+
+    where the entries mean:
+    \table
+      Property   | Description                        | Type | Reqd | Deflt
+      type       | Type name: surfaceCourantNumber    | word | yes  | -
+      libs       | Library name: regionFaModels       | word | yes  | -
+      area       | Name of finite-area region         | word | no   | region0
+      result     | Name of result field               | word | no   | surfaceCo
+      phis       | Name of edge flux field            | word | no   | phis
+      rho        | Name of density field              | word | no   | rho
+    \endtable
+
+    The inherited entries are elaborated in:
+     - \link fvMeshFunctionObject.H \endlink
+     - \link writeFile.H \endlink
+
+Note
+  - The \c surfaceCourantNumber calculates the Courant number at face centers,
+  rather than at edge centers.
+
+SourceFiles
+    surfaceCourantNumber.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef functionObjects_surfaceCourantNumber_H
+#define functionObjects_surfaceCourantNumber_H
+
+#include "fvMeshFunctionObject.H"
+#include "writeFile.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+// Forward Declarations
+class faMesh;
+
+namespace functionObjects
+{
+
+/*---------------------------------------------------------------------------*\
+                    Class surfaceCourantNumber Declaration
+\*---------------------------------------------------------------------------*/
+
+class surfaceCourantNumber
+:
+    public fvMeshFunctionObject,
+    public writeFile
+{
+    // Private Data
+
+        //- Name of result field
+        word resultName_;
+
+        //- Name of edge flux field
+        word phisName_;
+
+        //- Name of density field
+        word rhoName_;
+
+        //- Reference to finite-area object registry
+        std::shared_ptr<const faMesh> faMeshPtr_;
+
+
+    // Private Member Functions
+
+        //- Output file header information
+        virtual void writeFileHeader(Ostream& os);
+
+
+public:
+
+    //- Runtime type information
+    TypeName("surfaceCourantNumber");
+
+
+    // Constructors
+
+        //- Construct from Time and dictionary
+        surfaceCourantNumber
+        (
+            const word& name,
+            const Time& runTime,
+            const dictionary& dict
+        );
+
+
+    //- Destructor
+    virtual ~surfaceCourantNumber() = default;
+
+
+    // Member Functions
+
+        //- Read the surfaceCourantNumber data
+        virtual bool read(const dictionary&);
+
+        //- Calculate the Courant number field and return true if successful
+        virtual bool execute();
+
+        //- Write the result field
+        virtual bool write();
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace functionObjects
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/tutorials/finiteArea/liquidFilmFoam/cylinder/system/FOsurfaceCourantNumber b/tutorials/finiteArea/liquidFilmFoam/cylinder/system/FOsurfaceCourantNumber
new file mode 100644
index 0000000000000000000000000000000000000000..aa255d634cc555748fdf0a7abf4ad7515470176b
--- /dev/null
+++ b/tutorials/finiteArea/liquidFilmFoam/cylinder/system/FOsurfaceCourantNumber
@@ -0,0 +1,38 @@
+/*--------------------------------*- C++ -*----------------------------------*\
+| =========                 |                                                 |
+| \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox           |
+|  \\    /   O peration     | Version:  v2406                                 |
+|   \\  /    A nd           | Website:  www.openfoam.com                      |
+|    \\/     M anipulation  |                                                 |
+\*---------------------------------------------------------------------------*/
+
+surfaceCourantNumber1
+{
+    // Mandatory entries
+    type            surfaceCourantNumber;
+    libs            (regionFaModels);
+
+    // Optional entries
+    area            region0;
+    result          surfaceCourantNumberField;
+    rho             rho;
+    phis            phis;
+
+    // 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;
+}
+
+
+// ************************************************************************* //
diff --git a/tutorials/finiteArea/liquidFilmFoam/cylinder/system/controlDict b/tutorials/finiteArea/liquidFilmFoam/cylinder/system/controlDict
index 70074e2c4af941e8eab61c7aad5dcad49090fea4..02c1b5c8b59d5852b32512b6e49cb2d95299a41f 100644
--- a/tutorials/finiteArea/liquidFilmFoam/cylinder/system/controlDict
+++ b/tutorials/finiteArea/liquidFilmFoam/cylinder/system/controlDict
@@ -50,5 +50,9 @@ maxCo               5;
 
 maxDeltaT           0.1;
 
+functions
+{
+    #include "FOsurfaceCourantNumber"
+}
 
 // ************************************************************************* //