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