InjectionModel.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 "InjectionModel.H"
27 #include "mathematicalConstants.H"
28 #include "meshTools.H"
29 #include "volFields.H"
30 #include "Scale.H"
31 
32 using namespace Foam::constant::mathematical;
33 
34 // * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * * //
35 
36 template<class CloudType>
38 (
39  const dictionary& dict,
40  CloudType& owner
41 )
42 {
43  if (dict.found("nParticle"))
44  {
45  if (dict.found("massTotal"))
46  {
48  << "If nParticle is specified then the massTotal "
49  << "setting has no effect " << endl;
50  }
51 
52  return NaN;
53  }
54 
55  if (owner.solution().steadyState())
56  {
58  << "The " << type() << " injection model is not compatible with "
59  << "steady state solution"
60  << exit(FatalError);
61 
62  return NaN;
63  }
64 
65  return dict.lookup<scalar>("massTotal");
66 }
67 
68 
69 template<class CloudType>
71 (
72  const dictionary& dict,
73  CloudType& owner
74 )
75 {
76  const Time& time = owner.mesh().time();
77 
78  if (owner.solution().steadyState())
79  {
80  return vGreat;
81  }
82 
83  return
84  time.userTimeToTime
85  (
86  this->coeffDict().template lookup<scalar>("duration")
87  );
88 }
89 
90 
91 template<class CloudType>
94 (
95  const dictionary& dict,
96  CloudType& owner,
97  const scalar duration
98 )
99 {
100  const Time& time = owner.mesh().time();
101 
102  const bool haveMassFlowRate = dict.found("massFlowRate");
103  const bool haveMassTotal = dict.found("massTotal");
104 
105  if (dict.found("nParticle"))
106  {
107  if (haveMassFlowRate || haveMassTotal)
108  {
110  << "If nParticle is specified then massFlowRate and massTotal "
111  << "settings have no effect " << endl;
112  }
113 
114  return
116  (
117  time,
119  );
120  }
121 
122  if (owner.solution().steadyState() && haveMassTotal)
123  {
125  << "Cannot specify the massTotal of a steady injection. Use "
126  << "massFlowRate instead." << exit(FatalIOError);
127  }
128 
129  if (haveMassFlowRate && haveMassTotal)
130  {
132  << "Cannot specify both massFlowRate and massTotal. Use one or "
133  << "the other." << exit(FatalIOError);
134  }
135 
136  if (owner.solution().steadyState() || haveMassFlowRate)
137  {
138  return TimeFunction1<scalar>(time, "massFlowRate", dict);
139  }
140 
141  const scalar massTotal = dict.lookup<scalar>("massTotal");
142 
143  if (!dict.found("flowRateProfile"))
144  {
145  return
147  (
148  time,
149  Function1s::Constant<scalar>("massFlowRate", massTotal/duration)
150  );
151  }
152 
153  autoPtr<Function1<scalar>> flowRateProfile =
154  Function1<scalar>::New("flowRateProfile", dict);
155 
156  const scalar sumFlowRateProfile = flowRateProfile->integral(0, duration);
157 
158  return
160  (
161  time,
163  (
164  "massFlowRate",
165  Function1s::Constant<scalar>("m", massTotal/sumFlowRateProfile),
166  Function1s::Constant<scalar>("one", scalar(1)),
167  flowRateProfile()
168  )
169  );
170 }
171 
172 
173 template<class CloudType>
176 (
177  const dictionary& dict,
178  CloudType& owner
179 )
180 {
181  const Time& time = owner.mesh().time();
182 
183  return TimeFunction1<scalar>(time, "parcelsPerSecond", dict);
184 }
185 
186 
187 template<class CloudType>
189 {
190  forAll(this->owner().injectors(), i)
191  {
192  if (this->owner().injectors()(i) == this)
193  {
194  return i;
195  }
196  }
197 
198  return -1;
199 }
200 
201 
202 template<class CloudType>
204 (
205  const point& position,
206  barycentric& coordinates,
207  label& celli,
208  label& tetFacei,
209  label& tetPti,
210  bool errorOnNotFound
211 )
212 {
213  // Subroutine for finding the cell
214  auto findProcAndCell = [this](const point& pos)
215  {
216  // Find the containing cell
217  label celli = this->owner().mesh().findCell(pos);
218 
219  // Synchronise so only a single processor finds this position
220  label proci = celli >= 0 ? Pstream::myProcNo() : -1;
221  reduce(proci, maxOp<label>());
222  if (proci != Pstream::myProcNo())
223  {
224  celli = -1;
225  }
226 
227  return labelPair(proci, celli);
228  };
229 
230  point pos = position;
231 
232  // Try and find the cell at the given position
233  const labelPair procAndCelli = findProcAndCell(pos);
234  label proci = procAndCelli.first();
235  celli = procAndCelli.second();
236 
237  // Didn't find it. The point may be awkwardly on an edge or face. Try
238  // again, but move the point into its nearest cell a little bit.
239  if (proci == -1)
240  {
241  pos += small*(this->owner().mesh().C()[celli] - pos);
242  const labelPair procAndCelli = findProcAndCell(pos);
243  proci = procAndCelli.first();
244  celli = procAndCelli.second();
245  }
246 
247  // Didn't find it. Error or return false.
248  if (proci == -1)
249  {
250  if (errorOnNotFound)
251  {
253  << "Cannot find parcel injection cell. "
254  << "Parcel position = " << position << nl
255  << exit(FatalError);
256  }
257 
258  return false;
259  }
260 
261  // Found it. Construct the barycentric coordinates.
262  if (proci == Pstream::myProcNo())
263  {
264  particle p(this->owner().mesh(), pos, celli);
265  coordinates = p.coordinates();
266  celli = p.cell();
267  tetFacei = p.tetFace();
268  tetPti = p.tetPt();
269  }
270 
271  return true;
272 }
273 
274 
275 template<class CloudType>
277 (
278  typename CloudType::parcelType::trackingData& td,
279  typename CloudType::parcelType& parcel
280 )
281 {
282  const vector d = parcel.deviationFromMeshCentre(td.mesh);
283 
284  if (d == vector::zero)
285  {
286  return;
287  }
288 
289  const label facei = parcel.face();
290 
291  // If the parcel is not on a face, then just track it to the mesh centre
292  if (facei == -1)
293  {
294  parcel.track(td.mesh, - d, 0);
295  }
296 
297  // If the parcel is on a face, then track in two steps, going slightly into
298  // the current cell. This prevents a boundary hit from ending the track
299  // prematurely.
300  if (facei != -1)
301  {
302  const vector pc =
303  td.mesh.cellCentres()[parcel.cell()] - parcel.position(td.mesh);
304 
305  parcel.track(td.mesh, - d/2 + rootSmall*pc, 0);
306  parcel.track(td.mesh, - d/2 - rootSmall*pc, 0);
307  }
308 
309  // Restore any face-association that got changed during tracking
310  parcel.face() = facei;
311 }
312 
313 
314 template<class CloudType>
316 {
317  switch (uniformParcelSize_)
318  {
319  case uniformParcelSize::nParticle:
320  return 0;
321  case uniformParcelSize::surfaceArea:
322  return 2;
323  case uniformParcelSize::volume:
324  return 3;
325  }
326 
327  return -labelMax;
328 }
329 
330 
331 template<class CloudType>
333 (
334  PtrList<parcelType>& parcelPtrs,
335  const scalar mass
336 ) const
337 {
338  auto size = [&](const parcelType& p)
339  {
340  switch (uniformParcelSize_)
341  {
342  case uniformParcelSize::nParticle:
343  return scalar(1);
344  case uniformParcelSize::surfaceArea:
345  return p.areaS();
346  case uniformParcelSize::volume:
347  return p.volume();
348  }
349  return NaN;
350  };
351 
352  // Determine the total mass and size of all created particles
353  scalar sumMassBySize = 0;
354  forAll(parcelPtrs, parceli)
355  {
356  if (parcelPtrs.set(parceli))
357  {
358  const parcelType& p = parcelPtrs[parceli];
359  sumMassBySize += p.mass()/size(p);
360  }
361  }
362 
363  reduce(sumMassBySize, sumOp<scalar>());
364 
365  // Set the numbers of particles on each parcel
366  forAll(parcelPtrs, parceli)
367  {
368  if (parcelPtrs.set(parceli))
369  {
370  parcelType& p = parcelPtrs[parceli];
371  p.nParticle() = mass/size(p)/sumMassBySize;
372  }
373  }
374 
375  // Check that the constraints are correct
376  if (debug)
377  {
378  scalar massN = 0, minSizeN = vGreat, maxSizeN = -vGreat;
379  forAll(parcelPtrs, parceli)
380  {
381  if (parcelPtrs.set(parceli))
382  {
383  const parcelType& p = parcelPtrs[parceli];
384  massN += p.nParticle()*p.mass();
385  minSizeN = min(minSizeN, p.nParticle()*size(p));
386  maxSizeN = max(minSizeN, p.nParticle()*size(p));
387  }
388  }
389 
390  reduce(massN, sumOp<scalar>());
391 
392  if (mag(massN - mass) > rootSmall*(massN + mass)/2)
393  {
395  << "Parcels do not have the required mass"
396  << exit(FatalError);
397  }
398 
399  reduce(minSizeN, minOp<scalar>());
400  reduce(maxSizeN, maxOp<scalar>());
401 
402  if (maxSizeN - minSizeN > rootSmall*(maxSizeN + minSizeN)/2)
403  {
405  << "Parcel sizes are not uniform"
406  << exit(FatalError);
407  }
408  }
409 }
410 
411 
412 template<class CloudType>
414 (
415  const label nParcelsAdded,
416  const scalar massAdded
417 )
418 {
419  const label allNParcelsAdded = returnReduce(nParcelsAdded, sumOp<label>());
420 
421  if (allNParcelsAdded > 0)
422  {
423  Info<< nl
424  << "Cloud: " << this->owner().name()
425  << " injector: " << this->modelName() << nl
426  << " Added " << allNParcelsAdded << " new parcels" << nl << endl;
427  }
428 
429  // Increment total number of parcels added
430  parcelsAddedTotal_ += allNParcelsAdded;
431 
432  // Increment total mass injected
433  massInjected_ += returnReduce(massAdded, sumOp<scalar>());
434 
435  // Update time for start of next injection
436  time0_ = this->owner().db().time().value();
437 
438  // Increment number of injections
439  nInjections_++;
440 }
441 
442 
443 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
444 
445 template<class CloudType>
447 :
449  SOI_(0),
450  massInjected_(this->template getModelProperty<scalar>("massInjected")),
451  nInjections_(this->template getModelProperty<label>("nInjections")),
452  parcelsAddedTotal_
453  (
454  this->template getModelProperty<scalar>("parcelsAddedTotal")
455  ),
456  nParticleFixed_(-vGreat),
457  uniformParcelSize_(uniformParcelSize::nParticle),
458  time0_(0),
459  timeStep0_(this->template getModelProperty<scalar>("timeStep0"))
460 {}
461 
462 
463 template<class CloudType>
465 (
466  const dictionary& dict,
467  CloudType& owner,
468  const word& modelName,
469  const word& modelType
470 )
471 :
472  CloudSubModelBase<CloudType>(modelName, owner, dict, typeName, modelType),
473  SOI_(0),
474  massInjected_(this->template getModelProperty<scalar>("massInjected")),
475  nInjections_(this->template getModelProperty<scalar>("nInjections")),
476  parcelsAddedTotal_
477  (
478  this->template getModelProperty<scalar>("parcelsAddedTotal")
479  ),
480  nParticleFixed_(dict.lookupOrDefault<scalar>("nParticle", -vGreat)),
481  uniformParcelSize_
482  (
483  uniformParcelSizeNames_
484  [
485  !dict.found("parcelBasisType") && nParticleFixed_ > 0
486  ? dict.lookupOrDefault<word>
487  (
488  "uniformParcelSize",
489  uniformParcelSizeNames_[uniformParcelSize::nParticle]
490  )
491  : dict.lookup<word>("uniformParcelSize")
492  ]
493  ),
494  time0_(owner.db().time().value()),
495  timeStep0_(this->template getModelProperty<scalar>("timeStep0"))
496 {
497  // Provide some info. Also serves to initialise mesh dimensions. This may
498  // be needed for parallel runs due to lazy evaluation of valid mesh
499  // dimensions.
500  Info<< " Constructing " << owner.mesh().nGeometricD() << "-D injection"
501  << endl;
502 
503  if
504  (
505  nParticleFixed_ > 0
507  )
508  {
510  << "If nParticle is specified then the uniformParcelSize must be "
512  << exit(FatalIOError);
513  }
514 
515  if (owner.solution().transient())
516  {
517  SOI_ =
519  (
520  this->coeffDict().template lookup<scalar>("SOI")
521  );
522  }
523 }
524 
525 
526 template<class CloudType>
528 (
529  const InjectionModel<CloudType>& im
530 )
531 :
533  SOI_(im.SOI_),
534  massInjected_(im.massInjected_),
535  nInjections_(im.nInjections_),
536  parcelsAddedTotal_(im.parcelsAddedTotal_),
537  nParticleFixed_(im.nParticleFixed_),
538  uniformParcelSize_(im.uniformParcelSize_),
539  time0_(im.time0_),
540  timeStep0_(im.timeStep0_)
541 {}
542 
543 
544 // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
545 
546 template<class CloudType>
548 {}
549 
550 
551 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
552 
553 template<class CloudType>
555 {}
556 
557 
558 template<class CloudType>
560 {
561  const scalar deltaT =
562  this->owner().solution().transient() ? timeEnd() - timeStart() : 1;
563 
564  return massToInject(0, deltaT)/nParcelsToInject(0, deltaT);
565 }
566 
567 
568 template<class CloudType>
569 template<class TrackCloudType>
571 (
572  TrackCloudType& cloud,
573  typename CloudType::parcelType::trackingData& td
574 )
575 {
576  const polyMesh& mesh = this->owner().mesh();
577 
578  const scalar time = this->owner().db().time().value();
579 
580  // Reset counters
581  label nParcelsAdded = 0;
582  scalar massAdded = 0;
583 
584  // Get amounts to inject
585  label nParcels;
586  scalar mass;
587  bool inject;
588  if (time < SOI_)
589  {
590  // Injection has not started yet
591  nParcels = 0;
592  mass = 0;
593  inject = false;
594  timeStep0_ = time;
595  }
596  else
597  {
598  // Injection has started. Get amounts between times.
599  const scalar t0 = timeStep0_ - SOI_, t1 = time - SOI_;
600  nParcels = nParcelsToInject(t0, t1);
601  mass = nParticleFixed_ < 0 ? massToInject(t0, t1) : NaN;
602 
603  if (nParcels > 0 && (nParticleFixed_ > 0 || mass > 0))
604  {
605  // Injection is valid
606  inject = true;
607  timeStep0_ = time;
608  }
609  else if (nParcels == 0 && (nParticleFixed_ < 0 && mass > 0))
610  {
611  // Injection should have started, but there is not a sufficient
612  // amount to inject a single parcel. Do not inject, but hold
613  // advancement of the old-time so that the mass gets added to a
614  // subsequent injection.
615  inject = false;
616  }
617  else
618  {
619  // Nothing to inject
620  inject = false;
621  timeStep0_ = time;
622  }
623  }
624 
625  // Do injection
626  if (inject)
627  {
628  // Duration of injection period during this timestep
629  const scalar deltaT =
630  max
631  (
632  scalar(0),
633  min
634  (
635  td.trackTime(),
636  min
637  (
638  time - SOI_,
639  timeEnd() - time0_
640  )
641  )
642  );
643 
644  // Pad injection time if injection starts during this timestep
645  const scalar padTime = max(scalar(0), SOI_ - time0_);
646 
647  // Create new parcels linearly across carrier phase timestep
648  PtrList<parcelType> parcelPtrs(nParcels);
649  forAll(parcelPtrs, parceli)
650  {
651  // Calculate the pseudo time of injection for parcel 'parceli'
652  scalar timeInj = time0_ + padTime + deltaT*parceli/nParcels;
653 
654  // Determine the injection coordinates and owner cell,
655  // tetFace and tetPt
656  barycentric coordinates = barycentric::uniform(NaN);
657  label celli = -1, tetFacei = -1, tetPti = -1, facei = -1;
658  setPositionAndCell
659  (
660  parceli,
661  nParcels,
662  timeInj,
663  coordinates,
664  celli,
665  tetFacei,
666  tetPti,
667  facei
668  );
669 
670  if (celli > -1)
671  {
672  // Lagrangian timestep
673  const scalar dt = timeInj - time0_;
674 
675  // Create a new parcel
676  parcelPtrs.set
677  (
678  parceli,
679  new parcelType
680  (
681  mesh,
682  coordinates,
683  celli,
684  tetFacei,
685  tetPti,
686  facei
687  )
688  );
689  parcelType& p = parcelPtrs[parceli];
690 
691  // Correct the position for reduced-dimension cases
692  constrainPosition(td, p);
693 
694  // Check/set new parcel thermo properties
695  cloud.setParcelThermoProperties(p);
696 
697  // Assign new parcel properties in injection model
698  setProperties(parceli, nParcels, timeInj, p);
699 
700  // Check/set new parcel injection properties
701  cloud.checkParcelProperties(p, index());
702 
703  // Apply correction to velocity for 2-D cases
705  (
706  mesh,
707  mesh.solutionD(),
708  p.U()
709  );
710 
711  // Modify the step fraction so that the particles are
712  // injected continually through the time-step
713  p.stepFraction() = dt/td.trackTime();
714 
715  // Set the number of particles. If not fixed, this will set
716  // a junk value, which will get corrected below.
717  p.nParticle() = nParticleFixed_;
718  }
719  }
720 
721  // Set the number of particles so that the introduced mass is correct
722  // and the uniform size is as specified
723  if (nParticleFixed_ < 0)
724  {
725  setNumberOfParticles(parcelPtrs, mass);
726  }
727 
728  // Add the new parcels
729  forAll(parcelPtrs, parceli)
730  {
731  if (parcelPtrs.set(parceli))
732  {
733  parcelType& p = parcelPtrs[parceli];
734  nParcelsAdded ++;
735  massAdded += p.nParticle()*p.mass();
736  cloud.addParticle(parcelPtrs.set(parceli, nullptr).ptr());
737  }
738  }
739  }
740 
741  postInjectCheck(nParcelsAdded, massAdded);
742 }
743 
744 
745 template<class CloudType>
746 template<class TrackCloudType>
748 (
749  TrackCloudType& cloud,
750  typename CloudType::parcelType::trackingData& td
751 )
752 {
753  const polyMesh& mesh = this->owner().mesh();
754 
755  // Reset counters
756  label nParcelsAdded = 0;
757  scalar massAdded = 0;
758 
759  // Get amounts to inject based on first second of injection
760  const label nParcels = nParcelsToInject(0, 1);
761  const scalar mass = nParticleFixed_ < 0 ? massToInject(0, 1) : NaN;
762 
763  // Do injection
764  if (nParcels > 0)
765  {
766  PtrList<parcelType> parcelPtrs(nParcels);
767  forAll(parcelPtrs, parceli)
768  {
769  // Determine the injection coordinates and owner cell,
770  // tetFace and tetPt
771  barycentric coordinates = barycentric::uniform(NaN);
772  label celli = -1, tetFacei = -1, tetPti = -1, facei = -1;
773  setPositionAndCell
774  (
775  parceli,
776  nParcels,
777  0,
778  coordinates,
779  celli,
780  tetFacei,
781  tetPti,
782  facei
783  );
784 
785  if (celli > -1)
786  {
787  // Create a new parcel
788  parcelPtrs.set
789  (
790  parceli,
791  new parcelType
792  (
793  mesh,
794  coordinates,
795  celli,
796  tetFacei,
797  tetPti,
798  facei
799  )
800  );
801  parcelType& p = parcelPtrs[parceli];
802 
803  // Correct the position for reduced-dimension cases
804  constrainPosition(td, p);
805 
806  // Check/set new parcel thermo properties
807  cloud.setParcelThermoProperties(p);
808 
809  // Assign new parcel properties in injection model
810  setProperties(parceli, nParcels, 0, p);
811 
812  // Check/set new parcel injection properties
813  cloud.checkParcelProperties(p, index());
814 
815  // Apply correction to velocity for 2-D cases
816  meshTools::constrainDirection(mesh, mesh.solutionD(), p.U());
817 
818  // Initial step fraction
819  p.stepFraction() = 0;
820 
821  // Set the number of particles. If not fixed, this will set
822  // a junk value, which will get corrected below.
823  p.nParticle() = nParticleFixed_;
824  }
825  }
826 
827  // Set the number of particles so that the introduced mass is correct
828  // and the uniform size is as specified
829  if (nParticleFixed_ < 0)
830  {
831  setNumberOfParticles(parcelPtrs, mass);
832  }
833 
834  // Add the new parcels
835  forAll(parcelPtrs, parceli)
836  {
837  if (parcelPtrs.set(parceli))
838  {
839  parcelType& p = parcelPtrs[parceli];
840  nParcelsAdded ++;
841  massAdded += p.nParticle()*p.mass();
842  cloud.addParticle(parcelPtrs.set(parceli, nullptr).ptr());
843  }
844  }
845  }
846 
847  postInjectCheck(nParcelsAdded, massAdded);
848 }
849 
850 
851 template<class CloudType>
853 {
854  os << " " << this->modelName() << ":" << nl
855  << " number of parcels added = " << parcelsAddedTotal_ << nl
856  << " mass introduced = " << massInjected_ << nl;
857 
858  if (this->writeTime())
859  {
860  this->setModelProperty("massInjected", massInjected_);
861  this->setModelProperty("nInjections", nInjections_);
862  this->setModelProperty("parcelsAddedTotal", parcelsAddedTotal_);
863  this->setModelProperty("timeStep0", timeStep0_);
864  }
865 }
866 
867 
868 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
869 
870 #include "InjectionModelNew.C"
871 
872 // ************************************************************************* //
bool found
#define forAll(list, i)
Loop across all elements in list.
Definition: UList.H:434
Base class for cloud sub-models.
const CloudType & owner() const
Return const access to the owner cloud.
Templated base class for dsmc cloud.
Definition: DSMCCloud.H:79
ParcelType parcelType
Type of parcel the cloud was instantiated for.
Definition: DSMCCloud.H:221
const fvMesh & mesh() const
Return references to the mesh.
Definition: DSMCCloudI.H:41
static autoPtr< Function1< Type > > New(const word &name, const dictionary &dict)
Selector.
Definition: Function1New.C:32
Templated function that returns a constant value.
Definition: Constant.H:60
Function1 which scales a given 'value' function by a 'scale' scalar function and scales the 'x' argum...
Definition: Scale.H:155
const objectRegistry & db() const
Return the local objectRegistry.
Definition: IOobject.C:312
Templated injection model class.
virtual void topoChange()
Update mesh.
virtual ~InjectionModel()
Destructor.
void inject(TrackCloudType &cloud, typename CloudType::parcelType::trackingData &td)
Main injection loop.
void constrainPosition(typename CloudType::parcelType::trackingData &td, typename CloudType::parcelType &parcel)
Constrain a parcel's position appropriately to the geometric.
bool findCellAtPosition(const point &position, barycentric &coordinates, label &celli, label &tetFacei, label &tetPti, bool errorOnNotFound=true)
Find the cell that contains the supplied position.
scalar readMassTotal(const dictionary &dict, CloudType &owner)
Read the total mass value for instantaneous injections.
virtual void info(Ostream &os)
Write injection info to stream.
void setNumberOfParticles(PtrList< parcelType > &parcelPtrs, const scalar mass) const
Set number of particles to inject given parcel properties.
uniformParcelSize uniformParcelSize_
Size uniform to all parcels.
label sizeSampleQ() const
Return the sampling moment to be used by the size distribution.
scalar averageParcelMass()
Return the average injected parcel mass.
scalar nParticleFixed_
Fixed nParticle to assign to parcels. Only valid if.
label index() const
Get the index of this injector.
TimeFunction1< scalar > readParcelsPerSecond(const dictionary &dict, CloudType &owner)
Read the number of parcels injected per second for continuous.
void injectSteadyState(TrackCloudType &cloud, typename CloudType::parcelType::trackingData &td)
Main injection loop - steady-state.
scalar readDuration(const dictionary &dict, CloudType &owner)
Read the duration for continuous injections.
InjectionModel(CloudType &owner)
Construct null from owner.
TimeFunction1< scalar > readMassFlowRate(const dictionary &dict, CloudType &owner, const scalar duration)
Read the mass flow rate function for continuous injections.
CloudType::parcelType parcelType
Convenience typedef for parcelType.
scalar SOI_
Start of injection [s].
void postInjectCheck(const label parcelsAdded, const scalar massAdded)
Post injection checks.
An Ostream is an abstract base class for all output systems (streams, files, token lists,...
Definition: Ostream.H:57
const Type & second() const
Return second.
Definition: Pair.H:110
const Type & first() const
Return first.
Definition: Pair.H:98
A templated 1D list of pointers to objects of type <T>, where the size of the array is known and used...
Definition: PtrList.H:75
bool set(const label) const
Is element set.
Definition: PtrListI.H:65
Light wrapper around Function1 to provide a mechanism to update time-based entries.
Definition: TimeFunction1.H:61
Class to control time during OpenFOAM simulations that is also the top-level objectRegistry.
Definition: Time.H:76
scalar userTimeToTime(const scalar tau) const
Convert the user-time (e.g. CA deg) to real-time (s).
Definition: Time.C:824
static int myProcNo(const label communicator=0)
Number of this process (starting from masterNo() = 0)
Definition: UPstream.H:429
static const Form zero
Definition: VectorSpace.H:113
static Form uniform(const Cmpt &s)
Return a VectorSpace with all elements = s.
Definition: VectorSpaceI.H:168
An auto-pointer similar to the STL auto_ptr but with automatic casting to a reference to the type and...
Definition: autoPtr.H:51
A cloud is a collection of lagrangian particles.
Definition: cloud.H:55
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.
const Time & time() const
Return the top-level database.
Definition: fvMesh.H:406
uniformParcelSize
Enumeration for the parcels' uniform size.
static const NamedEnum< uniformParcelSize, 3 > uniformParcelSizeNames_
Names of the parcels' uniform size.
const Time & time() const
Return time.
Base particle class.
Definition: particle.H:83
Mesh consisting of general polyhedral cells.
Definition: polyMesh.H:80
label nGeometricD() const
Return the number of valid geometric dimensions in the mesh.
Definition: polyMesh.C:1055
const Vector< label > & solutionD() const
Return the vector of solved-for directions in mesh.
Definition: polyMesh.C:1061
const dictionary & coeffDict() const
Return const access to the coefficients dictionary.
Definition: subModelBase.C:128
const dictionary & dict() const
Return const access to the cloud dictionary.
Definition: subModelBase.C:110
A class for handling words, derived from string.
Definition: word.H:62
#define FatalIOErrorInFunction(ios)
Report an error message using Foam::FatalIOError.
Definition: error.H:318
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:306
#define IOWarningInFunction(ios)
Report an IO warning using Foam::Warning.
mathematical constants.
void constrainDirection(const polyMesh &mesh, const Vector< label > &dirs, vector &d)
Set the constrained components of directions/velocity to zero.
Definition: meshTools.C:671
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)
Pair< label > labelPair
Label pair.
Definition: labelPair.H:48
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
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:251
messageStream Info
layerAndWeight min(const layerAndWeight &a, const layerAndWeight &b)
void reduce(const List< UPstream::commsStruct > &comms, T &Value, const BinaryOp &bop, const int tag, const label comm)
T returnReduce(const T &Value, const BinaryOp &bop, const int tag=Pstream::msgType(), const label comm=UPstream::worldComm)
dimensioned< scalar > mag(const dimensioned< Type > &)
layerAndWeight max(const layerAndWeight &a, const layerAndWeight &b)
IOerror FatalIOError
error FatalError
static const label labelMax
Definition: label.H:62
static const char nl
Definition: Ostream.H:260
fileType type(const fileName &, const bool checkVariants=true, const bool followLink=true)
Return the file type: directory or file.
Definition: POSIX.C:488
dictionary dict
volScalarField & p