ConeInjection.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) 2011-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 "ConeInjection.H"
27 #include "Constant.H"
28 #include "mathematicalConstants.H"
29 
30 using namespace Foam::constant::mathematical;
31 
32 // * * * * * * * * * * * Private Member Functions * * * * * * * * * * * * * //
33 
34 template<class CloudType>
36 {
37  const word injectionMethod =
38  this->typeDict().template lookupOrDefault<word>
39  (
40  "injectionMethod",
42  );
43 
44  if (injectionMethod == "point" || injectionMethod == word::null)
45  {
46  injectionMethod_ = imPoint;
47 
48  topoChange();
49  }
50  else if (injectionMethod == "disc")
51  {
52  injectionMethod_ = imDisc;
53 
54  dInner_ =
55  this->typeDict().template lookup<scalar>("dInner", dimLength);
56  dOuter_ =
57  this->typeDict().template lookup<scalar>("dOuter", dimLength);
58  }
59  else
60  {
62  << "injectionMethod must be either 'point' or 'disc'"
63  << exit(FatalError);
64  }
65 }
66 
67 
68 template<class CloudType>
70 {
71  const word flowType =
72  this->typeDict().template lookupOrDefault<word>
73  (
74  "flowType",
76  );
77 
78  if (flowType == "constantVelocity" || flowType == word::null)
79  {
80  flowType_ = ftConstantVelocity;
81 
82  Umag_.reset
83  (
85  (
86  "Umag",
87  this->owner().time().userUnits(),
89  this->typeDict()
90  ).ptr()
91  );
92  }
93  else if (flowType == "pressureDrivenVelocity")
94  {
95  flowType_ = ftPressureDrivenVelocity;
96 
97  Pinj_.reset
98  (
100  (
101  "Pinj",
102  this->owner().time().userUnits(),
103  dimPressure,
104  this->typeDict()
105  ).ptr()
106  );
107  }
108  else if (flowType == "flowRateAndDischarge")
109  {
110  flowType_ = ftFlowRateAndDischarge;
111 
112  dInner_ =
113  this->typeDict().template lookup<scalar>("dInner", dimLength);
114  dOuter_ =
115  this->typeDict().template lookup<scalar>("dOuter", dimLength);
116 
117  Cd_.reset
118  (
120  (
121  "Cd",
122  this->owner().time().userUnits(),
123  dimless,
124  this->typeDict()
125  ).ptr()
126  );
127  }
128  else
129  {
131  << "flowType must be either 'constantVelocity', "
132  << "'pressureDrivenVelocity' or 'flowRateAndDischarge'"
133  << exit(FatalError);
134  }
135 }
136 
137 
138 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
139 
140 template<class CloudType>
142 (
143  const dictionary& dict,
144  CloudType& owner,
145  const word& modelName
146 )
147 :
149  injectionMethod_(imPoint),
150  flowType_(ftConstantVelocity),
151  position_
152  (
154  (
155  "position",
156  this->owner().time().userUnits(),
157  dimLength,
158  this->typeDict()
159  )
160  ),
161  direction_
162  (
164  (
165  "direction",
166  this->owner().time().userUnits(),
167  dimless,
168  this->typeDict()
169  )
170  ),
171  injectorCoordinates_(barycentric::uniform(NaN)),
172  injectorCell_(-1),
173  injectorTetFace_(-1),
174  injectorTetPt_(-1),
175  duration_(this->readDuration(dict, owner)),
176  massFlowRate_(this->readMassFlowRate(dict, owner, duration_)),
177  parcelsPerSecond_(this->readParcelsPerSecond(dict, owner)),
178  thetaInner_
179  (
180  Function1<scalar>::New
181  (
182  "thetaInner",
183  this->owner().time().userUnits(),
184  units::degrees,
185  this->typeDict()
186  )
187  ),
188  thetaOuter_
189  (
190  Function1<scalar>::New
191  (
192  "thetaOuter",
193  this->owner().time().userUnits(),
194  units::degrees,
195  this->typeDict()
196  )
197  ),
198  sizeDistribution_
199  (
201  (
202  dimLength,
203  this->typeDict().subDict("sizeDistribution"),
204  this->sizeSampleQ(),
205  owner.rndGen().generator()
206  )
207  ),
208  dInner_(vGreat),
209  dOuter_(vGreat),
210  Umag_(nullptr),
211  Cd_(nullptr),
212  Pinj_(nullptr)
213 {
214  setInjectionMethod();
215 
216  setFlowType();
217 
218  topoChange();
219 }
220 
221 
222 template<class CloudType>
224 (
225  const ConeInjection<CloudType>& im
226 )
227 :
229  injectionMethod_(im.injectionMethod_),
230  flowType_(im.flowType_),
231  position_(im.position_, false),
232  direction_(im.direction_, false),
233  injectorCoordinates_(im.injectorCoordinates_),
234  injectorCell_(im.injectorCell_),
235  injectorTetFace_(im.injectorTetFace_),
236  injectorTetPt_(im.injectorTetPt_),
237  duration_(im.duration_),
238  massFlowRate_(im.massFlowRate_, false),
239  parcelsPerSecond_(im.parcelsPerSecond_, false),
240  thetaInner_(im.thetaInner_, false),
241  thetaOuter_(im.thetaOuter_, false),
242  sizeDistribution_(im.sizeDistribution_, false),
243  dInner_(im.dInner_),
244  dOuter_(im.dOuter_),
245  Umag_(im.Umag_, false),
246  Cd_(im.Cd_, false),
247  Pinj_(im.Pinj_, false)
248 {}
249 
250 
251 // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
252 
253 template<class CloudType>
255 {}
256 
257 
258 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
259 
260 template<class CloudType>
262 {
263  if (injectionMethod_ == imPoint && position_->constant())
264  {
265  vector position = position_->value(0);
266 
267  this->findCellAtPosition
268  (
269  meshSearch::New(this->owner().mesh()),
270  position,
271  injectorCoordinates_,
272  injectorCell_,
273  injectorTetFace_,
274  injectorTetPt_
275  );
276  }
277 }
278 
279 
280 template<class CloudType>
282 {
283  return this->SOI_ + duration_;
284 }
285 
286 
287 template<class CloudType>
289 (
290  const scalar t0,
291  const scalar t1
292 )
293 {
294  if (t1 >= 0 && t0 < duration_)
295  {
296  return parcelsPerSecond_->integral(max(t0, 0), min(t1, duration_));
297  }
298  else
299  {
300  return 0;
301  }
302 }
303 
304 
305 template<class CloudType>
307 (
308  const scalar t0,
309  const scalar t1
310 )
311 {
312  if (t1 >= 0 && t0 < duration_)
313  {
314  return massFlowRate_->integral(max(t0, 0), min(t1, duration_));
315  }
316  else
317  {
318  return 0;
319  }
320 }
321 
322 
323 template<class CloudType>
325 (
326  const meshSearch& searchEngine,
327  const label parcelI,
328  const label,
329  const scalar time,
331  label& celli,
332  label& tetFacei,
333  label& tetPti,
334  label& facei
335 )
336 {
337  randomGenerator& rndGen = this->owner().rndGen();
338 
339  const scalar t = time - this->SOI_;
340 
341  switch (injectionMethod_)
342  {
343  case imPoint:
344  {
345  const point pos = position_->value(t);
346  if (position_->constant())
347  {
348  coordinates = injectorCoordinates_;
349  celli = injectorCell_;
350  tetFacei = injectorTetFace_;
351  tetPti = injectorTetPt_;
352  }
353  else
354  {
355  this->findCellAtPosition
356  (
357  searchEngine,
358  pos,
359  coordinates,
360  celli,
361  tetFacei,
362  tetPti,
363  false
364  );
365  }
366  break;
367  }
368  case imDisc:
369  {
370  const scalar beta = twoPi*this->globalScalar01(rndGen);
371  const scalar frac = this->globalScalar01(rndGen);
372  const vector n = normalised(direction_->value(t));
373  const vector t1 = normalised(perpendicular(n));
374  const vector t2 = normalised(n ^ t1);
375  const vector tanVec = t1*cos(beta) + t2*sin(beta);
376  const scalar d = sqrt((1 - frac)*sqr(dInner_) + frac*sqr(dOuter_));
377  const point pos = position_->value(t) + d/2*tanVec;
378  this->findCellAtPosition
379  (
380  searchEngine,
381  pos,
382  coordinates,
383  celli,
384  tetFacei,
385  tetPti,
386  false
387  );
388  break;
389  }
390  default:
391  {
392  break;
393  }
394  }
395 }
396 
397 
398 template<class CloudType>
400 (
401  const label parcelI,
402  const label,
403  const scalar time,
404  typename CloudType::parcelType::trackingData& td,
405  typename CloudType::parcelType& parcel
406 )
407 {
408  const polyMesh& mesh = this->owner().mesh();
409 
410  randomGenerator& rndGen = this->owner().rndGen();
411 
412  const scalar t = time - this->SOI_;
413 
414  // Get the angle from the axis and the vector perpendicular from the axis.
415  // If injecting at a point, then these are calculated from two new random
416  // numbers. If a disc, then these calculations have already been done in
417  // setPositionAndCell, so the angle and vector can be reverse engineered
418  // from the position.
419  scalar theta = vGreat;
420  vector tanVec = vector::max;
421  switch (injectionMethod_)
422  {
423  case imPoint:
424  {
425  const scalar beta = twoPi*rndGen.scalar01();
426  const scalar frac = rndGen.scalar01();
427  const vector n = normalised(direction_->value(t));
428  const vector t1 = normalised(perpendicular(n));
429  const vector t2 = normalised(n ^ t1);
430  tanVec = t1*cos(beta) + t2*sin(beta);
431  theta =
432  sqrt
433  (
434  (1 - frac)*sqr(thetaInner_->value(t))
435  + frac*sqr(thetaOuter_->value(t))
436  );
437  break;
438  }
439  case imDisc:
440  {
441  const scalar r = mag(parcel.position(mesh) - position_->value(t));
442  const scalar frac = (2*r - dInner_)/(dOuter_ - dInner_);
443  tanVec = normalised(parcel.position(mesh) - position_->value(t));
444  theta =
445  (1 - frac)*thetaInner_->value(t)
446  + frac*thetaOuter_->value(t);
447  break;
448  }
449  default:
450  {
451  break;
452  }
453  }
454 
455  // The direction of injection
456  const vector dirVec =
457  normalised
458  (
459  cos(theta)*normalised(direction_->value(t))
460  + sin(theta)*tanVec
461  );
462 
463  // Set the velocity
464  switch (flowType_)
465  {
466  case ftConstantVelocity:
467  {
468  parcel.U() = Umag_->value(t)*dirVec;
469  break;
470  }
471  case ftPressureDrivenVelocity:
472  {
473  const scalar pAmbient = this->owner().pAmbient();
474  const scalar rho = parcel.rho();
475  const scalar Umag = ::sqrt(2*(Pinj_->value(t) - pAmbient)/rho);
476  parcel.U() = Umag*dirVec;
477  break;
478  }
479  case ftFlowRateAndDischarge:
480  {
481  const scalar A = 0.25*pi*(sqr(dOuter_) - sqr(dInner_));
482  const scalar Umag =
483  massFlowRate_->value(t)/(parcel.rho()*Cd_->value(t)*A);
484  parcel.U() = Umag*dirVec;
485  break;
486  }
487  default:
488  {
489  break;
490  }
491  }
492 
493  // Set the particle diameter
494  parcel.d() = sizeDistribution_->sample();
495 }
496 
497 
498 template<class CloudType>
500 {
501  return false;
502 }
503 
504 
505 // ************************************************************************* //
label n
This injector injects particles in a number of cones. The user specifies a position and a direction t...
virtual scalar nParcelsToInject(const scalar time0, const scalar time1)
Number of parcels to introduce relative to SOI.
virtual ~ConeInjection()
Destructor.
virtual void topoChange()
Set injector locations when mesh is updated.
ConeInjection(const dictionary &dict, CloudType &owner, const word &modelName)
Construct from dictionary.
virtual void setProperties(const label parcelI, const label nParcels, const scalar time, typename CloudType::parcelType::trackingData &td, typename CloudType::parcelType &parcel)
Set the parcel properties.
virtual void setPositionAndCell(const meshSearch &searchEngine, const label parcelI, const label nParcels, const scalar time, barycentric &coordinates, label &celli, label &tetFacei, label &tetPti, label &facei)
Set the injection position and owner cell, tetFace and tetPt.
virtual bool fullyDescribed() const
Flag to identify whether model fully describes the parcel.
scalar timeEnd() const
Return the end-of-injection time.
virtual scalar massToInject(const scalar time0, const scalar time1)
Parcel mass to introduce relative to SOI.
Templated base class for dsmc cloud.
Definition: DSMCCloud.H:80
ParcelType parcelType
Type of parcel the cloud was instantiated for.
Definition: DSMCCloud.H:225
Run-time selectable general function of one variable.
Definition: Function1.H:62
static autoPtr< Function1< scalar > > New(const word &name, const Function1s::unitSets &units, const dictionary &dict)
Select from dictionary.
Definition: Function1New.C:32
Templated injection model class.
static const Form max
Definition: VectorSpace.H:120
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.
Base class for statistical distributions.
Definition: distribution.H:76
Mesh object that implements searches within the local cells and faces.
Definition: meshSearch.H:59
static const meshSearch & New(const polyMesh &mesh, const pointInCellShapes=pointInCellShapes::tets)
Lookup or construct from mesh and cell decomposition option.
Definition: meshSearch.C:61
Mesh consisting of general polyhedral cells.
Definition: polyMesh.H:78
Random number generator.
scalar scalar01()
Return a scalar uniformly distributed between zero and one.
Template function which returns the un-mangled name of a given type. Useful for types which do not ha...
A class for handling words, derived from string.
Definition: word.H:63
static const word null
An empty word.
Definition: word.H:78
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
rho
Definition: pEqn.H:1
mathematical constants.
const scalar twoPi(2 *pi)
const dimensionSet time
static const coefficient A("A", dimPressure, 611.21)
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
point position(const polyMesh &mesh, const barycentric &coordinates, const label celli, const label facei, const label faceTrii, const scalar stepFraction)
Return the position given the coordinates and tet topology.
Definition: trackingI.H:224
const unitSet degrees
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:124
dimensionedScalar pos(const dimensionedScalar &ds)
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)
const dimensionSet & dimVelocity
Definition: dimensions.C:154
dimensionSet normalised(const dimensionSet &)
Definition: dimensionSet.C:509
dimensioned< Type > min(const DimensionedField< Type, GeoMesh, PrimitiveField > &df)
const dimensionSet & dimPressure
Definition: dimensions.C:163
tmp< DimensionedField< scalar, GeoMesh, Field > > mag(const DimensionedField< Type, GeoMesh, PrimitiveField > &df)
error FatalError
dimensionSet perpendicular(const dimensionSet &)
Definition: dimensionSet.C:515
void sqrt(LagrangianPatchField< scalar > &f, const LagrangianPatchField< scalar > &f1)
tmp< DimensionedField< TypeR, GeoMesh, Field > > New(const tmp< DimensionedField< TypeR, GeoMesh, Field >> &tdf1, const word &name, const dimensionSet &dimensions)
dimensionedScalar cos(const dimensionedScalar &ds)
dimensioned< Type > max(const DimensionedField< Type, GeoMesh, PrimitiveField > &df)
dictionary dict
randomGenerator rndGen(653213)