moleculeCloud.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-2024 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 "moleculeCloud.H"
27 #include "fvMesh.H"
28 #include "mathematicalConstants.H"
29 
30 using namespace Foam::constant::mathematical;
31 
32 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
33 
34 namespace Foam
35 {
37 }
38 
39 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
40 
41 void Foam::moleculeCloud::buildConstProps()
42 {
43  Info<< nl << "Reading moleculeProperties dictionary." << endl;
44 
45  const List<word>& idList(pot_.idList());
46 
47  constPropList_.setSize(idList.size());
48 
49  const List<word>& siteIdList(pot_.siteIdList());
50 
51  IOdictionary moleculePropertiesDict
52  (
53  IOobject
54  (
55  "moleculeProperties",
56  mesh_.time().constant(),
57  mesh_,
60  false
61  )
62  );
63 
64  forAll(idList, i)
65  {
66  const word& id = idList[i];
67  const dictionary& molDict = moleculePropertiesDict.subDict(id);
68 
69  List<word> siteIdNames = molDict.lookup("siteIds");
70 
71  List<label> siteIds(siteIdNames.size());
72 
73  forAll(siteIdNames, sI)
74  {
75  const word& siteId = siteIdNames[sI];
76 
77  siteIds[sI] = findIndex(siteIdList, siteId);
78 
79  if (siteIds[sI] == -1)
80  {
82  << siteId << " site not found."
83  << nl << abort(FatalError);
84  }
85  }
86 
87  molecule::constantProperties& constProp = constPropList_[i];
88 
89  constProp = molecule::constantProperties(molDict);
90 
91  constProp.siteIds() = siteIds;
92  }
93 }
94 
95 
96 void Foam::moleculeCloud::setSiteSizesAndPositions()
97 {
98  forAllIter(moleculeCloud, *this, mol)
99  {
100  const molecule::constantProperties& cP = constProps(mol().id());
101 
102  mol().setSiteSizes(cP.nSites());
103 
104  mol().setSitePositions(mesh(), cP);
105  }
106 }
107 
108 
109 void Foam::moleculeCloud::buildCellOccupancy()
110 {
111  forAll(cellOccupancy_, cO)
112  {
113  cellOccupancy_[cO].clear();
114  }
115 
116  forAllIter(moleculeCloud, *this, mol)
117  {
118  cellOccupancy_[mol().cell()].append(&mol());
119  }
120 
121  forAll(cellOccupancy_, cO)
122  {
123  cellOccupancy_[cO].shrink();
124  }
125 }
126 
127 
128 void Foam::moleculeCloud::calculatePairForce()
129 {
130  PstreamBuffers pBufs(Pstream::commsTypes::nonBlocking);
131 
132  // Start sending referred data
133  label startOfRequests = Pstream::nRequests();
134  il_.sendReferredData(cellOccupancy(), pBufs);
135 
136  molecule* molI = nullptr;
137  molecule* molJ = nullptr;
138 
139  {
140  // Real-Real interactions
141 
142  const labelListList& dil = il_.dil();
143 
144  forAll(dil, d)
145  {
146  forAll(cellOccupancy_[d],cellIMols)
147  {
148  molI = cellOccupancy_[d][cellIMols];
149 
150  forAll(dil[d], interactingCells)
151  {
152  List<molecule*> cellJ =
153  cellOccupancy_[dil[d][interactingCells]];
154 
155  forAll(cellJ, cellJMols)
156  {
157  molJ = cellJ[cellJMols];
158 
159  evaluatePair(*molI, *molJ);
160  }
161  }
162 
163  forAll(cellOccupancy_[d], cellIOtherMols)
164  {
165  molJ = cellOccupancy_[d][cellIOtherMols];
166 
167  if (molJ > molI)
168  {
169  evaluatePair(*molI, *molJ);
170  }
171  }
172  }
173  }
174  }
175 
176  // Receive referred data
177  il_.receiveReferredData(pBufs, startOfRequests);
178 
179  {
180  // Real-Referred interactions
181 
182  const labelListList& ril = il_.ril();
183 
184  List<IDLList<molecule>>& referredMols = il_.referredParticles();
185 
186  forAll(ril, r)
187  {
188  const List<label>& realCells = ril[r];
189 
190  IDLList<molecule>& refMols = referredMols[r];
191 
192  forAllIter
193  (
194  IDLList<molecule>,
195  refMols,
196  refMol
197  )
198  {
199  forAll(realCells, rC)
200  {
201  List<molecule*> celli = cellOccupancy_[realCells[rC]];
202 
203  forAll(celli, cellIMols)
204  {
205  molI = celli[cellIMols];
206 
207  evaluatePair(*molI, refMol());
208  }
209  }
210  }
211  }
212  }
213 }
214 
215 
216 void Foam::moleculeCloud::calculateTetherForce()
217 {
218  const tetherPotentialList& tetherPot(pot_.tetherPotentials());
219 
220  forAllIter(moleculeCloud, *this, mol)
221  {
222  if (mol().tethered())
223  {
224  vector rIT = mol().position(mesh()) - mol().specialPosition();
225 
226  label idI = mol().id();
227 
228  scalar massI = constProps(idI).mass();
229 
230  vector fIT = tetherPot.force(idI, rIT);
231 
232  mol().a() += fIT/massI;
233 
234  mol().potentialEnergy() += tetherPot.energy(idI, rIT);
235 
236  // What to do here?
237  // mol().rf() += rIT*fIT;
238  }
239  }
240 }
241 
242 
243 void Foam::moleculeCloud::calculateExternalForce()
244 {
245  forAllIter(moleculeCloud, *this, mol)
246  {
247  mol().a() += pot_.gravity();
248  }
249 }
250 
251 
252 void Foam::moleculeCloud::removeHighEnergyOverlaps()
253 {
254  Info<< nl << "Removing high energy overlaps, limit = "
255  << pot_.potentialEnergyLimit()
256  << nl << "Removal order:";
257 
258  forAll(pot_.removalOrder(), rO)
259  {
260  Info<< ' ' << pot_.idList()[pot_.removalOrder()[rO]];
261  }
262 
263  Info<< nl ;
264 
265  label initialSize = this->size();
266 
267  buildCellOccupancy();
268 
269  // Real-Real interaction
270 
271  molecule* molI = nullptr;
272  molecule* molJ = nullptr;
273 
274  {
275  DynamicList<molecule*> molsToDelete;
276 
277  const labelListList& dil(il_.dil());
278 
279  forAll(dil, d)
280  {
281  forAll(cellOccupancy_[d],cellIMols)
282  {
283  molI = cellOccupancy_[d][cellIMols];
284 
285  forAll(dil[d], interactingCells)
286  {
287  List<molecule*> cellJ =
288  cellOccupancy_[dil[d][interactingCells]];
289 
290  forAll(cellJ, cellJMols)
291  {
292  molJ = cellJ[cellJMols];
293 
294  if (evaluatePotentialLimit(*molI, *molJ))
295  {
296  label idI = molI->id();
297 
298  label idJ = molJ->id();
299 
300  if
301  (
302  idI == idJ
303  || findIndex(pot_.removalOrder(), idJ)
304  < findIndex(pot_.removalOrder(), idI)
305  )
306  {
307  if (findIndex(molsToDelete, molJ) == -1)
308  {
309  molsToDelete.append(molJ);
310  }
311  }
312  else if (findIndex(molsToDelete, molI) == -1)
313  {
314  molsToDelete.append(molI);
315  }
316  }
317  }
318  }
319  }
320 
321  forAll(cellOccupancy_[d], cellIOtherMols)
322  {
323  molJ = cellOccupancy_[d][cellIOtherMols];
324 
325  if (molJ > molI)
326  {
327  if (evaluatePotentialLimit(*molI, *molJ))
328  {
329  label idI = molI->id();
330 
331  label idJ = molJ->id();
332 
333  if
334  (
335  idI == idJ
336  || findIndex(pot_.removalOrder(), idJ)
337  < findIndex(pot_.removalOrder(), idI)
338  )
339  {
340  if (findIndex(molsToDelete, molJ) == -1)
341  {
342  molsToDelete.append(molJ);
343  }
344  }
345  else if (findIndex(molsToDelete, molI) == -1)
346  {
347  molsToDelete.append(molI);
348  }
349  }
350  }
351  }
352  }
353 
354  forAll(molsToDelete, mTD)
355  {
356  deleteParticle(*(molsToDelete[mTD]));
357  }
358  }
359 
360  buildCellOccupancy();
361 
362  PstreamBuffers pBufs(Pstream::commsTypes::nonBlocking);
363 
364  // Start sending referred data
365  label startOfRequests = Pstream::nRequests();
366 
367  il_.sendReferredData(cellOccupancy(), pBufs);
368 
369  // Receive referred data
370  il_.receiveReferredData(pBufs, startOfRequests);
371 
372  // Real-Referred interaction
373 
374  {
375  DynamicList<molecule*> molsToDelete;
376 
377  const labelListList& ril(il_.ril());
378 
379  List<IDLList<molecule>>& referredMols = il_.referredParticles();
380 
381  forAll(ril, r)
382  {
383  IDLList<molecule>& refMols = referredMols[r];
384 
385  forAllIter
386  (
387  IDLList<molecule>,
388  refMols,
389  refMol
390  )
391  {
392  molJ = &refMol();
393 
394  const List<label>& realCells = ril[r];
395 
396  forAll(realCells, rC)
397  {
398  label celli = realCells[rC];
399 
400  List<molecule*> cellIMols = cellOccupancy_[celli];
401 
402  forAll(cellIMols, cIM)
403  {
404  molI = cellIMols[cIM];
405 
406  if (evaluatePotentialLimit(*molI, *molJ))
407  {
408  label idI = molI->id();
409 
410  label idJ = molJ->id();
411 
412  if
413  (
414  findIndex(pot_.removalOrder(), idI)
415  < findIndex(pot_.removalOrder(), idJ)
416  )
417  {
418  if (findIndex(molsToDelete, molI) == -1)
419  {
420  molsToDelete.append(molI);
421  }
422  }
423  else if
424  (
425  findIndex(pot_.removalOrder(), idI)
426  == findIndex(pot_.removalOrder(), idJ)
427  )
428  {
429  // Remove one of the molecules
430  // arbitrarily, assuring that a
431  // consistent decision is made for
432  // both real-referred pairs.
433 
434  if (molI->origId() > molJ->origId())
435  {
436  if (findIndex(molsToDelete, molI) == -1)
437  {
438  molsToDelete.append(molI);
439  }
440  }
441  }
442  }
443  }
444  }
445  }
446  }
447 
448  forAll(molsToDelete, mTD)
449  {
450  deleteParticle(*(molsToDelete[mTD]));
451  }
452  }
453 
454  buildCellOccupancy();
455 
456  // Start sending referred data
457  startOfRequests = Pstream::nRequests();
458 
459  il_.sendReferredData(cellOccupancy(), pBufs);
460 
461  // Receive referred data
462  il_.receiveReferredData(pBufs, startOfRequests);
463 
464  label molsRemoved = initialSize - this->size();
465 
466  if (Pstream::parRun())
467  {
468  reduce(molsRemoved, sumOp<label>());
469  }
470 
471  Info<< tab << molsRemoved << " molecules removed" << endl;
472 }
473 
474 
475 void Foam::moleculeCloud::initialiseMolecules
476 (
477  const IOdictionary& mdInitialiseDict
478 )
479 {
480  Info<< nl
481  << "Initialising molecules in each zone specified in mdInitialiseDict."
482  << endl;
483 
484  const cellZoneList& cellZones = mesh_.cellZones();
485 
486  if (!cellZones.size())
487  {
489  << "No cellZones found in the mesh."
490  << abort(FatalError);
491  }
492 
493  label nLocateBoundaryHits = 0;
494 
495  forAll(cellZones, z)
496  {
497  const cellZone& zone(cellZones[z]);
498 
499  if (zone.size())
500  {
501  if (!mdInitialiseDict.found(zone.name()))
502  {
503  Info<< "No specification subDictionary for zone "
504  << zone.name() << " found, skipping." << endl;
505  }
506  else
507  {
508  const dictionary& zoneDict =
509  mdInitialiseDict.subDict(zone.name());
510 
511  const scalar temperature
512  (
513  zoneDict.template lookup<scalar>("temperature")
514  );
515 
516  const vector bulkVelocity(zoneDict.lookup("bulkVelocity"));
517 
518  List<word> latticeIds
519  (
520  zoneDict.lookup("latticeIds")
521  );
522 
523  List<vector> latticePositions
524  (
525  zoneDict.lookup("latticePositions")
526  );
527 
528  if (latticeIds.size() != latticePositions.size())
529  {
531  << "latticeIds and latticePositions must be the same "
532  << " size." << nl
533  << abort(FatalError);
534  }
535 
536  diagTensor latticeCellShape
537  (
538  zoneDict.lookup("latticeCellShape")
539  );
540 
541  scalar latticeCellScale = 0.0;
542 
543  if (zoneDict.found("numberDensity"))
544  {
545  scalar numberDensity =
546  zoneDict.lookup<scalar>("numberDensity");
547 
548  if (numberDensity < vSmall)
549  {
551  << "numberDensity too small, not filling zone "
552  << zone.name() << endl;
553 
554  continue;
555  }
556 
557  latticeCellScale = pow
558  (
559  latticeIds.size()/(det(latticeCellShape)*numberDensity),
560  (1.0/3.0)
561  );
562  }
563  else if (zoneDict.found("massDensity"))
564  {
565  scalar unitCellMass = 0.0;
566 
567  forAll(latticeIds, i)
568  {
569  label id = findIndex(pot_.idList(), latticeIds[i]);
570 
571  const molecule::constantProperties& cP(constProps(id));
572 
573  unitCellMass += cP.mass();
574  }
575 
576  scalar massDensity = zoneDict.lookup<scalar>("massDensity");
577 
578  if (massDensity < vSmall)
579  {
581  << "massDensity too small, not filling zone "
582  << zone.name() << endl;
583 
584  continue;
585  }
586 
587 
588  latticeCellScale = pow
589  (
590  unitCellMass/(det(latticeCellShape)*massDensity),
591  (1.0/3.0)
592  );
593  }
594  else
595  {
597  << "massDensity or numberDensity not specified " << nl
598  << abort(FatalError);
599  }
600 
601  latticeCellShape *= latticeCellScale;
602 
603  vector anchor(zoneDict.lookup("anchor"));
604 
605  bool tethered = false;
606 
607  if (zoneDict.found("tetherSiteIds"))
608  {
609  tethered = bool
610  (
611  List<word>(zoneDict.lookup("tetherSiteIds")).size()
612  );
613  }
614 
615  const vector orientationAngles
616  (
617  zoneDict.lookup("orientationAngles")
618  );
619 
620  scalar phi(orientationAngles.x()*pi/180.0);
621 
622  scalar theta(orientationAngles.y()*pi/180.0);
623 
624  scalar psi(orientationAngles.z()*pi/180.0);
625 
626  const tensor R
627  (
628  cos(psi)*cos(phi) - cos(theta)*sin(phi)*sin(psi),
629  cos(psi)*sin(phi) + cos(theta)*cos(phi)*sin(psi),
630  sin(psi)*sin(theta),
631  - sin(psi)*cos(phi) - cos(theta)*sin(phi)*cos(psi),
632  - sin(psi)*sin(phi) + cos(theta)*cos(phi)*cos(psi),
633  cos(psi)*sin(theta),
634  sin(theta)*sin(phi),
635  - sin(theta)*cos(phi),
636  cos(theta)
637  );
638 
639  // Find the optimal anchor position. Finding the approximate
640  // mid-point of the zone of cells and snapping to the nearest
641  // lattice location.
642 
643  vector zoneMin = vGreat*vector::one;
644 
645  vector zoneMax = -vGreat*vector::one;
646 
647  forAll(zone, cell)
648  {
649  const point cellCentre = mesh_.cellCentres()[zone[cell]];
650 
651  if (cellCentre.x() > zoneMax.x())
652  {
653  zoneMax.x() = cellCentre.x();
654  }
655  if (cellCentre.x() < zoneMin.x())
656  {
657  zoneMin.x() = cellCentre.x();
658  }
659  if (cellCentre.y() > zoneMax.y())
660  {
661  zoneMax.y() = cellCentre.y();
662  }
663  if (cellCentre.y() < zoneMin.y())
664  {
665  zoneMin.y() = cellCentre.y();
666  }
667  if (cellCentre.z() > zoneMax.z())
668  {
669  zoneMax.z() = cellCentre.z();
670  }
671  if (cellCentre.z() < zoneMin.z())
672  {
673  zoneMin.z() = cellCentre.z();
674  }
675  }
676 
677  point zoneMid = 0.5*(zoneMin + zoneMax);
678 
679  point latticeMid = inv(latticeCellShape) & (R.T()
680  & (zoneMid - anchor));
681 
682  point latticeAnchor
683  (
684  label(latticeMid.x() + 0.5*sign(latticeMid.x())),
685  label(latticeMid.y() + 0.5*sign(latticeMid.y())),
686  label(latticeMid.z() + 0.5*sign(latticeMid.z()))
687  );
688 
689  anchor += (R & (latticeCellShape & latticeAnchor));
690 
691  // Continue trying to place molecules as long as at
692  // least one molecule is placed in each iteration.
693  // The "|| totalZoneMols == 0" condition means that the
694  // algorithm will continue if the origin is outside the
695  // zone.
696 
697  label n = 0;
698 
699  label totalZoneMols = 0;
700 
701  label molsPlacedThisIteration = 0;
702 
703  while
704  (
705  molsPlacedThisIteration != 0
706  || totalZoneMols == 0
707  )
708  {
709  label sizeBeforeIteration = this->size();
710 
711  bool partOfLayerInBounds = false;
712 
713  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
714  // start of placement of molecules
715  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
716 
717  if (n == 0)
718  {
719  // Special treatment is required for the first position,
720  // i.e. iteration zero.
721 
722  labelVector unitCellLatticePosition(0,0,0);
723 
724  forAll(latticePositions, p)
725  {
726  label id = findIndex(pot_.idList(), latticeIds[p]);
727 
728  const vector& latticePosition =
729  vector
730  (
731  unitCellLatticePosition.x(),
732  unitCellLatticePosition.y(),
733  unitCellLatticePosition.z()
734  )
735  + latticePositions[p];
736 
737  point globalPosition =
738  anchor
739  + (R & (latticeCellShape & latticePosition));
740 
741  partOfLayerInBounds = mesh_.bounds().contains
742  (
743  globalPosition
744  );
745 
746  const label cell =
747  mesh_.cellTree().findInside(globalPosition);
748 
749  if (findIndex(zone, cell) != -1)
750  {
751  createMolecule
752  (
753  globalPosition,
754  cell,
755  nLocateBoundaryHits,
756  id,
757  tethered,
758  temperature,
759  bulkVelocity
760  );
761  }
762  }
763  }
764  else
765  {
766  // Place top and bottom caps.
767 
768  labelVector unitCellLatticePosition(0,0,0);
769 
770  for
771  (
772  unitCellLatticePosition.z() = -n;
773  unitCellLatticePosition.z() <= n;
774  unitCellLatticePosition.z() += 2*n
775  )
776  {
777  for
778  (
779  unitCellLatticePosition.y() = -n;
780  unitCellLatticePosition.y() <= n;
781  unitCellLatticePosition.y()++
782  )
783  {
784  for
785  (
786  unitCellLatticePosition.x() = -n;
787  unitCellLatticePosition.x() <= n;
788  unitCellLatticePosition.x()++
789  )
790  {
791  forAll(latticePositions, p)
792  {
793  label id = findIndex
794  (
795  pot_.idList(),
796  latticeIds[p]
797  );
798 
799  const vector& latticePosition =
800  vector
801  (
802  unitCellLatticePosition.x(),
803  unitCellLatticePosition.y(),
804  unitCellLatticePosition.z()
805  )
806  + latticePositions[p];
807 
808  point globalPosition =
809  anchor
810  + (
811  R
812  & (
813  latticeCellShape
814  & latticePosition
815  )
816  );
817 
818  partOfLayerInBounds =
819  mesh_.bounds().contains
820  (
821  globalPosition
822  );
823 
824  const label cell =
825  mesh_.cellTree().findInside
826  (
827  globalPosition
828  );
829 
830  if (findIndex(zone, cell) != -1)
831  {
832  createMolecule
833  (
834  globalPosition,
835  cell,
836  nLocateBoundaryHits,
837  id,
838  tethered,
839  temperature,
840  bulkVelocity
841  );
842  }
843  }
844  }
845  }
846  }
847 
848  for
849  (
850  unitCellLatticePosition.z() = -(n-1);
851  unitCellLatticePosition.z() <= (n-1);
852  unitCellLatticePosition.z()++
853  )
854  {
855  for (label iR = 0; iR <= 2*n -1; iR++)
856  {
857  unitCellLatticePosition.x() = n;
858 
859  unitCellLatticePosition.y() = -n + (iR + 1);
860 
861  for (label iK = 0; iK < 4; iK++)
862  {
863  forAll(latticePositions, p)
864  {
865  label id = findIndex
866  (
867  pot_.idList(),
868  latticeIds[p]
869  );
870 
871  const vector& latticePosition =
872  vector
873  (
874  unitCellLatticePosition.x(),
875  unitCellLatticePosition.y(),
876  unitCellLatticePosition.z()
877  )
878  + latticePositions[p];
879 
880  point globalPosition =
881  anchor
882  + (
883  R
884  & (
885  latticeCellShape
886  & latticePosition
887  )
888  );
889 
890  partOfLayerInBounds =
891  mesh_.bounds().contains
892  (
893  globalPosition
894  );
895 
896  const label cell =
897  mesh_.cellTree().findInside
898  (
899  globalPosition
900  );
901 
902  if (findIndex(zone, cell) != -1)
903  {
904  createMolecule
905  (
906  globalPosition,
907  cell,
908  nLocateBoundaryHits,
909  id,
910  tethered,
911  temperature,
912  bulkVelocity
913  );
914  }
915  }
916 
917  unitCellLatticePosition =
919  (
920  - unitCellLatticePosition.y(),
921  unitCellLatticePosition.x(),
922  unitCellLatticePosition.z()
923  );
924  }
925  }
926  }
927  }
928 
929  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
930  // end of placement of molecules
931  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
932 
933  if
934  (
935  totalZoneMols == 0
936  && !partOfLayerInBounds
937  )
938  {
940  << "A whole layer of unit cells was placed "
941  << "outside the bounds of the mesh, but no "
942  << "molecules have been placed in zone '"
943  << zone.name()
944  << "'. This is likely to be because the zone "
945  << "has few cells ("
946  << zone.size()
947  << " in this case) and no lattice position "
948  << "fell inside them. "
949  << "Aborting filling this zone."
950  << endl;
951 
952  break;
953  }
954 
955  molsPlacedThisIteration =
956  this->size() - sizeBeforeIteration;
957 
958  totalZoneMols += molsPlacedThisIteration;
959 
960  n++;
961  }
962  }
963  }
964  }
965 
966  reduce(nLocateBoundaryHits, sumOp<label>());
967  if (nLocateBoundaryHits != 0)
968  {
970  << "Initialisation of cloud " << this->name()
971  << " did not accurately locate " << nLocateBoundaryHits
972  << " particles" << endl;
973  }
974 }
975 
976 
977 void Foam::moleculeCloud::createMolecule
978 (
979  const point& position,
980  label cell,
981  label& nLocateBoundaryHits,
982  label id,
983  bool tethered,
984  scalar temperature,
985  const vector& bulkVelocity
986 )
987 {
988  point specialPosition(Zero);
989 
990  label special = 0;
991 
992  if (tethered)
993  {
994  specialPosition = position;
995 
996  special = molecule::SPECIAL_TETHERED;
997  }
998 
999  const molecule::constantProperties& cP(constProps(id));
1000 
1001  vector v = equipartitionLinearVelocity(temperature, cP.mass());
1002 
1003  v += bulkVelocity;
1004 
1005  vector pi = Zero;
1006 
1007  tensor Q = I;
1008 
1009  if (!cP.pointMolecule())
1010  {
1011  pi = equipartitionAngularMomentum(temperature, cP);
1012 
1013  scalar phi(rndGen_.scalar01()*twoPi);
1014 
1015  scalar theta(rndGen_.scalar01()*twoPi);
1016 
1017  scalar psi(rndGen_.scalar01()*twoPi);
1018 
1019  Q = tensor
1020  (
1021  cos(psi)*cos(phi) - cos(theta)*sin(phi)*sin(psi),
1022  cos(psi)*sin(phi) + cos(theta)*cos(phi)*sin(psi),
1023  sin(psi)*sin(theta),
1024  - sin(psi)*cos(phi) - cos(theta)*sin(phi)*cos(psi),
1025  - sin(psi)*sin(phi) + cos(theta)*cos(phi)*cos(psi),
1026  cos(psi)*sin(theta),
1027  sin(theta)*sin(phi),
1028  - sin(theta)*cos(phi),
1029  cos(theta)
1030  );
1031  }
1032 
1033  addParticle
1034  (
1035  new molecule
1036  (
1037  mesh_,
1038  position,
1039  cell,
1040  nLocateBoundaryHits,
1041  Q,
1042  v,
1043  Zero,
1044  pi,
1045  Zero,
1046  specialPosition,
1047  constProps(id),
1048  special,
1049  id
1050  )
1051  );
1052 }
1053 
1054 
1055 Foam::label Foam::moleculeCloud::nSites() const
1056 {
1057  label n = 0;
1058 
1059  forAllConstIter(moleculeCloud, *this, mol)
1060  {
1061  n += constProps(mol().id()).nSites();
1062  }
1063 
1064  return n;
1065 }
1066 
1067 
1068 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
1069 
1071 (
1072  const polyMesh& mesh,
1073  const potential& pot,
1074  bool readFields
1075 )
1076 :
1077  Cloud<molecule>(mesh, "moleculeCloud", false),
1078  mesh_(mesh),
1079  pot_(pot),
1080  cellOccupancy_(mesh_.nCells()),
1081  il_(mesh_, pot_.pairPotentials().rCutMax(), false),
1082  constPropList_(),
1083  rndGen_(clock::getTime()),
1084  stdNormal_(rndGen_.generator())
1085 {
1086  if (readFields)
1087  {
1088  molecule::readFields(*this);
1089  }
1090 
1091  buildConstProps();
1092 
1093  setSiteSizesAndPositions();
1094 
1095  removeHighEnergyOverlaps();
1096 
1097  calculateForce();
1098 }
1099 
1100 
1102 (
1103  const polyMesh& mesh,
1104  const potential& pot,
1105  const IOdictionary& mdInitialiseDict,
1106  bool readFields
1107 )
1108 :
1109  Cloud<molecule>(mesh, "moleculeCloud", false),
1110  mesh_(mesh),
1111  pot_(pot),
1112  il_(mesh_, 0.0, false),
1113  constPropList_(),
1114  rndGen_(clock::getTime()),
1115  stdNormal_(rndGen_.generator())
1116 {
1117  if (readFields)
1118  {
1119  molecule::readFields(*this);
1120  }
1121 
1122  clear();
1123 
1124  buildConstProps();
1125 
1126  initialiseMolecules(mdInitialiseDict);
1127 }
1128 
1129 
1130 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
1131 
1133 {
1134  molecule::trackingData td(*this);
1135 
1137  Cloud<molecule>::move(*this, td);
1138 
1140  Cloud<molecule>::move(*this, td);
1141 
1143  Cloud<molecule>::move(*this, td);
1144 
1145  calculateForce();
1146 
1148  Cloud<molecule>::move(*this, td);
1149 }
1150 
1151 
1153 {
1154  buildCellOccupancy();
1155 
1156  // Set accumulated quantities to zero
1157  forAllIter(moleculeCloud, *this, mol)
1158  {
1159  mol().siteForces() = Zero;
1160 
1161  mol().potentialEnergy() = 0.0;
1162 
1163  mol().rf() = Zero;
1164  }
1165 
1166  calculatePairForce();
1167 
1168  calculateTetherForce();
1169 
1170  calculateExternalForce();
1171 }
1172 
1173 
1175 (
1176  const scalar targetTemperature,
1177  const scalar measuredTemperature
1178 )
1179 {
1180  scalar temperatureCorrectionFactor =
1181  sqrt(targetTemperature/max(vSmall, measuredTemperature));
1182 
1183  Info<< "----------------------------------------" << nl
1184  << "Temperature equilibration" << nl
1185  << "Target temperature = "
1186  << targetTemperature << nl
1187  << "Measured temperature = "
1188  << measuredTemperature << nl
1189  << "Temperature correction factor = "
1190  << temperatureCorrectionFactor << nl
1191  << "----------------------------------------"
1192  << endl;
1193 
1194  forAllIter(moleculeCloud, *this, mol)
1195  {
1196  mol().v() *= temperatureCorrectionFactor;
1197 
1198  mol().pi() *= temperatureCorrectionFactor;
1199  }
1200 }
1201 
1202 
1203 void Foam::moleculeCloud::writeXYZ(const fileName& fName) const
1204 {
1205  OFstream os(fName);
1206 
1207  os << nSites() << nl << "moleculeCloud site positions in angstroms" << nl;
1208 
1209  forAllConstIter(moleculeCloud, *this, mol)
1210  {
1211  const molecule::constantProperties& cP = constProps(mol().id());
1212 
1213  forAll(mol().sitePositions(), i)
1214  {
1215  const point& sP = mol().sitePositions()[i];
1216 
1217  os << pot_.siteIdList()[cP.siteIds()[i]]
1218  << ' ' << sP.x()*1e10
1219  << ' ' << sP.y()*1e10
1220  << ' ' << sP.z()*1e10
1221  << nl;
1222  }
1223  }
1224 }
1225 
1226 
1227 // ************************************************************************* //
label n
#define forAll(list, i)
Loop across all elements in list.
Definition: UList.H:434
#define forAllIter(Container, container, iter)
Iterate across all elements in the container object of type.
Definition: UList.H:459
#define forAllConstIter(Container, container, iter)
Iterate across all elements in the container object of type.
Definition: UList.H:477
const List< DynamicList< molecule * > > & cellOccupancy
Base cloud calls templated on particle type.
Definition: Cloud.H:74
void move(TrackCloudType &cloud, typename ParticleType::trackingData &td)
Move the particles.
Definition: Cloud.C:281
IOdictionary is derived from dictionary and IOobject to give the dictionary automatic IO functionalit...
Definition: IOdictionary.H:57
@ MUST_READ_IF_MODIFIED
Definition: IOobject.H:119
Output to file stream.
Definition: OFstream.H:86
static label nRequests()
Get number of outstanding requests.
Definition: UPstream.C:137
static bool & parRun()
Is this a parallel run?
Definition: UPstream.H:399
static const Form one
Definition: VectorSpace.H:119
const Cmpt & z() const
Definition: VectorI.H:87
const Cmpt & y() const
Definition: VectorI.H:81
const Cmpt & x() const
Definition: VectorI.H:75
Read access to the system clock with formatting.
Definition: clock.H:51
A class for handling file names.
Definition: fileName.H:82
void writeXYZ(const fileName &fName) const
Write molecule sites in XYZ format.
void evolve()
Evolve the molecules (move, calculate forces, control state etc)
void applyConstraintsAndThermostats(const scalar targetTemperature, const scalar measuredTemperature)
moleculeCloud(const polyMesh &mesh, const potential &pot, bool readFields=true)
Construct given mesh and potential references.
Class to hold molecule constant properties.
Definition: molecule.H:91
const List< label > & siteIds() const
Definition: moleculeI.H:353
Class used to pass tracking data to the trackToFace function.
Definition: molecule.H:169
trackPart part() const
Return the part of the tracking operation taking place.
Definition: molecule.H:203
Foam::molecule.
Definition: molecule.H:68
@ SPECIAL_TETHERED
Definition: molecule.H:83
static void readFields(Cloud< molecule > &mC)
Definition: moleculeIO.C:89
Mesh consisting of general polyhedral cells.
Definition: polyMesh.H:80
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:334
const volScalarField & psi
#define WarningInFunction
Report a warning using Foam::Warning.
mathematical constants.
const scalar twoPi(2 *pi)
Namespace for OpenFOAM.
dimensionedScalar det(const dimensionedSphericalTensor &dt)
static const zero Zero
Definition: zero.H:97
dimensionedScalar sign(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
Tensor< scalar > tensor
Tensor of scalars.
Definition: tensor.H:51
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:257
word name(const bool)
Return a word representation of a bool.
Definition: boolIO.C:39
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
Vector< label > labelVector
Vector of labels.
Definition: labelVector.H:49
static const char tab
Definition: Ostream.H:265
errorManip< error > abort(error &err)
Definition: errorManip.H:131
dimensionedScalar sin(const dimensionedScalar &ds)
messageStream Info
static const Identity< scalar > I
Definition: Identity.H:93
vector point
Point is a vector.
Definition: point.H:41
dimensionedScalar pow(const dimensionedScalar &ds, const dimensionedScalar &expt)
DiagTensor< scalar > diagTensor
A scalar version of the templated DiagTensor.
Definition: diagTensor.H:48
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)
dimensionedScalar sqrt(const dimensionedScalar &ds)
List< labelList > labelListList
A List of labelList.
Definition: labelList.H:57
defineTypeNameAndDebug(combustionModel, 0)
layerAndWeight max(const layerAndWeight &a, const layerAndWeight &b)
static scalar R(const scalar a, const scalar x)
Definition: invIncGamma.C:102
label findIndex(const ListType &, typename ListType::const_reference, const label start=0)
Find first occurrence of given element and return index,.
error FatalError
dimensionedSphericalTensor inv(const dimensionedSphericalTensor &dt)
static const char nl
Definition: Ostream.H:266
dimensionedScalar cos(const dimensionedScalar &ds)
const word constProp(initialConditions.lookup("constantProperty"))
volScalarField & p