diff --git a/src/functionObjects/utilities/Make/files b/src/functionObjects/utilities/Make/files
index 6730f52128..e9aa439528 100644
--- a/src/functionObjects/utilities/Make/files
+++ b/src/functionObjects/utilities/Make/files
@@ -57,6 +57,7 @@ writeDictionary/writeDictionary.C
writeObjects/writeObjects.C
thermoCoupleProbes/thermoCoupleProbes.C
+radiometerProbes/radiometerProbes.C
syncObjects/syncObjects.C
diff --git a/src/functionObjects/utilities/Make/options b/src/functionObjects/utilities/Make/options
index c82dbfbdab..8efbb79f95 100644
--- a/src/functionObjects/utilities/Make/options
+++ b/src/functionObjects/utilities/Make/options
@@ -9,7 +9,9 @@ EXE_INC = \
-I$(LIB_SRC)/sampling/lnInclude \
-I$(LIB_SRC)/ODE/lnInclude \
-I$(LIB_SRC)/thermophysicalModels/basic/lnInclude \
- -I$(LIB_SRC)/transportModels/compressible/lnInclude
+ -I$(LIB_SRC)/transportModels/compressible/lnInclude \
+ -I$(LIB_SRC)/thermophysicalModels/radiation/lnInclude \
+ -I$(LIB_SRC)/parallel/distributed/lnInclude
LIB_LIBS = \
-lfiniteVolume \
@@ -22,4 +24,6 @@ LIB_LIBS = \
-lsampling \
-lODE \
-lfluidThermophysicalModels \
- -lcompressibleTransportModels
+ -lcompressibleTransportModels \
+ -lradiationModels \
+ -ldistributed
diff --git a/src/functionObjects/utilities/radiometerProbes/radiometerProbes.C b/src/functionObjects/utilities/radiometerProbes/radiometerProbes.C
new file mode 100644
index 0000000000..a6727199f3
--- /dev/null
+++ b/src/functionObjects/utilities/radiometerProbes/radiometerProbes.C
@@ -0,0 +1,260 @@
+/*---------------------------------------------------------------------------*\
+ ========= |
+ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
+ \\ / O peration |
+ \\ / A nd | www.openfoam.com
+ \\/ M anipulation |
+-------------------------------------------------------------------------------
+ Copyright (C) 2025 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 "radiometerProbes.H"
+#include "addToRunTimeSelectionTable.H"
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+namespace Foam
+{
+namespace functionObjects
+{
+ defineTypeNameAndDebug(radiometerProbes, 0);
+ addToRunTimeSelectionTable(functionObject, radiometerProbes, dictionary);
+}
+}
+
+// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
+
+void Foam::functionObjects::radiometerProbes::writeFileHeader(Ostream& os)
+{
+ const pointField& locs = probeLocations();
+
+ writeCommented(os, "Probe,Location,Normal");
+
+ os << nl;
+ for (label i = 0; i < szProbes_; ++i)
+ {
+ const vector& loc = locs[i];
+ const vector& n = n_[i];
+
+ os << '#' << ' ' << i
+ << ',' << loc.x() << ',' << loc.y() << ',' << loc.z()
+ << ',' << n.x() << ',' << n.y() << ',' << n.z()
+ << nl;
+ }
+
+ os << "# Time";
+ for (int i = 0; i < szProbes_; ++i)
+ {
+ os << ',' << i;
+ }
+ os << endl;
+
+ writtenHeader_ = true;
+}
+
+
+// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
+
+Foam::functionObjects::radiometerProbes::radiometerProbes
+(
+ const word& name,
+ const Time& runTime,
+ const dictionary& dict
+)
+:
+ regionFunctionObject(name, runTime, dict),
+ internalProber(mesh_, dict),
+ writeFile(mesh_, name, typeName, dict),
+ dom_(mesh_.lookupObject("radiationProperties")),
+ firstIter_(true)
+{
+ read(dict);
+}
+
+
+// * * * * * * * * * * * * * * Public Member Functions * * * * * * * * * * * //
+
+bool Foam::functionObjects::radiometerProbes::read(const dictionary& dict)
+{
+ if
+ (
+ !(
+ regionFunctionObject::read(dict)
+ && internalProber::read(dict)
+ && writeFile::read(dict)
+ )
+ )
+ {
+ return false;
+ }
+
+
+ // Skip if the radiation model is inactive
+ if (!dom_.radiation())
+ {
+ WarningInFunction
+ << "The radiation model is inactive."
+ << "Skipping the function object " << type() << ' ' << name()
+ << endl;
+ return false;
+ }
+
+ Log << type() << ':' << name() << ": read" << nl << nl;
+
+
+ // Probe locations are read by 'internalProber'
+ szProbes_ = this->size();
+
+ // If/when fvDOM is updated, the 'read' func is assumed to be executed to
+ // update the fvDOM properties
+ nRay_ = dom_.nRay();
+
+ if (!szProbes_ || !nRay_)
+ {
+ FatalIOErrorInFunction(dict)
+ << "size(probe locations): " << szProbes_ << nl
+ << "size(rays): " << nRay_ << nl
+ << "The input size of probe locations and rays cannot be zero."
+ << exit(FatalIOError);
+ }
+
+ // Read and check size consistency of probe normals with probe locations
+ dict.readEntry("probeNormals", n_);
+ if (n_.size() != szProbes_)
+ {
+ FatalIOErrorInFunction(dict)
+ << "size(probe locations): " << szProbes_ << nl
+ << "size(probe normals): " << n_.size() << nl
+ << "The input size of probe locations and normals must match."
+ << exit(FatalIOError);
+ }
+ n_.normalise();
+
+
+ // Pre-compute and cache inner product of 'n_' and 'dAve_', and 'C_'
+ // This simplification of calculation is valid only if I is non-negative
+ n_dAve_.resize(nRay_);
+ C_.resize(nRay_);
+
+ for (label rayi = 0; rayi < nRay_; ++rayi)
+ {
+ const vector& dAvei = dom_.IRay(rayi).dAve();
+
+ scalarList& n_dAveRay = n_dAve_[rayi];
+ boolList& Cray = C_[rayi];
+
+ n_dAveRay.resize(szProbes_, Zero);
+ Cray.resize(szProbes_, false);
+
+ for (label pi = 0; pi < szProbes_; ++pi)
+ {
+ n_dAveRay[pi] = n_[pi] & dAvei;
+
+ if (n_dAveRay[pi] < 0) // ray entering the probe
+ {
+ Cray[pi] = true;
+ }
+ }
+ }
+
+ qin_.resize(szProbes_);
+
+
+ if (writeFile::canResetFile())
+ {
+ writeFile::resetFile(typeName);
+ }
+
+ if (writeFile::canWriteHeader())
+ {
+ writeFileHeader(file());
+ }
+
+ return true;
+}
+
+
+bool Foam::functionObjects::radiometerProbes::execute()
+{
+ // Skip if there is no probe to sample, or the radiation model is inactive
+ if (!szProbes_ || !dom_.radiation() || !shouldCalcThisStep())
+ {
+ return false;
+ }
+
+ Log << type() << ' ' << name() << ": execute" << nl << nl;
+
+ qin_ = Zero; // resized in 'read'
+
+ for (label rayi = 0; rayi < nRay_; ++rayi)
+ {
+ // Radiative intensity field for this ray
+ const volScalarField& I = dom_.IRay(rayi).I();
+
+ // Sample radiative intensity ray at probe locations
+ tmp tIp = internalProber::sample(I);
+ const scalarField& Ip = tIp.cref();
+
+ const scalarList& n_dAveRay = n_dAve_[rayi];
+ const boolList& Cray = C_[rayi];
+
+ // Add incident radiative heat flux per probe location for each ray
+ for (label pi = 0; pi < szProbes_; ++pi)
+ {
+ if (Cray[pi])
+ {
+ qin_[pi] += Ip[pi]*n_dAveRay[pi];
+ }
+ }
+ }
+
+ return true;
+}
+
+
+bool Foam::functionObjects::radiometerProbes::write()
+{
+ // Skip if there is no probe to sample, or the radiation model is inactive
+ if (!szProbes_ || !dom_.radiation() || !shouldCalcThisStep())
+ {
+ return false;
+ }
+
+ Log << type() << ' ' << name() << ": write" << nl << nl;
+
+ if (UPstream::master())
+ {
+ Ostream& os = file();
+
+ os << mesh_.time().timeOutputValue();
+ for (label pi = 0; pi < szProbes_; ++pi)
+ {
+ os << ',' << qin_[pi];
+ }
+ os << endl;
+ }
+
+ firstIter_ = false;
+
+ return true;
+}
+
+
+// ************************************************************************* //
diff --git a/src/functionObjects/utilities/radiometerProbes/radiometerProbes.H b/src/functionObjects/utilities/radiometerProbes/radiometerProbes.H
new file mode 100644
index 0000000000..981bfa4497
--- /dev/null
+++ b/src/functionObjects/utilities/radiometerProbes/radiometerProbes.H
@@ -0,0 +1,200 @@
+/*---------------------------------------------------------------------------*\
+ ========= |
+ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
+ \\ / O peration |
+ \\ / A nd | www.openfoam.com
+ \\/ M anipulation |
+-------------------------------------------------------------------------------
+ Copyright (C) 2025 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::radiometerProbes
+
+Group
+ grpUtilitiesFunctionObjects
+
+Description
+ Probes the incident radiative heat flux, qin, at arbitrary points within a
+ domain.
+
+Usage
+ Minimal example by using \c system/controlDict.functions:
+ \verbatim
+ radiometer
+ {
+ // Mandatory entries
+ type radiometerProbes;
+ libs (utilityFunctionObjects);
+ probeLocations ();
+ probeNormals ();
+
+ // Inherited entries
+ ...
+ }
+ \endverbatim
+
+ where the entries mean:
+ \table
+ Property | Description | Type | Reqd | Deflt
+ type | Type name: radiometerProbes | word | yes | -
+ libs | Library name: utilityFunctionObjects | word | yes | -
+ probeLocations | Locations of probes | vectorList | yes | -
+ probeNormals | Normals of specified probes | vectorList | yes | -
+ \endtable
+
+ The inherited entries are elaborated in:
+ - \link regionFunctionObject.H \endlink
+ - \link internalProber.H \endlink
+ - \link writeFile.H \endlink
+ - \link fvDOM.H \endlink
+
+Note
+ - The function object can only be used with the \c fvDOM radiation model.
+ - The \c solverFreq input of the \c fvDOM model has superiority over
+ \c executeControl and \c writeControl entries.
+
+SourceFiles
+ radiometerProbes.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef Foam_functionObjects_radiometerProbes_H
+#define Foam_functionObjects_radiometerProbes_H
+
+#include "regionFunctionObject.H"
+#include "internalProber.H"
+#include "writeFile.H"
+#include "fvDOM.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+namespace functionObjects
+{
+
+/*---------------------------------------------------------------------------*\
+ Class radiometerProbes Declaration
+\*---------------------------------------------------------------------------*/
+
+class radiometerProbes
+:
+ public regionFunctionObject,
+ public internalProber,
+ public writeFile
+{
+ // Private Data
+
+ //- Const reference to the underlying radiation model
+ const radiation::fvDOM& dom_;
+
+ //- Normal vectors of the specified probes
+ vectorField n_;
+
+ //- Pre-computed inner product of probe normals (n_) and average
+ //- solid-angle direction (dAve) per radiative intensity ray
+ List n_dAve_;
+
+ //- Directional selection coefficient for radiative intensity rays
+ // false: ray entering the probe
+ // true: ray leaving the probe
+ List C_;
+
+ //- Incident radiative heat flux per probe location
+ scalarField qin_;
+
+ //- Number of radiative intensity rays
+ label nRay_;
+
+ //- Number of probe locations/normals
+ label szProbes_;
+
+ //- Flag to identify whether the iteration is the first iteration
+ // Resets with a restarted simulation
+ bool firstIter_;
+
+
+ // Private Member Functions
+
+ //- Write file-header information into the output file
+ virtual void writeFileHeader(Ostream& os);
+
+ //- Return the flag to decide if radiation-model calculations are
+ //- performed, so that function object calculations can proceed
+ bool shouldCalcThisStep() const
+ {
+ return
+ firstIter_
+ || (mesh_.time().timeIndex() % dom_.solverFreq() == 0);
+ }
+
+
+public:
+
+ //- Runtime type information
+ TypeName("radiometerProbes");
+
+
+ // Generated Methods
+
+ //- No copy construct
+ radiometerProbes(const radiometerProbes&) = delete;
+
+ //- No copy assignment
+ void operator=(const radiometerProbes&) = delete;
+
+
+ // Constructors
+
+ //- Construct from name, Time and dictionary
+ radiometerProbes
+ (
+ const word& name,
+ const Time& runTime,
+ const dictionary& dict
+ );
+
+
+ //- Destructor
+ virtual ~radiometerProbes() = default;
+
+
+ // Public Member Functions
+
+ //- Read the function object settings
+ virtual bool read(const dictionary&);
+
+ //- Execute the function object
+ virtual bool execute();
+
+ //- Write to data files/fields and to streams
+ virtual bool write();
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace functionObjects
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //