DSMCCloud.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 "DSMCCloud.H"
27 #include "NoBinaryCollision.H"
28 #include "WallInteractionModel.H"
29 #include "InflowBoundaryModel.H"
30 #include "constants.H"
33 
34 using namespace Foam::constant;
35 
36 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
37 
38 template<class ParcelType>
40 {
41  Info<< nl << "Constructing constant properties for" << endl;
42  constProps_.setSize(typeIdList_.size());
43 
44  dictionary moleculeProperties
45  (
46  particleProperties_.subDict("moleculeProperties")
47  );
48 
49  forAll(typeIdList_, i)
50  {
51  const word& id(typeIdList_[i]);
52 
53  Info<< " " << id << endl;
54 
55  const dictionary& molDict(moleculeProperties.subDict(id));
56 
57  constProps_[i] =
58  typename ParcelType::constantProperties(molDict);
59  }
60 }
61 
62 
63 template<class ParcelType>
65 {
66  forAll(cellOccupancy_, cO)
67  {
68  cellOccupancy_[cO].clear();
69  }
70 
71  forAllIter(typename DSMCCloud<ParcelType>, *this, iter)
72  {
73  cellOccupancy_[iter().cell()].append(&iter());
74  }
75 }
76 
77 
78 template<class ParcelType>
80 (
81  const IOdictionary& dsmcInitialiseDict
82 )
83 {
84  Info<< nl << "Initialising particles" << endl;
85 
86  const scalar temperature
87  (
88  dsmcInitialiseDict.template lookup<scalar>("temperature")
89  );
90 
91  const vector velocity(dsmcInitialiseDict.lookup("velocity"));
92 
93  const dictionary& numberDensitiesDict
94  (
95  dsmcInitialiseDict.subDict("numberDensities")
96  );
97 
98  const meshSearch& searchEngine = meshSearch::New(mesh_);
99 
100  List<word> molecules(numberDensitiesDict.toc());
101 
102  Field<scalar> numberDensities(molecules.size());
103 
104  forAll(molecules, i)
105  {
106  numberDensities[i] =
107  numberDensitiesDict.lookup<scalar>(molecules[i]);
108  }
109 
110  numberDensities /= nParticle_;
111 
112  label nLocateBoundaryHits = 0;
113 
114  forAll(mesh_.cells(), celli)
115  {
117  (
118  mesh_,
119  celli
120  );
121 
122  forAll(cellTets, tetI)
123  {
124  const tetIndices& cellTetIs = cellTets[tetI];
125  tetPointRef tet = cellTetIs.tet(mesh_);
126  scalar tetVolume = tet.mag();
127 
128  forAll(molecules, i)
129  {
130  const word& moleculeName(molecules[i]);
131 
132  label typeId(findIndex(typeIdList_, moleculeName));
133 
134  if (typeId == -1)
135  {
137  << "typeId " << moleculeName << "not defined." << nl
138  << abort(FatalError);
139  }
140 
141  const typename ParcelType::constantProperties& cP =
142  constProps(typeId);
143 
144  scalar numberDensity = numberDensities[i];
145 
146  // Calculate the number of particles required
147  scalar particlesRequired = numberDensity*tetVolume;
148 
149  // Only integer numbers of particles can be inserted
150  label nParticlesToInsert = label(particlesRequired);
151 
152  // Add another particle with a probability proportional to the
153  // remainder of taking the integer part of particlesRequired
154  if
155  (
156  (particlesRequired - nParticlesToInsert)
157  > rndGen_.scalar01()
158  )
159  {
160  nParticlesToInsert++;
161  }
162 
163  for (label pI = 0; pI < nParticlesToInsert; pI++)
164  {
165  point p = tet.randomPoint(rndGen_);
166 
167  vector U = equipartitionLinearVelocity
168  (
169  temperature,
170  cP.mass()
171  );
172 
173  scalar Ei = equipartitionInternalEnergy
174  (
175  temperature,
176  cP.internalDegreesOfFreedom()
177  );
178 
179  U += velocity;
180 
181  addNewParcel
182  (
183  searchEngine,
184  p,
185  celli,
186  nLocateBoundaryHits,
187  U,
188  Ei,
189  typeId
190  );
191  }
192  }
193  }
194  }
195 
196  reduce(nLocateBoundaryHits, sumOp<label>());
197  if (nLocateBoundaryHits != 0)
198  {
200  << "Initialisation of cloud " << this->name()
201  << " did not accurately locate " << nLocateBoundaryHits
202  << " particles" << endl;
203  }
204 
205  // Initialise the sigmaTcRMax_ field to the product of the cross section of
206  // the most abundant species and the most probable thermal speed (Bird,
207  // p222-223)
208 
209  label mostAbundantType(findMax(numberDensities));
210 
211  const typename ParcelType::constantProperties& cP = constProps
212  (
213  mostAbundantType
214  );
215 
216  sigmaTcRMax_.primitiveFieldRef() = cP.sigmaT()*maxwellianMostProbableSpeed
217  (
218  temperature,
219  cP.mass()
220  );
221 
222  sigmaTcRMax_.correctBoundaryConditions();
223 }
224 
225 
226 template<class ParcelType>
228 {
229  if (isType<NoBinaryCollision<DSMCCloud<ParcelType>>>(binaryCollision()))
230  {
231  return;
232  }
233 
234  // Temporary storage for subCells
235  List<DynamicList<label>> subCells(8);
236 
237  scalar deltaT = mesh().time().deltaTValue();
238 
239  label collisionCandidates = 0;
240 
241  label collisions = 0;
242 
243  forAll(cellOccupancy_, celli)
244  {
245  const DynamicList<ParcelType*>& cellParcels(cellOccupancy_[celli]);
246 
247  label nC(cellParcels.size());
248 
249  if (nC > 1)
250  {
251  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
252  // Assign particles to one of 8 Cartesian subCells
253 
254  // Clear temporary lists
255  forAll(subCells, i)
256  {
257  subCells[i].clear();
258  }
259 
260  // Inverse addressing specifying which subCell a parcel is in
261  List<label> whichSubCell(cellParcels.size());
262 
263  const point& cC = mesh_.cellCentres()[celli];
264 
265  forAll(cellParcels, i)
266  {
267  const ParcelType& p = *cellParcels[i];
268  vector relPos = p.position(mesh()) - cC;
269 
270  label subCell =
271  pos0(relPos.x()) + 2*pos0(relPos.y()) + 4*pos0(relPos.z());
272 
273  subCells[subCell].append(i);
274  whichSubCell[i] = subCell;
275  }
276 
277  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
278 
279  scalar sigmaTcRMax = sigmaTcRMax_[celli];
280 
281  scalar selectedPairs =
282  collisionSelectionRemainder_[celli]
283  + 0.5*nC*(nC - 1)*nParticle_*sigmaTcRMax*deltaT
284  /mesh_.cellVolumes()[celli];
285 
286  label nCandidates(selectedPairs);
287  collisionSelectionRemainder_[celli] = selectedPairs - nCandidates;
288  collisionCandidates += nCandidates;
289 
290  for (label c = 0; c < nCandidates; c++)
291  {
292  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
293  // subCell candidate selection procedure
294 
295  // Select the first collision candidate
296  label candidateP = rndGen_.sampleAB<label>(0, nC);
297 
298  // Declare the second collision candidate
299  label candidateQ = -1;
300 
301  List<label> subCellPs = subCells[whichSubCell[candidateP]];
302  label nSC = subCellPs.size();
303 
304  if (nSC > 1)
305  {
306  // If there are two or more particle in a subCell, choose
307  // another from the same cell. If the same candidate is
308  // chosen, choose again.
309 
310  do
311  {
312  candidateQ = subCellPs[rndGen_.sampleAB<label>(0, nSC)];
313  } while (candidateP == candidateQ);
314  }
315  else
316  {
317  // Select a possible second collision candidate from the
318  // whole cell. If the same candidate is chosen, choose
319  // again.
320 
321  do
322  {
323  candidateQ = rndGen_.sampleAB<label>(0, nC);
324  } while (candidateP == candidateQ);
325  }
326 
327  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
328  // uniform candidate selection procedure
329 
330  // // Select the first collision candidate
331  // label candidateP = rndGen_.sampleAB<label>(0, nC);
332 
333  // // Select a possible second collision candidate
334  // label candidateQ = rndGen_.sampleAB<label>(0, nC);
335 
336  // // If the same candidate is chosen, choose again
337  // while (candidateP == candidateQ)
338  // {
339  // candidateQ = rndGen_.sampleAB<label>(0, nC);
340  // }
341 
342  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
343 
344  ParcelType& parcelP = *cellParcels[candidateP];
345  ParcelType& parcelQ = *cellParcels[candidateQ];
346 
347  scalar sigmaTcR = binaryCollision().sigmaTcR
348  (
349  parcelP,
350  parcelQ
351  );
352 
353  // Update the maximum value of sigmaTcR stored, but use the
354  // initial value in the acceptance-rejection criteria because
355  // the number of collision candidates selected was based on this
356 
357  if (sigmaTcR > sigmaTcRMax_[celli])
358  {
359  sigmaTcRMax_[celli] = sigmaTcR;
360  }
361 
362  if ((sigmaTcR/sigmaTcRMax) > rndGen_.scalar01())
363  {
364  binaryCollision().collide
365  (
366  parcelP,
367  parcelQ
368  );
369 
370  collisions++;
371  }
372  }
373  }
374  }
375 
376  reduce(collisions, sumOp<label>());
377 
378  reduce(collisionCandidates, sumOp<label>());
379 
380  sigmaTcRMax_.correctBoundaryConditions();
381 
382  if (collisionCandidates)
383  {
384  Info<< " Collisions = "
385  << collisions << nl
386  << " Acceptance rate = "
387  << scalar(collisions)/scalar(collisionCandidates) << nl
388  << endl;
389  }
390  else
391  {
392  Info<< " No collisions" << endl;
393  }
394 }
395 
396 
397 template<class ParcelType>
399 {
400  q_ = dimensionedScalar( dimensionSet(1, 0, -3, 0, 0), 0);
401 
402  fD_ = dimensionedVector
403  (
404  "zero",
405  dimensionSet(1, -1, -2, 0, 0),
406  Zero
407  );
408 
409  rhoN_ = dimensionedScalar( dimensionSet(0, -3, 0, 0, 0), vSmall);
410  rhoM_ = dimensionedScalar( dimensionSet(1, -3, 0, 0, 0), vSmall);
411  dsmcRhoN_ = dimensionedScalar( dimensionSet(0, -3, 0, 0, 0), 0);
412  linearKE_ = dimensionedScalar( dimensionSet(1, -1, -2, 0, 0), 0);
413  internalE_ = dimensionedScalar( dimensionSet(1, -1, -2, 0, 0), 0);
414  iDof_ = dimensionedScalar( dimensionSet(0, -3, 0, 0, 0), vSmall);
415 
416  momentum_ = dimensionedVector
417  (
418  "zero",
419  dimensionSet(1, -2, -1, 0, 0),
420  Zero
421  );
422 }
423 
424 
425 template<class ParcelType>
427 {
428  scalarField& rhoN = rhoN_.primitiveFieldRef();
429  scalarField& rhoM = rhoM_.primitiveFieldRef();
430  scalarField& dsmcRhoN = dsmcRhoN_.primitiveFieldRef();
431  scalarField& linearKE = linearKE_.primitiveFieldRef();
432  scalarField& internalE = internalE_.primitiveFieldRef();
433  scalarField& iDof = iDof_.primitiveFieldRef();
434  vectorField& momentum = momentum_.primitiveFieldRef();
435 
436  forAllConstIter(typename DSMCCloud<ParcelType>, *this, iter)
437  {
438  const ParcelType& p = iter();
439  const label celli = p.cell();
440 
441  rhoN[celli]++;
442  rhoM[celli] += constProps(p.typeId()).mass();
443  dsmcRhoN[celli]++;
444  linearKE[celli] += 0.5*constProps(p.typeId()).mass()*(p.U() & p.U());
445  internalE[celli] += p.Ei();
446  iDof[celli] += constProps(p.typeId()).internalDegreesOfFreedom();
447  momentum[celli] += constProps(p.typeId()).mass()*p.U();
448  }
449 
450  rhoN *= nParticle_/mesh().cellVolumes();
451  rhoN_.correctBoundaryConditions();
452 
453  rhoM *= nParticle_/mesh().cellVolumes();
454  rhoM_.correctBoundaryConditions();
455 
456  dsmcRhoN_.correctBoundaryConditions();
457 
458  linearKE *= nParticle_/mesh().cellVolumes();
459  linearKE_.correctBoundaryConditions();
460 
461  internalE *= nParticle_/mesh().cellVolumes();
462  internalE_.correctBoundaryConditions();
463 
464  iDof *= nParticle_/mesh().cellVolumes();
465  iDof_.correctBoundaryConditions();
466 
467  momentum *= nParticle_/mesh().cellVolumes();
468  momentum_.correctBoundaryConditions();
469 }
470 
471 
472 // * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * * //
473 
474 template<class ParcelType>
476 (
477  const meshSearch& searchEngine,
478  const vector& position,
479  const label celli,
480  label& nLocateBoundaryHits,
481  const vector& U,
482  const scalar Ei,
483  const label typeId
484 )
485 {
486  this->addParticle
487  (
488  new ParcelType
489  (
490  searchEngine,
491  position,
492  celli,
493  nLocateBoundaryHits,
494  U,
495  Ei,
496  typeId
497  )
498  );
499 }
500 
501 
502 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
503 
504 template<class ParcelType>
506 (
507  const word& cloudName,
508  const fvMesh& mesh,
509  bool readFields
510 )
511 :
512  lagrangian::Cloud<ParcelType>(mesh, cloudName, false),
513  cloudName_(cloudName),
514  mesh_(mesh),
515  particleProperties_
516  (
517  IOobject
518  (
519  cloudName + "Properties",
520  mesh_.time().constant(),
521  mesh_,
522  IOobject::MUST_READ_IF_MODIFIED,
523  IOobject::NO_WRITE
524  )
525  ),
526  typeIdList_(particleProperties_.lookup("typeIdList")),
527  nParticle_
528  (
529  particleProperties_.template lookup<scalar>("nEquivalentParticles")
530  ),
531  cellOccupancy_(mesh_.nCells()),
532  sigmaTcRMax_
533  (
534  IOobject
535  (
536  this->name() + "SigmaTcRMax",
537  mesh_.time().name(),
538  mesh_,
539  IOobject::MUST_READ,
540  IOobject::AUTO_WRITE
541  ),
542  mesh_
543  ),
544  collisionSelectionRemainder_
545  (
546  IOobject
547  (
548  this->name() + ":collisionSelectionRemainder",
549  mesh_.time().name(),
550  mesh_
551  ),
552  mesh_,
554  ),
555  q_
556  (
557  IOobject
558  (
559  "q",
560  mesh_.time().name(),
561  mesh_,
562  IOobject::MUST_READ,
563  IOobject::AUTO_WRITE
564  ),
565  mesh_
566  ),
567  fD_
568  (
569  IOobject
570  (
571  "fD",
572  mesh_.time().name(),
573  mesh_,
574  IOobject::MUST_READ,
575  IOobject::AUTO_WRITE
576  ),
577  mesh_
578  ),
579  rhoN_
580  (
581  IOobject
582  (
583  "rhoN",
584  mesh_.time().name(),
585  mesh_,
586  IOobject::MUST_READ,
587  IOobject::AUTO_WRITE
588  ),
589  mesh_
590  ),
591  rhoM_
592  (
593  IOobject
594  (
595  "rhoM",
596  mesh_.time().name(),
597  mesh_,
598  IOobject::MUST_READ,
599  IOobject::AUTO_WRITE
600  ),
601  mesh_
602  ),
603  dsmcRhoN_
604  (
605  IOobject
606  (
607  "dsmcRhoN",
608  mesh_.time().name(),
609  mesh_,
610  IOobject::MUST_READ,
611  IOobject::AUTO_WRITE
612  ),
613  mesh_
614  ),
615  linearKE_
616  (
617  IOobject
618  (
619  "linearKE",
620  mesh_.time().name(),
621  mesh_,
622  IOobject::MUST_READ,
623  IOobject::AUTO_WRITE
624  ),
625  mesh_
626  ),
627  internalE_
628  (
629  IOobject
630  (
631  "internalE",
632  mesh_.time().name(),
633  mesh_,
634  IOobject::MUST_READ,
635  IOobject::AUTO_WRITE
636  ),
637  mesh_
638  ),
639  iDof_
640  (
641  IOobject
642  (
643  "iDof",
644  mesh_.time().name(),
645  mesh_,
646  IOobject::MUST_READ,
647  IOobject::AUTO_WRITE
648  ),
649  mesh_
650  ),
651  momentum_
652  (
653  IOobject
654  (
655  "momentum",
656  mesh_.time().name(),
657  mesh_,
658  IOobject::MUST_READ,
659  IOobject::AUTO_WRITE
660  ),
661  mesh_
662  ),
663  constProps_(),
664  rndGen_(label(149382906)),
665  stdNormal_(rndGen_.generator()),
666  boundaryT_
667  (
669  (
670  IOobject
671  (
672  "boundaryT",
673  mesh_.time().name(),
674  mesh_,
675  IOobject::MUST_READ,
676  IOobject::AUTO_WRITE
677  ),
678  mesh_
679  )
680  ),
681  boundaryU_
682  (
684  (
685  IOobject
686  (
687  "boundaryU",
688  mesh_.time().name(),
689  mesh_,
690  IOobject::MUST_READ,
691  IOobject::AUTO_WRITE
692  ),
693  mesh_
694  )
695  ),
696  binaryCollisionModel_
697  (
698  BinaryCollisionModel<DSMCCloud<ParcelType>>::New
699  (
700  particleProperties_,
701  *this
702  )
703  ),
704  wallInteractionModel_
705  (
706  WallInteractionModel<DSMCCloud<ParcelType>>::New
707  (
708  particleProperties_,
709  *this
710  )
711  ),
712  inflowBoundaryModel_
713  (
714  InflowBoundaryModel<DSMCCloud<ParcelType>>::New
715  (
716  particleProperties_,
717  *this
718  )
719  )
720 {
721  buildConstProps();
722  buildCellOccupancy();
723 
724  // Initialise the collision selection remainder to a random value between 0
725  // and 1.
726  forAll(collisionSelectionRemainder_, i)
727  {
728  collisionSelectionRemainder_[i] = rndGen_.scalar01();
729  }
730 
731  if (readFields)
732  {
733  ParcelType::readFields(*this);
734  }
735 }
736 
737 
738 template<class ParcelType>
740 (
741  const word& cloudName,
742  const fvMesh& mesh,
743  const IOdictionary& dsmcInitialiseDict
744 )
745 :
746  lagrangian::Cloud<ParcelType>(mesh, cloudName, false),
747  cloudName_(cloudName),
748  mesh_(mesh),
749  particleProperties_
750  (
751  IOobject
752  (
753  cloudName + "Properties",
754  mesh_.time().constant(),
755  mesh_,
756  IOobject::MUST_READ_IF_MODIFIED,
757  IOobject::NO_WRITE
758  )
759  ),
760  typeIdList_(particleProperties_.lookup("typeIdList")),
761  nParticle_
762  (
763  particleProperties_.template lookup<scalar>("nEquivalentParticles")
764  ),
765  cellOccupancy_(),
766  sigmaTcRMax_
767  (
768  IOobject
769  (
770  this->name() + "SigmaTcRMax",
771  mesh_.time().name(),
772  mesh_,
773  IOobject::NO_READ,
774  IOobject::AUTO_WRITE
775  ),
776  mesh_,
777  dimensionedScalar( dimensionSet(0, 3, -1, 0, 0), 0),
778  zeroGradientFvPatchScalarField::typeName
779  ),
780  collisionSelectionRemainder_
781  (
782  IOobject
783  (
784  this->name() + ":collisionSelectionRemainder",
785  mesh_.time().name(),
786  mesh_
787  ),
788  mesh_,
790  ),
791  q_
792  (
793  IOobject
794  (
795  this->name() + "q_",
796  mesh_.time().name(),
797  mesh_,
798  IOobject::NO_READ,
799  IOobject::NO_WRITE
800  ),
801  mesh_,
802  dimensionedScalar( dimensionSet(1, 0, -3, 0, 0), 0)
803  ),
804  fD_
805  (
806  IOobject
807  (
808  this->name() + "fD_",
809  mesh_.time().name(),
810  mesh_,
811  IOobject::NO_READ,
812  IOobject::NO_WRITE
813  ),
814  mesh_,
816  (
817  "zero",
818  dimensionSet(1, -1, -2, 0, 0),
819  Zero
820  )
821  ),
822  rhoN_
823  (
824  IOobject
825  (
826  this->name() + "rhoN_",
827  mesh_.time().name(),
828  mesh_,
829  IOobject::NO_READ,
830  IOobject::NO_WRITE
831  ),
832  mesh_,
833  dimensionedScalar( dimensionSet(0, -3, 0, 0, 0), vSmall)
834  ),
835  rhoM_
836  (
837  IOobject
838  (
839  this->name() + "rhoM_",
840  mesh_.time().name(),
841  mesh_,
842  IOobject::NO_READ,
843  IOobject::NO_WRITE
844  ),
845  mesh_,
846  dimensionedScalar( dimensionSet(1, -3, 0, 0, 0), vSmall)
847  ),
848  dsmcRhoN_
849  (
850  IOobject
851  (
852  this->name() + "dsmcRhoN_",
853  mesh_.time().name(),
854  mesh_,
855  IOobject::NO_READ,
856  IOobject::NO_WRITE
857  ),
858  mesh_,
859  dimensionedScalar( dimensionSet(0, -3, 0, 0, 0), 0)
860  ),
861  linearKE_
862  (
863  IOobject
864  (
865  this->name() + "linearKE_",
866  mesh_.time().name(),
867  mesh_,
868  IOobject::NO_READ,
869  IOobject::NO_WRITE
870  ),
871  mesh_,
872  dimensionedScalar( dimensionSet(1, -1, -2, 0, 0), 0)
873  ),
874  internalE_
875  (
876  IOobject
877  (
878  this->name() + "internalE_",
879  mesh_.time().name(),
880  mesh_,
881  IOobject::NO_READ,
882  IOobject::NO_WRITE
883  ),
884  mesh_,
885  dimensionedScalar( dimensionSet(1, -1, -2, 0, 0), 0)
886  ),
887  iDof_
888  (
889  IOobject
890  (
891  this->name() + "iDof_",
892  mesh_.time().name(),
893  mesh_,
894  IOobject::NO_READ,
895  IOobject::NO_WRITE
896  ),
897  mesh_,
898  dimensionedScalar( dimensionSet(0, -3, 0, 0, 0), vSmall)
899  ),
900  momentum_
901  (
902  IOobject
903  (
904  this->name() + "momentum_",
905  mesh_.time().name(),
906  mesh_,
907  IOobject::NO_READ,
908  IOobject::NO_WRITE
909  ),
910  mesh_,
912  (
913  "zero",
914  dimensionSet(1, -2, -1, 0, 0),
915  Zero
916  )
917  ),
918  constProps_(),
919  rndGen_(label(971501)),
920  stdNormal_(rndGen_.generator()),
921  boundaryT_
922  (
924  (
925  IOobject
926  (
927  "boundaryT",
928  mesh_.time().name(),
929  mesh_,
930  IOobject::NO_READ,
931  IOobject::NO_WRITE
932  ),
933  mesh_,
934  dimensionedScalar( dimensionSet(0, 0, 0, 1, 0), 0)
935  )
936  ),
937  boundaryU_
938  (
940  (
941  IOobject
942  (
943  "boundaryU",
944  mesh_.time().name(),
945  mesh_,
946  IOobject::NO_READ,
947  IOobject::NO_WRITE
948  ),
949  mesh_,
951  (
952  "zero",
953  dimensionSet(0, 1, -1, 0, 0),
954  Zero
955  )
956  )
957  ),
958  binaryCollisionModel_(),
959  wallInteractionModel_(),
960  inflowBoundaryModel_()
961 {
962  clear();
963  buildConstProps();
964  initialise(dsmcInitialiseDict);
965 }
966 
967 
968 // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
969 
970 template<class ParcelType>
972 {}
973 
974 
975 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
976 
977 template<class ParcelType>
979 {
980  typename ParcelType::trackingData td(*this);
981 
982  // Reset the data collection fields
983  resetFields();
984 
985  if (debug)
986  {
987  this->dumpParticlePositions();
988  }
989 
990  // Insert new particles from the inflow boundary
991  this->inflowBoundary().inflow();
992 
993  // Move the particles ballistically with their current velocities
995 
996  // Update cell occupancy
997  buildCellOccupancy();
998 
999  // Calculate new velocities via stochastic collisions
1000  collisions();
1001 
1002  // Calculate the volume field data
1003  calculateFields();
1004 }
1005 
1006 
1007 template<class ParcelType>
1009 {
1010  label nDSMCParticles = this->size();
1011  reduce(nDSMCParticles, sumOp<label>());
1012 
1013  scalar nMol = nDSMCParticles*nParticle_;
1014 
1015  vector linearMomentum = linearMomentumOfSystem();
1016  reduce(linearMomentum, sumOp<vector>());
1017 
1018  scalar linearKineticEnergy = linearKineticEnergyOfSystem();
1019  reduce(linearKineticEnergy, sumOp<scalar>());
1020 
1021  scalar internalEnergy = internalEnergyOfSystem();
1022  reduce(internalEnergy, sumOp<scalar>());
1023 
1024  Info<< "Cloud name: " << this->name() << nl
1025  << " Number of dsmc particles = "
1026  << nDSMCParticles
1027  << endl;
1028 
1029  if (nDSMCParticles)
1030  {
1031  Info<< " Number of molecules = "
1032  << nMol << nl
1033  << " Mass in system = "
1034  << returnReduce(massInSystem(), sumOp<scalar>()) << nl
1035  << " Average linear momentum = "
1036  << linearMomentum/nMol << nl
1037  << " |Average linear momentum| = "
1038  << mag(linearMomentum)/nMol << nl
1039  << " Average linear kinetic energy = "
1040  << linearKineticEnergy/nMol << nl
1041  << " Average internal energy = "
1042  << internalEnergy/nMol << nl
1043  << " Average total energy = "
1044  << (internalEnergy + linearKineticEnergy)/nMol
1045  << endl;
1046  }
1047 }
1048 
1049 
1050 template<class ParcelType>
1052 (
1053  scalar temperature,
1054  scalar mass
1055 )
1056 {
1057  return
1059  *stdNormal_.sample<vector>();
1060 }
1061 
1062 
1063 template<class ParcelType>
1065 (
1066  scalar temperature,
1067  direction iDof
1068 )
1069 {
1070  scalar Ei = 0.0;
1071 
1072  if (iDof == 0)
1073  {
1074  return Ei;
1075  }
1076  else if (iDof == 2)
1077  {
1078  // Special case for iDof = 2, i.e. diatomics;
1079  Ei = -log(rndGen_.scalar01())*physicoChemical::k.value()*temperature;
1080  }
1081  else
1082  {
1083  scalar a = 0.5*iDof - 1;
1084  scalar energyRatio;
1085  scalar P = -1;
1086 
1087  do
1088  {
1089  energyRatio = 10*rndGen_.scalar01();
1090  P = pow((energyRatio/a), a)*exp(a - energyRatio);
1091  } while (P < rndGen_.scalar01());
1092 
1093  Ei = energyRatio*physicoChemical::k.value()*temperature;
1094  }
1095 
1096  return Ei;
1097 }
1098 
1099 
1100 template<class ParcelType>
1102 {
1103  OFstream pObj
1104  (
1105  this->time().path()/"parcelPositions_"
1106  + this->name() + "_"
1107  + this->time().name() + ".obj"
1108  );
1109 
1110  forAllConstIter(typename DSMCCloud<ParcelType>, *this, iter)
1111  {
1112  const point pos = iter().position(mesh());
1113 
1114  pObj<< "v " << pos.x() << " " << pos.y() << " " << pos.z() << nl;
1115  }
1116 
1117  pObj.flush();
1118 }
1119 
1120 
1121 template<class ParcelType>
1123 {
1125 
1126  // Update the cell occupancy field
1127  cellOccupancy_.setSize(mesh_.nCells());
1128  buildCellOccupancy();
1129 
1130  // Update the inflow BCs
1131  this->inflowBoundary().topoChange();
1132 }
1133 
1134 
1135 template<class ParcelType>
1137 {
1139 
1140  // Update the cell occupancy field
1141  cellOccupancy_.setSize(mesh_.nCells());
1142  buildCellOccupancy();
1143 
1144  // Update the inflow BCs
1145  this->inflowBoundary().topoChange();
1146 }
1147 
1148 
1149 template<class ParcelType>
1151 {
1153 
1154  // Update the cell occupancy field
1155  cellOccupancy_.setSize(mesh_.nCells());
1156  buildCellOccupancy();
1157 
1158  // Update the inflow BCs
1159  this->inflowBoundary().topoChange();
1160 }
1161 
1162 
1163 // ************************************************************************* //
label k
#define forAll(list, i)
Loop across all elements in list.
Definition: UList.H:449
#define forAllIter(Container, container, iter)
Iterate across all elements in the container object of type.
Definition: UList.H:474
#define forAllConstIter(Container, container, iter)
Iterate across all elements in the container object of type.
Definition: UList.H:492
Templated DSMC particle collision class.
Base cloud calls templated on particle type.
Templated base class for dsmc cloud.
Definition: DSMCCloud.H:80
void addNewParcel(const meshSearch &searchEngine, const vector &position, const label celli, label &nLocateBoundaryHits, const vector &U, const scalar Ei, const label typeId)
Add new parcel.
Definition: DSMCCloud.C:476
DSMCCloud(const word &cloudName, const fvMesh &mesh, bool readFields=true)
Construct given name and mesh, will read Parcels and fields from.
Definition: DSMCCloud.C:506
virtual ~DSMCCloud()
Destructor.
Definition: DSMCCloud.C:971
vector equipartitionLinearVelocity(scalar temperature, scalar mass)
Generate a random velocity sampled from the Maxwellian speed.
Definition: DSMCCloud.C:1052
virtual void topoChange(const polyTopoChangeMap &)
Update topology using the given map.
Definition: DSMCCloud.C:1122
virtual void distribute(const polyDistributionMap &)
Redistribute or update using the given distribution map.
Definition: DSMCCloud.C:1150
virtual void mapMesh(const polyMeshMap &)
Update from another mesh using the given map.
Definition: DSMCCloud.C:1136
void evolve()
Evolve the cloud (move, collide)
Definition: DSMCCloud.C:978
scalar equipartitionInternalEnergy(scalar temperature, direction internalDegreesOfFreedom)
Generate a random internal energy, sampled from the.
Definition: DSMCCloud.C:1065
void clear()
Clear the Cloud.
Definition: DSMCCloudI.H:488
void dumpParticlePositions() const
Dump particle positions to .obj file.
Definition: DSMCCloud.C:1101
void info() const
Print cloud information.
Definition: DSMCCloud.C:1008
Generic GeometricField class.
IOdictionary is derived from dictionary and IOobject to give the dictionary automatic IO functionalit...
Definition: IOdictionary.H:57
IOobject defines the attributes of an object for which implicit objectRegistry management is supporte...
Definition: IOobject.H:99
Templated inflow boundary model class.
Output to file stream.
Definition: OFstream.H:87
virtual void flush()
Flush stream.
Definition: OSstream.C:231
scalar deltaTValue() const
Return time step value.
Definition: TimeStateI.H:34
Templated wall interaction model class.
Dimension set for the base types.
Definition: dimensionSet.H:125
Mesh data needed to do the Finite Volume discretisation.
Definition: fvMesh.H:98
const Time & time() const
Return the top-level database.
Definition: fvMesh.H:433
virtual void topoChange(const polyTopoChangeMap &)
Update topology using the given map.
Definition: Cloud.C:469
virtual void distribute(const polyDistributionMap &)
Redistribute or update using the given distribution map.
Definition: Cloud.C:649
virtual void mapMesh(const polyMeshMap &)
Update from another mesh using the given map.
Definition: Cloud.C:518
void move(TrackCloudType &cloud, typename ParticleType::trackingData &td)
Move the particles.
Definition: Cloud.C:292
Mesh object that implements searches within the local cells and faces.
Definition: meshSearch.H:59
static const meshSearch & New(const polyMesh &mesh, const pointInCellShapes=pointInCellShapes::tets)
Lookup or construct from mesh and cell decomposition option.
Definition: meshSearch.C:61
Motion of the mesh specified as a list of pointMeshMovers.
Class containing mesh-to-mesh mapping information after a mesh distribution where we send parts of me...
Class containing mesh-to-mesh mapping information.
Definition: polyMeshMap.H:51
static List< tetIndices > cellTetIndices(const polyMesh &mesh, label cI)
Return the tet decomposition of the given cell, see.
Class containing mesh-to-mesh mapping information after a change in polyMesh topology.
const scalarField & cellVolumes() const
scalar scalar01()
Return a scalar uniformly distributed between zero and one.
Template function which returns the un-mangled name of a given type. Useful for types which do not ha...
A class for handling words, derived from string.
Definition: word.H:63
Foam::fvMesh mesh(Foam::IOobject(regionName, runTime.name(), runTime, Foam::IOobject::MUST_READ), false)
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:334
U
Definition: pEqn.H:72
#define WarningInFunction
Report a warning using Foam::Warning.
const dimensionedScalar c
Speed of light in a vacuum.
Collection of constants.
const dimensionSet velocity
const dimensionSet time
const dimensionSet temperature
const dimensionSet mass
const dimensionSet momentum
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
static const zero Zero
Definition: zero.H:97
dimensionedScalar pos(const dimensionedScalar &ds)
dimensionedScalar exp(const dimensionedScalar &ds)
const dimensionSet & dimless
Definition: dimensions.C:138
dimensionedScalar pos0(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
label findMax(const ListType &, const label start=0)
Find index of max element (and larger than given element).
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:288
void readFields(const typename GeoFieldType::Mesh &mesh, const IOobjectList &objects, const HashSet< word > &selectedFields, LIFOStack< regIOobject * > &storedObjects)
Read the selected GeometricFields of the specified type.
Definition: ReadFields.C:244
errorManip< error > abort(error &err)
Definition: errorManip.H:131
bool isType(const Type &t)
Check the typeid.
Definition: typeInfo.H:170
messageStream Info
vector point
Point is a vector.
Definition: point.H:41
Field< scalar > scalarField
Specialisation of Field<T> for scalar.
dimensionedScalar log(const dimensionedScalar &ds)
Vector< scalar > vector
A scalar version of the templated Vector.
Definition: vector.H:49
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< vector > dimensionedVector
Dimensioned vector obtained from generic dimensioned type.
Field< vector > vectorField
Specialisation of Field<T> for vector.
word name(const LagrangianState state)
Return a string representation of a Lagrangian state enumeration.
tmp< DimensionedField< typename powProduct< Type, r >::type, GeoMesh, Field > > pow(const DimensionedField< Type, GeoMesh, PrimitiveField > &df, typename powProduct< Type, r >::type)
tmp< DimensionedField< scalar, GeoMesh, Field > > mag(const DimensionedField< Type, GeoMesh, PrimitiveField > &df)
label findIndex(const ListType &, typename ListType::const_reference, const label start=0)
Find first occurrence of given element and return index,.
error FatalError
void sqrt(LagrangianPatchField< scalar > &f, const LagrangianPatchField< scalar > &f1)
tmp< DimensionedField< TypeR, GeoMesh, Field > > New(const tmp< DimensionedField< TypeR, GeoMesh, Field >> &tdf1, const word &name, const dimensionSet &dimensions)
static const char nl
Definition: Ostream.H:297
uint8_t direction
Definition: direction.H:45
dimensioned< scalar > dimensionedScalar
Dimensioned scalar obtained from generic dimensioned type.
tetrahedron< point, const point & > tetPointRef
Definition: tetPointRef.H:44
volScalarField & p
const word cloudName(propsDict.lookup("cloudName"))