diff --git a/applications/utilities/preProcessing/createViewFactors/Allwmake b/applications/utilities/preProcessing/createViewFactors/Allwmake
new file mode 100755
index 0000000000..9de13a5b03
--- /dev/null
+++ b/applications/utilities/preProcessing/createViewFactors/Allwmake
@@ -0,0 +1,11 @@
+#!/bin/sh
+cd "${0%/*}" || exit # Run from this directory
+
+ . "$WM_PROJECT_DIR"/wmake/scripts/AllwmakeParseArguments
+
+#------------------------------------------------------------------------------
+
+wmake $targetType viewFactorModels
+wmake $targetType createViewFactors
+
+#------------------------------------------------------------------------------
diff --git a/applications/utilities/preProcessing/createViewFactors/createViewFactors/Make/files b/applications/utilities/preProcessing/createViewFactors/createViewFactors/Make/files
new file mode 100644
index 0000000000..3f36ae84ca
--- /dev/null
+++ b/applications/utilities/preProcessing/createViewFactors/createViewFactors/Make/files
@@ -0,0 +1,3 @@
+createViewFactors.C
+
+EXE = $(FOAM_APPBIN)/createViewFactors
diff --git a/applications/utilities/preProcessing/createViewFactors/createViewFactors/Make/options b/applications/utilities/preProcessing/createViewFactors/createViewFactors/Make/options
new file mode 100644
index 0000000000..71f2b983ab
--- /dev/null
+++ b/applications/utilities/preProcessing/createViewFactors/createViewFactors/Make/options
@@ -0,0 +1,7 @@
+EXE_INC = \
+ -I../viewFactorModels/lnInclude \
+ -I$(LIB_SRC)/finiteVolume/lnInclude
+
+EXE_LIBS = \
+ -lviewFactorModels \
+ -lfiniteVolume
diff --git a/applications/utilities/preProcessing/createViewFactors/createViewFactors/createViewFactors.C b/applications/utilities/preProcessing/createViewFactors/createViewFactors/createViewFactors.C
new file mode 100644
index 0000000000..84ad369a99
--- /dev/null
+++ b/applications/utilities/preProcessing/createViewFactors/createViewFactors/createViewFactors.C
@@ -0,0 +1,165 @@
+/*---------------------------------------------------------------------------*\
+ ========= |
+ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
+ \\ / O peration |
+ \\ / A nd | www.openfoam.com
+ \\/ M anipulation |
+-------------------------------------------------------------------------------
+ Copyright (C) 2023 OpenCFD Ltd.
+-------------------------------------------------------------------------------
+License
+ This file is part of OpenFOAM.
+
+ OpenFOAM is free software: you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with OpenFOAM. If not, see .
+
+Application
+ createViewFactors
+
+Group
+ grpPreProcessingUtilities
+
+Description
+ Creates view factors to be used in the view-factor radiation model.
+
+ Operands:
+ \table
+ Operand | Type | Location
+ input | dictionary | \/viewFactorsDict
+ input | dictionary | \/finalAgglom
+ output | scalarListList | \/F
+ output | mapDistribute | \/mapDist
+ output | labelListList | \/globalFaceFaces
+ output | volScalarField | \/viewVectorField
+ output | OBJ | allVisibleFaces.obj
+ \endtable
+
+ where the dictionaries mean:
+ \table
+ Dictionary | Description
+ viewFactorsDict | Main-control dictionary
+ finalAgglom | (Optional) Agglomeration addressing (from faceAgglomerate)
+ F | View factors (matrix)
+ mapDist | Map used for parallel running
+ globalFaceFaces | Face addressing
+ viewVectorField | View factors as a volume field
+ allVisibleFaces.obj | The visualisation of the rays
+ \endtable
+
+Usage
+ Minimal example in \c /viewFactorsDict:
+ \verbatim
+ // Inherited entries
+ raySearchEngine ;
+ agglomerate ;
+ nRayPerFace ;
+ writeViewFactors ;
+ writeRays ;
+ ...
+ \endverbatim
+
+ where the entries mean:
+ \table
+ Property | Description | Type | Reqd | Deflt
+ raySearchEngine | Ray search engine type | word | yes | -
+ agglomerate | Flag to agglomeration | bool | yes | -
+ nRayPerFace | Number of rays issued per face | label | yes | -
+ writeViewFactors | Flag to write the view factor field | bool | yes |-
+ writeRays | Flag to write the ray geometry | bool | no | false
+ \endtable
+
+ Options for the \c raySearchEngine entry:
+ \verbatim
+ voxel | Ray search engine discretising space into uniform voxels
+ \endverbatim
+
+ The inherited entries are elaborated in:
+ - \link viewFactorModel.H \endlink
+ - \link raySearchEngine.H \endlink
+
+Note
+
+ - Participating patches must be in the \c vewFactorWall group, i.e. using the
+ \c inGroups entry of the "\/polyMesh/boundary" file.
+
+ \verbatim
+ myPatch
+ {
+ type wall;
+ inGroups 2(wall viewFactorWall);
+ ...
+ }
+ \endverbatim
+
+ Reads:
+
+ - /viewFactorsDict : main controls
+ - /finalAgglom : agglomeration addressing (from faceAgglomerate)
+
+
+ Generates:
+
+ - /F : view factors (matrix)
+ - /mapDist : map used for parallel running
+ - /globalFaceFaces : face addressing
+
+SeeAlso
+- Foam::VF::raySearchEngine
+- Foam::VE::viewFactorModel
+
+\*---------------------------------------------------------------------------*/
+
+#include "argList.H"
+#include "Time.H"
+#include "fvMesh.H"
+#include "viewFactorModel.H"
+
+using namespace Foam;
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+int main(int argc, char *argv[])
+{
+ #include "addRegionOption.H"
+ #include "setRootCase.H"
+ #include "createTime.H"
+ #include "createNamedMesh.H"
+
+ IOdictionary dict
+ (
+ IOobject
+ (
+ "viewFactorsDict",
+ runTime.constant(),
+ mesh,
+ IOobject::MUST_READ
+ )
+ );
+
+
+ // Calculate the view factors
+ auto modelPtr = VF::viewFactorModel::New(mesh, dict);
+
+ modelPtr->calculate();
+
+ Info<< nl;
+
+ runTime.printExecutionTime(Info);
+
+ Info<< "End\n" << endl;
+
+ return 0;
+}
+
+
+// ************************************************************************* //
\ No newline at end of file
diff --git a/applications/utilities/preProcessing/createViewFactors/viewFactorModels/Make/files b/applications/utilities/preProcessing/createViewFactors/viewFactorModels/Make/files
new file mode 100644
index 0000000000..eddbe68d67
--- /dev/null
+++ b/applications/utilities/preProcessing/createViewFactors/viewFactorModels/Make/files
@@ -0,0 +1,12 @@
+raySearchEngine/raySearchEngine/raySearchEngine.C
+raySearchEngine/raySearchEngine/raySearchEngineNew.C
+raySearchEngine/voxel/voxelRaySearchEngine.C
+/* raySearchEngine/nonUniformVoxel/nonUniformVoxelRaySearchEngine.C */
+/* raySearchEngine/allToAll/allToAllRaySearchEngine.C */
+viewFactorModel/viewFactorModel/viewFactorModel.C
+viewFactorModel/viewFactorModel/viewFactorModelNew.C
+viewFactorModel/viewFactor2AI/viewFactor2AI.C
+viewFactorModel/viewFactor2LI/viewFactor2LI.C
+viewFactorModel/viewFactorHottel/viewFactorHottel.C
+
+LIB = $(FOAM_LIBBIN)/libviewFactorModels
diff --git a/applications/utilities/preProcessing/createViewFactors/viewFactorModels/Make/options b/applications/utilities/preProcessing/createViewFactors/viewFactorModels/Make/options
new file mode 100644
index 0000000000..ddcc52fdc7
--- /dev/null
+++ b/applications/utilities/preProcessing/createViewFactors/viewFactorModels/Make/options
@@ -0,0 +1,14 @@
+EXE_INC = \
+ -I$(LIB_SRC)/finiteVolume/lnInclude \
+ -I$(LIB_SRC)/surfMesh/lnInclude \
+ -I$(LIB_SRC)/meshTools/lnInclude \
+ -I$(LIB_SRC)/parallel/distributed/lnInclude \
+ -I$(LIB_SRC)/fileFormats/lnInclude
+
+EXE_LIBS = \
+ -lfiniteVolume \
+ -lsurfMesh \
+ -lmeshTools \
+ -ldistributed \
+ -lradiationModels \
+ -lfileFormats
diff --git a/applications/utilities/preProcessing/createViewFactors/viewFactorModels/raySearchEngine/raySearchEngine/raySearchEngine.C b/applications/utilities/preProcessing/createViewFactors/viewFactorModels/raySearchEngine/raySearchEngine/raySearchEngine.C
new file mode 100644
index 0000000000..f1a37bbc5f
--- /dev/null
+++ b/applications/utilities/preProcessing/createViewFactors/viewFactorModels/raySearchEngine/raySearchEngine/raySearchEngine.C
@@ -0,0 +1,620 @@
+/*---------------------------------------------------------------------------*\
+ ========= |
+ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
+ \\ / O peration |
+ \\ / A nd | www.openfoam.com
+ \\/ M anipulation |
+-------------------------------------------------------------------------------
+ Copyright (C) 2023-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 .
+
+\*---------------------------------------------------------------------------*/
+
+#include "raySearchEngine.H"
+#include "surfaceFields.H"
+#include "volFields.H"
+#include "meshTools.H"
+
+using namespace Foam::constant;
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+namespace Foam
+{
+namespace VF
+{
+ defineTypeNameAndDebug(raySearchEngine, 0);
+ defineRunTimeSelectionTable(raySearchEngine, mesh);
+
+ const label raySearchEngine::maxDynListLength = 1000000000;
+}
+}
+
+// * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
+
+void Foam::VF::raySearchEngine::check
+(
+ const labelList& nVisibleFaceFaces
+)
+{
+ label nRay = 0;
+ label nFaceMin = labelMax;
+ label nFaceMax = labelMin;
+ for (const label n : nVisibleFaceFaces)
+ {
+ nFaceMin = min(nFaceMin, n);
+ nFaceMax = max(nFaceMax, n);
+ nRay += n;
+ }
+
+ const label nFace = nVisibleFaceFaces.size();
+ const label nGlobalRays = returnReduce(nRay, sumOp());
+
+ if (nGlobalRays == 0)
+ {
+ FatalErrorInFunction
+ << "No rays identified - view factors will not be calculated"
+ << exit(FatalError);
+ }
+
+ const label globalNFacesMin = returnReduce(nFaceMin, minOp());
+ const label globalNFacesMax = returnReduce(nFaceMax, maxOp());
+ const label nGlobalFaces = returnReduce(nFace, sumOp());
+ const scalar avgFaces = nGlobalRays/scalar(nGlobalFaces);
+
+ Info<< "\nRay summary:" << nl
+ << " Number of rays: " << nGlobalRays << nl
+ << " Number of rays-per-face (min, max, average): ("
+ << globalNFacesMin << ", "
+ << globalNFacesMax << ", "
+ << avgFaces << ")" << endl;
+}
+
+
+Foam::label Foam::VF::raySearchEngine::closestPointIndex
+(
+ const point& p0,
+ const List& pts
+)
+{
+ label pointi = -1;
+
+ scalar distSqr = GREAT;
+ forAll(pts, pti)
+ {
+ const scalar d2 = magSqr(pts[pti] - p0);
+ if (d2 < distSqr)
+ {
+ pointi = pti;
+ distSqr = d2;
+ }
+ }
+
+ return pointi;
+}
+
+
+void Foam::VF::raySearchEngine::createAgglomeration(const IOobject& io)
+{
+ Info<< "\nFace agglomeration: active" << nl
+ << " Reading file " << io.name() << endl;
+
+ // Read agglomeration map
+ const labelListIOList finalAgglom(io);
+
+ Info<< " Creating coarse mesh" << nl;
+
+ agglomMeshPtr_.reset
+ (
+ new singleCellFvMesh
+ (
+ IOobject
+ (
+ IOobject::scopedName("agglom", mesh_.name()),
+ mesh_.time().timeName(),
+ mesh_.time(),
+ IOobject::NO_READ,
+ IOobject::NO_WRITE
+ ),
+ mesh_,
+ finalAgglom
+ )
+ );
+
+ const auto& coarseMesh = agglomMeshPtr_();
+
+
+ // Calculate total number of fine and coarse faces
+
+ nCoarseFace_ = 0;
+ nFace_ = 0;
+
+ const polyBoundaryMesh& finePatches = mesh_.boundaryMesh();
+ const polyBoundaryMesh& coarsePatches = coarseMesh.boundaryMesh();
+
+ for (const label patchi : patchIDs_)
+ {
+ nCoarseFace_ += coarsePatches[patchi].size();
+ nFace_ += finePatches[patchi].size();
+ }
+
+ Info<< "\nTotal number of coarse faces: "
+ << returnReduce(nCoarseFace_, sumOp())
+ << endl;
+
+ Info<< "\nTotal number of fine faces: "
+ << returnReduce(nFace_, sumOp())
+ << endl;
+
+ // Collect local Cf, Sf, agglom index on coarse mesh
+ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ DynamicList localCf(nCoarseFace_);
+ DynamicList localSf(nCoarseFace_);
+ DynamicList localAgg(nCoarseFace_);
+
+ for (const label patchi : patchIDs_)
+ {
+ const labelList& agglom = finalAgglom[patchi];
+
+ if (agglom.empty()) continue;
+
+ label nAgglom = max(agglom) + 1;
+ const labelListList coarseToFine(invertOneToMany(nAgglom, agglom));
+ const labelList& coarsePatchFace = coarseMesh.patchFaceMap()[patchi];
+
+ const pointField& coarseCf = coarseMesh.Cf().boundaryField()[patchi];
+ const vectorField& coarseSf = coarseMesh.Sf().boundaryField()[patchi];
+
+ const polyPatch& pp = finePatches[patchi];
+ patchAreas_[patchi] += sum(coarseMesh.magSf().boundaryField()[patchi]);
+
+ forAll(coarseCf, facei)
+ {
+ const label coarseFacei = coarsePatchFace[facei];
+ const label agglomi = coarseFacei + coarsePatches[patchi].start();
+
+ // Construct single coarse face
+ const labelList& fineFaces = coarseToFine[coarseFacei];
+ uindirectPrimitivePatch cf
+ (
+ UIndirectList(pp, fineFaces),
+ pp.points()
+ );
+
+ // Collect all points (vertices, face centres)
+ const label nFaces = cf.faceCentres().size();
+ const label nPoints = cf.localPoints().size();
+ List allPoints(nFaces + nPoints);
+ SubList(allPoints, nFaces) = cf.faceCentres();
+ SubList(allPoints, nPoints, nFaces) = cf.localPoints();
+
+ // Coarse face centre set to closest point
+ const label pti = closestPointIndex(coarseCf[facei], allPoints);
+
+ if (pti != -1)
+ {
+ localCf.push_back(allPoints[pti]);
+ localSf.push_back(coarseSf[facei]);
+ localAgg.push_back(agglomi);
+ }
+ }
+ }
+
+ Info<< "\nAssembled coarse patch data" << endl;
+
+ // Distribute local coarse Cf and Sf for shooting rays
+ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ allCf_[Pstream::myProcNo()].transfer(localCf);
+ allSf_[Pstream::myProcNo()].transfer(localSf);
+ allAgg_[Pstream::myProcNo()].transfer(localAgg);
+
+ Pstream::allGatherList(allCf_);
+ Pstream::allGatherList(allSf_);
+ Pstream::allGatherList(allAgg_);
+
+ Pstream::listCombineGather(patchAreas_, plusEqOp());
+ Pstream::broadcast(patchAreas_);
+
+ globalNumbering_ = globalIndex(nCoarseFace_);
+}
+
+
+void Foam::VF::raySearchEngine::createGeometry()
+{
+ DynamicList localCf(mesh_.nBoundaryFaces());
+ DynamicList localSf(mesh_.nBoundaryFaces());
+
+ const auto& pbm = mesh_.boundaryMesh();
+
+ for (const label patchi : patchIDs_)
+ {
+ localCf.push_back(pbm[patchi].faceCentres());
+ localSf.push_back(pbm[patchi].faceAreas());
+
+ patchAreas_[patchi] += sum(mesh_.magSf().boundaryField()[patchi]);
+ }
+
+ Info<< "\nAssembled patch data" << endl;
+
+ nFace_ = localCf.size();
+ nCoarseFace_ = -1;
+
+ allCf_[Pstream::myProcNo()].transfer(localCf);
+ allSf_[Pstream::myProcNo()].transfer(localSf);
+
+ Pstream::allGatherList(allCf_);
+ Pstream::allGatherList(allSf_);
+
+ Pstream::listCombineGather(patchAreas_, plusEqOp());
+ Pstream::broadcast(patchAreas_);
+
+ globalNumbering_ = globalIndex(nFace_);
+}
+
+
+void Foam::VF::raySearchEngine::createParallelAddressing
+(
+ labelList& rayEndFace
+) const
+{
+ // Construct compact numbering
+ // - return map from remote to compact indices
+ // (per processor (!= myProcNo) a map from remote index to compact index)
+ // - construct distribute map
+ // - renumber rayEndFace into compact addressing
+
+ DebugInfo << "\nCreating map distribute" << endl;
+
+ List> compactMap(Pstream::nProcs());
+ mapPtr_.reset(new mapDistribute(globalNumbering_, rayEndFace, compactMap));
+
+ DebugInfo << "\nCreating compact-to-global addressing" << endl;
+
+ // Invert compactMap (from processor+localface to compact) to go
+ // from compact to processor+localface (expressed as a globalIndex)
+ compactToGlobal_.resize_nocopy(mapPtr_->constructSize());
+
+ // Local indices first
+ // Note: are not in compactMap
+ for (label i = 0; i < globalNumbering_.localSize(); ++i)
+ {
+ compactToGlobal_[i] = globalNumbering_.toGlobal(i);
+ }
+
+ forAll(compactMap, proci)
+ {
+ const Map& localToCompactMap = compactMap[proci];
+
+ forAllConstIters(localToCompactMap, iter)
+ {
+ compactToGlobal_[*iter] =
+ globalNumbering_.toGlobal(proci, iter.key());
+ }
+ }
+}
+
+
+Foam::coordSystem::cartesian Foam::VF::raySearchEngine::createCoordSystem
+(
+ const point& origin,
+ const vector& dir
+) const
+{
+ vector axis(Zero);
+
+ for (direction d=0; d<3; ++d)
+ {
+ axis = dir^tensor::I.col(d);
+
+ // Remove empty direction for 2D
+ if (mesh_.nSolutionD() == 2)
+ {
+ meshTools::constrainDirection(mesh_, mesh_.solutionD(), axis);
+ }
+
+ if (magSqr(axis) > 0)
+ {
+ axis.normalise();
+ break;
+ }
+ }
+
+ return coordSystem::cartesian(origin, dir, axis);
+}
+
+
+Foam::tmp Foam::VF::raySearchEngine::createHemiPoints
+(
+ const label nRayPerFace
+) const
+{
+ auto themiPts = tmp::New(nRayPerFace);
+ auto& hemiPts = themiPts.ref();
+
+ const label nPoints = hemiPts.size();
+
+ if (mesh_.nSolutionD() == 3)
+ {
+ // Point in range -1 < x < 1; -1 < y < 1; 0 < z 1
+
+ forAll(hemiPts, pointi)
+ {
+ const scalar phi = Foam::acos(1 - (pointi + 0.5)/nPoints);
+ const scalar theta =
+ mathematical::pi*(1 + Foam::sqrt(5.0))*(pointi + 0.5);
+
+ hemiPts[pointi] =
+ vector
+ (
+ Foam::cos(theta)*Foam::sin(phi),
+ Foam::sin(theta)*Foam::sin(phi),
+ Foam::cos(phi)
+ );
+ }
+ }
+ else if (mesh_.nSolutionD() == 2)
+ {
+ // Point in range -1 < x < 1; y = 0; 0 < z < 1; _\|/_
+
+ forAll(hemiPts, pointi)
+ {
+ const scalar theta = mathematical::pi*(pointi+0.5)/nPoints;
+ hemiPts[pointi] = vector(Foam::cos(theta), 0, Foam::sin(theta));
+ }
+ }
+
+ return themiPts;
+}
+
+
+// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
+
+Foam::VF::raySearchEngine::raySearchEngine
+(
+ const fvMesh& mesh,
+ const dictionary& dict
+)
+:
+ mesh_(mesh),
+ mapPtr_(nullptr),
+ compactToGlobal_(),
+ globalNumbering_(),
+ patchGroup_(dict.getOrDefault("patchGroup", "viewFactorWall")),
+ patchIDs_(mesh_.boundaryMesh().indices(patchGroup_)),
+ patchAreas_(mesh_.boundaryMesh().nNonProcessor(), Zero),
+ agglomerate_(dict.get("agglomerate")),
+ agglomMeshPtr_(nullptr),
+ nFace_(-1),
+ nCoarseFace_(-1),
+ allCf_(UPstream::nProcs()),
+ allSf_(UPstream::nProcs()),
+ allAgg_(UPstream::nProcs())
+{
+ Info<< "\nParticipating patches:" << endl;
+
+ forAll(patchIDs_, i)
+ {
+ const label patchi = patchIDs_[i];
+ Info<< " " << i << ": " << mesh_.boundaryMesh()[patchi].name()
+ << endl;
+ }
+
+ const word agglomName(dict.getOrDefault("agglom", "finalAgglom"));
+
+ IOobject agglomIO
+ (
+ agglomName,
+ mesh_.facesInstance(),
+ mesh_,
+ IOobject::MUST_READ
+ );
+
+
+ if (agglomerate_)
+ {
+ // Sets allCf_, allSf_, allAgg_ based on coarse mesh
+ // Sets nFace_, nCoarseFace_
+ createAgglomeration(agglomIO);
+ }
+ else
+ {
+ // Check for presence of finalAgglom - will cause problems in later
+ // calculations with viewFactor radiation model
+ if (agglomIO.typeHeaderOk())
+ {
+ WarningInFunction
+ << "Found agglomeration file: " << agglomIO.objectPath() << nl
+ << " This is inconsistent with the view factor calculation "
+ << "and should be removed" << nl << endl;
+ }
+
+ // Sets allCf_, allSf_ based on fine mesh
+ // Sets nFace_; nCoarseFace_ remains unset (-1)
+ createGeometry();
+ }
+
+ globalNumbering_ =
+ nCoarseFace_ == -1 ? globalIndex(nFace_) : globalIndex(nCoarseFace_);
+}
+
+
+// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
+
+void Foam::VF::raySearchEngine::correct
+(
+ labelListList& visibleFaceFaces
+) const
+{
+ labelList rayStartFace;
+ labelList rayEndFace;
+ shootRays(rayStartFace, rayEndFace);
+
+ const label nFace = nParticipatingFaces();
+
+ // Calculate number of visible faces from each local start face
+ labelList nVisibleFaceFaces(nFace, Zero);
+ for (const label facei : rayStartFace)
+ {
+ ++nVisibleFaceFaces[facei];
+ }
+
+ check(nVisibleFaceFaces);
+
+ createParallelAddressing(rayEndFace);
+
+ // Set visible face-faces
+
+ // visibleFaceFaces has:
+ // (local face, local viewed face) = compact viewed face
+ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ visibleFaceFaces.resize_nocopy(nFace);
+ forAll(nVisibleFaceFaces, facei)
+ {
+ visibleFaceFaces[facei].resize_nocopy(nVisibleFaceFaces[facei]);
+ }
+
+ nVisibleFaceFaces = 0;
+ forAll(rayStartFace, i)
+ {
+ const label facei = rayStartFace[i];
+ const label sloti = rayEndFace[i];
+ visibleFaceFaces[facei][nVisibleFaceFaces[facei]++] = sloti;
+ }
+}
+
+
+void Foam::VF::raySearchEngine::compactAddressing
+(
+ const mapDistribute& map,
+ pointField& compactCf,
+ vectorField& compactSf,
+ List>& compactFineSf,
+ List>& compactFineCf,
+ DynamicList>& compactPoints,
+ DynamicList& compactPatchId
+) const
+{
+ compactCf.resize_nocopy(map.constructSize());
+ compactSf.resize_nocopy(map.constructSize());
+ compactFineSf.resize_nocopy(map.constructSize());
+ compactFineCf.resize_nocopy(map.constructSize());
+ compactPoints.setCapacity(map.constructSize());
+ compactPatchId.setCapacity(map.constructSize());
+
+ // Insert my local values area and centre values
+ if (agglomMeshPtr_)
+ {
+ SubList(compactSf, nCoarseFace_) = allSf_[Pstream::myProcNo()];
+ SubList(compactCf, nCoarseFace_) = allCf_[Pstream::myProcNo()];
+
+ const auto& coarseMesh = agglomMeshPtr_();
+ const auto& coarsePatches = coarseMesh.boundaryMesh();
+ const auto& coarseFaces = coarseMesh.faces();
+ const auto& coarsePoints = coarseMesh.points();
+
+ const auto& finalAgglom = coarseMesh.patchFaceAgglomeration();
+
+ // Insert my fine local values per coarse face
+ label sloti = 0;
+ for (const label patchi : patchIDs_)
+ {
+ const auto& fineCfp = mesh_.Cf().boundaryField()[patchi];
+ const auto& fineSfp = mesh_.Sf().boundaryField()[patchi];
+ const labelList& agglom = finalAgglom[patchi];
+
+ if (agglom.empty()) continue;
+
+ const label nAgglom = max(agglom) + 1;
+ const labelListList coarseToFine = invertOneToMany(nAgglom, agglom);
+ const labelList& coarsePatchFace =
+ coarseMesh.patchFaceMap()[patchi];
+
+ const label coarseStart = coarsePatches[patchi].start();
+
+ forAll(coarseToFine, coarsei)
+ {
+ compactPatchId.push_back(patchi);
+
+ const vectorField localPoints
+ (
+ coarsePoints,
+ coarseFaces[coarseStart + coarsei]
+ );
+ compactPoints.push_back(localPoints);
+
+ const label coarseFacei = coarsePatchFace[coarsei];
+ const labelList& fineFaces = coarseToFine[coarseFacei];
+
+ List& fineCf = compactFineCf[sloti];
+ fineCf.resize_nocopy(fineFaces.size());
+ fineCf = UIndirectList(fineCfp, fineFaces);
+
+ List& fineSf = compactFineSf[sloti];
+ fineSf.resize_nocopy(fineFaces.size());
+ fineSf = UIndirectList(fineSfp, fineFaces);
+
+ ++sloti;
+ }
+ }
+ }
+ else
+ {
+ SubList(compactSf, nFace_) = allSf_[Pstream::myProcNo()];
+ SubList(compactCf, nFace_) = allCf_[Pstream::myProcNo()];
+
+ const auto& patches = mesh_.boundaryMesh();
+ const faceList& faces = mesh_.faces();
+
+ label sloti = 0;
+
+ for (const label patchi : patchIDs_)
+ {
+ const auto& Sfp = mesh_.Sf().boundaryField()[patchi];
+ const auto& Cfp = mesh_.Cf().boundaryField()[patchi];
+
+ const polyPatch& pp = patches[patchi];
+
+ forAll(pp, facei)
+ {
+ compactPatchId.push_back(patchi);
+
+ const auto& fpts = faces[facei + pp.start()];
+ compactPoints.push_back(List(mesh_.points(), fpts));
+
+ compactFineCf[sloti] = List({Cfp[facei]});
+ compactFineSf[sloti] = List({Sfp[facei]});
+ ++sloti;
+ }
+ }
+ }
+
+
+ // Do all swapping
+ map.distribute(compactSf);
+ map.distribute(compactCf);
+ map.distribute(compactFineCf);
+ map.distribute(compactFineSf);
+ map.distribute(compactPoints);
+ map.distribute(compactPatchId);
+}
+
+
+// ************************************************************************* //
diff --git a/applications/utilities/preProcessing/createViewFactors/viewFactorModels/raySearchEngine/raySearchEngine/raySearchEngine.H b/applications/utilities/preProcessing/createViewFactors/viewFactorModels/raySearchEngine/raySearchEngine/raySearchEngine.H
new file mode 100644
index 0000000000..31a6d6f8d9
--- /dev/null
+++ b/applications/utilities/preProcessing/createViewFactors/viewFactorModels/raySearchEngine/raySearchEngine/raySearchEngine.H
@@ -0,0 +1,289 @@
+/*---------------------------------------------------------------------------*\
+ ========= |
+ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
+ \\ / O peration |
+ \\ / A nd | www.openfoam.com
+ \\/ M anipulation |
+-------------------------------------------------------------------------------
+ Copyright (C) 2023-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 .
+
+Class
+ Foam::VF::raySearchEngine
+
+Description
+ Base class for ray search engines
+
+ Participating patches must be in the \c viewFactorWall group, i.e. using the
+ \c inGroups entry of the "\/polyMesh/boundary" file.
+
+ \verbatim
+ myPatch
+ {
+ type wall;
+ inGroups 2(wall viewFactorWall);
+ ...
+ }
+ \endverbatim
+
+ Face agglomeration can be employed, created using the \c faceAgglomerate
+ utility. The file name to be read can be user-defined:
+
+ \verbatim
+ // Name of agglomeration file; default = finalAgglom
+ agglom finalAgglom;
+ \endverbatim
+
+SourceFiles
+ raySearchEngine.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef Foam_vf_raySearchEngine_H
+#define Foam_vf_raySearchEngine_H
+
+#include "cartesianCS.H"
+#include "mapDistribute.H"
+#include "singleCellFvMesh.H"
+#include "runTimeSelectionTables.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+namespace VF
+{
+
+/*---------------------------------------------------------------------------*\
+ Class raySearchEngine Declaration
+\*---------------------------------------------------------------------------*/
+
+class raySearchEngine
+{
+protected:
+
+ // Protected Data
+
+ //- Reference to the mesh
+ const fvMesh& mesh_;
+
+ //- Parallel map
+ mutable autoPtr mapPtr_;
+
+ //- Compact to global addressing
+ mutable labelList compactToGlobal_;
+
+ //- Global numbering
+ globalIndex globalNumbering_;
+
+ //- Name of patch group to identify participating patches
+ const word patchGroup_;
+
+ //- List of participating patch IDs
+ labelList patchIDs_;
+
+ //- Patch areas
+ scalarList patchAreas_;
+
+ //- Agglomeration flag
+ bool agglomerate_;
+
+ //- Agglomerated mesh representation
+ autoPtr agglomMeshPtr_;
+
+ //- Number of original faces
+ label nFace_;
+
+ //- Number of coarse faces
+ label nCoarseFace_;
+
+ //- List of all face centres per processor
+ List allCf_;
+
+ //- List of all face areas per processor
+ List allSf_;
+
+ //- List of all face agglomeration index per processor
+ List allAgg_;
+
+
+ // Protected Member Functions
+
+ static void check(const labelList& nVisibleFaceFaces);
+
+ static label closestPointIndex
+ (
+ const point& p0,
+ const List& pts
+ );
+
+ //- Create patch geometry based on the original mesh
+ void createGeometry();
+
+ //- Create parallel addressing - map, compact-to-global
+ void createParallelAddressing(labelList& rayEndFace) const;
+
+ //- Create Cartesian co-ordinate system
+ coordSystem::cartesian createCoordSystem
+ (
+ const point& origin,
+ const vector& dir
+ ) const;
+
+ //- Create patch geometry based on the agglomerated mesh
+ void createAgglomeration(const IOobject& io);
+
+ //- Create a set of points describing a hemisphere
+ // Note: origin is (0 0 0)
+ tmp createHemiPoints(const label nRayPerFace) const;
+
+
+public:
+
+ static const label maxDynListLength;
+
+ //- Run-time type information
+ TypeName("raySearchEngine");
+
+ //- Selection table
+ declareRunTimeSelectionTable
+ (
+ autoPtr,
+ raySearchEngine,
+ mesh,
+ (
+ const fvMesh& mesh,
+ const dictionary& dict
+ ),
+ (mesh, dict)
+ );
+
+ //- Selector
+ static autoPtr New
+ (
+ const fvMesh& mesh,
+ const dictionary& dict
+ );
+
+
+ // Generated Methods
+
+ //- No copy construct
+ raySearchEngine(const raySearchEngine&) = delete;
+
+ //- No copy assignment
+ void operator=(const raySearchEngine&) = delete;
+
+
+ //- Constructor
+ raySearchEngine(const fvMesh& mesh, const dictionary& dict);
+
+ //- Destructor
+ virtual ~raySearchEngine() = default;
+
+
+ // Public Member Functions
+
+ // Access
+
+ //- Reference to the mesh
+ inline const fvMesh& mesh() const noexcept;
+
+ //- Parallel map
+ inline const mapDistribute& map() const;
+
+ //- Compact to global addressing
+ inline const labelList& compactToGlobal() const noexcept;
+
+ //- Global numbering
+ inline const globalIndex& globalNumbering() const noexcept;
+
+ //- List of participating patch IDs
+ inline const labelList& patchIDs() const noexcept;
+
+ //- Patch areas
+ inline const scalarList& patchAreas() const noexcept;
+
+ //- Number of participating faces
+ inline label nParticipatingFaces() const;
+
+ //- List of all face centres per processor
+ inline const List& allCf() const noexcept;
+
+ //- List of all face areas per processor
+ inline const List& allSf() const noexcept;
+
+ //- List of all face agglomeration index per processor
+ inline const List& allAgg() const noexcept;
+
+
+ // Main calculation functions
+
+ //- Shoot rays; returns lists of ray start and end faces
+ virtual void shootRays
+ (
+ labelList& rayStartFaceOut,
+ labelList& rayEndFaceOut
+ ) const = 0;
+
+ //- Correct
+ virtual void correct(labelListList& visibleFaceFaces) const;
+
+ //- Create compact addressing
+ void compactAddressing
+ (
+ const mapDistribute& map,
+ pointField& compactCf,
+ vectorField& compactSf,
+ List>& compactFineSf,
+ List>& compactFineCf,
+ DynamicList>& compactPoints,
+ DynamicList& compactPatchId
+ ) const;
+
+ //- Interpolate field
+ template
+ void interpolate
+ (
+ GeometricField& fld,
+ const List>& values
+ ) const;
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace VF
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#include "raySearchEngineI.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#ifdef NoRepository
+ #include "raySearchEngineTemplates.C"
+#endif
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/applications/utilities/preProcessing/createViewFactors/viewFactorModels/raySearchEngine/raySearchEngine/raySearchEngineI.H b/applications/utilities/preProcessing/createViewFactors/viewFactorModels/raySearchEngine/raySearchEngine/raySearchEngineI.H
new file mode 100644
index 0000000000..bc410e8b21
--- /dev/null
+++ b/applications/utilities/preProcessing/createViewFactors/viewFactorModels/raySearchEngine/raySearchEngine/raySearchEngineI.H
@@ -0,0 +1,101 @@
+/*---------------------------------------------------------------------------*\
+ ========= |
+ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
+ \\ / O peration |
+ \\ / A nd | www.openfoam.com
+ \\/ M anipulation |
+-------------------------------------------------------------------------------
+ Copyright (C) 2023-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 .
+
+\*---------------------------------------------------------------------------*/
+
+const Foam::fvMesh& Foam::VF::raySearchEngine::mesh() const noexcept
+{
+ return mesh_;
+}
+
+
+const Foam::mapDistribute& Foam::VF::raySearchEngine::map() const
+{
+ if (!mapPtr_)
+ {
+ FatalErrorInFunction
+ << "mapDistribute has not been set"
+ << abort(FatalError);
+ }
+
+ return mapPtr_;
+}
+
+
+const Foam::labelList&
+Foam::VF::raySearchEngine::compactToGlobal() const noexcept
+{
+ return compactToGlobal_;
+}
+
+
+const Foam::globalIndex&
+Foam::VF::raySearchEngine::globalNumbering() const noexcept
+{
+ return globalNumbering_;
+}
+
+
+const Foam::labelList& Foam::VF::raySearchEngine::patchIDs() const noexcept
+{
+ return patchIDs_;
+}
+
+
+const Foam::scalarList& Foam::VF::raySearchEngine::patchAreas() const noexcept
+{
+ return patchAreas_;
+}
+
+
+Foam::label Foam::VF::raySearchEngine::nParticipatingFaces() const
+{
+ if (nCoarseFace_ == -1) return nFace_;
+ return nCoarseFace_;
+}
+
+
+const Foam::List&
+Foam::VF::raySearchEngine::allCf() const noexcept
+{
+ return allCf_;
+}
+
+
+const Foam::List&
+Foam::VF::raySearchEngine::allSf() const noexcept
+{
+ return allSf_;
+}
+
+
+const Foam::List&
+Foam::VF::raySearchEngine::allAgg() const noexcept
+{
+ return allAgg_;
+}
+
+
+// ************************************************************************* //
diff --git a/applications/utilities/preProcessing/createViewFactors/viewFactorModels/raySearchEngine/raySearchEngine/raySearchEngineNew.C b/applications/utilities/preProcessing/createViewFactors/viewFactorModels/raySearchEngine/raySearchEngine/raySearchEngineNew.C
new file mode 100644
index 0000000000..78af6adc52
--- /dev/null
+++ b/applications/utilities/preProcessing/createViewFactors/viewFactorModels/raySearchEngine/raySearchEngine/raySearchEngineNew.C
@@ -0,0 +1,59 @@
+/*---------------------------------------------------------------------------*\
+ ========= |
+ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
+ \\ / O peration |
+ \\ / A nd | www.openfoam.com
+ \\/ M anipulation |
+-------------------------------------------------------------------------------
+ Copyright (C) 2023-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 .
+
+\*---------------------------------------------------------------------------*/
+
+#include "raySearchEngine.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+Foam::autoPtr Foam::VF::raySearchEngine::New
+(
+ const fvMesh& mesh,
+ const dictionary& dict
+)
+{
+ const word modelType(dict.get("raySearchEngine"));
+
+ Info<< "Selecting " << typeName << ": " << modelType << endl;
+
+ auto* ctorPtr = meshConstructorTable(modelType);
+
+ if (!ctorPtr)
+ {
+ FatalIOErrorInLookup
+ (
+ dict,
+ typeName,
+ modelType,
+ *meshConstructorTablePtr_
+ ) << exit(FatalIOError);
+ }
+
+ return autoPtr(ctorPtr(mesh, dict));
+}
+
+
+// ************************************************************************* //
diff --git a/applications/utilities/preProcessing/createViewFactors/viewFactorModels/raySearchEngine/raySearchEngine/raySearchEngineTemplates.C b/applications/utilities/preProcessing/createViewFactors/viewFactorModels/raySearchEngine/raySearchEngine/raySearchEngineTemplates.C
new file mode 100644
index 0000000000..b345451ada
--- /dev/null
+++ b/applications/utilities/preProcessing/createViewFactors/viewFactorModels/raySearchEngine/raySearchEngine/raySearchEngineTemplates.C
@@ -0,0 +1,86 @@
+/*---------------------------------------------------------------------------*\
+ ========= |
+ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
+ \\ / O peration |
+ \\ / A nd | www.openfoam.com
+ \\/ M anipulation |
+-------------------------------------------------------------------------------
+ Copyright (C) 2023-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 .
+
+\*---------------------------------------------------------------------------*/
+
+template
+void Foam::VF::raySearchEngine::interpolate
+(
+ GeometricField& fld,
+ const List>& values
+) const
+{
+ label compacti = 0;
+
+ auto& vfbf = fld.boundaryFieldRef();
+
+ if (agglomMeshPtr_)
+ {
+ const auto& coarseMesh = agglomMeshPtr_();
+ const auto& finalAgglom = coarseMesh.patchFaceAgglomeration();
+
+ for (const label patchi : patchIDs_)
+ {
+ const labelList& agglom = finalAgglom[patchi];
+
+ if (agglom.empty()) continue;
+
+ label nAgglom = max(agglom) + 1;
+ const labelListList coarseToFine(invertOneToMany(nAgglom, agglom));
+ const labelList& coarsePatchFace =
+ coarseMesh.patchFaceMap()[patchi];
+
+ forAll(coarseToFine, i)
+ {
+ const label coarseFacei = coarsePatchFace[i];
+ const labelList& fineFaces = coarseToFine[coarseFacei];
+ const Type sumValues = sum(values[compacti]);
+
+ for (const label fineFacei : fineFaces)
+ {
+ vfbf[patchi][fineFacei] = sumValues;
+ }
+
+ ++compacti;
+ }
+ }
+ }
+ else
+ {
+ label compacti = 0;
+ for (const label patchi : patchIDs_)
+ {
+ auto& vfp = vfbf[patchi];
+
+ for (Type& vfi : vfp)
+ {
+ vfi = sum(values[compacti++]);
+ }
+ }
+ }
+}
+
+
+// ************************************************************************* //
diff --git a/applications/utilities/preProcessing/createViewFactors/viewFactorModels/raySearchEngine/voxel/voxelRaySearchEngine.C b/applications/utilities/preProcessing/createViewFactors/viewFactorModels/raySearchEngine/voxel/voxelRaySearchEngine.C
new file mode 100644
index 0000000000..757f162044
--- /dev/null
+++ b/applications/utilities/preProcessing/createViewFactors/viewFactorModels/raySearchEngine/voxel/voxelRaySearchEngine.C
@@ -0,0 +1,948 @@
+/*---------------------------------------------------------------------------*\
+ ========= |
+ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
+ \\ / O peration |
+ \\ / A nd | www.openfoam.com
+ \\/ M anipulation |
+-------------------------------------------------------------------------------
+ Copyright (C) 2023-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 .
+
+\*---------------------------------------------------------------------------*/
+
+#include "voxelRaySearchEngine.H"
+#include "processorPolyPatch.H"
+#include "addToRunTimeSelectionTable.H"
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+namespace Foam
+{
+namespace VF
+{
+ defineTypeNameAndDebug(voxel, 0);
+ addToRunTimeSelectionTable(raySearchEngine, voxel, mesh);
+}
+}
+
+// * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
+
+void Foam::VF::voxel::setTriangulation(const fvMesh& mesh)
+{
+ Info<< "\nCreating triangulated surface" << endl;
+
+ // Storage for surfaceMesh. Size estimate.
+ DynamicList triangles(mesh.nBoundaryFaces());
+ DynamicList globalFaces(mesh.nBoundaryFaces());
+ label nFace = 0;
+
+ const auto& pbm = mesh.boundaryMesh();
+
+ forAll(patchIDs_, i)
+ {
+ const label patchi = patchIDs_[i];
+ const polyPatch& patch = pbm[patchi];
+ const pointField& points = patch.points();
+
+ for (const face& f : patch)
+ {
+ label nTri = 0;
+ faceList triFaces(f.nTriangles(points));
+ f.triangles(points, nTri, triFaces);
+
+ const label globalFacei =
+ globalNumbering_.toGlobal(Pstream::myProcNo(), nFace++);
+
+ for (const face& f : triFaces)
+ {
+ triangles.push_back(labelledTri(f[0], f[1], f[2], i));
+ globalFaces.push_back(globalFacei);
+ }
+ }
+ }
+
+ triToGlobalFace_.transfer(globalFaces);
+
+ Info<< " Total number of triangles: "
+ << returnReduce(triangles.size(), sumOp())
+ << endl;
+
+ triangles.shrink();
+ surface_ = triSurface(triangles, mesh.points());
+ surface_.compactPoints();
+}
+
+
+Foam::labelList Foam::VF::voxel::setFaceVertexHits
+(
+ const fvMesh& mesh,
+ const labelList& patchIDs
+)
+{
+ labelList vertHits(mesh.points().size(), Zero);
+
+ if (mesh.nSolutionD() == 3)
+ {
+ const auto& pbm = mesh.boundaryMesh();
+
+ // Create a new triangulation based on the surface agglomeration
+ for (const label patchI : patchIDs)
+ {
+ const polyPatch& patch = pbm[patchI];
+ for (const face& f : patch)
+ {
+ for (const label fpi : f)
+ {
+ ++vertHits[fpi];
+ }
+ }
+ }
+
+ for (const auto& pp : pbm)
+ {
+ const labelList& meshPts = pp.meshPoints();
+
+ if (pp.size())
+ {
+ if (isA(pp))
+ {
+ // Add all processor patch points
+ for (const label pi : meshPts)
+ {
+ ++vertHits[pi];
+ }
+ }
+ else
+ {
+ // Add boundary points
+
+ const auto& bndyPts = pp.boundaryPoints();
+
+ for (const label pi : bndyPts)
+ {
+ ++vertHits[meshPts[pi]];
+ }
+ }
+ }
+ }
+ }
+
+ return vertHits;
+}
+
+
+void Foam::VF::voxel::setCoarseTriangulation(const fvMesh& mesh)
+{
+ Info<< "\nCreating triangulated surface" << endl;
+
+
+ // Filter out fine mesh points along coarse mesh faces
+
+
+ // Storage for surfaceMesh. Size estimate.
+ DynamicList triangles(mesh.nBoundaryFaces());
+ DynamicList globalFaces(mesh.nBoundaryFaces());
+ labelList vertHits = setFaceVertexHits(mesh, patchIDs_);
+
+
+ // Only simplifying edges for 3D
+ const label nVertMin = mesh.nSolutionD() == 3 ? 2 : 0;
+
+ label nInvalid = 0;
+ label nFace = 0;
+
+ const auto& pbm = mesh.boundaryMesh();
+
+ for (const label patchi : patchIDs_)
+ {
+ const polyPatch& patch = pbm[patchi];
+ const pointField& points = patch.points();
+
+ for (const face& f : patch)
+ {
+ DynamicList faceVerts;
+ for (const label fpi : f)
+ {
+ if (vertHits[fpi] > nVertMin)
+ {
+ faceVerts.push_back(fpi);
+ }
+ }
+
+ if (faceVerts.size() < 3)
+ {
+ ++nInvalid;
+ continue;
+ }
+
+ label nTri = 0;
+ const face reducedFace(faceVerts);
+ faceList triFaces(reducedFace.nTriangles(points));
+ reducedFace.triangles(points, nTri, triFaces);
+
+ const label globalFacei =
+ globalNumbering_.toGlobal(Pstream::myProcNo(), nFace++);
+
+ for (const face& f : triFaces)
+ {
+ triangles.push_back(labelledTri(f[0], f[1], f[2], patchi));
+ globalFaces.push_back(globalFacei);
+ }
+ }
+ }
+
+ triToGlobalFace_.transfer(globalFaces);
+
+ Info<< " Total number of triangles: "
+ << returnReduce(triangles.size(), sumOp())
+ << "\n Number of invalid (removed) triangles: "
+ << returnReduce(nInvalid, sumOp())
+ << endl;
+
+ triangles.shrink();
+ surface_ = triSurface(triangles, mesh.points());
+ surface_.compactPoints();
+}
+
+
+void Foam::VF::voxel::broadcast()
+{
+ // Every processor has whole surface
+ const globalIndex globalFaceIdx(globalIndex::gatherOnly{}, surface_.size());
+ const globalIndex globalPointIdx
+ (
+ globalIndex::gatherOnly{},
+ surface_.points().size()
+ );
+
+ List globalSurfaceTris(globalFaceIdx.gather(surface_));
+ pointField globalSurfacePoints(globalPointIdx.gather(surface_.points()));
+ List globalTriToGlobalFace(globalFaceIdx.gather(triToGlobalFace_));
+
+
+ for (const label proci : globalPointIdx.allProcs())
+ {
+ const label offset = globalPointIdx.localStart(proci);
+
+ if (offset)
+ {
+ for
+ (
+ labelledTri& tri
+ : globalSurfaceTris.slice(globalFaceIdx.range(proci))
+ )
+ {
+ tri[0] += offset;
+ tri[1] += offset;
+ tri[2] += offset;
+ }
+ }
+ }
+
+ surface_ =
+ triSurface
+ (
+ std::move(globalSurfaceTris),
+ std::move(globalSurfacePoints)
+ );
+
+ Pstream::broadcast(surface_);
+
+ triToGlobalFace_ = std::move(globalTriToGlobalFace);
+ Pstream::broadcast(triToGlobalFace_);
+}
+
+
+Foam::pointHit Foam::VF::voxel::rayTriIntersect
+(
+ const label trii,
+ const point& origin,
+ const vector& direction
+) const
+{
+ // Note: origin was made relative to voxel mesh on entry to hit function
+ // - need to convert back into global position to be consistent with
+ // triangles for intersection tests
+
+ const auto& ind = surface_[trii];
+ const auto& pts = surface_.points();
+
+ // Note: flipped orientation of triangle (into domain) so that we can use
+ // visibility check when performing ray-triangle intersections
+ const triPointRef tri(pts[ind[0]], pts[ind[2]], pts[ind[1]]);
+
+ static scalar tolerance = 1e-6;
+
+ return
+ tri.intersection
+ (
+ globalPosition(origin),
+ direction,
+ intersection::VISIBLE,
+ tolerance
+ );
+}
+
+
+void Foam::VF::voxel::writeBox
+(
+ OBJstream& os,
+ bool lines,
+ const boundBox& bb
+) const
+{
+ os.write(treeBoundBox(bb), lines);
+}
+
+
+void Foam::VF::voxel::writeVoxels(const word& fName) const
+{
+ if (!UPstream::master()) return;
+
+ OBJstream os(fName);
+ Info<< "Writing voxels to " << os.name() << endl;
+
+ boundBox bb;
+ const bool lines = true;
+ for (label i = 0; i < nijk_[0]; ++i)
+ {
+ for (label j = 0; j < nijk_[1]; ++j)
+ {
+ for (label k = 0; k < nijk_[2]; ++k)
+ {
+ bb.min() = voxelMin(i, j, k);
+ bb.max() = voxelMax(i, j, k);
+ writeBox(os, lines, bb);
+ }
+ }
+ }
+
+ Info<< "- done" << endl;
+}
+
+
+void Foam::VF::voxel::writeTriBoundBoxes(const word& fName) const
+{
+ if (!UPstream::master()) return;
+
+ OBJstream os(fName);
+ Info<< "Writing triangle boundBoxes to " << os.name() << endl;
+
+ const bool lines = true;
+ for (const auto& voxeli : objects_)
+ {
+ for (const label trii : voxeli)
+ {
+ writeBox(os, lines, objectBbs_[trii]);
+ }
+ }
+
+ Info<< "- done" << endl;
+}
+
+
+void Foam::VF::voxel::writeHitObjects
+(
+ const label voxeli,
+ const point& origin,
+ const vector& dir
+) const
+{
+ OBJstream os("voxel_" + Foam::name(voxeli) + ".obj");
+
+ // Write voxel
+ labelVector ijk = this->ijk(voxeli);
+
+ boundBox voxelBb;
+ voxelBb.min() = voxelMin(ijk[0], ijk[1], ijk[2]);
+ voxelBb.max() = voxelMax(ijk[0], ijk[1], ijk[2]);
+
+ writeBox(os, true, voxelBb);
+
+ for (const auto& trii : objects_[voxeli])
+ {
+ writeBox(os, true, objectBbs_[trii]);
+
+ const auto& ind = surface_[trii];
+ const auto& pts = surface_.points();
+ const triPointRef tri(pts[ind[0]], pts[ind[1]], pts[ind[2]]);
+ os.write(tri, false);
+ }
+}
+
+
+Foam::pointIndexHit Foam::VF::voxel::hitObject
+(
+ const label voxeli,
+ const point& origin,
+ const vector& dir,
+ const vector& t,
+ const scalar minDistance
+) const
+{
+ if (objects_[voxeli].empty()) return pointIndexHit();
+
+ // boundBox rayBb;
+ // rayBb.add(origin);
+ // rayBb.add(origin + dir*(dir & t));
+
+ label triHiti = -1;
+ //rayBb.add(origin + dir);
+ //rayBb.inflate(0.01);
+
+ if (debug > 2)
+ {
+ writeHitObjects(voxeli, origin, dir);
+ }
+
+ // Determine all triangles that intersect with ray
+ // - only keep nearest hit
+
+ pointHit closestHit;
+ for (const auto& trii : objects_[voxeli])
+ {
+ // Only perform ray/tri intersection if bound boxes intersect
+ //if (objectBbs_[trii].overlaps(rayBb))
+ {
+ pointHit pi = rayTriIntersect(trii, origin, dir);
+
+ if
+ (
+ pi.hit()
+ && (
+ pi.distance() > minDistance
+ && pi.distance() < closestHit.distance()
+ )
+ )
+ {
+ triHiti = trii;
+ closestHit = pi;
+ }
+ }
+ }
+
+ return pointIndexHit(closestHit, triHiti);
+}
+
+
+// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
+
+Foam::VF::voxel::voxel(const fvMesh& mesh, const dictionary& dict)
+:
+ raySearchEngine(mesh, dict),
+ bb0_(),
+ span0_(Zero),
+ nijk_(Zero),
+ dxyz_(Zero),
+ nRayPerFace_(dict.get("nRayPerFace")),
+ nTriPerVoxelMax_(dict.getOrDefault("nTriPerVoxelMax", 50)),
+ depthMax_(dict.getOrDefault("depthMax", 5)),
+ objects_(),
+ objectBbs_()
+{
+ if (agglomMeshPtr_)
+ {
+ setCoarseTriangulation(*agglomMeshPtr_);
+ }
+ else
+ {
+ setTriangulation(mesh);
+ }
+
+ broadcast();
+
+ objectBbs_.resize_nocopy(surface_.size());
+
+ bb0_.add(surface_.points());
+ bb0_.inflate(0.01);
+ span0_ = bb0_.span();
+
+ {
+ scalar maxDx = span0_.x();
+ scalar maxDy = span0_.y();
+ scalar maxDz = span0_.z();
+
+ const label refDn = 8;
+ scalar maxDim = max(maxDx, max(maxDy, maxDz));
+
+ setVoxelDims
+ (
+ refDn*maxDx/maxDim,
+ refDn*maxDy/maxDim,
+ refDn*maxDz/maxDim
+ );
+
+ objects_.resize_nocopy(nVoxel());
+ }
+
+ label depth = 0;
+ label trii = 0;
+ voxelise(objects_, trii, depth);
+
+ Info<< "\nCreated voxel mesh: " << nijk_ << endl;
+
+ if ((debug > 3) && UPstream::master())
+ {
+ writeVoxels("voxels.obj");
+ writeTriBoundBoxes("triBoundBoxes.obj");
+ }
+}
+
+
+// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
+
+void Foam::VF::voxel::refineObjects
+(
+ List>& objects,
+ const label triMax
+)
+{
+ refineVoxelDims();
+
+ if (debug > 2) Pout<< "Refining voxels: n=" << nijk_ << endl;
+
+ List> objectsNew(objects.size()*8);
+
+ for (label trii = 0; trii <= triMax; ++trii)
+ {
+ addBbToVoxels(objectBbs_[trii], trii, objectsNew);
+ }
+
+ objects.transfer(objectsNew);
+}
+
+
+Foam::label Foam::VF::voxel::addBbToVoxels
+(
+ const boundBox& bb,
+ const label trii,
+ List>& objects
+) const
+{
+ //Pout<< "addBbToVoxels: trii=" << trii << " n=" << nijk_ << endl;
+
+ const point minbb(localPosition(bb.min()));
+ const label i0 = max(0, floor(minbb.x()/dxyz_[0]));
+ const label j0 = max(0, floor(minbb.y()/dxyz_[1]));
+ const label k0 = max(0, floor(minbb.z()/dxyz_[2]));
+
+ const point maxbb(localPosition(bb.max()));
+ const label i1 = min(nijk_[0], ceil(maxbb.x()/dxyz_[0]));
+ const label j1 = min(nijk_[1], ceil(maxbb.y()/dxyz_[1]));
+ const label k1 = min(nijk_[2], ceil(maxbb.z()/dxyz_[2]));
+
+ label nTriMax = 0;
+
+ for (label ii = i0; ii < i1; ++ii)
+ {
+ for (label jj = j0; jj < j1; ++jj)
+ {
+ for (label kk = k0; kk < k1; ++kk)
+ {
+ const label voxeli = this->voxeli(ii, jj, kk);
+
+ objects[voxeli].push_back(trii);
+ nTriMax = max(nTriMax, objects[voxeli].size());
+ }
+ }
+ }
+
+ return nTriMax;
+}
+
+
+void Foam::VF::voxel::voxelise
+(
+ List>& objects,
+ const label trii0,
+ const label depth
+)
+{
+ if (debug > 2)
+ {
+ Pout<< "voxelise - start at tri=" << trii0
+ << " depth=" << depth
+ << endl;
+ }
+
+ const auto& points = surface_.points();
+
+ for (label trii = trii0; trii < surface_.size(); ++trii)
+ {
+ // Triangle bounding box
+ boundBox bb(points, surface_[trii]);
+ bb.inflate(0.01);
+ objectBbs_[trii] = bb;
+
+ const label nVoxelMax = addBbToVoxels(bb, trii, objects);
+
+ // Number of triangles per voxel - if exceed limit, refine voxels...
+ if (nVoxelMax > nTriPerVoxelMax_ && depth < depthMax_)
+ {
+ refineObjects(objects, trii);
+ voxelise(objects, trii + 1, depth + 1);
+ break;
+ }
+ }
+}
+
+
+Foam::pointIndexHit Foam::VF::voxel::hit
+(
+ const label tri0,
+ const vector& direction0
+) const
+{
+ if (tri0 > surface_.size()-1)
+ {
+ FatalErrorInFunction
+ << "Index tri0 out of bounds: " << tri0
+ << ". Surface size: " << surface_.size()
+ << abort(FatalError);
+ }
+
+ return hit(surface_.faceCentres()[tri0], direction0);
+}
+
+
+Foam::pointIndexHit Foam::VF::voxel::hit
+(
+ const point& origin0,
+ const vector& direction0
+) const
+{
+ // Initialise return value
+ pointIndexHit hitInfo;
+
+ const point origin(localPosition(origin0));
+
+ if (cmptMin(origin) < 0)
+ {
+ FatalErrorInFunction
+ << "Point located outside voxel mesh"
+ << " - possible coarsening problem?"
+ << abort(FatalError);
+ }
+
+ if (magSqr(direction0) < ROOTVSMALL)
+ {
+ WarningInFunction
+ << "Supplied direction has zero size"
+ << endl;
+
+ return hitInfo;
+ }
+
+ const vector dir(normalised(direction0));
+
+ labelVector ijk(Zero);
+ labelVector step(Zero);
+ vector tDelta(vector::max);
+ vector tMax(vector::max);
+
+ for (direction d = 0; d < 3; ++d)
+ {
+ ijk[d] = floor(origin[d]/dxyz_[d]);
+ step[d] = sign0(dir[d]);
+ if (step[d] != 0)
+ {
+ tDelta[d] = mag(dxyz_[d]/dir[d]);
+
+ scalar voxelMax = (1 + ijk[d] - neg(dir[d]))*dxyz_[d];
+ tMax[d] = (voxelMax - origin[d])/dir[d];
+ }
+ }
+
+ if (debug > 2)
+ {
+ Info<< "surfBb:" << boundBox(surface_.points())
+ << " bb:" << bb0_
+ << " origin" << origin0
+ << " voxel_origin:" << origin
+ << " ijk:" << ijk
+ << " step:" << step
+ << " dxyz:" << dxyz_
+ << " tDelta:" << tDelta
+ << " tMax:" << tMax
+ << endl;
+ }
+
+ auto traverse = [&](const label i)
+ {
+ ijk[i] += step[i];
+ if (outOfBounds(ijk, i)) return false;
+ tMax[i] += tDelta[i];
+ return true;
+ };
+
+
+ while (true)
+ {
+ const label voxeli = this->voxeli(ijk);
+
+ if (debug > 2)
+ {
+ Info<< "ijk:" << ijk
+ << " voxeli:" << voxeli
+ << " t:" << tMax
+ << " objs:" << objects_[voxeli].size()
+ << endl;
+ }
+
+ hitInfo = hitObject(voxeli, origin, dir, tMax);
+
+ if (hitInfo.hit())
+ {
+ // Valid hit
+ break;
+ }
+ else
+ {
+ if (tMax[0] < tMax[1] && tMax[0] < tMax[2])
+ {
+ if (!traverse(0)) break;
+ }
+ else if (tMax[1] < tMax[2])
+ {
+ if (!traverse(1)) break;
+ }
+ else
+ {
+ if (!traverse(2)) break;
+ }
+ }
+ }
+
+ return hitInfo;
+}
+
+
+void Foam::VF::voxel::shootRays
+(
+ labelList& rayStartFaceOut,
+ labelList& rayEndFaceOut
+) const
+{
+ (void)mesh_.time().cpuTimeIncrement();
+
+ const pointField& myFc = allCf_[Pstream::myProcNo()];
+ const vectorField& myArea = allSf_[Pstream::myProcNo()];
+
+ const label nTotalRays = myFc.size()*nRayPerFace_;
+ if (nTotalRays > maxDynListLength)
+ {
+ FatalErrorInFunction
+ << "Dynamic list needs more capacity to support " << nRayPerFace_
+ << " rays per face (" << nTotalRays << "). "
+ << "Limit set by maxDynListLength: " << maxDynListLength
+ << abort(FatalError);
+ }
+
+ DynamicList rayStartFace(nTotalRays);
+ DynamicList rayEndFace(rayStartFace.size());
+
+ DynamicList endFacei(nTotalRays);
+ DynamicList startFacei(nTotalRays);
+
+ const pointField hemi(createHemiPoints(nRayPerFace_));
+
+ Info<< "\nShooting rays" << endl;
+
+ const scalar nDiv = 10;
+ const label nStep = floor(myFc.size()/nDiv);
+ labelHashSet localFaceHits;
+
+ for (label stepi=0; stepi()) + 1;
+
+ Info<< " " << globalStepi/nDiv*100 << "% complete" << endl;
+ }
+
+
+ // Ensure all rays are unique/filter out duplicates
+ // - add symmetric connections for non-self-intersecting rays
+
+ if (UPstream::parRun())
+ {
+ edgeHashSet uniqueRays;
+ List> remoteStartFace(Pstream::nProcs());
+ List> remoteEndFace(Pstream::nProcs());
+
+ const labelList globalStartFaceIDs
+ (
+ globalNumbering_.toGlobal(startFacei)
+ );
+
+ forAll(globalStartFaceIDs, rayi)
+ {
+ label start = globalStartFaceIDs[rayi];
+ label end = endFacei[rayi];
+
+ const label endProci = globalNumbering_.whichProcID(end);
+
+ if (start > end) Swap(start, end);
+
+ if (uniqueRays.insert(edge(start, end)))
+ {
+ if (endProci != UPstream::myProcNo())
+ {
+ // Convert local face into global face and vice-versa
+ remoteStartFace[endProci].push_back(start);
+ remoteEndFace[endProci].push_back(end);
+ }
+ }
+ }
+
+ PstreamBuffers pBufs(Pstream::commsTypes::nonBlocking);
+
+ // Send remote data
+ for (const int domain : Pstream::allProcs())
+ {
+ if (domain != Pstream::myProcNo())
+ {
+ UOPstream str(domain, pBufs);
+ str << remoteStartFace[domain]
+ << remoteEndFace[domain];
+ }
+ }
+
+ pBufs.finishedSends();
+
+ // Consume
+ for (const int domain : Pstream::allProcs())
+ {
+ if (domain != Pstream::myProcNo())
+ {
+ UIPstream is(domain, pBufs);
+ is >> remoteStartFace[domain]
+ >> remoteEndFace[domain];
+
+ forAll(remoteStartFace[domain], i)
+ {
+ const label start = remoteStartFace[domain][i];
+ const label end = remoteEndFace[domain][i];
+ uniqueRays.insert(edge(start, end));
+ }
+ }
+ }
+
+ // Populate ray addressing based on uniqueRays
+ for (const edge& e : uniqueRays)
+ {
+ const label start = e.first();
+ const label end = e.second();
+
+ bool sameFace = start == end;
+
+ if (globalNumbering_.isLocal(start))
+ {
+ // Ray originates from this processor
+ const label localStart = globalNumbering_.toLocal(start);
+
+ rayStartFace.append(localStart);
+ rayEndFace.append(end);
+ }
+
+ if (!sameFace && globalNumbering_.isLocal(end))
+ {
+ // Ray hits this processor
+ // - add symmetric ray from end->start
+ const label localEnd = globalNumbering_.toLocal(end);
+
+ rayStartFace.append(localEnd);
+ rayEndFace.append(start);
+ }
+ }
+ }
+ else
+ {
+ // Populate ray addressing based on uniqueRays
+
+ edgeHashSet uniqueRays;
+
+ forAll(startFacei, rayi)
+ {
+ label start = startFacei[rayi];
+ label end = endFacei[rayi];
+
+ if (start > end) Swap(start, end);
+
+ if (uniqueRays.insert(edge(start, end)))
+ {
+ rayStartFace.append(start);
+ rayEndFace.append(end);
+
+ if (start != end)
+ {
+ // Add symmetric ray from end->start
+ rayStartFace.append(end);
+ rayEndFace.append(start);
+ }
+ }
+ }
+ }
+
+ rayStartFaceOut.transfer(rayStartFace);
+ rayEndFaceOut.transfer(rayEndFace);
+
+ Info<< " Time taken: " << mesh_.time().cpuTimeIncrement() << " s"
+ << endl;
+}
+
+
+// ************************************************************************* //
diff --git a/applications/utilities/preProcessing/createViewFactors/viewFactorModels/raySearchEngine/voxel/voxelRaySearchEngine.H b/applications/utilities/preProcessing/createViewFactors/viewFactorModels/raySearchEngine/voxel/voxelRaySearchEngine.H
new file mode 100644
index 0000000000..1103e1a881
--- /dev/null
+++ b/applications/utilities/preProcessing/createViewFactors/viewFactorModels/raySearchEngine/voxel/voxelRaySearchEngine.H
@@ -0,0 +1,284 @@
+/*---------------------------------------------------------------------------*\
+ ========= |
+ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
+ \\ / O peration |
+ \\ / A nd | www.openfoam.com
+ \\/ M anipulation |
+-------------------------------------------------------------------------------
+ Copyright (C) 2023-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 .
+
+Class
+ Foam::VF::voxel
+
+Description
+ Ray search engine based on discretising space into uniform voxels
+
+ Voxels are refined using 2x2x2 refinement.
+
+ The number of rays per face is supplied by the user, whereby rays are
+ issued uniformly across a hemisphere.
+
+Usage
+ Minimal example by using \c /viewFactorsDict:
+ \verbatim
+ // Mandatory entries
+ nRayPerFace ;
+
+ // Optional entries
+ nTriPerVoxelMax ;
+ depthMax ;
+
+ // Inherited entries
+ ...
+ \endverbatim
+
+ where the entries mean:
+ \table
+ Property | Description | Type | Reqd | Deflt
+ nRayPerFace | Number of rays issued per face | label | yes | -
+ nRayPerFace | Target number of triangles per voxel | label | no | 50
+ depthMax | Maximum voxel refinement depth | label | no | 5
+ \endtable
+
+ The inherited entries are elaborated in:
+ - \link raySearchEngine.H \endlink
+
+SourceFiles
+ voxelRaySearchEngine.C
+
+SeeAlso
+- Foam::VF::raySearchEngine
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef Foam_vf_voxelRaySearchEngine_H
+#define Foam_vf_voxelRaySearchEngine_H
+
+#include "raySearchEngine.H"
+#include "labelVector.H"
+#include "OBJstream.H"
+#include "pointIndexHit.H"
+#include "triSurface.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+namespace VF
+{
+
+/*---------------------------------------------------------------------------*\
+ Class voxel Declaration
+\*---------------------------------------------------------------------------*/
+
+class voxel
+:
+ public raySearchEngine
+{
+ // Private Data
+
+ //- Triangulation of view factor patches
+ triSurface surface_;
+
+ //- Triangle to mesh face addressing
+ labelList triToGlobalFace_;
+
+ //- Surface bounding box
+ boundBox bb0_;
+
+ //- Span of surface bounding box
+ vector span0_;
+
+ //- Voxel discretisation
+ labelVector nijk_;
+
+ //- Voxel dimensions
+ vector dxyz_;
+
+ //- Number of rays issued per face
+ const label nRayPerFace_;
+
+ //- Maximum number of triangles per voxel (trigger to refine voxels)
+ const label nTriPerVoxelMax_;
+
+ //- Maximum depth of voxel refinement. Note: voxels are refined 2x2x2
+ const label depthMax_;
+
+ //- List of triangle per voxel
+ List> objects_;
+
+ //- List of triangle bounding boxes
+ List objectBbs_;
+
+
+ // Private Member Functions
+
+ inline bool outOfBounds(const labelVector& ijk, const label dir) const;
+
+ inline point localPosition(const vector& globalPosition) const;
+
+ inline point globalPosition(const vector& localPosition) const;
+
+ inline void setVoxelDims(const label i, const label j, const label k);
+
+ inline void refineVoxelDims();
+
+ inline point voxelMin
+ (
+ const label i,
+ const label j,
+ const label k
+ ) const;
+
+ inline point voxelMax
+ (
+ const label i,
+ const label j,
+ const label k
+ ) const;
+
+ inline constexpr label sign0(const scalar x) const;
+
+ //- Set triangulation based on original mesh
+ void setTriangulation(const fvMesh& mesh);
+
+ //- Set the participating face vertices when simplifying edges
+ static labelList setFaceVertexHits
+ (
+ const fvMesh& mesh,
+ const labelList& patchIDs
+ );
+
+ //- Set triangulation based on agglomeration
+ void setCoarseTriangulation(const fvMesh& mesh);
+
+ //- Broadcast triangulation across all procs
+ void broadcast();
+
+ void refineObjects
+ (
+ List>& objects,
+ const label triMax
+ );
+
+ label addBbToVoxels
+ (
+ const boundBox& bb,
+ const label trii,
+ List>& objects
+ ) const;
+
+ void voxelise
+ (
+ List>& objects,
+ const label trii0,
+ const label depth
+ );
+
+ pointHit rayTriIntersect
+ (
+ const label trii,
+ const point& origin,
+ const vector& direction
+ ) const;
+
+ pointIndexHit hitObject
+ (
+ const label voxeli,
+ const point& origin,
+ const vector& direction,
+ const vector& t,
+ const scalar minDistance = 1e-6
+ ) const;
+
+ void writeBox
+ (
+ OBJstream& os,
+ bool lines,
+ const boundBox& bb
+ ) const;
+
+ void writeVoxels(const word& fName) const;
+
+ void writeTriBoundBoxes(const word& fName) const;
+
+ void writeHitObjects
+ (
+ const label voxeli,
+ const point& origin,
+ const vector& dir
+ ) const;
+
+
+public:
+
+ //- Runtime type information
+ TypeName("voxel");
+
+ //- Constructor
+ voxel(const fvMesh& mesh, const dictionary& dict);
+
+ //- Destructor
+ virtual ~voxel() = default;
+
+
+ // Public Member Functions
+
+ inline labelVector nijk() const noexcept;
+
+ inline label nVoxel() const noexcept;
+
+ inline label voxeli(const labelVector ijk) const noexcept;
+
+ inline label voxeli
+ (
+ const label i,
+ const label j,
+ const label k
+ ) const noexcept;
+
+ inline labelVector ijk(const label voxeli) const noexcept;
+
+ pointIndexHit hit(const point& origin, const vector& dir) const;
+
+ pointIndexHit hit(const label tri0, const vector& dir) const;
+
+ virtual void shootRays
+ (
+ labelList& rayStartFaceOut,
+ labelList& rayEndFaceOut
+ ) const;
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace VF
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#include "voxelRaySearchEngineI.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/applications/utilities/preProcessing/createViewFactors/viewFactorModels/raySearchEngine/voxel/voxelRaySearchEngineI.H b/applications/utilities/preProcessing/createViewFactors/viewFactorModels/raySearchEngine/voxel/voxelRaySearchEngineI.H
new file mode 100644
index 0000000000..a46ca7e1da
--- /dev/null
+++ b/applications/utilities/preProcessing/createViewFactors/viewFactorModels/raySearchEngine/voxel/voxelRaySearchEngineI.H
@@ -0,0 +1,155 @@
+/*---------------------------------------------------------------------------*\
+ ========= |
+ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
+ \\ / O peration |
+ \\ / A nd | www.openfoam.com
+ \\/ M anipulation |
+-------------------------------------------------------------------------------
+ Copyright (C) 2023-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 .
+
+\*---------------------------------------------------------------------------*/
+
+bool Foam::VF::voxel::outOfBounds
+(
+ const labelVector& ijk,
+ const label dir
+) const
+{
+ return (ijk[dir] < 0 || ijk[dir] >= nijk_[dir]);
+};
+
+
+Foam::point Foam::VF::voxel::localPosition(const vector& globalPosition) const
+{
+ return globalPosition - bb0_.min();
+}
+
+
+Foam::point Foam::VF::voxel::globalPosition(const vector& localPosition) const
+{
+ return bb0_.min() + localPosition;
+}
+
+
+void Foam::VF::voxel::setVoxelDims(const label i, const label j, const label k)
+{
+ nijk_[0] = max(1, i);
+ nijk_[1] = max(1, j);
+ nijk_[2] = max(1, k);
+
+ dxyz_[0] = span0_[0]/nijk_[0];
+ dxyz_[1] = span0_[1]/nijk_[1];
+ dxyz_[2] = span0_[2]/nijk_[2];
+}
+
+
+void Foam::VF::voxel::refineVoxelDims()
+{
+ nijk_ *= 2;
+
+ // Do not refine empty direction for 2D
+ const auto& solutionD = mesh_.solutionD();
+ for (direction d=0; d<3; ++d)
+ {
+ if (solutionD[d] == -1)
+ {
+ nijk_[d] = 1;
+ }
+ }
+
+ setVoxelDims(nijk_[0], nijk_[1], nijk_[2]);
+}
+
+
+Foam::point Foam::VF::voxel::voxelMin
+(
+ const label i,
+ const label j,
+ const label k
+) const
+{
+ return point(i*dxyz_[0], j*dxyz_[1], k*dxyz_[2]);
+}
+
+
+Foam::point Foam::VF::voxel::voxelMax
+(
+ const label i,
+ const label j,
+ const label k
+) const
+{
+ return voxelMin(i+1, j+1, k+1);
+}
+
+
+constexpr Foam::label Foam::VF::voxel::sign0(const scalar x) const
+{
+ if (x > 0) return 1;
+ if (x < 0) return -1;
+ return 0;
+};
+
+
+Foam::labelVector Foam::VF::voxel::nijk() const noexcept
+{
+ return nijk_;
+}
+
+
+Foam::label Foam::VF::voxel::nVoxel() const noexcept
+{
+ return nijk_[0]*nijk_[1]*nijk_[2];
+}
+
+
+Foam::label Foam::VF::voxel::voxeli
+(
+ const labelVector ijk
+) const noexcept
+{
+ return voxeli(ijk[0], ijk[1], ijk[2]);
+}
+
+
+Foam::label Foam::VF::voxel::voxeli
+(
+ const label i,
+ const label j,
+ const label k
+) const noexcept
+{
+ return i + (nijk_[0]*(j + (nijk_[1]*k)));
+}
+
+
+Foam::labelVector Foam::VF::voxel::ijk(const label voxeli) const noexcept
+{
+ const label nx = nijk_[0];
+ const label ny = nijk_[1];
+
+ const label i = voxeli%nx;
+ const label k = voxeli/nx/ny;
+ const label j = (voxeli/nx)%ny;
+
+ return labelVector(i, j, k);
+}
+
+
+// ************************************************************************* //
diff --git a/applications/utilities/preProcessing/createViewFactors/viewFactorModels/viewFactorModel/viewFactor2AI/viewFactor2AI.C b/applications/utilities/preProcessing/createViewFactors/viewFactorModels/viewFactorModel/viewFactor2AI/viewFactor2AI.C
new file mode 100644
index 0000000000..2b7afe34bf
--- /dev/null
+++ b/applications/utilities/preProcessing/createViewFactors/viewFactorModels/viewFactorModel/viewFactor2AI/viewFactor2AI.C
@@ -0,0 +1,138 @@
+/*---------------------------------------------------------------------------*\
+ ========= |
+ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
+ \\ / O peration |
+ \\ / A nd | www.openfoam.com
+ \\/ M anipulation |
+-------------------------------------------------------------------------------
+ Copyright (C) 2023-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 .
+
+\*---------------------------------------------------------------------------*/
+
+#include "viewFactor2AI.H"
+#include "mathematicalConstants.H"
+#include "addToRunTimeSelectionTable.H"
+
+using namespace Foam::constant;
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+namespace Foam
+{
+namespace VF
+{
+ defineTypeNameAndDebug(viewFactor2AI, 0);
+ addToRunTimeSelectionTable(viewFactorModel, viewFactor2AI, mesh);
+}
+}
+
+// * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
+
+Foam::scalar Foam::VF::viewFactor2AI::calculateFij
+(
+ const point& xi,
+ const point& xj,
+ const vector& dAi,
+ const vector& dAj
+)
+{
+ const vector r(xj - xi);
+ const scalar rMag = mag(r);
+ const scalar dAiMag(mag(dAi));
+ const scalar dAjMag(mag(dAj));
+
+ if (rMag > SMALL && dAiMag > SMALL && dAjMag > SMALL)
+ {
+ const vector nr(r/rMag);
+ const vector ni(dAi/dAiMag);
+ const vector nj(dAj/dAjMag);
+
+ const scalar Fij =
+ -(nr & ni)*(nr & nj)/sqr(rMag)*dAjMag*dAiMag/mathematical::pi;
+
+ if (Fij > 0) return Fij;
+ }
+
+ return 0;
+}
+
+
+Foam::scalarListList Foam::VF::viewFactor2AI::calculate
+(
+ const labelListList& visibleFaceFaces,
+ const pointField& compactCf,
+ const vectorField& compactSf,
+ const List>& compactFineSf,
+ const List>& compactFineCf,
+ const DynamicList>& compactPoints,
+ const DynamicList& compactPatchId
+) const
+{
+ // Fill local view factor matrix
+ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ scalarListList Fij(visibleFaceFaces.size());
+
+ forAll(visibleFaceFaces, facei)
+ {
+ if (debug > 1)
+ {
+ Pout<< "facei:" << facei << "/" << visibleFaceFaces.size()
+ << endl;
+ }
+
+ const labelList& visibleFaces = visibleFaceFaces[facei];
+
+ Fij[facei].resize(visibleFaces.size(), Zero);
+
+ const point& dCi = compactCf[facei];
+ const vector& Ai = compactSf[facei];
+ const scalar magAi = mag(Ai);
+
+ if (magAi < SMALL) continue;
+
+ forAll(visibleFaces, visibleFacei)
+ {
+ const label sloti = visibleFaces[visibleFacei];
+ const point& dCj = compactCf[sloti];
+ const vector& Aj = compactSf[sloti];
+
+ const scalar Fij2AI = calculateFij(dCi, dCj, Ai, Aj);
+
+ Fij[facei][visibleFacei] = Fij2AI/magAi;
+ }
+ }
+
+
+ return Fij;
+}
+
+
+// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
+
+Foam::VF::viewFactor2AI::viewFactor2AI
+(
+ const fvMesh& mesh,
+ const dictionary& dict
+)
+:
+ viewFactorModel(mesh, dict)
+{}
+
+
+// ************************************************************************* //
diff --git a/applications/utilities/preProcessing/createViewFactors/viewFactorModels/viewFactorModel/viewFactor2AI/viewFactor2AI.H b/applications/utilities/preProcessing/createViewFactors/viewFactorModels/viewFactorModel/viewFactor2AI/viewFactor2AI.H
new file mode 100644
index 0000000000..9ba377435b
--- /dev/null
+++ b/applications/utilities/preProcessing/createViewFactors/viewFactorModels/viewFactorModel/viewFactor2AI/viewFactor2AI.H
@@ -0,0 +1,117 @@
+/*---------------------------------------------------------------------------*\
+ ========= |
+ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
+ \\ / O peration |
+ \\ / A nd | www.openfoam.com
+ \\/ M anipulation |
+-------------------------------------------------------------------------------
+ Copyright (C) 2023-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 .
+
+Class
+ Foam::VF::viewFactor2AI
+
+Description
+ Computes view factors according to the double area integral (2AI) method.
+
+Usage
+ Minimal example in \c /viewFactorsDict:
+ \verbatim
+ // Inherited entries
+ ...
+ \endverbatim
+
+ The inherited entries are elaborated in:
+ - \link viewFactorModel.H \endlink
+
+SourceFiles
+ viewFactorModel.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef Foam_vf_viewFactor2AI_H
+#define Foam_vf_viewFactor2AI_H
+
+#include "viewFactorModel.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+namespace VF
+{
+
+/*---------------------------------------------------------------------------*\
+ Class viewFactor2AI Declaration
+\*---------------------------------------------------------------------------*/
+
+class viewFactor2AI
+:
+ public viewFactorModel
+{
+
+protected:
+
+ // Protected Member Functions
+
+ //- Calculate view factor using the double-area integral
+ static scalar calculateFij
+ (
+ const point& xi,
+ const point& xj,
+ const vector& dAi,
+ const vector& dAj
+ );
+
+ //- Calculate
+ virtual scalarListList calculate
+ (
+ const labelListList& visibleFaceFaces,
+ const pointField& compactCf,
+ const vectorField& compactSf,
+ const List>& compactFineSf,
+ const List>& compactFineCf,
+ const DynamicList>& compactPoints,
+ const DynamicList& compactPatchId
+ ) const;
+
+
+public:
+
+ //- Runtime type information
+ TypeName("viewFactor2AI");
+
+ //- Constructor
+ viewFactor2AI(const fvMesh& mesh, const dictionary& dict);
+
+ //- Destructor
+ virtual ~viewFactor2AI() = default;
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace VF
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/applications/utilities/preProcessing/createViewFactors/viewFactorModels/viewFactorModel/viewFactor2LI/viewFactor2LI.C b/applications/utilities/preProcessing/createViewFactors/viewFactorModels/viewFactorModel/viewFactor2LI/viewFactor2LI.C
new file mode 100644
index 0000000000..273b5c2ac5
--- /dev/null
+++ b/applications/utilities/preProcessing/createViewFactors/viewFactorModels/viewFactorModel/viewFactor2LI/viewFactor2LI.C
@@ -0,0 +1,141 @@
+/*---------------------------------------------------------------------------*\
+ ========= |
+ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
+ \\ / O peration |
+ \\ / A nd | www.openfoam.com
+ \\/ M anipulation |
+-------------------------------------------------------------------------------
+ Copyright (C) 2023-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 .
+
+\*---------------------------------------------------------------------------*/
+
+#include "viewFactor2LI.H"
+#include "mathematicalConstants.H"
+#include "addToRunTimeSelectionTable.H"
+
+using namespace Foam::constant;
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+namespace Foam
+{
+namespace VF
+{
+ defineTypeNameAndDebug(viewFactor2LI, 0);
+ addToRunTimeSelectionTable(viewFactorModel, viewFactor2LI, mesh);
+}
+}
+
+// * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
+
+Foam::scalar Foam::VF::viewFactor2LI::calculateFij
+(
+ const List& lPoints,
+ const List& rPoints,
+ const scalar alpha
+)
+{
+ scalar Fij = 0;
+
+ forAll(lPoints, i)
+ {
+ // Edge vector and centroid of edge i
+ const vector si(lPoints[i] - lPoints.rcValue(i));
+ const point ci(0.5*(lPoints[i] + lPoints.rcValue(i)));
+
+ forAll(rPoints, j)
+ {
+ // Edge vector and centroid of edge j
+ const vector sj(rPoints[j] - rPoints.rcValue(j));
+ const point cj(0.5*(rPoints[j] + rPoints.rcValue(j)));
+
+ vector r(ci - cj);
+ if (mag(r) < SMALL)
+ {
+ r = alpha*si;
+ }
+
+ Fij += (si & sj)*Foam::log(r & r);
+ }
+ }
+
+ return max(0, 0.25*Fij/mathematical::pi);
+}
+
+
+Foam::scalarListList Foam::VF::viewFactor2LI::calculate
+(
+ const labelListList& visibleFaceFaces,
+ const pointField& compactCf,
+ const vectorField& compactSf,
+ const List>& compactFineSf,
+ const List>& compactFineCf,
+ const DynamicList>& compactPoints,
+ const DynamicList& compactPatchId
+) const
+{
+ // Fill local view factor matrix
+ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ scalarListList Fij(visibleFaceFaces.size());
+
+ forAll(visibleFaceFaces, facei)
+ {
+ if (debug > 1)
+ {
+ Pout<< "facei:" << facei << "/" << visibleFaceFaces.size()
+ << endl;
+ }
+
+ const labelList& visibleFaces = visibleFaceFaces[facei];
+
+ Fij[facei].resize(visibleFaces.size(), Zero);
+
+ const vector& Ai = compactSf[facei];
+ const scalar magAi = mag(Ai);
+
+ forAll(visibleFaces, visibleFacei)
+ {
+ const label sloti = visibleFaces[visibleFacei];
+ const List& lPoints = compactPoints[facei];
+ const List& rPoints = compactPoints[sloti];
+
+ const scalar Fij2LI = calculateFij(lPoints, rPoints, alpha_);
+
+ Fij[facei][visibleFacei] = Fij2LI/magAi;
+ }
+ }
+
+ return Fij;
+}
+
+
+// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
+
+Foam::VF::viewFactor2LI::viewFactor2LI
+(
+ const fvMesh& mesh,
+ const dictionary& dict
+)
+:
+ viewFactorModel(mesh, dict),
+ alpha_(dict.getOrDefault("alpha", 0.21))
+{}
+
+
+// ************************************************************************* //
diff --git a/applications/utilities/preProcessing/createViewFactors/viewFactorModels/viewFactorModel/viewFactor2LI/viewFactor2LI.H b/applications/utilities/preProcessing/createViewFactors/viewFactorModels/viewFactorModel/viewFactor2LI/viewFactor2LI.H
new file mode 100644
index 0000000000..0d2c0d273d
--- /dev/null
+++ b/applications/utilities/preProcessing/createViewFactors/viewFactorModels/viewFactorModel/viewFactor2LI/viewFactor2LI.H
@@ -0,0 +1,130 @@
+/*---------------------------------------------------------------------------*\
+ ========= |
+ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
+ \\ / O peration |
+ \\ / A nd | www.openfoam.com
+ \\/ M anipulation |
+-------------------------------------------------------------------------------
+ Copyright (C) 2023-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 .
+
+Class
+ Foam::VF::viewFactor2LI
+
+Description
+ Computes view factors according to the double line integral (2LI) method.
+
+Usage
+ Minimal example in \c /viewFactorsDict:
+ \verbatim
+ // Optional entries
+ alpha ;
+
+ // Inherited entries
+ ...
+ \endverbatim
+
+ where the entries mean:
+ \table
+ Property | Description | Type | Reqd | Deflt
+ alpha | Perturbation for common edges | scalar | no | 0.21
+ \endtable
+
+ The inherited entries are elaborated in:
+ - \link viewFactorModel.H \endlink
+
+SourceFiles
+ viewFactorModel.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef Foam_vf_viewFactor2LI_H
+#define Foam_vf_viewFactor2LI_H
+
+#include "viewFactorModel.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+namespace VF
+{
+
+/*---------------------------------------------------------------------------*\
+ Class viewFactor2LI Declaration
+\*---------------------------------------------------------------------------*/
+
+class viewFactor2LI
+:
+ public viewFactorModel
+{
+protected:
+
+ // Protected Data
+
+ //- Perturbation for common edges; default = 0.21
+ const scalar alpha_;
+
+
+ // Protected Member Functions
+
+ //- Calculate view factor using the double-area integral
+ static scalar calculateFij
+ (
+ const List& lPoints,
+ const List& rPoints,
+ const scalar alpha
+ );
+
+ //- Calculate
+ virtual scalarListList calculate
+ (
+ const labelListList& visibleFaceFaces,
+ const pointField& compactCf,
+ const vectorField& compactSf,
+ const List>& compactFineSf,
+ const List>& compactFineCf,
+ const DynamicList>& compactPoints,
+ const DynamicList& compactPatchId
+ ) const;
+
+
+public:
+
+ //- Runtime type information
+ TypeName("viewFactor2LI");
+
+ //- Constructor
+ viewFactor2LI(const fvMesh& mesh, const dictionary& dict);
+
+ //- Destructor
+ virtual ~viewFactor2LI() = default;
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace VF
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/applications/utilities/preProcessing/createViewFactors/viewFactorModels/viewFactorModel/viewFactorHottel/viewFactorHottel.C b/applications/utilities/preProcessing/createViewFactors/viewFactorModels/viewFactorModel/viewFactorHottel/viewFactorHottel.C
new file mode 100644
index 0000000000..3b352ab69f
--- /dev/null
+++ b/applications/utilities/preProcessing/createViewFactors/viewFactorModels/viewFactorModel/viewFactorHottel/viewFactorHottel.C
@@ -0,0 +1,153 @@
+/*---------------------------------------------------------------------------*\
+ ========= |
+ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
+ \\ / O peration |
+ \\ / A nd | www.openfoam.com
+ \\/ M anipulation |
+-------------------------------------------------------------------------------
+ Copyright (C) 2023-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 .
+
+\*---------------------------------------------------------------------------*/
+
+#include "viewFactorHottel.H"
+#include "mathematicalConstants.H"
+#include "fvMesh.H"
+#include "meshTools.H"
+//#include "addToRunTimeSelectionTable.H"
+
+using namespace Foam::constant;
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+namespace Foam
+{
+namespace VF
+{
+ defineTypeNameAndDebug(viewFactorHottel, 0);
+// addToRunTimeSelectionTable(viewFactorModel, viewFactorHottel, mesh);
+}
+}
+
+// * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
+
+Foam::scalar Foam::VF::viewFactorHottel::calculateFij
+(
+ const point& p0,
+ const point& p1,
+ const point& p2,
+ const point& p3
+)
+{
+ return 0.5*(mag(p2-p1) + mag(p3-p0) - mag(p2-p0) - mag(p3-p1));
+}
+
+
+Foam::scalarListList Foam::VF::viewFactorHottel::calculate
+(
+ const labelListList& visibleFaceFaces,
+ const pointField& compactCf,
+ const vectorField& compactSf,
+ const List>& compactFineSf,
+ const List>& compactFineCf,
+ const DynamicList>& compactPoints,
+ const DynamicList& compactPatchId
+) const
+{
+ // Fill local view factor matrix
+ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ scalarListList Fij(visibleFaceFaces.size());
+
+ forAll(visibleFaceFaces, facei)
+ {
+ if (debug > 1)
+ {
+ Pout<< "facei:" << facei << "/" << visibleFaceFaces.size()
+ << endl;
+ }
+
+ const labelList& visibleFaces = visibleFaceFaces[facei];
+
+ Fij[facei].resize_nocopy(visibleFaces.size());
+
+ const point& dCi = compactCf[facei];
+ const vector& Ai = compactSf[facei];
+ const scalar magAi = mag(Ai);
+
+ const vector d1((Ai/magAi) ^ emptyDir_);
+ const vector l1(0.5*magAi/w_*d1);
+ const point p0(dCi + l1);
+ const point p1(dCi - l1);
+
+ forAll(visibleFaces, visibleFacei)
+ {
+ const label sloti = visibleFaces[visibleFacei];
+
+ const point& dCj = compactCf[sloti];
+ const vector& Aj = compactSf[sloti];
+ const scalar magAj = mag(Aj);
+
+ const vector d2((Aj/magAj) ^ emptyDir_);
+ const vector l2(0.5*magAj/w_*d2);
+ const point p2(dCj - l2);
+ const point p3(dCj + l2);
+
+ const scalar FijH = calculateFij(p0, p1, p2, p3);
+
+ Fij[facei][visibleFacei] = FijH/(magAi/w_);
+ }
+ }
+
+
+ return Fij;
+}
+
+
+// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
+
+Foam::VF::viewFactorHottel::viewFactorHottel
+(
+ const fvMesh& mesh,
+ const dictionary& dict
+)
+:
+ viewFactorModel(mesh, dict),
+ emptyDir_(vector::one),
+ w_(0)
+{
+ if (mesh.nSolutionD() != 2)
+ {
+ FatalErrorInFunction
+ << "Hottel crossed strings method only applicable to 2D cases"
+ << exit(FatalError);
+ }
+
+ meshTools::constrainDirection(mesh, mesh.solutionD(), emptyDir_);
+ emptyDir_ = vector::one - emptyDir_;
+ emptyDir_.normalise();
+
+ // 2D width - assume slab
+ // TODO: warn wedge/axisymmetric?
+ w_ = mesh.bounds().span() & emptyDir_;
+
+ Info<< "\nEmpty direction: " << emptyDir_
+ << "\nWidth: " << w_ << endl;
+}
+
+
+// ************************************************************************* //
diff --git a/applications/utilities/preProcessing/createViewFactors/viewFactorModels/viewFactorModel/viewFactorHottel/viewFactorHottel.H b/applications/utilities/preProcessing/createViewFactors/viewFactorModels/viewFactorModel/viewFactorHottel/viewFactorHottel.H
new file mode 100644
index 0000000000..da7bade587
--- /dev/null
+++ b/applications/utilities/preProcessing/createViewFactors/viewFactorModels/viewFactorModel/viewFactorHottel/viewFactorHottel.H
@@ -0,0 +1,135 @@
+/*---------------------------------------------------------------------------*\
+ ========= |
+ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
+ \\ / O peration |
+ \\ / A nd | www.openfoam.com
+ \\/ M anipulation |
+-------------------------------------------------------------------------------
+ Copyright (C) 2023-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 .
+
+Class
+ Foam::VF::viewFactorHottel
+
+Description
+ Computes view factors according to Hottel's crossed strings method.
+
+ Reference:
+ \verbatim
+ Hottel, H. C., & Saforim, A. F. (1967).
+ Radiative transfer.
+ McGraw-Hill Book Company, New York.
+ \endverbatim
+
+Usage
+ Minimal example in \c /viewFactorsDict:
+ \verbatim
+ // Inherited entries
+ ...
+ \endverbatim
+
+ The inherited entries are elaborated in:
+ - \link viewFactorModel.H \endlink
+
+Note
+ Only applicable to 2D cases
+
+SourceFiles
+ viewFactorModel.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef Foam_vf_viewFactorHottel_H
+#define Foam_vf_viewFactorHottel_H
+
+#include "viewFactorModel.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+namespace VF
+{
+
+/*---------------------------------------------------------------------------*\
+ Class viewFactorHottel Declaration
+\*---------------------------------------------------------------------------*/
+
+class viewFactorHottel
+:
+ public viewFactorModel
+{
+ // Private Data
+
+ //- Mesh empty direction
+ vector emptyDir_;
+
+ //- Mesh 2D width
+ scalar w_;
+
+
+protected:
+
+ // Protected Member Functions
+
+ //- Calculate view factor using the double-area integral
+ static scalar calculateFij
+ (
+ const point& p0,
+ const point& p1,
+ const point& p2,
+ const point& p3
+ );
+
+ //- Calculate
+ virtual scalarListList calculate
+ (
+ const labelListList& visibleFaceFaces,
+ const pointField& compactCf,
+ const vectorField& compactSf,
+ const List>& compactFineSf,
+ const List>& compactFineCf,
+ const DynamicList>& compactPoints,
+ const DynamicList& compactPatchId
+ ) const;
+
+
+public:
+
+ //- Runtime type information
+ TypeName("viewFactorHottel");
+
+ //- Constructor
+ viewFactorHottel(const fvMesh& mesh, const dictionary& dict);
+
+ //- Destructor
+ virtual ~viewFactorHottel() = default;
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace VF
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/applications/utilities/preProcessing/createViewFactors/viewFactorModels/viewFactorModel/viewFactorModel/viewFactorModel.C b/applications/utilities/preProcessing/createViewFactors/viewFactorModels/viewFactorModel/viewFactorModel/viewFactorModel.C
new file mode 100644
index 0000000000..cc4e1442f6
--- /dev/null
+++ b/applications/utilities/preProcessing/createViewFactors/viewFactorModels/viewFactorModel/viewFactorModel/viewFactorModel.C
@@ -0,0 +1,290 @@
+/*---------------------------------------------------------------------------*\
+ ========= |
+ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
+ \\ / O peration |
+ \\ / A nd | www.openfoam.com
+ \\/ M anipulation |
+-------------------------------------------------------------------------------
+ Copyright (C) 2023-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 .
+
+\*---------------------------------------------------------------------------*/
+
+#include "viewFactorModel.H"
+#include "raySearchEngine.H"
+#include "OBJstream.H"
+#include "volFields.H"
+#include "IOmapDistribute.H"
+#include "scalarListIOList.H"
+
+// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
+
+namespace Foam
+{
+namespace VF
+{
+ defineTypeNameAndDebug(viewFactorModel, 0);
+ defineRunTimeSelectionTable(viewFactorModel, mesh);
+}
+}
+
+// * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
+
+void Foam::VF::viewFactorModel::writeRays
+(
+ const fileName& fName,
+ const pointField& compactCf,
+ const labelListList& visibleFaceFaces
+)
+{
+ OBJstream str(fName);
+
+ Pout<< "Writing rays to " << str.name() << endl;
+
+ forAll(visibleFaceFaces, facei)
+ {
+ const labelList& visibleSlots = visibleFaceFaces[facei];
+
+ for (const label sloti : visibleSlots)
+ {
+ str.write(linePointRef(compactCf[facei], compactCf[sloti]));
+ }
+ }
+
+ str.flush();
+}
+
+
+// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
+
+Foam::VF::viewFactorModel::viewFactorModel
+(
+ const fvMesh& mesh,
+ const dictionary& dict
+)
+:
+ mesh_(mesh),
+ searchEnginePtr_(raySearchEngine::New(mesh, dict)),
+ writeViewFactors_(dict.get("writeViewFactors")),
+ writeRays_(dict.getOrDefault("writeRays", false))
+{}
+
+
+// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
+
+Foam::VF::viewFactorModel::~viewFactorModel()
+{}
+
+
+// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
+
+void Foam::VF::viewFactorModel::calculate()
+{
+ const auto& searchEngine = *searchEnginePtr_;
+
+ labelListList visibleFaceFaces;
+ searchEngine.correct(visibleFaceFaces);
+ const auto& map = searchEngine.map();
+
+ // Construct data in compact addressing
+ // Compact addressing has all local data, followed by remote contributions
+
+ pointField compactCf;
+ vectorField compactSf;
+ List> compactFineSf;
+ List> compactFineCf;
+ DynamicList> compactPoints;
+ DynamicList compactPatchId;
+
+ searchEngine.compactAddressing
+ (
+ map,
+ compactCf,
+ compactSf,
+ compactFineSf,
+ compactFineCf,
+ compactPoints,
+ compactPatchId
+ );
+
+ if (writeRays_)
+ {
+ // Write all rays between visible faces using .OBJ format
+
+ writeRays
+ (
+ mesh_.time().path()/"allVisibleFaces.obj",
+ compactCf,
+ visibleFaceFaces
+ );
+ }
+
+ (void)mesh_.time().cpuTimeIncrement();
+
+ Info<< "\nCalculating view factors" << endl;
+
+ scalarListIOList Fij
+ (
+ IOobject
+ (
+ "F",
+ mesh_.facesInstance(),
+ mesh_,
+ IOobject::NO_READ
+ ),
+ calculate
+ (
+ visibleFaceFaces,
+ compactCf,
+ compactSf,
+ compactFineSf,
+ compactFineCf,
+ compactPoints,
+ compactPatchId
+ )
+ );
+
+ const label totalPatches = mesh_.boundaryMesh().nNonProcessor();
+
+ // Matrix sum in j(Fij) for each i
+ // Note: if enclosure sum should = 1
+ scalarSquareMatrix viewFactorPatch(totalPatches, Zero);
+
+ forAll(visibleFaceFaces, startFacei)
+ {
+ const scalar magAi = mag(compactSf[startFacei]);
+
+ const labelList& visibleSlots = visibleFaceFaces[startFacei];
+ const label patchi = compactPatchId[startFacei];
+
+ forAll(visibleSlots, visSloti)
+ {
+ const label sloti = visibleSlots[visSloti];
+ const label patchj = compactPatchId[sloti];
+
+ viewFactorPatch[patchi][patchj] += Fij[startFacei][visSloti]*magAi;
+ }
+ }
+
+ reduce(viewFactorPatch, sumOp());
+
+ const scalarList patchArea = searchEngine.patchAreas();
+
+
+ if (UPstream::master())
+ {
+ Info<< "\nPatch view factor contributions:" << nl << endl;
+
+ scalar vfSum = 0;
+
+ const auto& patchIDs = searchEngine.patchIDs();
+ const auto& patches = mesh_.boundaryMesh();
+
+ for (const label patchi : patchIDs)
+ {
+ Info<< " Patch " << patchi << ": " << patches[patchi].name()
+ << endl;
+
+ scalar vfPatch = 0;
+ for (const label patchj: patchIDs)
+ {
+ scalar vf = viewFactorPatch[patchi][patchj]/patchArea[patchi];
+ vfPatch += vf;
+
+ Info<< " F" << patchi << patchj << ": " << vf << endl;
+ }
+
+ Info<< " Sum: " << vfPatch << nl << endl;
+
+ vfSum += vfPatch;
+ }
+
+ Info<< "Sum(all patches) = " << vfSum << endl;
+ }
+
+ // Write view factors matrix in list-list form
+ Info<< "\nWriting view factor matrix" << endl;
+ Fij.write();
+
+ if (writeViewFactors_)
+ {
+ Info<< "\nWriting view factor field" << endl;
+
+ auto tviewFactorField =
+ volScalarField::New
+ (
+ "viewFactorField",
+ mesh_,
+ dimensionedScalar(dimless, Zero)
+ );
+
+ auto& viewFactorField = tviewFactorField.ref();
+
+ searchEngine.interpolate(viewFactorField, Fij);
+
+ viewFactorField.write();
+ }
+
+
+ // Create globalFaceFaces needed to insert view factors
+ // in F to the global matrix Fmatrix
+ {
+ labelListIOList IOglobalFaceFaces
+ (
+ IOobject
+ (
+ "globalFaceFaces",
+ mesh_.facesInstance(),
+ mesh_,
+ IOobject::NO_READ
+ ),
+ visibleFaceFaces.size()
+ );
+
+ forAll(IOglobalFaceFaces, facei)
+ {
+ IOglobalFaceFaces[facei] = renumber
+ (
+ searchEngine.compactToGlobal(),
+ visibleFaceFaces[facei]
+ );
+ }
+
+ IOglobalFaceFaces.write();
+ }
+
+ // Write parallel map
+ {
+ IOmapDistribute IOmapDist
+ (
+ IOobject
+ (
+ "mapDist",
+ mesh_.facesInstance(),
+ mesh_,
+ IOobject::NO_READ
+ ),
+ std::move(map)
+ );
+
+ IOmapDist.write();
+ }
+}
+
+
+// ************************************************************************* //
diff --git a/applications/utilities/preProcessing/createViewFactors/viewFactorModels/viewFactorModel/viewFactorModel/viewFactorModel.H b/applications/utilities/preProcessing/createViewFactors/viewFactorModels/viewFactorModel/viewFactorModel/viewFactorModel.H
new file mode 100644
index 0000000000..549c50978d
--- /dev/null
+++ b/applications/utilities/preProcessing/createViewFactors/viewFactorModels/viewFactorModel/viewFactorModel/viewFactorModel.H
@@ -0,0 +1,185 @@
+/*---------------------------------------------------------------------------*\
+ ========= |
+ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
+ \\ / O peration |
+ \\ / A nd | www.openfoam.com
+ \\/ M anipulation |
+-------------------------------------------------------------------------------
+ Copyright (C) 2023-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 .
+
+Namespace
+ Foam::VF
+
+Description
+ A namespace for various \c viewFactor model implementations.
+
+Class
+ Foam::VF::viewFactorModel
+
+Description
+ A base class for \c viewFactor models.
+
+Usage
+ Minimal example in \c /viewFactorsDict:
+ \verbatim
+ // Mandatory entries
+ writeViewFactors ;
+
+ // Optional entries
+ writeRays ;
+ \endverbatim
+
+ where the entries mean:
+ \table
+ Property | Description | Type | Reqd | Deflt
+ writeViewFactors | Flag to write the view factor field | bool | yes | -
+ writeRays | Flag to write the ray geometry | bool | no | false
+ \endtable
+
+SourceFiles
+ viewFactorModel.C
+ viewFactorModelNew.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef Foam_vf_viewFactorModel_H
+#define Foam_vf_viewFactorModel_H
+
+#include "autoPtr.H"
+#include "pointField.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+// Forward Declarations
+class fvMesh;
+
+namespace VF
+{
+
+// Forward Declarations
+class raySearchEngine;
+
+/*---------------------------------------------------------------------------*\
+ Class viewFactorModel Declaration
+\*---------------------------------------------------------------------------*/
+
+class viewFactorModel
+{
+protected:
+
+ // Protected Data
+
+ //- Reference to the mesh database
+ const fvMesh& mesh_;
+
+ //- Run-time selectable ray search engine
+ autoPtr searchEnginePtr_;
+
+ //- Flag to write the view factor field
+ bool writeViewFactors_;
+
+ //- Flag to write the ray geometry
+ bool writeRays_;
+
+
+ // Protected Member Functions
+
+ //- Write ray geometry to file
+ static void writeRays
+ (
+ const fileName& fName,
+ const pointField& compactCf,
+ const labelListList& visibleFaceFaces
+ );
+
+ //- Calculate the view factors using run-time selectable model
+ virtual scalarListList calculate
+ (
+ const labelListList& visibleFaceFaces,
+ const pointField& compactCoarseCf,
+ const vectorField& compactCoarseSf,
+ const List>& compactFineSf,
+ const List>& compactFineCf,
+ const DynamicList>& compactPoints,
+ const DynamicList& compactPatchId
+ ) const = 0;
+
+
+public:
+
+ //- Runtime type information
+ TypeName("viewFactorModel");
+
+ //- Selection table
+ declareRunTimeSelectionTable
+ (
+ autoPtr,
+ viewFactorModel,
+ mesh,
+ (
+ const fvMesh& mesh,
+ const dictionary& dict
+ ),
+ (mesh, dict)
+ );
+
+ //- Selector
+ static autoPtr New
+ (
+ const fvMesh& mesh,
+ const dictionary& dict
+ );
+
+
+ // Generated Methods
+
+ //- No copy construct
+ viewFactorModel(const viewFactorModel&) = delete;
+
+ //- No copy assignment
+ void operator=(const viewFactorModel&) = delete;
+
+
+ //- Constructor
+ viewFactorModel(const fvMesh& mesh, const dictionary& dict);
+
+ //- Destructor
+ virtual ~viewFactorModel();
+
+
+ // Public Member Functions
+
+ //- Calculate the view factors
+ virtual void calculate();
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace VF
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/applications/utilities/preProcessing/createViewFactors/viewFactorModels/viewFactorModel/viewFactorModel/viewFactorModelNew.C b/applications/utilities/preProcessing/createViewFactors/viewFactorModels/viewFactorModel/viewFactorModel/viewFactorModelNew.C
new file mode 100644
index 0000000000..e87f7d459f
--- /dev/null
+++ b/applications/utilities/preProcessing/createViewFactors/viewFactorModels/viewFactorModel/viewFactorModel/viewFactorModelNew.C
@@ -0,0 +1,72 @@
+/*---------------------------------------------------------------------------*\
+ ========= |
+ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
+ \\ / O peration |
+ \\ / A nd | www.openfoam.com
+ \\/ M anipulation |
+-------------------------------------------------------------------------------
+ Copyright (C) 2023-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 .
+
+\*---------------------------------------------------------------------------*/
+
+#include "viewFactorModel.H"
+#include "viewFactorHottel.H"
+#include "fvMesh.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+Foam::autoPtr Foam::VF::viewFactorModel::New
+(
+ const fvMesh& mesh,
+ const dictionary& dict
+)
+{
+ // Intercept 2D cases
+ if (mesh.nSolutionD() == 2)
+ {
+ Info<< "Selecting " << typeName << ": " << viewFactorHottel::typeName
+ << " for 2D cases" << nl << endl;
+ return autoPtr(new viewFactorHottel(mesh, dict));
+ }
+
+
+ // 3D cases - use run-time selection
+
+ const word modelType(dict.get("viewFactorModel"));
+
+ Info<< "Selecting " << typeName << ": " << modelType << endl;
+
+ auto* ctorPtr = meshConstructorTable(modelType);
+
+ if (!ctorPtr)
+ {
+ FatalIOErrorInLookup
+ (
+ dict,
+ typeName,
+ modelType,
+ *meshConstructorTablePtr_
+ ) << exit(FatalIOError);
+ }
+
+ return autoPtr(ctorPtr(mesh, dict));
+}
+
+
+// ************************************************************************* //