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-2023 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 "TimeFunction1.H"
28 #include "Constant.H"
29 #include "mathematicalConstants.H"
30 #include "unitConversion.H"
31 
32 using namespace Foam::constant::mathematical;
33 
34 // * * * * * * * * * * * Private Member Functions * * * * * * * * * * * * * //
35 
36 template<class CloudType>
38 {
39  const word injectionMethod =
40  this->coeffDict().template lookupOrDefault<word>
41  (
42  "injectionMethod",
44  );
45 
46  if (injectionMethod == "point" || injectionMethod == word::null)
47  {
48  injectionMethod_ = imPoint;
49 
50  topoChange();
51  }
52  else if (injectionMethod == "disc")
53  {
54  injectionMethod_ = imDisc;
55 
56  this->coeffDict().lookup("dInner") >> dInner_;
57  this->coeffDict().lookup("dOuter") >> dOuter_;
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->coeffDict().template lookupOrDefault<word>
73  (
74  "flowType",
76  );
77 
78  if (flowType == "constantVelocity" || flowType == word::null)
79  {
80  flowType_ = ftConstantVelocity;
81 
82  Umag_.reset(this->coeffDict());
83  }
84  else if (flowType == "pressureDrivenVelocity")
85  {
86  flowType_ = ftPressureDrivenVelocity;
87 
88  Pinj_.reset(this->coeffDict());
89  }
90  else if (flowType == "flowRateAndDischarge")
91  {
92  flowType_ = ftFlowRateAndDischarge;
93 
94  this->coeffDict().lookup("dInner") >> dInner_;
95  this->coeffDict().lookup("dOuter") >> dOuter_;
96 
97  Cd_.reset(this->coeffDict());
98  }
99  else
100  {
102  << "flowType must be either 'constantVelocity', "
103  << "'pressureDrivenVelocity' or 'flowRateAndDischarge'"
104  << exit(FatalError);
105  }
106 }
107 
108 
109 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
110 
111 template<class CloudType>
113 (
114  const dictionary& dict,
115  CloudType& owner,
116  const word& modelName
117 )
118 :
119  InjectionModel<CloudType>(dict, owner, modelName, typeName),
120  injectionMethod_(imPoint),
121  flowType_(ftConstantVelocity),
122  position_
123  (
125  (
126  owner.db().time(),
127  "position",
128  this->coeffDict()
129  )
130  ),
131  positionIsConstant_(isA<Function1s::Constant<vector>>(position_)),
132  direction_
133  (
135  (
136  owner.db().time(),
137  "direction",
138  this->coeffDict()
139  )
140  ),
141  injectorCoordinates_(barycentric::uniform(NaN)),
142  injectorCell_(-1),
143  injectorTetFace_(-1),
144  injectorTetPt_(-1),
145  duration_(this->readDuration(dict, owner)),
146  massFlowRate_(this->readMassFlowRate(dict, owner, duration_)),
147  parcelsPerSecond_(this->readParcelsPerSecond(dict, owner)),
148  thetaInner_
149  (
150  TimeFunction1<scalar>
151  (
152  owner.db().time(),
153  "thetaInner",
154  this->coeffDict()
155  )
156  ),
157  thetaOuter_
158  (
159  TimeFunction1<scalar>
160  (
161  owner.db().time(),
162  "thetaOuter",
163  this->coeffDict()
164  )
165  ),
166  sizeDistribution_
167  (
169  (
170  this->coeffDict().subDict("sizeDistribution"),
171  owner.rndGen(),
172  this->sizeSampleQ()
173  )
174  ),
175  dInner_(vGreat),
176  dOuter_(vGreat),
177  Umag_(owner.db().time(), "Umag"),
178  Cd_(owner.db().time(), "Cd"),
179  Pinj_(owner.db().time(), "Pinj")
180 {
181  setInjectionMethod();
182 
183  setFlowType();
184 
185  topoChange();
186 }
187 
188 
189 template<class CloudType>
191 (
192  const ConeInjection<CloudType>& im
193 )
194 :
196  injectionMethod_(im.injectionMethod_),
197  flowType_(im.flowType_),
198  position_(im.position_),
199  positionIsConstant_(im.positionIsConstant_),
200  direction_(im.direction_),
201  injectorCoordinates_(im.injectorCoordinates_),
202  injectorCell_(im.injectorCell_),
203  injectorTetFace_(im.injectorTetFace_),
204  injectorTetPt_(im.injectorTetPt_),
205  duration_(im.duration_),
206  massFlowRate_(im.massFlowRate_),
207  parcelsPerSecond_(im.parcelsPerSecond_),
208  thetaInner_(im.thetaInner_),
209  thetaOuter_(im.thetaOuter_),
210  sizeDistribution_(im.sizeDistribution_().clone().ptr()),
211  dInner_(im.dInner_),
212  dOuter_(im.dOuter_),
213  Umag_(im.Umag_),
214  Cd_(im.Cd_),
215  Pinj_(im.Pinj_)
216 {}
217 
218 
219 // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
220 
221 template<class CloudType>
223 {}
224 
225 
226 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
227 
228 template<class CloudType>
230 {
231  if (injectionMethod_ == imPoint && positionIsConstant_)
232  {
233  vector position = position_.value(0);
234  this->findCellAtPosition
235  (
236  position,
237  injectorCoordinates_,
238  injectorCell_,
239  injectorTetFace_,
240  injectorTetPt_
241  );
242  }
243 }
244 
245 
246 template<class CloudType>
248 {
249  return this->SOI_ + duration_;
250 }
251 
252 
253 template<class CloudType>
255 (
256  const scalar time0,
257  const scalar time1
258 )
259 {
260  if (time0 >= 0 && time0 < duration_)
261  {
263  //return floor(parcelsPerSecond_.integral(time0, time1));
264 
265  // Modified calculation to make numbers exact
266  return
267  floor
268  (
269  parcelsPerSecond_.integral(0, time1)
270  - this->parcelsAddedTotal()
271  );
272  }
273  else
274  {
275  return 0;
276  }
277 }
278 
279 
280 template<class CloudType>
282 (
283  const scalar time0,
284  const scalar time1
285 )
286 {
287  if (time0 >= 0 && time0 < duration_)
288  {
289  return massFlowRate_.integral(time0, time1);
290  }
291  else
292  {
293  return 0;
294  }
295 }
296 
297 
298 template<class CloudType>
300 (
301  const label parcelI,
302  const label,
303  const scalar time,
304  barycentric& coordinates,
305  label& celli,
306  label& tetFacei,
307  label& tetPti,
308  label& facei
309 )
310 {
311  Random& rndGen = this->owner().rndGen();
312 
313  const scalar t = time - this->SOI_;
314 
315  switch (injectionMethod_)
316  {
317  case imPoint:
318  {
319  const point pos = position_.value(t);
320  if (positionIsConstant_)
321  {
322  coordinates = injectorCoordinates_;
323  celli = injectorCell_;
324  tetFacei = injectorTetFace_;
325  tetPti = injectorTetPt_;
326  }
327  else
328  {
329  this->findCellAtPosition
330  (
331  pos,
332  coordinates,
333  celli,
334  tetFacei,
335  tetPti,
336  false
337  );
338  }
339  break;
340  }
341  case imDisc:
342  {
343  const scalar beta = twoPi*rndGen.globalScalar01();
344  const scalar frac = rndGen.globalScalar01();
345  const vector n = normalised(direction_.value(t));
346  const vector t1 = normalised(perpendicular(n));
347  const vector t2 = normalised(n ^ t1);
348  const vector tanVec = t1*cos(beta) + t2*sin(beta);
349  const scalar d = sqrt((1 - frac)*sqr(dInner_) + frac*sqr(dOuter_));
350  const point pos = position_.value(t) + d/2*tanVec;
351  this->findCellAtPosition
352  (
353  pos,
354  coordinates,
355  celli,
356  tetFacei,
357  tetPti,
358  false
359  );
360  break;
361  }
362  default:
363  {
364  break;
365  }
366  }
367 }
368 
369 
370 template<class CloudType>
372 (
373  const label parcelI,
374  const label,
375  const scalar time,
376  typename CloudType::parcelType& parcel
377 )
378 {
379  const polyMesh& mesh = this->owner().mesh();
380 
381  Random& rndGen = this->owner().rndGen();
382 
383  const scalar t = time - this->SOI_;
384 
385  // Get the angle from the axis and the vector perpendicular from the axis.
386  // If injecting at a point, then these are calculated from two new random
387  // numbers. If a disc, then these calculations have already been done in
388  // setPositionAndCell, so the angle and vector can be reverse engineered
389  // from the position.
390  scalar theta = vGreat;
391  vector tanVec = vector::max;
392  switch (injectionMethod_)
393  {
394  case imPoint:
395  {
396  const scalar beta = twoPi*rndGen.scalar01();
397  const scalar frac = rndGen.scalar01();
398  const vector n = normalised(direction_.value(t));
399  const vector t1 = normalised(perpendicular(n));
400  const vector t2 = normalised(n ^ t1);
401  tanVec = t1*cos(beta) + t2*sin(beta);
402  theta =
403  degToRad
404  (
405  sqrt
406  (
407  (1 - frac)*sqr(thetaInner_.value(t))
408  + frac*sqr(thetaOuter_.value(t))
409  )
410  );
411  break;
412  }
413  case imDisc:
414  {
415  const scalar r = mag(parcel.position(mesh) - position_.value(t));
416  const scalar frac = (2*r - dInner_)/(dOuter_ - dInner_);
417  tanVec = normalised(parcel.position(mesh) - position_.value(t));
418  theta =
419  degToRad
420  (
421  (1 - frac)*thetaInner_.value(t)
422  + frac*thetaOuter_.value(t)
423  );
424  break;
425  }
426  default:
427  {
428  break;
429  }
430  }
431 
432  // The direction of injection
433  const vector dirVec =
434  normalised
435  (
436  cos(theta)*normalised(direction_.value(t))
437  + sin(theta)*tanVec
438  );
439 
440  // Set the velocity
441  switch (flowType_)
442  {
443  case ftConstantVelocity:
444  {
445  parcel.U() = Umag_.value(t)*dirVec;
446  break;
447  }
448  case ftPressureDrivenVelocity:
449  {
450  const scalar pAmbient = this->owner().pAmbient();
451  const scalar rho = parcel.rho();
452  const scalar Umag = ::sqrt(2*(Pinj_.value(t) - pAmbient)/rho);
453  parcel.U() = Umag*dirVec;
454  break;
455  }
456  case ftFlowRateAndDischarge:
457  {
458  const scalar A = 0.25*pi*(sqr(dOuter_) - sqr(dInner_));
459  const scalar Umag =
460  massFlowRate_.value(t)/(parcel.rho()*Cd_.value(t)*A);
461  parcel.U() = Umag*dirVec;
462  break;
463  }
464  default:
465  {
466  break;
467  }
468  }
469 
470  // Set the particle diameter
471  parcel.d() = sizeDistribution_->sample();
472 }
473 
474 
475 template<class CloudType>
477 {
478  return false;
479 }
480 
481 
482 // ************************************************************************* //
static const Foam::dimensionedScalar A("A", Foam::dimPressure, 611.21)
label n
This injector injects particles in a number of cones. The user specifies a position and a direction t...
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 setPositionAndCell(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 void setProperties(const label parcelI, const label nParcels, const scalar time, typename CloudType::parcelType &parcel)
Set the parcel properties.
virtual label nParcelsToInject(const scalar time0, const scalar time1)
Number of parcels to introduce relative to SOI.
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:79
ParcelType parcelType
Type of parcel the cloud was instantiated for.
Definition: DSMCCloud.H:221
Templated injection model class.
Random number generator.
Definition: Random.H:58
scalar scalar01()
Advance the state and return a scalar sample from a uniform.
Definition: RandomI.H:56
scalar globalScalar01()
Advance the state and return a scalar sample from a uniform.
Definition: Random.C:94
Light wrapper around Function1 to provide a mechanism to update time-based entries.
Definition: TimeFunction1.H:61
static const Form max
Definition: VectorSpace.H:115
A list of keyword definitions, which are a keyword followed by any number of values (e....
Definition: dictionary.H:160
const Type & value() const
Return const reference to value.
Accumulating histogram of values. Specified bin resolution automatic generation of bins.
Definition: distribution.H:61
Mesh consisting of general polyhedral cells.
Definition: polyMesh.H:80
A class for handling words, derived from string.
Definition: word.H:62
static const word null
An empty word.
Definition: word.H:77
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:306
mathematical constants.
const scalar twoPi(2 *pi)
static Type NaN()
Return a primitive with all components set to NaN.
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:124
dimensionedScalar pos(const dimensionedScalar &ds)
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
dimensionedSymmTensor sqr(const dimensionedVector &dv)
dimensionedScalar sin(const dimensionedScalar &ds)
Vector< Cmpt > perpendicular(const Vector< Cmpt > &v)
Definition: VectorI.H:166
bool isA(const Type &t)
Check if a dynamic_cast to typeid is possible.
Definition: typeInfo.H:139
dimensionedScalar sqrt(const dimensionedScalar &ds)
dimensioned< scalar > mag(const dimensioned< Type > &)
tmp< DimensionedField< TypeR, GeoMesh > > New(const tmp< DimensionedField< TypeR, GeoMesh >> &tdf1, const word &name, const dimensionSet &dimensions)
T clone(const T &t)
Definition: List.H:55
Form normalised(const VectorSpace< Form, Cmpt, Ncmpts > &vs)
Definition: VectorSpaceI.H:413
error FatalError
dimensionedScalar cos(const dimensionedScalar &ds)
scalar degToRad(const scalar deg)
Conversion from degrees to radians.
dictionary dict
Unit conversion functions.
Random rndGen(label(0))