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-2025 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.db().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() || haveMassFlowRate)
126  {
127  return
129  (
130  "massFlowRate",
131  this->owner().db().time().userUnits(),
133  dict
134  );
135  }
136 
137  const scalar massTotal = dict.lookup<scalar>("massTotal", dimMass);
138 
139  if (!dict.found("flowRateProfile"))
140  {
141  return
143  (
145  (
146  "massFlowRate",
147  massTotal/duration
148  )
149  );
150  }
151 
152  autoPtr<Function1<scalar>> flowRateProfile
153  (
155  (
156  "flowRateProfile",
157  this->owner().db().time().userUnits(),
158  dimless,
159  dict
160  )
161  );
162 
163  const scalar sumFlowRateProfile = flowRateProfile->integral(0, duration);
164 
165  return
167  (
169  (
170  "massFlowRate",
171  Function1s::Constant<scalar>("m", massTotal/sumFlowRateProfile),
172  Function1s::Constant<scalar>("one", scalar(1)),
173  flowRateProfile()
174  )
175  );
176 }
177 
178 
179 template<class CloudType>
182 (
183  const dictionary& dict,
184  CloudType& owner
185 )
186 {
187  return
189  (
190  "parcelsPerSecond",
191  this->owner().db().time().userUnits(),
193  dict
194  );
195 }
196 
197 
198 template<class CloudType>
200 {
201  forAll(this->owner().injectors(), i)
202  {
203  if (this->owner().injectors()(i) == this)
204  {
205  return i;
206  }
207  }
208 
209  return -1;
210 }
211 
212 
213 template<class CloudType>
215 (
216  const meshSearch& searchEngine,
217  const point& position,
219  label& celli,
220  label& tetFacei,
221  label& tetPti,
222  bool errorOnNotFound
223 )
224 {
225  // Subroutine for finding the cell
226  auto findProcAndCell = [this,&searchEngine](const point& pos)
227  {
228  // Find the containing cell
229  label celli = searchEngine.findCell(pos);
230 
231  // Synchronise so only a single processor finds this position
232  label proci = celli >= 0 ? Pstream::myProcNo() : -1;
233  reduce(proci, maxOp<label>());
234  if (proci != Pstream::myProcNo())
235  {
236  celli = -1;
237  }
238 
239  return labelPair(proci, celli);
240  };
241 
242  point pos = position;
243 
244  // Try and find the cell at the given position
245  const labelPair procAndCelli = findProcAndCell(pos);
246  label proci = procAndCelli.first();
247  celli = procAndCelli.second();
248 
249  // Didn't find it. The point may be awkwardly on an edge or face. Try
250  // again, but move the point into its nearest cell a little bit.
251  if (proci == -1)
252  {
253  pos += small*(this->owner().mesh().C()[celli] - pos);
254  const labelPair procAndCelli = findProcAndCell(pos);
255  proci = procAndCelli.first();
256  celli = procAndCelli.second();
257  }
258 
259  // Didn't find it. Error or return false.
260  if (proci == -1)
261  {
262  if (errorOnNotFound)
263  {
265  << "Cannot find parcel injection cell. "
266  << "Parcel position = " << position << nl
267  << exit(FatalError);
268  }
269 
270  return false;
271  }
272 
273  // Found it. Construct the barycentric coordinates.
274  if (proci == Pstream::myProcNo())
275  {
276  label nLocateBoundaryHits = 0;
277  particle p(searchEngine, pos, celli, nLocateBoundaryHits);
278 
279  if (nLocateBoundaryHits != 0)
280  {
282  << "Injection model " << this->modelName()
283  << " for cloud " << this->owner().name()
284  << " did not accurately locate the position "
285  << pos << " within the mesh" << endl;
286  }
287 
288  coordinates = p.coordinates();
289  celli = p.cell();
290  tetFacei = p.tetFace();
291  tetPti = p.tetPt();
292  }
293 
294  return true;
295 }
296 
297 
298 template<class CloudType>
300 (
301  typename CloudType::parcelType::trackingData& td,
302  typename CloudType::parcelType& parcel
303 )
304 {
305  const vector d = parcel.deviationFromMeshCentre(td.mesh);
306 
307  if (d == vector::zero)
308  {
309  return;
310  }
311 
312  const label facei = parcel.face();
313 
314  // If the parcel is not on a face, then just track it to the mesh centre
315  if (facei == -1)
316  {
317  parcel.track(td.mesh, - d, 0);
318  }
319 
320  // If the parcel is on a face, then track in two steps, going slightly into
321  // the current cell. This prevents a boundary hit from ending the track
322  // prematurely.
323  if (facei != -1)
324  {
325  const vector pc =
326  td.mesh.cellCentres()[parcel.cell()] - parcel.position(td.mesh);
327 
328  parcel.track(td.mesh, - d/2 + rootSmall*pc, 0);
329  parcel.track(td.mesh, - d/2 - rootSmall*pc, 0);
330  }
331 
332  // Restore any face-association that got changed during tracking
333  parcel.face() = facei;
334 }
335 
336 
337 template<class CloudType>
339 {
340  switch (uniformParcelSize_)
341  {
342  case uniformParcelSize::nParticle:
343  return 0;
344  case uniformParcelSize::surfaceArea:
345  return 2;
346  case uniformParcelSize::volume:
347  return 3;
348  }
349 
350  return -labelMax;
351 }
352 
353 
354 template<class CloudType>
356 (
357  PtrList<parcelType>& parcelPtrs,
358  const scalar mass
359 ) const
360 {
361  auto size = [&](const parcelType& p)
362  {
363  switch (uniformParcelSize_)
364  {
365  case uniformParcelSize::nParticle:
366  return scalar(1);
367  case uniformParcelSize::surfaceArea:
368  return p.areaS();
369  case uniformParcelSize::volume:
370  return p.volume();
371  }
372  return NaN;
373  };
374 
375  // Determine the total mass and size of all created particles
376  scalar sumMassBySize = 0;
377  forAll(parcelPtrs, parceli)
378  {
379  if (parcelPtrs.set(parceli))
380  {
381  const parcelType& p = parcelPtrs[parceli];
382  sumMassBySize += p.mass()/size(p);
383  }
384  }
385 
386  reduce(sumMassBySize, sumOp<scalar>());
387 
388  // Set the numbers of particles on each parcel
389  forAll(parcelPtrs, parceli)
390  {
391  if (parcelPtrs.set(parceli))
392  {
393  parcelType& p = parcelPtrs[parceli];
394  p.nParticle() = mass/size(p)/sumMassBySize;
395  }
396  }
397 
398  // Check that the constraints are correct
399  if (debug)
400  {
401  scalar massN = 0, minSizeN = vGreat, maxSizeN = -vGreat;
402  forAll(parcelPtrs, parceli)
403  {
404  if (parcelPtrs.set(parceli))
405  {
406  const parcelType& p = parcelPtrs[parceli];
407  massN += p.nParticle()*p.mass();
408  minSizeN = min(minSizeN, p.nParticle()*size(p));
409  maxSizeN = max(minSizeN, p.nParticle()*size(p));
410  }
411  }
412 
413  reduce(massN, sumOp<scalar>());
414 
415  if (mag(massN - mass) > rootSmall*(massN + mass)/2)
416  {
418  << "Parcels do not have the required mass"
419  << exit(FatalError);
420  }
421 
422  reduce(minSizeN, minOp<scalar>());
423  reduce(maxSizeN, maxOp<scalar>());
424 
425  if (maxSizeN - minSizeN > rootSmall*(maxSizeN + minSizeN)/2)
426  {
428  << "Parcel sizes are not uniform"
429  << exit(FatalError);
430  }
431  }
432 }
433 
434 
435 template<class CloudType>
437 (
438  typename parcelType::trackingData& td
439 )
440 {}
441 
442 
443 template<class CloudType>
445 (
446  const label nParcelsAdded,
447  const scalar massAdded,
448  typename parcelType::trackingData& td
449 )
450 {
451  const label allNParcelsAdded = returnReduce(nParcelsAdded, sumOp<label>());
452 
453  if (allNParcelsAdded > 0)
454  {
455  Info<< nl
456  << "Cloud: " << this->owner().name()
457  << " injector: " << this->modelName() << nl
458  << " Added " << allNParcelsAdded << " new parcels" << nl << endl;
459  }
460 
461  // Increment total number of parcels added
462  nParcelsInjected_ += allNParcelsAdded;
463 
464  // Increment total mass injected
465  massInjected_ += returnReduce(massAdded, sumOp<scalar>());
466 }
467 
468 
469 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
470 
471 template<class CloudType>
473 :
475  SOI_(0),
476  massInjected_(this->template getModelProperty<scalar>("massInjected")),
477  nParcelsInjected_
478  (
479  this->template getModelProperty<scalar>("nParcelsInjected")
480  ),
481  massDeferred_
482  (
483  this->template getModelProperty<scalar>("massDeferred")
484  ),
485  nParcelsDeferred_
486  (
487  this->template getModelProperty<scalar>("nParcelsDeferred")
488  ),
489  nParticleFixed_(-vGreat),
490  uniformParcelSize_(uniformParcelSize::nParticle)
491 {}
492 
493 
494 template<class CloudType>
496 (
497  const dictionary& dict,
498  CloudType& owner,
499  const word& modelName,
500  const word& modelType
501 )
502 :
503  CloudSubModelBase<CloudType>(modelName, owner, dict, typeName, modelType),
504  SOI_(0),
505  massInjected_(this->template getModelProperty<scalar>("massInjected")),
506  nParcelsInjected_
507  (
508  this->template getModelProperty<scalar>("nParcelsInjected")
509  ),
510  massDeferred_
511  (
512  this->template getModelProperty<scalar>("massDeferred")
513  ),
514  nParcelsDeferred_
515  (
516  this->template getModelProperty<scalar>("nParcelsDeferred")
517  ),
518  nParticleFixed_(dict.lookupOrDefault<scalar>("nParticle", -vGreat)),
519  uniformParcelSize_
520  (
521  uniformParcelSizeNames_
522  [
523  !dict.found("parcelBasisType") && nParticleFixed_ > 0
524  ? dict.lookupOrDefault<word>
525  (
526  "uniformParcelSize",
527  uniformParcelSizeNames_[uniformParcelSize::nParticle]
528  )
529  : dict.lookup<word>("uniformParcelSize")
530  ]
531  )
532 {
533  // Provide some info. Also serves to initialise mesh dimensions. This may
534  // be needed for parallel runs due to lazy evaluation of valid mesh
535  // dimensions.
536  Info<< " Constructing " << owner.mesh().nGeometricD() << "-D injection"
537  << endl;
538 
539  if
540  (
541  nParticleFixed_ > 0
543  )
544  {
546  << "If nParticle is specified then the uniformParcelSize must be "
548  << exit(FatalIOError);
549  }
550 
551  if (owner.solution().transient())
552  {
553  SOI_ = dict.lookup<scalar>("SOI", owner.db().time().userUnits());
554  }
555 }
556 
557 
558 template<class CloudType>
560 (
561  const InjectionModel<CloudType>& im
562 )
563 :
565  SOI_(im.SOI_),
566  massInjected_(im.massInjected_),
567  nParcelsInjected_(im.nParcelsInjected_),
568  massDeferred_(im.massDeferred_),
569  nParcelsDeferred_(im.nParcelsDeferred_),
570  nParticleFixed_(im.nParticleFixed_),
571  uniformParcelSize_(im.uniformParcelSize_)
572 {}
573 
574 
575 // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
576 
577 template<class CloudType>
579 {}
580 
581 
582 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
583 
584 template<class CloudType>
586 {}
587 
588 
589 template<class CloudType>
591 {
592  const scalar deltaT =
593  this->owner().solution().transient() ? timeEnd() - timeStart() : 1;
594 
595  return massToInject(0, deltaT)/nParcelsToInject(0, deltaT);
596 }
597 
598 
599 template<class CloudType>
600 template<class TrackCloudType>
602 (
603  TrackCloudType& cloud,
604  typename CloudType::parcelType::trackingData& td
605 )
606 {
607  const polyMesh& mesh = this->owner().mesh();
608 
609  const meshSearch& searchEngine = meshSearch::New(mesh);
610 
611  const scalar time1 = this->owner().db().time().value();
612  const scalar time0 =
613  this->owner().db().time().value()
614  - this->owner().db().time().deltaTValue();
615 
616  preInject(td);
617 
618  // Reset counters
619  label nParcelsAdded = 0;
620  scalar massAdded = 0;
621 
622  // Get amounts to inject
623  label nParcels;
624  scalar mass = NaN;
625  {
626  // Injection has started. Get amounts between times.
627  const scalar t0 = time0 - SOI_, t1 = time1 - SOI_;
628 
629  // Get the number of parcels to inject, round it down to the nearest
630  // integer, and then store the excess to apply at a later time
631  const scalar nParcelsNoRound =
632  nParcelsToInject(t0, t1) + nParcelsDeferred_;
633  nParcels = floor(nParcelsNoRound);
634  nParcelsDeferred_ = nParcelsNoRound - nParcels;
635 
636  // If we need mass...
637  if (nParticleFixed_ < 0)
638  {
639  // Get the mass to inject, and if there are no parcels then store
640  // this to apply at a later time
641  mass = massToInject(t0, t1) + massDeferred_;
642  massDeferred_ = nParcels > 0 ? 0 : mass;
643 
644  // Special case. If there is no mass, then don't create anything.
645  // This is a hack so that mass flow rates that switch on and off
646  // still work tolerably with a constant number rate. Really,
647  // though, cases like these should be specified with a
648  // correspondingly varying number rate.
649  if (mass == 0)
650  {
651  nParcels = 0;
652  nParcelsDeferred_ = 0;
653  }
654  }
655  }
656 
657  // Do injection
658  if (nParcels > 0)
659  {
660  // Duration of injection period during this timestep
661  const scalar deltaT =
662  max
663  (
664  scalar(0),
665  min
666  (
667  td.trackTime(),
668  min
669  (
670  time1 - SOI_,
671  timeEnd() - time0
672  )
673  )
674  );
675 
676  // Pad injection time if injection starts during this timestep
677  const scalar padTime = max(scalar(0), SOI_ - time0);
678 
679  // Create new parcels linearly across carrier phase timestep
680  PtrList<parcelType> parcelPtrs(nParcels);
681  forAll(parcelPtrs, parceli)
682  {
683  // Calculate the pseudo time of injection for parcel 'parceli'
684  scalar timeInj = time0 + padTime + deltaT*parceli/nParcels;
685 
686  // Determine the injection coordinates and owner cell,
687  // tetFace and tetPt
689  label celli = -1, tetFacei = -1, tetPti = -1, facei = -1;
690  setPositionAndCell
691  (
692  searchEngine,
693  parceli,
694  nParcels,
695  timeInj,
696  coordinates,
697  celli,
698  tetFacei,
699  tetPti,
700  facei
701  );
702 
703  if (celli > -1)
704  {
705  // Lagrangian timestep
706  const scalar dt = timeInj - time0;
707 
708  // Create a new parcel
709  parcelPtrs.set
710  (
711  parceli,
712  new parcelType
713  (
714  mesh,
715  coordinates,
716  celli,
717  tetFacei,
718  tetPti,
719  facei
720  )
721  );
722  parcelType& p = parcelPtrs[parceli];
723 
724  // Correct the position for reduced-dimension cases
725  constrainPosition(td, p);
726 
727  // Check/set new parcel thermo properties
728  cloud.setParcelThermoProperties(p);
729 
730  // Assign new parcel properties in injection model
731  setProperties(parceli, nParcels, timeInj, td, p);
732 
733  // Check/set new parcel injection properties
734  cloud.checkParcelProperties(p, index());
735 
736  // Apply correction to velocity for 2-D cases
738  (
739  mesh,
740  mesh.solutionD(),
741  p.U()
742  );
743 
744  // Modify the step fraction so that the particles are
745  // injected continually through the time-step
746  p.stepFraction() = dt/td.trackTime();
747 
748  // Set the number of particles. If not fixed, this will set
749  // a junk value, which will get corrected below.
750  p.nParticle() = nParticleFixed_;
751  }
752  }
753 
754  // Set the number of particles so that the introduced mass is correct
755  // and the uniform size is as specified
756  if (nParticleFixed_ < 0)
757  {
758  setNumberOfParticles(parcelPtrs, mass);
759  }
760 
761  // Add the new parcels
762  forAll(parcelPtrs, parceli)
763  {
764  if (parcelPtrs.set(parceli))
765  {
766  parcelType& p = parcelPtrs[parceli];
767  nParcelsAdded ++;
768  massAdded += p.nParticle()*p.mass();
769  cloud.addParticle(parcelPtrs.set(parceli, nullptr).ptr());
770  }
771  }
772  }
773 
774  postInject(nParcelsAdded, massAdded, td);
775 }
776 
777 
778 template<class CloudType>
779 template<class TrackCloudType>
781 (
782  TrackCloudType& cloud,
783  typename CloudType::parcelType::trackingData& td
784 )
785 {
786  const polyMesh& mesh = this->owner().mesh();
787 
788  const meshSearch& searchEngine = meshSearch::New(mesh);
789 
790  preInject(td);
791 
792  // Reset counters
793  label nParcelsAdded = 0;
794  scalar massAdded = 0;
795 
796  // Get amounts to inject based on first second of injection
797  const label nParcels = floor(nParcelsToInject(0, 1));
798  const scalar mass = nParticleFixed_ < 0 ? massToInject(0, 1) : NaN;
799 
800  // Do injection
801  if (nParcels > 0)
802  {
803  PtrList<parcelType> parcelPtrs(nParcels);
804  forAll(parcelPtrs, parceli)
805  {
806  // Determine the injection coordinates and owner cell,
807  // tetFace and tetPt
809  label celli = -1, tetFacei = -1, tetPti = -1, facei = -1;
810  setPositionAndCell
811  (
812  searchEngine,
813  parceli,
814  nParcels,
815  0,
816  coordinates,
817  celli,
818  tetFacei,
819  tetPti,
820  facei
821  );
822 
823  if (celli > -1)
824  {
825  // Create a new parcel
826  parcelPtrs.set
827  (
828  parceli,
829  new parcelType
830  (
831  mesh,
832  coordinates,
833  celli,
834  tetFacei,
835  tetPti,
836  facei
837  )
838  );
839  parcelType& p = parcelPtrs[parceli];
840 
841  // Correct the position for reduced-dimension cases
842  constrainPosition(td, p);
843 
844  // Check/set new parcel thermo properties
845  cloud.setParcelThermoProperties(p);
846 
847  // Assign new parcel properties in injection model
848  setProperties(parceli, nParcels, 0, td, p);
849 
850  // Check/set new parcel injection properties
851  cloud.checkParcelProperties(p, index());
852 
853  // Apply correction to velocity for 2-D cases
855 
856  // Initial step fraction
857  p.stepFraction() = 0;
858 
859  // Set the number of particles. If not fixed, this will set
860  // a junk value, which will get corrected below.
861  p.nParticle() = nParticleFixed_;
862  }
863  }
864 
865  // Set the number of particles so that the introduced mass is correct
866  // and the uniform size is as specified
867  if (nParticleFixed_ < 0)
868  {
869  setNumberOfParticles(parcelPtrs, mass);
870  }
871 
872  // Add the new parcels
873  forAll(parcelPtrs, parceli)
874  {
875  if (parcelPtrs.set(parceli))
876  {
877  parcelType& p = parcelPtrs[parceli];
878  nParcelsAdded ++;
879  massAdded += p.nParticle()*p.mass();
880  cloud.addParticle(parcelPtrs.set(parceli, nullptr).ptr());
881  }
882  }
883  }
884 
885  postInject(nParcelsAdded, massAdded, td);
886 }
887 
888 
889 template<class CloudType>
891 {
892  os << " " << this->modelName() << ":" << nl
893  << " number of parcels added = " << nParcelsInjected_ << nl
894  << " mass introduced = " << massInjected_ << nl;
895 
896  if (this->writeTime())
897  {
898  this->setModelProperty("massInjected", massInjected_);
899  this->setModelProperty("nParcelsInjected", nParcelsInjected_);
900  this->setModelProperty("massDeferred", massDeferred_);
901  this->setModelProperty("nParcelsDeferred", nParcelsDeferred_);
902  }
903 }
904 
905 
906 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
907 
908 #include "InjectionModelNew.C"
909 
910 // ************************************************************************* //
bool found
#define forAll(list, i)
Loop across all elements in list.
Definition: UList.H:433
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:125
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:309
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 unitConversion & userUnits() const
Return the user-time unit conversion.
Definition: Time.C:858
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:63
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:740
const polyMesh & mesh() const
Return reference to polyMesh.
Definition: fvMesh.H:443
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:1021
const Vector< label > & solutionD() const
Return the vector of solved-for directions in mesh.
Definition: polyMesh.C:1027
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
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.
autoPtr< CompressibleMomentumTransportModel > New(const volScalarField &rho, const volVectorField &U, const surfaceScalarField &phi, const viscosity &viscosity)
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
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:258
const dimensionSet dimless
messageStream Info
void mag(LagrangianPatchField< scalar > &f, const LagrangianPatchField< Type > &f1)
layerAndWeight min(const layerAndWeight &a, const layerAndWeight &b)
const dimensionSet dimTime
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)
layerAndWeight max(const layerAndWeight &a, const layerAndWeight &b)
IOerror FatalIOError
const dimensionSet dimMass
error FatalError
static const label labelMax
Definition: label.H:62
static const char nl
Definition: Ostream.H:267
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