diskInjection.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-2026 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 "LagrangianMesh.H"
27 #include "LagrangianSubFieldsFwd.H"
28 #include "diskInjection.H"
30 #include "LagrangianFields.H"
31 #include "mathematicalConstants.H"
32 
33 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
34 
35 namespace Foam
36 {
37 namespace Lagrangian
38 {
41 }
42 }
43 
44 
45 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
46 
47 void Foam::Lagrangian::diskInjection::readCoeffs(const dictionary& modelDict)
48 {
49  centre_.reset
50  (
52  (
53  "centre",
54  mesh().time().userUnits(),
55  dimLength,
56  modelDict
57  ).ptr()
58  );
59 
60  axis_.reset
61  (
63  (
64  "axis",
65  mesh().time().userUnits(),
66  dimless,
67  modelDict
68  ).ptr()
69  );
70 
71  const bool haveDiameter = modelDict.found("diameter");
72  const bool haveInnerDiameter = modelDict.found("innerDiameter");
73  const bool haveOuterDiameter = modelDict.found("outerDiameter");
74 
75  if (haveDiameter == (haveInnerDiameter || haveOuterDiameter))
76  {
77  FatalIOErrorInFunction(modelDict)
78  << "keywords diameter and innerDiameter/outerDiameter are both "
79  << (haveDiameter ? "" : "un") << "defined in "
80  << "dictionary " << modelDict.name()
81  << exit(FatalIOError);
82  }
83 
84  if (haveInnerDiameter != haveOuterDiameter)
85  {
86  FatalIOErrorInFunction(modelDict)
87  << "keywords innerDiameter and outerDiameter are not both defined "
88  << "in dictionary " << modelDict.name()
89  << exit(FatalIOError);
90  }
91 
92  if (haveDiameter)
93  {
94  innerDiameter_ = 0;
95  outerDiameter_ = modelDict.lookup<scalar>("diameter", dimLength);
96  }
97  else
98  {
99  innerDiameter_ = modelDict.lookup<scalar>("innerDiameter", dimLength);
100  outerDiameter_ = modelDict.lookup<scalar>("outerDiameter", dimLength);
101  }
102 
103  numberRate_.reset
104  (
106  (
107  "numberRate",
108  mesh().time().userUnits(),
109  dimRate,
110  modelDict
111  ).ptr()
112  );
113 
114  numberDeferred_ = 0;
115 }
116 
117 
118 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
119 
121 (
122  const word& name,
123  const LagrangianMesh& mesh,
124  const dictionary& modelDict,
125  const dictionary& stateDict
126 )
127 :
129  centre_(nullptr),
130  axis_(nullptr),
131  innerDiameter_(NaN),
132  outerDiameter_(NaN),
133  numberRate_(nullptr),
134  numberDeferred_(stateDict.lookupOrDefault<scalar>("numberDeferred", 0)),
135  rndGen_("rndGen", stateDict, name, true),
136  timeIndex_(-1)
137 {
138  readCoeffs(modelDict);
139 }
140 
141 
142 // * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * * //
143 
145 {
146  return
148  (
149  dimArea,
151  *(sqr(outerDiameter_) - sqr(innerDiameter_))
152  );
153 }
154 
155 
158 {
159  if (!axisPtr_.valid())
160  {
162  << "Axis requested outside of the injection"
163  << exit(FatalError);
164  }
165 
166  return axisPtr_();
167 }
168 
169 
172 {
173  if (!rFracPtr_.valid())
174  {
176  << "Radius fraction requested outside of the injection"
177  << exit(FatalError);
178  }
179 
180  return rFracPtr_();
181 }
182 
183 
186 {
187  if (!radialPtr_.valid())
188  {
190  << "Axis requested outside of the injection"
191  << exit(FatalError);
192  }
193 
194  return radialPtr_();
195 }
196 
197 
199 (
201  const LagrangianSubMesh&
202 ) const
203 {
204  const scalar t1 = mesh.time().value();
205  const scalar t0 = t1 - mesh.time().deltaT().value();
206 
207  // Restart the generator if necessary and set the time index up to date
208  rndGen_.start(timeIndex_ == time().timeIndex());
209  timeIndex_ = time().timeIndex();
210 
211  // Calculate the number of particles to inject. Round down to get an
212  // integer number. Store the excess to apply at a later time.
213  const scalar number = numberRate_->integral(t0, t1) + numberDeferred_;
214  const label numberInt = floor(number);
215  numberDeferred_ = number - numberInt;
216 
217  // Inject at random times throughout the time-step
218  scalarField fraction(rndGen_.scalar01(numberInt));
219 
220  // Evaluate the variable centre and axis, and create radial vectors to
221  // complete the local coordinate system
222  tmp<pointField> centre(centre_->value(t0 + fraction*(t1 - t0)));
223  tmp<vectorField> axis(normalised(axis_->value(t0 + fraction*(t1 - t0))));
224  tmp<vectorField> radial1(normalised(perpendicular(axis())));
225  tmp<vectorField> radial2(axis() ^ radial1());
226 
227  // Create random radii within and angles around the disk
228  tmp<scalarField> rFrac(rndGen_.scalar01(numberInt));
230  (
231  sqrt
232  (
233  (1 - rFrac())*sqr(innerDiameter_/2)
234  + rFrac()*sqr(outerDiameter_/2)
235  )
236  );
237  tmp<scalarField> phi
238  (
239  constant::mathematical::twoPi*rndGen_.scalar01(numberInt)
240  );
241 
242  // Evaluate the radial vector
243  tmp<vectorField> radial(cos(phi())*radial1 + sin(phi())*radial2);
244  phi.clear();
245 
246  // Evaluate the positions
247  const pointField positions(centre + r*radial());
248 
249  // Locate within the mesh
251  labelField celli(number, -1), facei(number), faceTrii(number);
252  const List<LagrangianMesh::location> locations =
253  mesh.locate
254  (
255  positions,
256  coordinates,
257  celli,
258  facei,
259  faceTrii,
260  fraction
261  );
262 
263  // Check for any failures
264  checkLocation(locations, positions);
265 
266  // Remove particles not on this process
267  filter
268  (
269  coordinates,
270  celli,
271  facei,
272  faceTrii,
273  fraction,
274  axis.ref(),
275  rFrac.ref(),
276  radial.ref()
277  );
278 
279  // Construct the injection sub-mesh
280  const LagrangianSubMesh injectionMesh = mesh.injectionMesh(numberInt);
281 
282  // Cache the geometry for use by source conditions
283  axisPtr_.set
284  (
286  (
287  "axis",
288  injectionMesh,
289  dimless,
290  axis
291  ).ptr()
292  );
293  rFracPtr_.set
294  (
296  (
297  "rFrac",
298  injectionMesh,
299  dimless,
300  rFrac
301  ).ptr()
302  );
303  radialPtr_.set
304  (
306  (
307  "radial",
308  injectionMesh,
309  dimless,
310  radial
311  ).ptr()
312  );
313 
314  // Inject particles
315  mesh.inject
316  (
317  *this,
318  injectionMesh,
319  coordinates,
320  celli,
321  facei,
322  faceTrii,
324  fraction
325  );
326 
327  // Clean up
328  axisPtr_.clear();
329  rFracPtr_.clear();
330  radialPtr_.clear();
331 
332  return injectionMesh;
333 }
334 
335 
337 {
338  if (LagrangianInjection::read(modelDict))
339  {
340  readCoeffs(modelDict);
341  return true;
342  }
343  else
344  {
345  return false;
346  }
347 }
348 
349 
351 {
353 
354  writeEntry(os, "numberDeferred", numberDeferred_);
355  writeEntry(os, "rndGen", rndGen_);
356 }
357 
358 
359 // ************************************************************************* //
Macros for easy insertion into run-time selection tables.
Field with dimensions and associated with geometry type GeoMesh which is used to size the field and a...
static tmp< DimensionedField< Type, GeoMesh, PrimitiveField > > New(const word &name, const GeoMesh &mesh, const dimensionSet &, const PrimitiveField< Type > &)
Return a temporary field constructed from name, mesh,.
static autoPtr< Function1< Type > > New(const word &name, const Function1s::unitSets &units, const dictionary &dict)
Select from dictionary.
Definition: Function1New.C:32
bool set(const Key &, const T &newElmt)
Set a new hashedEntry, overwriting existing entries.
Definition: HashTableI.H:91
Base class for Lagrangian injections. Minimal wrapper over LagrangianSource. Implements some utility ...
Class containing Lagrangian geometry and topology.
const Time & time() const
Return time.
static const word fractionName
Name of the tracked fraction field.
Base class for Lagrangian models.
const Time & time() const
Return time.
virtual bool read(const dictionary &modelDict)
Read dictionary.
const LagrangianMesh & mesh() const
The mesh.
Mesh that relates to a sub-section of a Lagrangian mesh. This is used to construct fields that relate...
Disk injection model. This injects particles continuously over a disk with a given number rate....
const LagrangianSubVectorField & radial() const
Access the cached radial vectors. Only valid during injection.
virtual void writeState(Ostream &os) const
Write state.
LagrangianSubMesh modify(LagrangianMesh &mesh, const LagrangianSubMesh &) const
Create new elements in the Lagrangian mesh.
diskInjection(const word &name, const LagrangianMesh &mesh, const dictionary &modelDict, const dictionary &stateDict)
Construct from components.
const LagrangianSubVectorField & axis() const
Access the cached axes. Only valid during injection.
const dimensionedScalar area() const
Return the area of the disk.
virtual bool read(const dictionary &modelDict)
Read dictionary.
const LagrangianSubScalarField & rFrac() const
Access the cached radius fractions. Only valid during injection.
A 1D array of objects of type <T>, where the size of the vector is known and used for subscript bound...
Definition: List.H:91
An Ostream is an abstract base class for all output systems (streams, files, token lists,...
Definition: Ostream.H:57
dimensionedScalar deltaT() const
Return time step.
Definition: TimeStateI.H:46
const unitSet & userUnits() const
Return the user-time unit conversion.
Definition: Time.C:877
A list of keywords followed by any number of values (e.g. words and numbers) or sub-dictionaries.
Definition: dictionary.H:162
const Type & value() const
Return const reference to value.
const Time & time() const
Return the top-level database.
Definition: fvMesh.H:433
void clear()
Remove all regIOobject owned by the registry.
bool writeState(const bool write) const
Write state.
Definition: stateModel.C:104
A class for managing temporary objects.
Definition: tmp.H:55
void clear() const
If object pointer points to valid object:
Definition: tmpI.H:253
T & ref() const
Return non-const reference or generate a fatal error.
Definition: tmpI.H:197
A class for handling words, derived from string.
Definition: word.H:63
Foam::fvMesh mesh(Foam::IOobject(regionName, runTime.name(), runTime, Foam::IOobject::MUST_READ), false)
#define FatalIOErrorInFunction(ios)
Report an error message using Foam::FatalIOError.
Definition: error.H:346
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:334
defineTypeNameAndDebug(collisionPhaseTransfer, 0)
addToRunTimeSelectionTable(LagrangianModel, collisionPhaseTransfer, dictionary)
const scalar twoPi(2 *pi)
const dimensionSet time
barycentric coordinates(const polyMesh &mesh, const point &position, const label celli, const label facei, const label faceTrii, const scalar stepFraction)
Return the coordinates given the position and tet topology.
Definition: tracking.C:1258
const unitSet fraction
Namespace for OpenFOAM.
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:124
const dimensionSet & dimless
Definition: dimensions.C:138
intWM_LABEL_SIZE_t label
A label is an int32_t or int64_t as specified by the pre-processor macro WM_LABEL_SIZE.
Definition: label.H:59
const dimensionSet & dimLength
Definition: dimensions.C:141
dimensionedScalar sin(const dimensionedScalar &ds)
tmp< DimensionedField< typename outerProduct< Type, Type >::type, GeoMesh, Field >> sqr(const DimensionedField< Type, GeoMesh, PrimitiveField > &df)
dimensionSet normalised(const dimensionSet &)
Definition: dimensionSet.C:509
IOerror FatalIOError
const dimensionSet & dimRate
Definition: dimensions.C:152
word name(const LagrangianState state)
Return a string representation of a Lagrangian state enumeration.
error FatalError
const dimensionSet & dimArea
Definition: dimensions.C:149
dimensionSet perpendicular(const dimensionSet &)
Definition: dimensionSet.C:515
void sqrt(LagrangianPatchField< scalar > &f, const LagrangianPatchField< scalar > &f1)
void writeEntry(Ostream &os, const word &key, const DimensionedFieldFunction< DimensionedFieldType > &f)
dimensionedScalar cos(const dimensionedScalar &ds)
dimensioned< scalar > dimensionedScalar
Dimensioned scalar obtained from generic dimensioned type.
label timeIndex
Definition: getTimeIndex.H:4