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-------------------------------------------------------------------------------
11License
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
35namespace Foam
36{
38}
39
40// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
41
42void 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
100void 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
113void 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
132void 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
215void 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
242void Foam::moleculeCloud::calculateExternalForce()
243{
244 for (molecule& mol : *this)
245 {
246 mol.a() += pot_.gravity();
247 }
248}
249
250
251void 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
466void 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
952void 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
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
1026Foam::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
1042(
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
1068}
1069
1070
1072(
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
1142(
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
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// ************************************************************************* //
#define R(A, B, C, D, E, F, K, M)
label n
const List< DynamicList< molecule * > > & cellOccupancy
surfaceScalarField & phi
Base cloud calls templated on particle type.
Definition: Cloud.H:68
void clear()
Clear the contents of the list.
Definition: ILList.C:101
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:180
Output to file stream, using an OSstream.
Definition: OFstream.H:57
const word & constant() const
Return constant name.
Definition: TimePathsI.H:96
@ nonBlocking
"nonBlocking"
static label nRequests()
Get number of outstanding requests.
Definition: UPstream.C:90
static bool & parRun() noexcept
Test if this a parallel run.
Definition: UPstream.H:433
const Cmpt & z() const
Access to the vector z component.
Definition: VectorI.H:85
const Cmpt & y() const
Access to the vector y component.
Definition: VectorI.H:79
const Cmpt & x() const
Access to the vector x component.
Definition: VectorI.H:73
Read access to the system clock with formatting.
Definition: clock.H:53
virtual void move()=0
A class for handling file names.
Definition: fileName.H:76
Reads fields from the time directories and adds them to the mesh database for further post-processing...
Definition: readFields.H:158
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)
Class to hold molecule constant properties.
Definition: molecule.H:92
const List< label > & siteIds() const
Definition: moleculeI.H:395
Class used to pass tracking data to the trackToFace function.
Definition: molecule.H:170
Foam::molecule.
Definition: molecule.H:70
const vector & v() const
Definition: moleculeI.H:521
const List< vector > & sitePositions() const
Definition: moleculeI.H:581
const List< vector > & siteForces() const
Definition: moleculeI.H:569
@ SPECIAL_TETHERED
Definition: molecule.H:83
const tensor & rf() const
Definition: moleculeI.H:617
scalar potentialEnergy() const
Definition: moleculeI.H:605
const vector & pi() const
Definition: moleculeI.H:545
label id() const
Definition: moleculeI.H:641
const Time & time() const noexcept
Return time registry.
Mesh consisting of general polyhedral cells.
Definition: polyMesh.H:81
const List< word > & siteIdList() const
Definition: potentialI.H:42
const List< word > & idList() const
Definition: potentialI.H:36
Tensor of scalars, i.e. Tensor<scalar>.
A Vector of values with scalar precision, where scalar is float/double depending on the compilation f...
#define defineTemplateTypeNameAndDebug(Type, DebugSwitch)
Define the typeName and debug information for templates, useful.
Definition: className.H:132
volScalarField & p
const volScalarField & psi
bool
Definition: EEqn.H:20
dynamicFvMesh & mesh
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:453
OBJstream os(runTime.globalPath()/outputName)
#define WarningInFunction
Report a warning using Foam::Warning.
constexpr scalar pi(M_PI)
constexpr scalar twoPi(2 *M_PI)
Namespace for OpenFOAM.
dimensionedScalar det(const dimensionedSphericalTensor &dt)
label max(const labelHashSet &set, label maxValue=labelMin)
Find the max value in labelHashSet, optionally limited by second argument.
Definition: hashSets.C:47
dimensionedScalar sign(const dimensionedScalar &ds)
ZoneMesh< cellZone, polyMesh > cellZoneMesh
A ZoneMesh with the type cellZone.
Vector< label > labelVector
Vector of labels.
Definition: labelVector.H:51
dimensionedScalar sin(const dimensionedScalar &ds)
messageStream Info
Information stream (stdout output on master, null elsewhere)
static const Identity< scalar > I
Definition: Identity.H:94
void readFields(const typename GeoFieldType::Mesh &mesh, const IOobjectList &objects, const wordHashSet &selectedFields, LIFOStack< regIOobject * > &storedObjects)
Read the selected GeometricFields of the templated type.
vector point
Point is a vector.
Definition: point.H:43
Tensor< scalar > tensor
Definition: symmTensor.H:61
dimensionedScalar pow(const dimensionedScalar &ds, const dimensionedScalar &expt)
DiagTensor< scalar > diagTensor
DiagTensor of scalars, i.e. DiagTensor<scalar>.
Definition: diagTensor.H:53
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:372
constexpr scalar degToRad() noexcept
Multiplication factor for degrees to radians conversion.
dimensionedScalar sqrt(const dimensionedScalar &ds)
List< labelList > labelListList
A List of labelList.
Definition: labelList.H:56
void reduce(const List< UPstream::commsStruct > &comms, T &value, const BinaryOp &bop, const int tag, const label comm)
errorManip< error > abort(error &err)
Definition: errorManip.H:144
static constexpr const zero Zero
Global zero (0)
Definition: zero.H:131
error FatalError
dimensionedSphericalTensor inv(const dimensionedSphericalTensor &dt)
Vector< scalar > vector
Definition: vector.H:61
dimensionedScalar cos(const dimensionedScalar &ds)
constexpr char nl
The newline '\n' character (0x0a)
Definition: Ostream.H:53
constexpr char tab
The tab '\t' character(0x09)
Definition: Ostream.H:52
word constProp(initialConditions.get< word >("constantProperty"))
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:333
Unit conversion functions.