turbulentDispersion.C
Go to the documentation of this file.
1 /*---------------------------------------------------------------------------*\
2  ========= |
3  \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4  \\ / O peration | Website: https://openfoam.org
5  \\ / A nd | Copyright (C) 2025 OpenFOAM Foundation
6  \\/ M anipulation |
7 -------------------------------------------------------------------------------
8 License
9  This file is part of OpenFOAM.
10 
11  OpenFOAM is free software: you can redistribute it and/or modify it
12  under the terms of the GNU General Public License as published by
13  the Free Software Foundation, either version 3 of the License, or
14  (at your option) any later version.
15 
16  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
17  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
18  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
19  for more details.
20 
21  You should have received a copy of the GNU General Public License
22  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
23 
24 \*---------------------------------------------------------------------------*/
25 
26 #include "turbulentDispersion.H"
29 #include "coupledToFluid.H"
30 #include "standardNormal.H"
34 
35 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
36 
37 namespace Foam
38 {
39 namespace Lagrangian
40 {
43  (
47  );
48 }
49 }
50 
51 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
52 
53 template<class Type>
55 Foam::Lagrangian::turbulentDispersion::initialiseTurbField
56 (
57  const word& name,
58  const dimensionSet& dims,
59  const Type& value
60 )
61 {
63  cloud().stateField<Type>
64  (
65  IOobject
66  (
67  name,
68  mesh().time().name(),
69  mesh(),
72  ),
73  mesh(),
74  dimensioned<Type>(name, dims, value)
75  );
76 
77  return Tuple2<bool, CloudStateField<Type>&>(ref.headerOk(), ref);
78 }
79 
80 
81 template<class InjectionFieldSourceType, class Type>
82 void Foam::Lagrangian::turbulentDispersion::completeTurbField
83 (
84  Tuple2<bool, CloudStateField<Type>&>& turbField
85 )
86 {
87  if (turbField.first()) return;
88 
89  turbField.first() = true;
90 
91  LagrangianModels& modelList = cloud().LagrangianModels();
92 
93  turbField.second().sourcesRef().table().transfer
94  (
96  (
97  turbField.second(),
99  <
101  InjectionFieldSourceType,
104  >()
105  ).table()
106  );
107 }
108 
109 
110 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
111 
113 (
114  const word& name,
115  const LagrangianMesh& mesh,
116  const dictionary& modelDict,
117  const dictionary& stateDict
118 )
119 :
121  cloudLagrangianModel(static_cast<const LagrangianModel&>(*this)),
122  dragPtr_(nullptr),
123  momentumTransportModel_(mesh.mesh().lookupType<momentumTransportModel>()),
124  kc_
125  (
126  cloud<clouds::coupled>().carrierField<scalar>
127  (
128  clouds::coupled::carrierName
129  (
130  momentumTransportModel_.k()().name()
131  ),
132  [&]()
133  {
134  return momentumTransportModel_.k();
135  }
136  )
137  ),
138  epsilonc_
139  (
140  cloud<clouds::coupled>().carrierField<scalar>
141  (
143  (
144  momentumTransportModel_.epsilon()().name()
145  ),
146  [&]()
147  {
148  return momentumTransportModel_.epsilon();
149  }
150  )
151  ),
152  fractionTurb_(initialiseTurbField("fractionTurb", dimless, vGreat)),
153  tTurb_(initialiseTurbField("tTurb", dimTime, NaN)),
154  Uturb_(initialiseTurbField("Uturb", dimVelocity, vector::uniform(NaN))),
155  rndGen_("rndGen", stateDict, name, false),
156  avgUturbPtr_(nullptr)
157 {}
158 
159 
160 // * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * * //
161 
163 {
164  return wordList(1, cloud().U.name());
165 }
166 
167 
169 {
170  LagrangianModels& modelList = cloud().LagrangianModels();
171 
172  dragPtr_ = nullptr;
173 
174  forAll(modelList, i)
175  {
176  if (!isA<drag>(modelList[i])) continue;
177 
178  if (dragPtr_ != nullptr)
179  {
181  << "Multiple drag models found. Turbulent dispersion "
182  << "requires exactly one drag model."
183  << exit(FatalError);
184  }
185 
186  dragPtr_ = &refCast<const drag>(modelList[i]);
187  }
188 
189  if (dragPtr_ == nullptr)
190  {
192  << "No drag models found. Turbulent dispersion "
193  << "requires exactly one drag model."
194  << exit(FatalError);
195  }
196 
197  completeTurbField<maxLagrangianScalarFieldSource>(fractionTurb_);
198  completeTurbField<NaNLagrangianScalarFieldSource>(tTurb_);
199  completeTurbField<NaNLagrangianVectorFieldSource>(Uturb_);
200 }
201 
202 
204 (
205  const LagrangianSubScalarField& deltaT,
206  const bool final
207 )
208 {
209  const LagrangianSubMesh& subMesh = deltaT.mesh();
210 
211  // References to the evolving fields
212  LagrangianSubScalarSubField& fractionTurb =
213  fractionTurb_.second().ref(subMesh);
214  LagrangianSubScalarSubField& tTurb = tTurb_.second().ref(subMesh);
215  LagrangianSubVectorSubField& Uturb = Uturb_.second().ref(subMesh);
216  const LagrangianSubScalarField& fractionTurb0 = fractionTurb.oldTime();
217  const LagrangianSubVectorField& Uturb0 = Uturb.oldTime();
218 
219  // Update the eddy time-scale
220  const dimensionedScalar cps(dimless, 0.16432); // (model constant ?)
221  const LagrangianSubScalarField magUrel
222  (
223  mag(cloud().U(subMesh) - cloud<clouds::coupled>().Uc(subMesh))
224  );
225  static const dimensionedScalar rootVSmallEpsilon
226  (
228  rootVSmall
229  );
230  static const dimensionedScalar rootVSmallEpsilonU
231  (
233  rootVSmall
234  );
235  tTurb =
236  max
237  (
238  kc_(subMesh)/max(epsilonc_(subMesh), rootVSmallEpsilon),
239  cps*kc_(subMesh)*sqrt(kc_(subMesh))
240  /max(epsilonc_(subMesh)*magUrel, rootVSmallEpsilonU)
241  );
242 
243  // Velocity fluctuation magnitude
244  const LagrangianSubScalarField magUturb(sqrt(2.0/3.0*kc_(subMesh)));
245 
246  // Initialise the average turbulent velocities
247  avgUturbPtr_.reset
248  (
250  (
251  "avgUturb",
252  subMesh,
254  ).ptr()
255  );
256 
257  // Consider each particle in turn
258  forAll(subMesh, subi)
259  {
260  // Create independent generators for each particle. That way if the
261  // sequence grows or shrinks across iterations, that effect doesn't
262  // propagate and affect other particles' sequences
264  (
265  rndGen_.sampleAB(labelMin, labelMax),
266  false
267  );
269  (
270  rndGen_.sampleAB(labelMin, labelMax),
271  false
272  );
273 
274  // Set up sub-stepping
275  const scalar Dt = max(deltaT[subi], rootVSmall);
276  scalar dt = 0;
277 
278  // Continue/complete the previous eddy
279  if (fractionTurb0[subi] < 1)
280  {
281  dt += tTurb[subi]*(1 - fractionTurb0[subi]);
282 
283  avgUturbPtr_()[subi] += min(dt/Dt, 1)*Uturb0[subi];
284  }
285 
286  // Add new eddies across the time-step
287  while (dt < Dt)
288  {
289  const scalar dtPrev = dt;
290  dt += tTurb[subi];
291 
292  // Create a random direction with normally distributed magnitude
293  const scalar theta =
295  const scalar u = 2*rndGen.scalar01() - 1;
296  const scalar a = sqrt(1 - sqr(u));
297  const vector dir(a*cos(theta), a*sin(theta), u);
298 
299  // Set the new turbulent fluctuation velocity
300  Uturb[subi] = dir*stdNormal.sample()*magUturb[subi];
301 
302  // Add it to the average
303  avgUturbPtr_()[subi] += (min(dt, Dt) - dtPrev)/Dt*Uturb[subi];
304  }
305 
306  // Set the fraction to where we got to in the current eddy
307  fractionTurb[subi] = 1 - (dt - Dt)/tTurb[subi];
308  }
309 
310  // If this is not the final iteration then rewind the generator so that the
311  // same numbers are generated in the next iteration
312  rndGen_.start(!final);
313 }
314 
315 
317 (
318  const LagrangianSubScalarField& deltaT,
321 ) const
322 {
323  assertCloud<clouds::coupledToIncompressibleFluid>();
324 
325  eqn.Su += dragPtr_->D(deltaT.mesh())*avgUturbPtr_();
326 }
327 
328 
330 (
331  const LagrangianSubScalarField& deltaT,
332  const LagrangianSubScalarSubField& vOrM,
335 ) const
336 {
337  assertCloud<clouds::coupledToIncompressibleFluid, clouds::coupledToFluid>();
338 
339  eqn.Su += dragPtr_->D(deltaT.mesh())*avgUturbPtr_();
340 }
341 
342 
344 (
345  Ostream& os
346 ) const
347 {
349 
350  writeEntry(os, "rndGen", rndGen_);
351 }
352 
353 
354 // ************************************************************************* //
label k
#define forAll(list, i)
Loop across all elements in list.
Definition: UList.H:433
Macros for easy insertion into run-time selection tables.
A field which is stored as part of the state of the cloud. This is a light wrapper around a dynamic L...
Field with dimensions and associated with geometry type GeoMesh which is used to size the field and a...
const Mesh & mesh() const
Return mesh.
static tmp< DimensionedField< Type, GeoMesh, PrimitiveField > > New(const word &name, const Mesh &mesh, const dimensionSet &, const PrimitiveField< Type > &)
Return a temporary field constructed from name, mesh,.
Part of a geometric field used for setting the values associated with optional sources.
const HashPtrTable< Source > & table() const
Access the underlying field table.
IOobject defines the attributes of an object for which implicit objectRegistry management is supporte...
Definition: IOobject.H:99
This class stores the coefficients of a Lagrangian equation, and facilitates solving that equation an...
Definition: LagrangianEqn.H:56
LagrangianCoeff< Type, false > Su
Explicit coefficient.
Base class for Lagrangian injections. Minimal wrapper over LagrangianSource. Implements some utility ...
Class containing Lagrangian geometry and topology.
Base class for Lagrangian models.
const LagrangianMesh & mesh() const
The mesh.
const word & name() const
The source name.
List of Lagrangian models, constructed as a (Lagrangian) mesh object. Provides similar functions to t...
HashTable< word > modelTypeFieldSourceTypes() const
Return a table of field source types that are chosen to match given.
Base class for Lagrangian sources. Minimal wrapper over LagrangianModel that provides an interface to...
Mesh that relates to a sub-section of a Lagrangian mesh. This is used to construct fields that relate...
virtual wordList addSupFields() const
Return the name of the velocity field.
virtual void postConstruct()
Do post construction steps which require access to other models.
virtual void addSup(const LagrangianSubScalarField &deltaT, const LagrangianSubVectorSubField &U, LagrangianEqn< vector > &eqn) const
Add a source term to the velocity equation.
virtual void writeProcessorState(Ostream &os) const
Write state.
turbulentDispersion(const word &name, const LagrangianMesh &mesh, const dictionary &modelDict, const dictionary &stateDict)
Construct from components.
virtual void calculate(const LagrangianSubScalarField &deltaT, const bool final)
Solve equations and/or update continually changing properties.
const Field0Type & oldTime() const
Return the old-time field.
Definition: OldTimeField.C:322
An Ostream is an abstract base class for all output systems (streams, files, token lists,...
Definition: Ostream.H:57
A 2-tuple for storing two objects of different types.
Definition: Tuple2.H:66
static Form uniform(const Cmpt &s)
Return a VectorSpace with all elements = s.
Definition: VectorSpaceI.H:168
Base class for Lagrangian models that refer to a cloud. Not a Lagrangian model in itself....
const Cloud & cloud() const
Get a reference to the cloud.
Base class for clouds. Provides a basic evolution algorithm, models, and a database for caching deriv...
Definition: cloud.H:63
Foam::LagrangianModels & LagrangianModels() const
Access the models.
Definition: cloud.C:623
static word carrierName(const word &name)
Modify a name to disambiguate it as relating to the carrier.
Definition: coupled.C:246
A list of keywords followed by any number of values (e.g. words and numbers) or sub-dictionaries.
Definition: dictionary.H:162
void transfer(dictionary &)
Transfer the contents of the argument and annul the argument.
Definition: dictionary.C:1368
Dimension set for the base types.
Definition: dimensionSet.H:125
Generic dimensioned Type class.
Standard normal distribution. Not selectable.
virtual scalar sample() const
Sample the distribution.
This source condition retains the internal value.
Abstract base class for turbulence models (RAS, LES and laminar).
virtual tmp< volScalarField > k() const =0
Return the turbulence kinetic energy.
Random number generator.
virtual void writeProcessorState(Ostream &os) const
Write processor state.
Definition: stateModel.C:132
A class for handling words, derived from string.
Definition: word.H:62
trAU ref().rename("rAU")
Foam::fvMesh mesh(Foam::IOobject(regionName, runTime.name(), runTime, Foam::IOobject::MUST_READ), false)
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:334
U
Definition: pEqn.H:72
addToRunTimeSelectionTable(LagrangianModel, constantCoefficientVirtualMass, dictionary)
defineTypeNameAndDebug(constantCoefficientVirtualMass, 0)
const scalar twoPi(2 *pi)
Namespace for OpenFOAM.
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:124
static const zero Zero
Definition: zero.H:97
List< word > wordList
A List of words.
Definition: fileName.H:54
const dimensionSet dimEnergy
const dimensionSet dimless
dimensionedScalar sin(const dimensionedScalar &ds)
void mag(LagrangianPatchField< scalar > &f, const LagrangianPatchField< Type > &f1)
layerAndWeight min(const layerAndWeight &a, const layerAndWeight &b)
const dimensionSet dimTime
void writeEntry(Ostream &os, const HashTable< T, Key, Hash > &ht)
Definition: HashTableIO.C:96
layerAndWeight max(const layerAndWeight &a, const layerAndWeight &b)
dimensioned< vector > dimensionedVector
Dimensioned vector obtained from generic dimensioned type.
void sqr(LagrangianPatchField< typename outerProduct< Type, Type >::type > &f, const LagrangianPatchField< Type > &f1)
word name(const LagrangianState state)
Return a string representation of a Lagrangian state enumeration.
const dimensionSet dimMass
const dimensionSet dimVelocity
error FatalError
static const label labelMax
Definition: label.H:62
void sqrt(LagrangianPatchField< scalar > &f, const LagrangianPatchField< scalar > &f1)
dimensionedScalar cos(const dimensionedScalar &ds)
static const label labelMin
Definition: label.H:61
randomGenerator rndGen(653213)