/*---------------------------------------------------------------------------*\ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | \\ / A nd | www.openfoam.com \\/ M anipulation | ------------------------------------------------------------------------------- Copyright (C) 2022-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 setTurbulenceFields Group grpPreProcessingUtilities Description Initialises turbulence fields according to various empirical governing equations. References: \verbatim Initialisation method (tag:M): Manceau, R. (n.d.). A two-step automatic initialization procedure for RANS computations. (Unpublished). Previous-version of the initialisation model (tag:LM): Lardeau, S., & Manceau, R. (2014). Computations of complex flow configurations using a modified elliptic-blending Reynolds-stress model. 10th International ERCOFTAC Symposium on Engineering Turbulence Modelling and Measurements. Marbella, Spain. https://hal.archives-ouvertes.fr/hal-01051799 \endverbatim Usage Minimal example by using \c system/setTurbulenceFieldsDict: \verbatim // Mandatory entries uRef ; // Optional entries initialiseU ; initialiseEpsilon ; initialiseK ; initialiseOmega ; initialiseR ; writeF ; kappa ; Cmu ; dPlusRef ; f ; U ; epsilon ; k ; omega ; R ; \endverbatim where the entries mean: \table Property | Description | Type | Reqd | Deflt uRef | Reference speed | scalar | yes | - initialiseU | Flag to initialise U | bool | no | false initialiseEpsilon | Flag to initialise epsilon | bool | no | false initialiseK | Flag to initialise k | bool | no | false initialiseOmega | Flag to initialise omega | bool | no | false initialiseR | Flag to initialise R | bool | no | false writeF | Flag to write elliptic-blending field, f | bool | no | false kappa | von Karman constant | scalar | no | 0.41 Cmu | Empirical constant | scalar | no | 0.09 dPlusRef | Reference dPlus | scalar | no | 15 f | Name of operand f field | word | no | f U | Name of operand U field | word | no | U epsilon | Name of operand epsilon field | word | no | epsilon k | Name of operand k field | word | no | k omega | Name of operand omega field | word | no | omega R | Name of operand R field | word | no | R \endtable Note - Time that the utility applies to is determined by the \c controlDict.startFrom and \c controlDict.startTime entries. - The utility modifies near-wall fields, hence can be more effective for low-Re mesh cases. \*---------------------------------------------------------------------------*/ #include "fvCFD.H" #include "singlePhaseTransportModel.H" #include "turbulentTransportModel.H" #include "turbulentFluidThermoModel.H" #include "wallFvPatch.H" #include "processorFvPatch.H" #include "fixedValueFvPatchFields.H" using namespace Foam; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // void InfoField(const word& fldName) { Info<< "Writing field: " << fldName << nl << endl; } template void correctProcessorPatches(GeometricField& fld) { if (UPstream::parRun()) { fld.boundaryFieldRef().template evaluateCoupled(); } } IOobject createIOobject ( const fvMesh& mesh, const word& name, IOobject::readOption rOpt = IOobject::READ_IF_PRESENT ) { return IOobject ( name, mesh.time().timeName(), mesh, rOpt, IOobject::NO_WRITE, IOobject::NO_REGISTER ); } tmp nu ( const fvMesh& mesh, const volVectorField& U ) { const Time& runTime = mesh.time(); if ( IOobject ( basicThermo::dictName, runTime.constant(), mesh ).typeHeaderOk(true) ) { // Compressible autoPtr pThermo(fluidThermo::New(mesh)); fluidThermo& thermo = pThermo(); volScalarField rho(thermo.rho()); // Create phi #include "compressibleCreatePhi.H" autoPtr turbulence ( compressible::turbulenceModel::New ( rho, U, phi, thermo ) ); turbulence->validate(); return tmp::New(turbulence->nu()); } else { // Incompressible // Create phi #include "createPhi.H" singlePhaseTransportModel laminarTransport(U, phi); autoPtr turbulence ( incompressible::turbulenceModel::New(U, phi, laminarTransport) ); turbulence->validate(); return tmp::New(turbulence->nu()); } } // Calculate elliptic blending function // between near-wall and weakly-inhomogeneous regions void calcF ( const volScalarField& L, volScalarField& f ) { tmp tinvLsqr = scalar(1)/sqr(L); const volScalarField& invLsqr = tinvLsqr.cref(); // (M:Eq. 6) tmp fEqn ( fvm::Sp(invLsqr, f) - fvm::laplacian(f) == invLsqr ); tinvLsqr.clear(); fEqn.ref().relax(); solve(fEqn); // (M:p. 2) f.clamp_range(0, scalar(1) - Foam::exp(-scalar(400)/scalar(50))); } // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // // Main program: int main(int argc, char *argv[]) { argList::addNote ( "Sets initial turbulence fields based on" " various empirical equations" ); argList::noFunctionObjects(); argList::addOption("dict", "file", "Alternative setTurbulenceFieldsDict"); #include "addRegionOption.H" #include "setRootCase.H" #include "createTime.H" const word dictName("setTurbulenceFieldsDict"); #include "setSystemRunTimeDictionaryIO.H" Info<< "Reading " << dictIO.name() << nl << endl; IOdictionary dict(dictIO); #include "createNamedMesh.H" // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // IOstream::defaultPrecision(15); // Dictionary input (M:p. 2) const scalar uRef = dict.getCheck("uRef", scalarMinMax::ge(SMALL)); const dimensionedScalar uTau(dimVelocity, 0.05*uRef); const scalar kappa = dict.getCheckOrDefault("kappa", 0.41, scalarMinMax::ge(SMALL)); const scalar Cmu = dict.getCheckOrDefault("Cmu", 0.09, scalarMinMax::ge(SMALL)); const scalar dPlusRef = dict.getCheckOrDefault("dPlusRef", 15, scalarMinMax::ge(SMALL)); const word fName = dict.getOrDefault("f", "f"); const word UName = dict.getOrDefault("U", "U"); const word epsilonName = dict.getOrDefault("epsilon", "epsilon"); const word kName = dict.getOrDefault("k", "k"); const word omegaName = dict.getOrDefault("omega", "omega"); const word RName = dict.getOrDefault("R", "R"); const bool initU = dict.getOrDefault("initialiseU", false); const bool initEpsilon = dict.getOrDefault("initialiseEpsilon", false); const bool initK = dict.getOrDefault("initialiseK", false); const bool initOmega = dict.getOrDefault("initialiseOmega", false); const bool initR = dict.getOrDefault("initialiseR", false); const bool writeF = dict.getOrDefault("writeF", false); // Start initialising the operand fields // Read operand fields auto tU = tmp::New ( createIOobject(mesh, UName, IOobject::MUST_READ), mesh ); // Infer the initial BCs from the velocity const wordList bcTypes ( tU.cref().boundaryField().size(), fixedValueFvPatchScalarField::typeName ); tmp tepsilon; tmp tk; tmp tomega; tmp tR; if (initEpsilon) { tepsilon = tmp::New ( createIOobject(mesh, epsilonName), mesh, dimensionedScalar(sqr(dimLength)/pow3(dimTime), SMALL), bcTypes ); } if (initK) { tk = tmp::New ( createIOobject(mesh, kName), mesh, dimensionedScalar(sqr(dimLength/dimTime), SMALL), bcTypes ); } if (initOmega) { tomega = tmp::New ( createIOobject(mesh, omegaName), mesh, dimensionedScalar(dimless/dimTime, SMALL), bcTypes ); } if (initR) { tR = tmp::New ( createIOobject(mesh, RName), mesh, dimensionedSymmTensor(sqr(dimLength/dimTime), Zero), bcTypes ); } // Create elliptic blending factor field volScalarField f ( IOobject ( fName, runTime.timeName(), mesh, IOobject::NO_READ, IOobject::NO_WRITE, IOobject::NO_REGISTER ), mesh, dimensionedScalar(dimless, scalar(1)), fixedValueFvPatchScalarField::typeName ); for (fvPatchScalarField& pfld : f.boundaryFieldRef()) { if (isA(pfld.patch())) { pfld == Zero; } } // Create auxillary fields for the initialisation tmp tnu = nu(mesh, tU.cref()); const volScalarField& nu = tnu.cref(); // (M:p. 2) tmp tL = scalar(50)*nu/uTau; const volScalarField& L = tL.cref(); calcF(L, f); // (M:Eq. 8) tmp td = -tL*Foam::log(scalar(1) - f); const volScalarField& d = td.cref(); // (M:p. 2) const volScalarField dPlus(d*uTau/nu); // (M:Eq. 11) const volScalarField epsilon ( pow4(uTau)/(kappa*nu*max(dPlus, dPlusRef)) ); // (M:Eq. 13) const volScalarField fk(Foam::exp(-dPlus/scalar(25))); // (M:Eq. 12) const volScalarField k ( (epsilon*sqr(td)*sqr(fk))/(2*nu) + sqr(uTau)*sqr(scalar(1) - fk)/Foam::sqrt(Cmu) ); // Initialise operand fields if (initU) { volVectorField& U = tU.ref(); // Reichardt’s law (M:Eq. 10) const scalar C = 7.8; const scalar B1 = 11; const scalar B2 = 3; const volScalarField fRei ( Foam::log(scalar(1) + kappa*dPlus)/kappa + C* ( scalar(1) - Foam::exp(-dPlus/B1) - dPlus/B1*Foam::exp(-dPlus/B2) ) ); // (M:Eq. 9) const dimensionedScalar maxU(dimVelocity, SMALL); U *= min(scalar(1), fRei*uTau/max(mag(U), maxU)); correctProcessorPatches(U); } if (tepsilon.valid()) { tepsilon.ref() = epsilon; correctProcessorPatches(tepsilon.ref()); } if (tk.valid()) { tk.ref() = k; correctProcessorPatches(tk.ref()); } if (tomega.valid()) { const dimensionedScalar k0(sqr(dimLength/dimTime), SMALL); tomega.ref() = Cmu*epsilon/(k + k0); correctProcessorPatches(tomega.ref()); } if (tR.valid()) { auto& R = tR.ref(); // (M:Eq. 3) const volSphericalTensorField Rdiag(k*twoThirdsI); forAll(R, celli) { R[celli] = Rdiag[celli]; } correctProcessorPatches(R); } // Write operand fields Info<< endl; if (initU) { InfoField(tU->name()); tU->write(); } if (tepsilon.valid()) { InfoField(tepsilon->name()); tepsilon->write(); } if (tk.valid()) { InfoField(tk->name()); tk->write(); } if (tomega.valid()) { InfoField(tomega->name()); tomega->write(); } if (tR.valid()) { InfoField(tR->name()); tR->write(); } if (writeF) { InfoField(f.name()); f.write(); } Info<< nl; runTime.printExecutionTime(Info); Info<< "End\n" << endl; return 0; } // ************************************************************************* //