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-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 "InjectionModel.H"
27 #include "meshTools.H"
28 #include "meshSearch.H"
29 #include "volFields.H"
30 #include "Scale.H"
31 
32 // * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * * //
33 
34 template<class CloudType>
36 (
37  const dictionary& dict,
38  CloudType& owner
39 )
40 {
41  if (dict.found("nParticle"))
42  {
43  if (dict.found("massTotal"))
44  {
46  << "If nParticle is specified then the massTotal "
47  << "setting has no effect " << endl;
48  }
49 
50  return NaN;
51  }
52 
53  if (owner.solution().steadyState())
54  {
56  << "The " << type() << " injection model is not compatible with "
57  << "steady state solution"
58  << exit(FatalError);
59 
60  return NaN;
61  }
62 
63  return dict.lookup<scalar>("massTotal", dimMass);
64 }
65 
66 
67 template<class CloudType>
69 (
70  const dictionary& dict,
71  CloudType& owner
72 )
73 {
74  if (owner.solution().steadyState())
75  {
76  return vGreat;
77  }
78 
79  return dict.lookup<scalar>("duration", owner.time().userUnits());
80 }
81 
82 
83 template<class CloudType>
86 (
87  const dictionary& dict,
88  CloudType& owner,
89  const scalar duration
90 )
91 {
92  const bool haveMassFlowRate = dict.found("massFlowRate");
93  const bool haveMassTotal = dict.found("massTotal");
94 
95  if (dict.found("nParticle"))
96  {
97  if (haveMassFlowRate || haveMassTotal)
98  {
100  << "If nParticle is specified then massFlowRate and massTotal "
101  << "settings have no effect " << endl;
102  }
103 
104  return
106  (
107  new Function1s::Constant<scalar>("NaN", NaN)
108  );
109  }
110 
111  if (owner.solution().steadyState() && haveMassTotal)
112  {
114  << "Cannot specify the massTotal of a steady injection. Use "
115  << "massFlowRate instead." << exit(FatalIOError);
116  }
117 
118  if (haveMassFlowRate && haveMassTotal)
119  {
121  << "Cannot specify both massFlowRate and massTotal. Use one or "
122  << "the other." << exit(FatalIOError);
123  }
124 
125  if (owner.solution().steadyState())
126  {
127  return
129  (
131  (
132  "massFlowRate",
133  dict.lookup<scalar>("massFlowRate", dimMass/dimTime)
134  )
135  );
136  }
137 
138  if (haveMassFlowRate)
139  {
140  return
142  (
143  "massFlowRate",
144  this->owner().time().userUnits(),
146  dict
147  );
148  }
149 
150  const scalar massTotal = dict.lookup<scalar>("massTotal", dimMass);
151 
152  if (!dict.found("flowRateProfile"))
153  {
154  return
156  (
158  (
159  "massFlowRate",
160  massTotal/duration
161  )
162  );
163  }
164 
165  autoPtr<Function1<scalar>> flowRateProfile
166  (
168  (
169  "flowRateProfile",
170  this->owner().time().userUnits(),
171  dimless,
172  dict
173  )
174  );
175 
176  const scalar sumFlowRateProfile = flowRateProfile->integral(0, duration);
177 
178  return
180  (
182  (
183  "massFlowRate",
184  Function1s::Constant<scalar>("m", massTotal/sumFlowRateProfile),
185  Function1s::Constant<scalar>("one", scalar(1)),
186  flowRateProfile()
187  )
188  );
189 }
190 
191 
192 template<class CloudType>
195 (
196  const dictionary& dict,
197  CloudType& owner
198 )
199 {
200  return
202  (
203  "parcelsPerSecond",
204  this->owner().time().userUnits(),
206  dict
207  );
208 }
209 
210 
211 template<class CloudType>
213 {
214  forAll(this->owner().injectors(), i)
215  {
216  if (this->owner().injectors()(i) == this)
217  {
218  return i;
219  }
220  }
221 
222  return -1;
223 }
224 
225 
226 template<class CloudType>
228 (
229  const meshSearch& searchEngine,
230  const point& position,
232  label& celli,
233  label& tetFacei,
234  label& tetPti,
235  bool errorOnNotFound
236 )
237 {
238  // Subroutine for finding the cell
239  auto findProcAndCell = [this,&searchEngine](const point& pos)
240  {
241  // Find the containing cell
242  label celli = searchEngine.findCell(pos);
243 
244  // Synchronise so only a single processor finds this position
245  label proci = celli >= 0 ? Pstream::myProcNo() : -1;
246  reduce(proci, maxOp<label>());
247  if (proci != Pstream::myProcNo())
248  {
249  celli = -1;
250  }
251 
252  return labelPair(proci, celli);
253  };
254 
255  point pos = position;
256 
257  // Try and find the cell at the given position
258  const labelPair procAndCelli = findProcAndCell(pos);
259  label proci = procAndCelli.first();
260  celli = procAndCelli.second();
261 
262  // Didn't find it. The point may be awkwardly on an edge or face. Try
263  // again, but move the point into its nearest cell a little bit.
264  if (proci == -1)
265  {
266  pos += small*(this->owner().mesh().C()[celli] - pos);
267  const labelPair procAndCelli = findProcAndCell(pos);
268  proci = procAndCelli.first();
269  celli = procAndCelli.second();
270  }
271 
272  // Didn't find it. Error or return false.
273  if (proci == -1)
274  {
275  if (errorOnNotFound)
276  {
278  << "Cannot find parcel injection cell. "
279  << "Parcel position = " << position << nl
280  << exit(FatalError);
281  }
282 
283  return false;
284  }
285 
286  // Found it. Construct the barycentric coordinates.
287  if (proci == Pstream::myProcNo())
288  {
289  label nLocateBoundaryHits = 0;
290  particle p(searchEngine, pos, celli, nLocateBoundaryHits);
291 
292  if (nLocateBoundaryHits != 0)
293  {
295  << "Injection model " << this->modelName()
296  << " for cloud " << this->owner().name()
297  << " did not accurately locate the position "
298  << pos << " within the mesh" << endl;
299  }
300 
301  coordinates = p.coordinates();
302  celli = p.cell();
303  tetFacei = p.tetFace();
304  tetPti = p.tetPt();
305  }
306 
307  return true;
308 }
309 
310 
311 template<class CloudType>
313 (
314  typename CloudType::parcelType::trackingData& td,
315  typename CloudType::parcelType& parcel
316 )
317 {
318  const vector d = parcel.deviationFromMeshCentre(td.mesh);
319 
320  if (d == vector::zero)
321  {
322  return;
323  }
324 
325  const label facei = parcel.face();
326 
327  // If the parcel is not on a face, then just track it to the mesh centre
328  if (facei == -1)
329  {
330  parcel.track(td.mesh, - d, 0);
331  }
332 
333  // If the parcel is on a face, then track in two steps, going slightly into
334  // the current cell. This prevents a boundary hit from ending the track
335  // prematurely.
336  if (facei != -1)
337  {
338  const vector pc =
339  td.mesh.cellCentres()[parcel.cell()] - parcel.position(td.mesh);
340 
341  parcel.track(td.mesh, - d/2 + rootSmall*pc, 0);
342  parcel.track(td.mesh, - d/2 - rootSmall*pc, 0);
343  }
344 
345  // Restore any face-association that got changed during tracking
346  parcel.face() = facei;
347 }
348 
349 
350 template<class CloudType>
352 {
353  switch (uniformParcelSize_)
354  {
355  case uniformParcelSize::nParticle:
356  return 0;
357  case uniformParcelSize::surfaceArea:
358  return 2;
360  return 3;
361  }
362 
363  return -labelMax;
364 }
365 
366 
367 template<class CloudType>
369 (
370  PtrList<parcelType>& parcelPtrs,
371  const scalar mass
372 ) const
373 {
374  auto size = [&](const parcelType& p)
375  {
376  switch (uniformParcelSize_)
377  {
378  case uniformParcelSize::nParticle:
379  return scalar(1);
380  case uniformParcelSize::surfaceArea:
381  return p.areaS();
383  return p.volume();
384  }
385  return NaN;
386  };
387 
388  // Determine the total mass and size of all created particles
389  scalar sumMassBySize = 0;
390  forAll(parcelPtrs, parceli)
391  {
392  if (parcelPtrs.set(parceli))
393  {
394  const parcelType& p = parcelPtrs[parceli];
395  sumMassBySize += p.mass()/size(p);
396  }
397  }
398 
399  reduce(sumMassBySize, sumOp<scalar>());
400 
401  // Set the numbers of particles on each parcel
402  forAll(parcelPtrs, parceli)
403  {
404  if (parcelPtrs.set(parceli))
405  {
406  parcelType& p = parcelPtrs[parceli];
407  p.nParticle() = mass/size(p)/sumMassBySize;
408  }
409  }
410 
411  // Check that the constraints are correct
412  if (debug)
413  {
414  scalar massN = 0, minSizeN = vGreat, maxSizeN = -vGreat;
415  forAll(parcelPtrs, parceli)
416  {
417  if (parcelPtrs.set(parceli))
418  {
419  const parcelType& p = parcelPtrs[parceli];
420  massN += p.nParticle()*p.mass();
421  minSizeN = min(minSizeN, p.nParticle()*size(p));
422  maxSizeN = max(minSizeN, p.nParticle()*size(p));
423  }
424  }
425 
426  reduce(massN, sumOp<scalar>());
427 
428  if (mag(massN - mass) > rootSmall*(massN + mass)/2)
429  {
431  << "Parcels do not have the required mass"
432  << exit(FatalError);
433  }
434 
435  reduce(minSizeN, minOp<scalar>());
436  reduce(maxSizeN, maxOp<scalar>());
437 
438  if (maxSizeN - minSizeN > rootSmall*(maxSizeN + minSizeN)/2)
439  {
441  << "Parcel sizes are not uniform"
442  << exit(FatalError);
443  }
444  }
445 }
446 
447 
448 template<class CloudType>
450 (
451  typename parcelType::trackingData& td
452 )
453 {}
454 
455 
456 template<class CloudType>
458 (
459  const label nParcelsAdded,
460  const scalar massAdded,
461  typename parcelType::trackingData& td
462 )
463 {
464  const label allNParcelsAdded = returnReduce(nParcelsAdded, sumOp<label>());
465 
466  if (allNParcelsAdded > 0)
467  {
468  Info<< nl
469  << "Cloud: " << this->owner().name()
470  << " injector: " << this->modelName() << nl
471  << " Added " << allNParcelsAdded << " new parcels" << nl << endl;
472  }
473 
474  // Increment total number of parcels added
475  nParcelsInjected_ += allNParcelsAdded;
476 
477  // Increment total mass injected
478  massInjected_ += returnReduce(massAdded, sumOp<scalar>());
479 }
480 
481 
482 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
483 
484 template<class CloudType>
486 :
488  SOI_(0),
489  massInjected_(this->template getModelProperty<scalar>("massInjected")),
490  nParcelsInjected_
491  (
492  this->template getModelProperty<scalar>("nParcelsInjected")
493  ),
494  massDeferred_
495  (
496  this->template getModelProperty<scalar>("massDeferred")
497  ),
498  nParcelsDeferred_
499  (
500  this->template getModelProperty<scalar>("nParcelsDeferred")
501  ),
502  nParticleFixed_(-vGreat),
503  uniformParcelSize_(uniformParcelSize::nParticle)
504 {}
505 
506 
507 template<class CloudType>
509 (
510  const dictionary& dict,
511  CloudType& owner,
512  const word& modelName,
513  const word& modelType
514 )
515 :
516  CloudSubModelBase<CloudType>(modelName, owner, dict, typeName, modelType),
517  SOI_(0),
518  massInjected_(this->template getModelProperty<scalar>("massInjected")),
519  nParcelsInjected_
520  (
521  this->template getModelProperty<scalar>("nParcelsInjected")
522  ),
523  massDeferred_
524  (
525  this->template getModelProperty<scalar>("massDeferred")
526  ),
527  nParcelsDeferred_
528  (
529  this->template getModelProperty<scalar>("nParcelsDeferred")
530  ),
531  nParticleFixed_(dict.lookupOrDefault<scalar>("nParticle", -vGreat)),
532  uniformParcelSize_
533  (
534  uniformParcelSizeNames_
535  [
536  !dict.found("parcelBasisType") && nParticleFixed_ > 0
537  ? dict.lookupOrDefault<word>
538  (
539  "uniformParcelSize",
540  uniformParcelSizeNames_[uniformParcelSize::nParticle]
541  )
542  : dict.lookup<word>("uniformParcelSize")
543  ]
544  )
545 {
546  // Provide some info. Also serves to initialise mesh dimensions. This may
547  // be needed for parallel runs due to lazy evaluation of valid mesh
548  // dimensions.
549  Info<< " Constructing " << owner.mesh().nGeometricD() << "-D injection"
550  << endl;
551 
552  if
553  (
554  nParticleFixed_ > 0
556  )
557  {
559  << "If nParticle is specified then the uniformParcelSize must be "
561  << exit(FatalIOError);
562  }
563 
564  if (owner.solution().transient())
565  {
566  SOI_ = dict.lookup<scalar>("SOI", owner.time().userUnits());
567  }
568 }
569 
570 
571 template<class CloudType>
573 (
574  const InjectionModel<CloudType>& im
575 )
576 :
578  SOI_(im.SOI_),
579  massInjected_(im.massInjected_),
580  nParcelsInjected_(im.nParcelsInjected_),
581  massDeferred_(im.massDeferred_),
582  nParcelsDeferred_(im.nParcelsDeferred_),
583  nParticleFixed_(im.nParticleFixed_),
584  uniformParcelSize_(im.uniformParcelSize_)
585 {}
586 
587 
588 // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
589 
590 template<class CloudType>
592 {}
593 
594 
595 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
596 
597 template<class CloudType>
599 {}
600 
601 
602 template<class CloudType>
604 {
605  const scalar deltaT =
606  this->owner().solution().transient() ? timeEnd() - timeStart() : 1;
607 
608  return massToInject(0, deltaT)/nParcelsToInject(0, deltaT);
609 }
610 
611 
612 template<class CloudType>
613 template<class TrackCloudType>
615 (
616  TrackCloudType& cloud,
617  typename CloudType::parcelType::trackingData& td
618 )
619 {
620  const polyMesh& mesh = this->owner().mesh();
621 
622  const meshSearch& searchEngine = meshSearch::New(mesh);
623 
624  const scalar time1 = this->owner().time().value();
625  const scalar time0 =
626  this->owner().time().value()
627  - this->owner().time().deltaTValue();
628 
629  preInject(td);
630 
631  // Reset counters
632  label nParcelsAdded = 0;
633  scalar massAdded = 0;
634 
635  // Get amounts to inject
636  label nParcels;
637  scalar mass = NaN;
638  {
639  // Injection has started. Get amounts between times.
640  const scalar t0 = time0 - SOI_, t1 = time1 - SOI_;
641 
642  // Get the number of parcels to inject, round it down to the nearest
643  // integer, and then store the excess to apply at a later time
644  const scalar nParcelsNoRound =
645  nParcelsToInject(t0, t1) + nParcelsDeferred_;
646  nParcels = floor(nParcelsNoRound);
647  nParcelsDeferred_ = nParcelsNoRound - nParcels;
648 
649  // If we need mass...
650  if (nParticleFixed_ < 0)
651  {
652  // Get the mass to inject, and if there are no parcels then store
653  // this to apply at a later time
654  mass = massToInject(t0, t1) + massDeferred_;
655  massDeferred_ = nParcels > 0 ? 0 : mass;
656 
657  // Special case. If there is no mass, then don't create anything.
658  // This is a hack so that mass flow rates that switch on and off
659  // still work tolerably with a constant number rate. Really,
660  // though, cases like these should be specified with a
661  // correspondingly varying number rate.
662  if (mass == 0)
663  {
664  nParcels = 0;
665  nParcelsDeferred_ = 0;
666  }
667  }
668  }
669 
670  // Do injection
671  if (nParcels > 0)
672  {
673  // Duration of injection period during this timestep
674  const scalar deltaT =
675  max
676  (
677  scalar(0),
678  min
679  (
680  td.trackTime(),
681  min
682  (
683  time1 - SOI_,
684  timeEnd() - time0
685  )
686  )
687  );
688 
689  // Pad injection time if injection starts during this timestep
690  const scalar padTime = max(scalar(0), SOI_ - time0);
691 
692  // Create new parcels linearly across carrier phase timestep
693  PtrList<parcelType> parcelPtrs(nParcels);
694  forAll(parcelPtrs, parceli)
695  {
696  // Calculate the pseudo time of injection for parcel 'parceli'
697  scalar timeInj = time0 + padTime + deltaT*parceli/nParcels;
698 
699  // Determine the injection coordinates and owner cell,
700  // tetFace and tetPt
702  label celli = -1, tetFacei = -1, tetPti = -1, facei = -1;
703  setPositionAndCell
704  (
705  searchEngine,
706  parceli,
707  nParcels,
708  timeInj,
709  coordinates,
710  celli,
711  tetFacei,
712  tetPti,
713  facei
714  );
715 
716  if (celli > -1)
717  {
718  // Lagrangian timestep
719  const scalar dt = timeInj - time0;
720 
721  // Create a new parcel
722  parcelPtrs.set
723  (
724  parceli,
725  new parcelType
726  (
727  mesh,
728  coordinates,
729  celli,
730  tetFacei,
731  tetPti,
732  facei
733  )
734  );
735  parcelType& p = parcelPtrs[parceli];
736 
737  // Correct the position for reduced-dimension cases
738  constrainPosition(td, p);
739 
740  // Check/set new parcel thermo properties
741  cloud.setParcelThermoProperties(p);
742 
743  // Assign new parcel properties in injection model
744  setProperties(parceli, nParcels, timeInj, td, p);
745 
746  // Check/set new parcel injection properties
747  cloud.checkParcelProperties(p, index());
748 
749  // Apply correction to velocity for 2-D cases
751  (
752  mesh,
753  mesh.solutionD(),
754  p.U()
755  );
756 
757  // Modify the step fraction so that the particles are
758  // injected continually through the time-step
759  p.stepFraction() = dt/td.trackTime();
760 
761  // Set the number of particles. If not fixed, this will set
762  // a junk value, which will get corrected below.
763  p.nParticle() = nParticleFixed_;
764  }
765  }
766 
767  // Set the number of particles so that the introduced mass is correct
768  // and the uniform size is as specified
769  if (nParticleFixed_ < 0)
770  {
771  setNumberOfParticles(parcelPtrs, mass);
772  }
773 
774  // Add the new parcels
775  forAll(parcelPtrs, parceli)
776  {
777  if (parcelPtrs.set(parceli))
778  {
779  parcelType& p = parcelPtrs[parceli];
780  nParcelsAdded ++;
781  massAdded += p.nParticle()*p.mass();
782  cloud.addParticle(parcelPtrs.set(parceli, nullptr).ptr());
783  }
784  }
785  }
786 
787  postInject(nParcelsAdded, massAdded, td);
788 }
789 
790 
791 template<class CloudType>
792 template<class TrackCloudType>
794 (
795  TrackCloudType& cloud,
796  typename CloudType::parcelType::trackingData& td
797 )
798 {
799  const polyMesh& mesh = this->owner().mesh();
800 
801  const meshSearch& searchEngine = meshSearch::New(mesh);
802 
803  preInject(td);
804 
805  // Reset counters
806  label nParcelsAdded = 0;
807  scalar massAdded = 0;
808 
809  // Get amounts to inject based on first second of injection
810  const label nParcels = floor(nParcelsToInject(0, 1));
811  const scalar mass = nParticleFixed_ < 0 ? massToInject(0, 1) : NaN;
812 
813  // Do injection
814  if (nParcels > 0)
815  {
816  PtrList<parcelType> parcelPtrs(nParcels);
817  forAll(parcelPtrs, parceli)
818  {
819  // Determine the injection coordinates and owner cell,
820  // tetFace and tetPt
822  label celli = -1, tetFacei = -1, tetPti = -1, facei = -1;
823  setPositionAndCell
824  (
825  searchEngine,
826  parceli,
827  nParcels,
828  0,
829  coordinates,
830  celli,
831  tetFacei,
832  tetPti,
833  facei
834  );
835 
836  if (celli > -1)
837  {
838  // Create a new parcel
839  parcelPtrs.set
840  (
841  parceli,
842  new parcelType
843  (
844  mesh,
845  coordinates,
846  celli,
847  tetFacei,
848  tetPti,
849  facei
850  )
851  );
852  parcelType& p = parcelPtrs[parceli];
853 
854  // Correct the position for reduced-dimension cases
855  constrainPosition(td, p);
856 
857  // Check/set new parcel thermo properties
858  cloud.setParcelThermoProperties(p);
859 
860  // Assign new parcel properties in injection model
861  setProperties(parceli, nParcels, 0, td, p);
862 
863  // Check/set new parcel injection properties
864  cloud.checkParcelProperties(p, index());
865 
866  // Apply correction to velocity for 2-D cases
868 
869  // Initial step fraction
870  p.stepFraction() = 0;
871 
872  // Set the number of particles. If not fixed, this will set
873  // a junk value, which will get corrected below.
874  p.nParticle() = nParticleFixed_;
875  }
876  }
877 
878  // Set the number of particles so that the introduced mass is correct
879  // and the uniform size is as specified
880  if (nParticleFixed_ < 0)
881  {
882  setNumberOfParticles(parcelPtrs, mass);
883  }
884 
885  // Add the new parcels
886  forAll(parcelPtrs, parceli)
887  {
888  if (parcelPtrs.set(parceli))
889  {
890  parcelType& p = parcelPtrs[parceli];
891  nParcelsAdded ++;
892  massAdded += p.nParticle()*p.mass();
893  cloud.addParticle(parcelPtrs.set(parceli, nullptr).ptr());
894  }
895  }
896  }
897 
898  postInject(nParcelsAdded, massAdded, td);
899 }
900 
901 
902 template<class CloudType>
904 {
905  os << " " << this->modelName() << ":" << nl
906  << " number of parcels added = " << nParcelsInjected_ << nl
907  << " mass introduced = " << massInjected_ << nl;
908 
909  if (this->writeTime())
910  {
911  this->setModelProperty("massInjected", massInjected_);
912  this->setModelProperty("nParcelsInjected", nParcelsInjected_);
913  this->setModelProperty("massDeferred", massDeferred_);
914  this->setModelProperty("nParcelsDeferred", nParcelsDeferred_);
915  }
916 }
917 
918 
919 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
920 
921 #include "InjectionModelNew.C"
922 
923 // ************************************************************************* //
bool found
#define forAll(list, i)
Loop across all elements in list.
Definition: UList.H:449
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:80
ParcelType parcelType
Type of parcel the cloud was instantiated for.
Definition: DSMCCloud.H:225
const fvMesh & mesh() const
Return references to the mesh.
Definition: DSMCCloudI.H:41
Run-time selectable general function of one variable.
Definition: Function1.H:62
Function1 which scales a given 'value' function by a 'scale' scalar function and scales the 'x' argum...
Definition: Scale.H:155
Templated injection model class.
virtual void topoChange()
Update mesh.
virtual ~InjectionModel()
Destructor.
void inject(TrackCloudType &cloud, typename CloudType::parcelType::trackingData &td)
Main injection loop.
autoPtr< Function1< scalar > > readParcelsPerSecond(const dictionary &dict, CloudType &owner)
Read the number of parcels injected per second for continuous.
void constrainPosition(typename CloudType::parcelType::trackingData &td, typename CloudType::parcelType &parcel)
Constrain a parcel's position appropriately to the geometric.
virtual void postInject(const label parcelsAdded, const scalar massAdded, typename parcelType::trackingData &td)
Post injection hook.
virtual void preInject(typename parcelType::trackingData &td)
Pre injection hook.
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.
autoPtr< Function1< scalar > > readMassFlowRate(const dictionary &dict, CloudType &owner, const scalar duration)
Read the mass flow rate function for continuous injections.
uniformParcelSize uniformParcelSize_
Size uniform to all parcels.
label sizeSampleQ() const
Return the sampling moment to be used by the size distribution.
bool findCellAtPosition(const meshSearch &searchEngine, const point &position, barycentric &coordinates, label &celli, label &tetFacei, label &tetPti, bool errorOnNotFound=true)
Find the cell that contains the supplied position.
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.
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.
CloudType::parcelType parcelType
Convenience typedef for parcelType.
scalar SOI_
Start of injection [s].
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: PairI.H:121
const Type & first() const
Return first.
Definition: PairI.H:107
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:62
const unitSet & userUnits() const
Return the user-time unit conversion.
Definition: Time.C:877
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
Base class for clouds. Provides a basic evolution algorithm, models, and a database for caching deriv...
Definition: cloud.H:61
A list of keywords followed by any number of values (e.g. words and numbers) or sub-dictionaries.
Definition: dictionary.H:162
ITstream & lookup(const word &, bool recursive=false, bool patternMatch=true) const
Find and return an entry data stream.
Definition: dictionary.C:669
uniformParcelSize
Enumeration for the parcels' uniform size.
static const NamedEnum< uniformParcelSize, 3 > uniformParcelSizeNames_
Names of the parcels' uniform size.
Mesh object that implements searches within the local cells and faces.
Definition: meshSearch.H:59
label findCell(const point &p, const pointInCellShapes=pointInCellShapes::tets) const
Find the cell containing the given point.
Definition: meshSearch.C:173
static const meshSearch & New(const polyMesh &mesh, const pointInCellShapes=pointInCellShapes::tets)
Lookup or construct from mesh and cell decomposition option.
Definition: meshSearch.C:61
const Time & time() const
Return time.
Base particle class.
Definition: particle.H:85
Mesh consisting of general polyhedral cells.
Definition: polyMesh.H:78
label nGeometricD() const
Return the number of valid geometric dimensions in the mesh.
Definition: polyMesh.C:1041
const Vector< label > & solutionD() const
Return the vector of solved-for directions in mesh.
Definition: polyMesh.C:1047
const dictionary & dict() const
Return const access to the cloud dictionary.
Definition: subModelBase.C:109
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
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
#define IOWarningInFunction(ios)
Report an IO warning using Foam::Warning.
#define WarningInFunction
Report a warning using Foam::Warning.
const dimensionSet dimless
const dimensionSet time
const dimensionSet mass
const dimensionSet volume
Definition: annulus.H:177
void constrainDirection(const polyMesh &mesh, const Vector< label > &dirs, vector &d)
Set the constrained components of directions/velocity to zero.
Definition: meshTools.C:671
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 & lookup(const word &unitName)
Lookup and return the named unit from the table.
Definition: units.C:346
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
const dimensionSet & dimMass
Definition: dimensions.C:140
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:288
messageStream Info
void reduce(const List< UPstream::commsStruct > &comms, T &Value, const BinaryOp &bop, const int tag, const label comm)
const dimensionSet & dimTime
Definition: dimensions.C:142
T returnReduce(const T &Value, const BinaryOp &bop, const int tag=Pstream::msgType(), const label comm=UPstream::worldComm)
dimensioned< Type > min(const DimensionedField< Type, GeoMesh, PrimitiveField > &df)
IOerror FatalIOError
tmp< DimensionedField< scalar, GeoMesh, Field > > mag(const DimensionedField< Type, GeoMesh, PrimitiveField > &df)
error FatalError
static const label labelMax
Definition: label.H:62
tmp< DimensionedField< TypeR, GeoMesh, Field > > New(const tmp< DimensionedField< TypeR, GeoMesh, Field >> &tdf1, const word &name, const dimensionSet &dimensions)
static const char nl
Definition: Ostream.H:297
dimensioned< Type > max(const DimensionedField< Type, GeoMesh, PrimitiveField > &df)
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