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-2019 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 "BinaryCollisionModel.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  List<word> molecules(numberDensitiesDict.toc());
99 
100  Field<scalar> numberDensities(molecules.size());
101 
102  forAll(molecules, i)
103  {
104  numberDensities[i] =
105  numberDensitiesDict.lookup<scalar>(molecules[i]);
106  }
107 
108  numberDensities /= nParticle_;
109 
110  forAll(mesh_.cells(), celli)
111  {
112  List<tetIndices> cellTets = polyMeshTetDecomposition::cellTetIndices
113  (
114  mesh_,
115  celli
116  );
117 
118  forAll(cellTets, tetI)
119  {
120  const tetIndices& cellTetIs = cellTets[tetI];
121  tetPointRef tet = cellTetIs.tet(mesh_);
122  scalar tetVolume = tet.mag();
123 
124  forAll(molecules, i)
125  {
126  const word& moleculeName(molecules[i]);
127 
128  label typeId(findIndex(typeIdList_, moleculeName));
129 
130  if (typeId == -1)
131  {
133  << "typeId " << moleculeName << "not defined." << nl
134  << abort(FatalError);
135  }
136 
137  const typename ParcelType::constantProperties& cP =
138  constProps(typeId);
139 
140  scalar numberDensity = numberDensities[i];
141 
142  // Calculate the number of particles required
143  scalar particlesRequired = numberDensity*tetVolume;
144 
145  // Only integer numbers of particles can be inserted
146  label nParticlesToInsert = label(particlesRequired);
147 
148  // Add another particle with a probability proportional to the
149  // remainder of taking the integer part of particlesRequired
150  if
151  (
152  (particlesRequired - nParticlesToInsert)
153  > rndGen_.scalar01()
154  )
155  {
156  nParticlesToInsert++;
157  }
158 
159  for (label pI = 0; pI < nParticlesToInsert; pI++)
160  {
161  point p = tet.randomPoint(rndGen_);
162 
163  vector U = equipartitionLinearVelocity
164  (
165  temperature,
166  cP.mass()
167  );
168 
169  scalar Ei = equipartitionInternalEnergy
170  (
171  temperature,
172  cP.internalDegreesOfFreedom()
173  );
174 
175  U += velocity;
176 
177  addNewParcel(p, celli, U, Ei, typeId);
178  }
179  }
180  }
181  }
182 
183  // Initialise the sigmaTcRMax_ field to the product of the cross section of
184  // the most abundant species and the most probable thermal speed (Bird,
185  // p222-223)
186 
187  label mostAbundantType(findMax(numberDensities));
188 
189  const typename ParcelType::constantProperties& cP = constProps
190  (
191  mostAbundantType
192  );
193 
194  sigmaTcRMax_.primitiveFieldRef() = cP.sigmaT()*maxwellianMostProbableSpeed
195  (
196  temperature,
197  cP.mass()
198  );
199 
200  sigmaTcRMax_.correctBoundaryConditions();
201 }
202 
203 
204 template<class ParcelType>
206 {
207  if (!binaryCollision().active())
208  {
209  return;
210  }
211 
212  // Temporary storage for subCells
213  List<DynamicList<label>> subCells(8);
214 
215  scalar deltaT = mesh().time().deltaTValue();
216 
217  label collisionCandidates = 0;
218 
219  label collisions = 0;
220 
221  forAll(cellOccupancy_, celli)
222  {
223  const DynamicList<ParcelType*>& cellParcels(cellOccupancy_[celli]);
224 
225  label nC(cellParcels.size());
226 
227  if (nC > 1)
228  {
229  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
230  // Assign particles to one of 8 Cartesian subCells
231 
232  // Clear temporary lists
233  forAll(subCells, i)
234  {
235  subCells[i].clear();
236  }
237 
238  // Inverse addressing specifying which subCell a parcel is in
239  List<label> whichSubCell(cellParcels.size());
240 
241  const point& cC = mesh_.cellCentres()[celli];
242 
243  forAll(cellParcels, i)
244  {
245  const ParcelType& p = *cellParcels[i];
246  vector relPos = p.position() - cC;
247 
248  label subCell =
249  pos0(relPos.x()) + 2*pos0(relPos.y()) + 4*pos0(relPos.z());
250 
251  subCells[subCell].append(i);
252  whichSubCell[i] = subCell;
253  }
254 
255  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
256 
257  scalar sigmaTcRMax = sigmaTcRMax_[celli];
258 
259  scalar selectedPairs =
260  collisionSelectionRemainder_[celli]
261  + 0.5*nC*(nC - 1)*nParticle_*sigmaTcRMax*deltaT
262  /mesh_.cellVolumes()[celli];
263 
264  label nCandidates(selectedPairs);
265  collisionSelectionRemainder_[celli] = selectedPairs - nCandidates;
266  collisionCandidates += nCandidates;
267 
268  for (label c = 0; c < nCandidates; c++)
269  {
270  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
271  // subCell candidate selection procedure
272 
273  // Select the first collision candidate
274  label candidateP = rndGen_.sampleAB<label>(0, nC);
275 
276  // Declare the second collision candidate
277  label candidateQ = -1;
278 
279  List<label> subCellPs = subCells[whichSubCell[candidateP]];
280  label nSC = subCellPs.size();
281 
282  if (nSC > 1)
283  {
284  // If there are two or more particle in a subCell, choose
285  // another from the same cell. If the same candidate is
286  // chosen, choose again.
287 
288  do
289  {
290  candidateQ = subCellPs[rndGen_.sampleAB<label>(0, nSC)];
291  } while (candidateP == candidateQ);
292  }
293  else
294  {
295  // Select a possible second collision candidate from the
296  // whole cell. If the same candidate is chosen, choose
297  // again.
298 
299  do
300  {
301  candidateQ = rndGen_.sampleAB<label>(0, nC);
302  } while (candidateP == candidateQ);
303  }
304 
305  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
306  // uniform candidate selection procedure
307 
308  // // Select the first collision candidate
309  // label candidateP = rndGen_.sampleAB<label>(0, nC);
310 
311  // // Select a possible second collision candidate
312  // label candidateQ = rndGen_.sampleAB<label>(0, nC);
313 
314  // // If the same candidate is chosen, choose again
315  // while (candidateP == candidateQ)
316  // {
317  // candidateQ = rndGen_.sampleAB<label>(0, nC);
318  // }
319 
320  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
321 
322  ParcelType& parcelP = *cellParcels[candidateP];
323  ParcelType& parcelQ = *cellParcels[candidateQ];
324 
325  scalar sigmaTcR = binaryCollision().sigmaTcR
326  (
327  parcelP,
328  parcelQ
329  );
330 
331  // Update the maximum value of sigmaTcR stored, but use the
332  // initial value in the acceptance-rejection criteria because
333  // the number of collision candidates selected was based on this
334 
335  if (sigmaTcR > sigmaTcRMax_[celli])
336  {
337  sigmaTcRMax_[celli] = sigmaTcR;
338  }
339 
340  if ((sigmaTcR/sigmaTcRMax) > rndGen_.scalar01())
341  {
342  binaryCollision().collide
343  (
344  parcelP,
345  parcelQ
346  );
347 
348  collisions++;
349  }
350  }
351  }
352  }
353 
354  reduce(collisions, sumOp<label>());
355 
356  reduce(collisionCandidates, sumOp<label>());
357 
358  sigmaTcRMax_.correctBoundaryConditions();
359 
360  if (collisionCandidates)
361  {
362  Info<< " Collisions = "
363  << collisions << nl
364  << " Acceptance rate = "
365  << scalar(collisions)/scalar(collisionCandidates) << nl
366  << endl;
367  }
368  else
369  {
370  Info<< " No collisions" << endl;
371  }
372 }
373 
374 
375 template<class ParcelType>
377 {
378  q_ = dimensionedScalar( dimensionSet(1, 0, -3, 0, 0), 0);
379 
380  fD_ = dimensionedVector
381  (
382  "zero",
383  dimensionSet(1, -1, -2, 0, 0),
384  Zero
385  );
386 
387  rhoN_ = dimensionedScalar( dimensionSet(0, -3, 0, 0, 0), vSmall);
388  rhoM_ = dimensionedScalar( dimensionSet(1, -3, 0, 0, 0), vSmall);
389  dsmcRhoN_ = dimensionedScalar( dimensionSet(0, -3, 0, 0, 0), 0);
390  linearKE_ = dimensionedScalar( dimensionSet(1, -1, -2, 0, 0), 0);
391  internalE_ = dimensionedScalar( dimensionSet(1, -1, -2, 0, 0), 0);
392  iDof_ = dimensionedScalar( dimensionSet(0, -3, 0, 0, 0), vSmall);
393 
394  momentum_ = dimensionedVector
395  (
396  "zero",
397  dimensionSet(1, -2, -1, 0, 0),
398  Zero
399  );
400 }
401 
402 
403 template<class ParcelType>
405 {
406  scalarField& rhoN = rhoN_.primitiveFieldRef();
407  scalarField& rhoM = rhoM_.primitiveFieldRef();
408  scalarField& dsmcRhoN = dsmcRhoN_.primitiveFieldRef();
409  scalarField& linearKE = linearKE_.primitiveFieldRef();
410  scalarField& internalE = internalE_.primitiveFieldRef();
411  scalarField& iDof = iDof_.primitiveFieldRef();
412  vectorField& momentum = momentum_.primitiveFieldRef();
413 
414  forAllConstIter(typename DSMCCloud<ParcelType>, *this, iter)
415  {
416  const ParcelType& p = iter();
417  const label celli = p.cell();
418 
419  rhoN[celli]++;
420  rhoM[celli] += constProps(p.typeId()).mass();
421  dsmcRhoN[celli]++;
422  linearKE[celli] += 0.5*constProps(p.typeId()).mass()*(p.U() & p.U());
423  internalE[celli] += p.Ei();
424  iDof[celli] += constProps(p.typeId()).internalDegreesOfFreedom();
425  momentum[celli] += constProps(p.typeId()).mass()*p.U();
426  }
427 
428  rhoN *= nParticle_/mesh().cellVolumes();
429  rhoN_.correctBoundaryConditions();
430 
431  rhoM *= nParticle_/mesh().cellVolumes();
432  rhoM_.correctBoundaryConditions();
433 
434  dsmcRhoN_.correctBoundaryConditions();
435 
436  linearKE *= nParticle_/mesh().cellVolumes();
437  linearKE_.correctBoundaryConditions();
438 
439  internalE *= nParticle_/mesh().cellVolumes();
440  internalE_.correctBoundaryConditions();
441 
442  iDof *= nParticle_/mesh().cellVolumes();
443  iDof_.correctBoundaryConditions();
444 
445  momentum *= nParticle_/mesh().cellVolumes();
446  momentum_.correctBoundaryConditions();
447 }
448 
449 
450 // * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * * //
451 
452 template<class ParcelType>
454 (
455  const vector& position,
456  const label celli,
457  const vector& U,
458  const scalar Ei,
459  const label typeId
460 )
461 {
462  this->addParticle(new ParcelType(mesh_, position, celli, U, Ei, typeId));
463 }
464 
465 
466 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
467 
468 template<class ParcelType>
470 (
471  const word& cloudName,
472  const fvMesh& mesh,
473  bool readFields
474 )
475 :
477  DSMCBaseCloud(),
478  cloudName_(cloudName),
479  mesh_(mesh),
480  particleProperties_
481  (
482  IOobject
483  (
484  cloudName + "Properties",
485  mesh_.time().constant(),
486  mesh_,
489  )
490  ),
491  typeIdList_(particleProperties_.lookup("typeIdList")),
492  nParticle_
493  (
494  particleProperties_.template lookup<scalar>("nEquivalentParticles")
495  ),
496  cellOccupancy_(mesh_.nCells()),
497  sigmaTcRMax_
498  (
499  IOobject
500  (
501  this->name() + "SigmaTcRMax",
502  mesh_.time().timeName(),
503  mesh_,
506  ),
507  mesh_
508  ),
509  collisionSelectionRemainder_
510  (
511  IOobject
512  (
513  this->name() + ":collisionSelectionRemainder",
514  mesh_.time().timeName(),
515  mesh_
516  ),
517  mesh_,
519  ),
520  q_
521  (
522  IOobject
523  (
524  "q",
525  mesh_.time().timeName(),
526  mesh_,
529  ),
530  mesh_
531  ),
532  fD_
533  (
534  IOobject
535  (
536  "fD",
537  mesh_.time().timeName(),
538  mesh_,
541  ),
542  mesh_
543  ),
544  rhoN_
545  (
546  IOobject
547  (
548  "rhoN",
549  mesh_.time().timeName(),
550  mesh_,
553  ),
554  mesh_
555  ),
556  rhoM_
557  (
558  IOobject
559  (
560  "rhoM",
561  mesh_.time().timeName(),
562  mesh_,
565  ),
566  mesh_
567  ),
568  dsmcRhoN_
569  (
570  IOobject
571  (
572  "dsmcRhoN",
573  mesh_.time().timeName(),
574  mesh_,
577  ),
578  mesh_
579  ),
580  linearKE_
581  (
582  IOobject
583  (
584  "linearKE",
585  mesh_.time().timeName(),
586  mesh_,
589  ),
590  mesh_
591  ),
592  internalE_
593  (
594  IOobject
595  (
596  "internalE",
597  mesh_.time().timeName(),
598  mesh_,
601  ),
602  mesh_
603  ),
604  iDof_
605  (
606  IOobject
607  (
608  "iDof",
609  mesh_.time().timeName(),
610  mesh_,
613  ),
614  mesh_
615  ),
616  momentum_
617  (
618  IOobject
619  (
620  "momentum",
621  mesh_.time().timeName(),
622  mesh_,
625  ),
626  mesh_
627  ),
628  constProps_(),
629  rndGen_(label(149382906) + 7183*Pstream::myProcNo()),
630  boundaryT_
631  (
633  (
634  IOobject
635  (
636  "boundaryT",
637  mesh_.time().timeName(),
638  mesh_,
641  ),
642  mesh_
643  )
644  ),
645  boundaryU_
646  (
648  (
649  IOobject
650  (
651  "boundaryU",
652  mesh_.time().timeName(),
653  mesh_,
656  ),
657  mesh_
658  )
659  ),
660  binaryCollisionModel_
661  (
663  (
664  particleProperties_,
665  *this
666  )
667  ),
668  wallInteractionModel_
669  (
671  (
672  particleProperties_,
673  *this
674  )
675  ),
676  inflowBoundaryModel_
677  (
679  (
680  particleProperties_,
681  *this
682  )
683  )
684 {
685  buildConstProps();
686  buildCellOccupancy();
687 
688  // Initialise the collision selection remainder to a random value between 0
689  // and 1.
690  forAll(collisionSelectionRemainder_, i)
691  {
692  collisionSelectionRemainder_[i] = rndGen_.scalar01();
693  }
694 
695  if (readFields)
696  {
697  ParcelType::readFields(*this);
698  }
699 }
700 
701 
702 template<class ParcelType>
704 (
705  const word& cloudName,
706  const fvMesh& mesh,
707  const IOdictionary& dsmcInitialiseDict
708 )
709  :
711  DSMCBaseCloud(),
712  cloudName_(cloudName),
713  mesh_(mesh),
714  particleProperties_
715  (
716  IOobject
717  (
718  cloudName + "Properties",
719  mesh_.time().constant(),
720  mesh_,
723  )
724  ),
725  typeIdList_(particleProperties_.lookup("typeIdList")),
726  nParticle_
727  (
728  particleProperties_.template lookup<scalar>("nEquivalentParticles")
729  ),
730  cellOccupancy_(),
731  sigmaTcRMax_
732  (
733  IOobject
734  (
735  this->name() + "SigmaTcRMax",
736  mesh_.time().timeName(),
737  mesh_,
740  ),
741  mesh_,
742  dimensionedScalar( dimensionSet(0, 3, -1, 0, 0), 0),
743  zeroGradientFvPatchScalarField::typeName
744  ),
745  collisionSelectionRemainder_
746  (
747  IOobject
748  (
749  this->name() + ":collisionSelectionRemainder",
750  mesh_.time().timeName(),
751  mesh_
752  ),
753  mesh_,
755  ),
756  q_
757  (
758  IOobject
759  (
760  this->name() + "q_",
761  mesh_.time().timeName(),
762  mesh_,
765  ),
766  mesh_,
767  dimensionedScalar( dimensionSet(1, 0, -3, 0, 0), 0)
768  ),
769  fD_
770  (
771  IOobject
772  (
773  this->name() + "fD_",
774  mesh_.time().timeName(),
775  mesh_,
778  ),
779  mesh_,
781  (
782  "zero",
783  dimensionSet(1, -1, -2, 0, 0),
784  Zero
785  )
786  ),
787  rhoN_
788  (
789  IOobject
790  (
791  this->name() + "rhoN_",
792  mesh_.time().timeName(),
793  mesh_,
796  ),
797  mesh_,
798  dimensionedScalar( dimensionSet(0, -3, 0, 0, 0), vSmall)
799  ),
800  rhoM_
801  (
802  IOobject
803  (
804  this->name() + "rhoM_",
805  mesh_.time().timeName(),
806  mesh_,
809  ),
810  mesh_,
811  dimensionedScalar( dimensionSet(1, -3, 0, 0, 0), vSmall)
812  ),
813  dsmcRhoN_
814  (
815  IOobject
816  (
817  this->name() + "dsmcRhoN_",
818  mesh_.time().timeName(),
819  mesh_,
822  ),
823  mesh_,
824  dimensionedScalar( dimensionSet(0, -3, 0, 0, 0), 0)
825  ),
826  linearKE_
827  (
828  IOobject
829  (
830  this->name() + "linearKE_",
831  mesh_.time().timeName(),
832  mesh_,
835  ),
836  mesh_,
837  dimensionedScalar( dimensionSet(1, -1, -2, 0, 0), 0)
838  ),
839  internalE_
840  (
841  IOobject
842  (
843  this->name() + "internalE_",
844  mesh_.time().timeName(),
845  mesh_,
848  ),
849  mesh_,
850  dimensionedScalar( dimensionSet(1, -1, -2, 0, 0), 0)
851  ),
852  iDof_
853  (
854  IOobject
855  (
856  this->name() + "iDof_",
857  mesh_.time().timeName(),
858  mesh_,
861  ),
862  mesh_,
863  dimensionedScalar( dimensionSet(0, -3, 0, 0, 0), vSmall)
864  ),
865  momentum_
866  (
867  IOobject
868  (
869  this->name() + "momentum_",
870  mesh_.time().timeName(),
871  mesh_,
874  ),
875  mesh_,
877  (
878  "zero",
879  dimensionSet(1, -2, -1, 0, 0),
880  Zero
881  )
882  ),
883  constProps_(),
884  rndGen_(label(971501) + 1526*Pstream::myProcNo()),
885  boundaryT_
886  (
888  (
889  IOobject
890  (
891  "boundaryT",
892  mesh_.time().timeName(),
893  mesh_,
896  ),
897  mesh_,
898  dimensionedScalar( dimensionSet(0, 0, 0, 1, 0), 0)
899  )
900  ),
901  boundaryU_
902  (
904  (
905  IOobject
906  (
907  "boundaryU",
908  mesh_.time().timeName(),
909  mesh_,
912  ),
913  mesh_,
915  (
916  "zero",
917  dimensionSet(0, 1, -1, 0, 0),
918  Zero
919  )
920  )
921  ),
922  binaryCollisionModel_(),
923  wallInteractionModel_(),
924  inflowBoundaryModel_()
925 {
926  clear();
927  buildConstProps();
928  initialise(dsmcInitialiseDict);
929 }
930 
931 
932 // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
933 
934 template<class ParcelType>
936 {}
937 
938 
939 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
940 
941 template<class ParcelType>
943 {
944  typename ParcelType::trackingData td(*this);
945 
946  // Reset the data collection fields
947  resetFields();
948 
949  if (debug)
950  {
951  this->dumpParticlePositions();
952  }
953 
954  // Insert new particles from the inflow boundary
955  this->inflowBoundary().inflow();
956 
957  // Move the particles ballistically with their current velocities
958  Cloud<ParcelType>::move(*this, td, mesh_.time().deltaTValue());
959 
960  // Update cell occupancy
961  buildCellOccupancy();
962 
963  // Calculate new velocities via stochastic collisions
964  collisions();
965 
966  // Calculate the volume field data
967  calculateFields();
968 }
969 
970 
971 template<class ParcelType>
973 {
974  label nDSMCParticles = this->size();
975  reduce(nDSMCParticles, sumOp<label>());
976 
977  scalar nMol = nDSMCParticles*nParticle_;
978 
979  vector linearMomentum = linearMomentumOfSystem();
980  reduce(linearMomentum, sumOp<vector>());
981 
982  scalar linearKineticEnergy = linearKineticEnergyOfSystem();
983  reduce(linearKineticEnergy, sumOp<scalar>());
984 
985  scalar internalEnergy = internalEnergyOfSystem();
986  reduce(internalEnergy, sumOp<scalar>());
987 
988  Info<< "Cloud name: " << this->name() << nl
989  << " Number of dsmc particles = "
990  << nDSMCParticles
991  << endl;
992 
993  if (nDSMCParticles)
994  {
995  Info<< " Number of molecules = "
996  << nMol << nl
997  << " Mass in system = "
998  << returnReduce(massInSystem(), sumOp<scalar>()) << nl
999  << " Average linear momentum = "
1000  << linearMomentum/nMol << nl
1001  << " |Average linear momentum| = "
1002  << mag(linearMomentum)/nMol << nl
1003  << " Average linear kinetic energy = "
1004  << linearKineticEnergy/nMol << nl
1005  << " Average internal energy = "
1006  << internalEnergy/nMol << nl
1007  << " Average total energy = "
1008  << (internalEnergy + linearKineticEnergy)/nMol
1009  << endl;
1010  }
1011 }
1012 
1013 
1014 template<class ParcelType>
1017  scalar temperature,
1018  scalar mass
1019 )
1020 {
1021  return
1022  sqrt(physicoChemical::k.value()*temperature/mass)
1023  *rndGen_.sampleNormal<vector>();
1024 }
1025 
1026 
1027 template<class ParcelType>
1030  scalar temperature,
1031  direction iDof
1032 )
1033 {
1034  scalar Ei = 0.0;
1035 
1036  if (iDof == 0)
1037  {
1038  return Ei;
1039  }
1040  else if (iDof == 2)
1041  {
1042  // Special case for iDof = 2, i.e. diatomics;
1043  Ei = -log(rndGen_.scalar01())*physicoChemical::k.value()*temperature;
1044  }
1045  else
1046  {
1047  scalar a = 0.5*iDof - 1;
1048  scalar energyRatio;
1049  scalar P = -1;
1050 
1051  do
1052  {
1053  energyRatio = 10*rndGen_.scalar01();
1054  P = pow((energyRatio/a), a)*exp(a - energyRatio);
1055  } while (P < rndGen_.scalar01());
1056 
1057  Ei = energyRatio*physicoChemical::k.value()*temperature;
1058  }
1059 
1060  return Ei;
1061 }
1062 
1063 
1064 template<class ParcelType>
1066 {
1067  OFstream pObj
1068  (
1069  this->db().time().path()/"parcelPositions_"
1070  + this->name() + "_"
1071  + this->db().time().timeName() + ".obj"
1072  );
1073 
1074  forAllConstIter(typename DSMCCloud<ParcelType>, *this, iter)
1075  {
1076  const ParcelType& p = iter();
1077 
1078  pObj<< "v " << p.position().x()
1079  << " " << p.position().y()
1080  << " " << p.position().z()
1081  << nl;
1082  }
1083 
1084  pObj.flush();
1085 }
1086 
1087 
1088 template<class ParcelType>
1090 {
1092 
1093  // Update the cell occupancy field
1094  cellOccupancy_.setSize(mesh_.nCells());
1095  buildCellOccupancy();
1096 
1097  // Update the inflow BCs
1098  this->inflowBoundary().autoMap(mapper);
1099 }
1100 
1101 
1102 // ************************************************************************* //
Collection of constants.
tetrahedron< point, const point & > tetPointRef
Definition: tetPointRef.H:44
virtual void autoMap(const mapPolyMesh &)
Remap the particles to the correct cells following mesh change.
Definition: DSMCCloud.C:1089
#define forAll(list, i)
Loop across all elements in list.
Definition: UList.H:434
tUEqn clear()
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
dimensionedScalar log(const dimensionedScalar &ds)
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
error FatalError
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:319
void info() const
Print cloud information.
Definition: DSMCCloud.C:972
const dimensionedScalar & k
Boltzmann constant.
Templated inflow boundary model class.
Definition: DSMCCloud.H:62
#define forAllIter(Container, container, iter)
Iterate across all elements in the container object of type.
Definition: UList.H:459
Output to file stream.
Definition: OFstream.H:82
uint8_t direction
Definition: direction.H:45
static int myProcNo(const label communicator=0)
Number of this process (starting from masterNo() = 0)
Definition: UPstream.H:429
Templated DSMC particle collision class.
Definition: DSMCCloud.H:56
dimensioned< vector > dimensionedVector
Dimensioned vector obtained from generic dimensioned type.
dimensionedScalar sqrt(const dimensionedScalar &ds)
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:251
Vector< scalar > vector
A scalar version of the templated Vector.
Definition: vector.H:49
const dimensionedScalar & c
Speed of light in a vacuum.
void autoMap(const mapPolyMesh &)
Remap the cells of particles corresponding to the.
Definition: Cloud.C:341
Class containing mesh-to-mesh mapping information after a change in polyMesh topology.
Definition: mapPolyMesh.H:158
static List< tetIndices > cellTetIndices(const polyMesh &mesh, label cI)
Return the tet decomposition of the given cell, see.
tmp< DimensionedField< TypeR, GeoMesh > > New(const tmp< DimensionedField< TypeR, GeoMesh >> &tdf1, const word &name, const dimensionSet &dimensions)
IOdictionary is derived from dictionary and IOobject to give the dictionary automatic IO functionalit...
Definition: IOdictionary.H:52
Dimension set for the base types.
Definition: dimensionSet.H:120
dynamicFvMesh & mesh
dimensionedScalar exp(const dimensionedScalar &ds)
A class for handling words, derived from string.
Definition: word.H:59
Field< scalar > scalarField
Specialisation of Field<T> for scalar.
virtual void flush()
Flush stream.
Definition: OSstream.C:207
const word & constant() const
Return constant name.
Definition: TimePaths.H:124
const Type & value() const
Return const reference to value.
word timeName
Definition: getTimeIndex.H:3
static const zero Zero
Definition: zero.H:97
forAllConstIter(PtrDictionary< phaseModel >, mixture.phases(), phase)
Definition: pEqn.H:29
errorManip< error > abort(error &err)
Definition: errorManip.H:131
scalar equipartitionInternalEnergy(scalar temperature, direction internalDegreesOfFreedom)
Generate a random internal energy, sampled from the.
Definition: DSMCCloud.C:1029
label findMax(const ListType &, const label start=0)
Find index of max element (and larger than given element).
dimensionedScalar pos0(const dimensionedScalar &ds)
static const char nl
Definition: Ostream.H:260
const Time & time() const
Return time.
label findIndex(const ListType &, typename ListType::const_reference, const label start=0)
Find first occurrence of given element and return index,.
void reduce(const List< UPstream::commsStruct > &comms, T &Value, const BinaryOp &bop, const int tag, const label comm)
word name(const complex &)
Return a string representation of a complex.
Definition: complex.C:47
DSMCCloud(const word &cloudName, const fvMesh &mesh, bool readFields=true)
Construct given name and mesh, will read Parcels and fields from.
Definition: DSMCCloud.C:470
void addNewParcel(const vector &position, const label celli, const vector &U, const scalar Ei, const label typeId)
Add new parcel.
Definition: DSMCCloud.C:454
virtual ~DSMCCloud()
Destructor.
Definition: DSMCCloud.C:935
dimensionedScalar pow(const dimensionedScalar &ds, const dimensionedScalar &expt)
void move(TrackCloudType &cloud, typename ParticleType::trackingData &td, const scalar trackTime)
Move the particles.
Definition: Cloud.C:142
const dimensionSet dimless(0, 0, 0, 0, 0, 0, 0)
Definition: dimensionSets.H:47
vector point
Point is a vector.
Definition: point.H:41
dimensioned< scalar > dimensionedScalar
Dimensioned scalar obtained from generic dimensioned type.
Mesh data needed to do the Finite Volume discretisation.
Definition: fvMesh.H:78
void evolve()
Evolve the cloud (move, collide)
Definition: DSMCCloud.C:942
messageStream Info
dimensioned< scalar > mag(const dimensioned< Type > &)
Field< vector > vectorField
Specialisation of Field<T> for vector.
Virtual abstract base class for templated DSMCCloud.
Definition: DSMCBaseCloud.H:48
T returnReduce(const T &Value, const BinaryOp &bop, const int tag=Pstream::msgType(), const label comm=UPstream::worldComm)
const word cloudName(propsDict.lookup("cloudName"))
Templated wall interaction model class.
Definition: DSMCCloud.H:59
void dumpParticlePositions() const
Dump particle positions to .obj file.
Definition: DSMCCloud.C:1065
vector equipartitionLinearVelocity(scalar temperature, scalar mass)
Generate a random velocity sampled from the Maxwellian speed.
Definition: DSMCCloud.C:1016
IOobject defines the attributes of an object for which implicit objectRegistry management is supporte...
Definition: IOobject.H:92
Templated base class for dsmc cloud.
Definition: DSMCCloud.H:69
fileName path(UMean.rootPath()/UMean.caseName()/functionObjects::writeFile::outputPrefix/"graphs"/UMean.instance())