1 /*---------------------------------------------------------------------------*\
3 \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
5 \\ / A nd | Copyright (C) 2011 OpenFOAM Foundation
7 -------------------------------------------------------------------------------
9 This file is part of OpenFOAM.
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.
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
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/>.
24 \*----------------------------------------------------------------------------*/
26 #include "moleculeCloud.H"
28 #include "mathematicalConstants.H"
30 using namespace Foam::constant::mathematical;
32 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
36 defineTemplateTypeNameAndDebug(Cloud<molecule>, 0);
39 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
41 void Foam::moleculeCloud::buildConstProps()
43 Info<< nl << "Reading moleculeProperties dictionary." << endl;
45 const List<word>& idList(pot_.idList());
47 constPropList_.setSize(idList.size());
49 const List<word>& siteIdList(pot_.siteIdList());
51 IOdictionary moleculePropertiesDict
56 mesh_.time().constant(),
58 IOobject::MUST_READ_IF_MODIFIED,
66 const word& id = idList[i];
67 const dictionary& molDict = moleculePropertiesDict.subDict(id);
69 List<word> siteIdNames = molDict.lookup("siteIds");
71 List<label> siteIds(siteIdNames.size());
73 forAll(siteIdNames, sI)
75 const word& siteId = siteIdNames[sI];
77 siteIds[sI] = findIndex(siteIdList, siteId);
79 if (siteIds[sI] == -1)
81 FatalErrorIn("moleculeCloud::buildConstProps()")
82 << siteId << " site not found."
83 << nl << abort(FatalError);
87 molecule::constantProperties& constProp = constPropList_[i];
89 constProp = molecule::constantProperties(molDict);
91 constProp.siteIds() = siteIds;
96 void Foam::moleculeCloud::setSiteSizesAndPositions()
98 forAllIter(moleculeCloud, *this, mol)
100 const molecule::constantProperties& cP = constProps(mol().id());
102 mol().setSiteSizes(cP.nSites());
104 mol().setSitePositions(cP);
109 void Foam::moleculeCloud::buildCellOccupancy()
111 forAll(cellOccupancy_, cO)
113 cellOccupancy_[cO].clear();
116 forAllIter(moleculeCloud, *this, mol)
118 cellOccupancy_[mol().cell()].append(&mol());
121 forAll(cellOccupancy_, cO)
123 cellOccupancy_[cO].shrink();
128 void Foam::moleculeCloud::calculatePairForce()
130 PstreamBuffers pBufs(Pstream::nonBlocking);
132 // Start sending referred data
133 il_.sendReferredData(cellOccupancy(), pBufs);
135 molecule* molI = NULL;
136 molecule* molJ = NULL;
139 // Real-Real interactions
141 const labelListList& dil = il_.dil();
145 forAll(cellOccupancy_[d],cellIMols)
147 molI = cellOccupancy_[d][cellIMols];
149 forAll(dil[d], interactingCells)
151 List<molecule*> cellJ =
152 cellOccupancy_[dil[d][interactingCells]];
154 forAll(cellJ, cellJMols)
156 molJ = cellJ[cellJMols];
158 evaluatePair(*molI, *molJ);
162 forAll(cellOccupancy_[d], cellIOtherMols)
164 molJ = cellOccupancy_[d][cellIOtherMols];
168 evaluatePair(*molI, *molJ);
175 // Receive referred data
176 il_.receiveReferredData(pBufs);
179 // Real-Referred interactions
181 const labelListList& ril = il_.ril();
183 List<IDLList<molecule> >& referredMols = il_.referredParticles();
187 const List<label>& realCells = ril[r];
189 IDLList<molecule>& refMols = referredMols[r];
198 forAll(realCells, rC)
200 List<molecule*> cellI = cellOccupancy_[realCells[rC]];
202 forAll(cellI, cellIMols)
204 molI = cellI[cellIMols];
206 evaluatePair(*molI, refMol());
215 void Foam::moleculeCloud::calculateTetherForce()
217 const tetherPotentialList& tetherPot(pot_.tetherPotentials());
219 forAllIter(moleculeCloud, *this, mol)
221 if (mol().tethered())
223 vector rIT = mol().position() - mol().specialPosition();
225 label idI = mol().id();
227 scalar massI = constProps(idI).mass();
229 vector fIT = tetherPot.force(idI, rIT);
231 mol().a() += fIT/massI;
233 mol().potentialEnergy() += tetherPot.energy(idI, rIT);
236 // mol().rf() += rIT*fIT;
242 void Foam::moleculeCloud::calculateExternalForce()
244 forAllIter(moleculeCloud, *this, mol)
246 mol().a() += pot_.gravity();
251 void Foam::moleculeCloud::removeHighEnergyOverlaps()
253 Info<< nl << "Removing high energy overlaps, limit = "
254 << pot_.potentialEnergyLimit()
255 << nl << "Removal order:";
257 forAll(pot_.removalOrder(), rO)
259 Info<< ' ' << pot_.idList()[pot_.removalOrder()[rO]];
264 label initialSize = this->size();
266 buildCellOccupancy();
268 // Real-Real interaction
270 molecule* molI = NULL;
271 molecule* molJ = NULL;
274 DynamicList<molecule*> molsToDelete;
276 const labelListList& dil(il_.dil());
280 forAll(cellOccupancy_[d],cellIMols)
282 molI = cellOccupancy_[d][cellIMols];
284 forAll(dil[d], interactingCells)
286 List<molecule*> cellJ =
287 cellOccupancy_[dil[d][interactingCells]];
289 forAll(cellJ, cellJMols)
291 molJ = cellJ[cellJMols];
293 if (evaluatePotentialLimit(*molI, *molJ))
295 label idI = molI->id();
297 label idJ = molJ->id();
302 || findIndex(pot_.removalOrder(), idJ)
303 < findIndex(pot_.removalOrder(), idI)
306 if (findIndex(molsToDelete, molJ) == -1)
308 molsToDelete.append(molJ);
311 else if (findIndex(molsToDelete, molI) == -1)
313 molsToDelete.append(molI);
320 forAll(cellOccupancy_[d], cellIOtherMols)
322 molJ = cellOccupancy_[d][cellIOtherMols];
326 if (evaluatePotentialLimit(*molI, *molJ))
328 label idI = molI->id();
330 label idJ = molJ->id();
335 || findIndex(pot_.removalOrder(), idJ)
336 < findIndex(pot_.removalOrder(), idI)
339 if (findIndex(molsToDelete, molJ) == -1)
341 molsToDelete.append(molJ);
344 else if (findIndex(molsToDelete, molI) == -1)
346 molsToDelete.append(molI);
353 forAll(molsToDelete, mTD)
355 deleteParticle(*(molsToDelete[mTD]));
359 buildCellOccupancy();
361 PstreamBuffers pBufs(Pstream::nonBlocking);
363 // Start sending referred data
364 il_.sendReferredData(cellOccupancy(), pBufs);
366 // Receive referred data
367 il_.receiveReferredData(pBufs);
369 // Real-Referred interaction
372 DynamicList<molecule*> molsToDelete;
374 const labelListList& ril(il_.ril());
376 List<IDLList<molecule> >& referredMols = il_.referredParticles();
380 IDLList<molecule>& refMols = referredMols[r];
391 const List<label>& realCells = ril[r];
393 forAll(realCells, rC)
395 label cellI = realCells[rC];
397 List<molecule*> cellIMols = cellOccupancy_[cellI];
399 forAll(cellIMols, cIM)
401 molI = cellIMols[cIM];
403 if (evaluatePotentialLimit(*molI, *molJ))
405 label idI = molI->id();
407 label idJ = molJ->id();
411 findIndex(pot_.removalOrder(), idI)
412 < findIndex(pot_.removalOrder(), idJ)
415 if (findIndex(molsToDelete, molI) == -1)
417 molsToDelete.append(molI);
422 findIndex(pot_.removalOrder(), idI)
423 == findIndex(pot_.removalOrder(), idJ)
426 // Remove one of the molecules
427 // arbitrarily, assuring that a
428 // consistent decision is made for
429 // both real-referred pairs.
431 if (molI->origId() > molJ->origId())
433 if (findIndex(molsToDelete, molI) == -1)
435 molsToDelete.append(molI);
445 forAll(molsToDelete, mTD)
447 deleteParticle(*(molsToDelete[mTD]));
451 buildCellOccupancy();
453 // Start sending referred data
454 il_.sendReferredData(cellOccupancy(), pBufs);
456 // Receive referred data
457 il_.receiveReferredData(pBufs);
459 label molsRemoved = initialSize - this->size();
461 if (Pstream::parRun())
463 reduce(molsRemoved, sumOp<label>());
466 Info<< tab << molsRemoved << " molecules removed" << endl;
470 void Foam::moleculeCloud::initialiseMolecules
472 const IOdictionary& mdInitialiseDict
476 << "Initialising molecules in each zone specified in mdInitialiseDict."
479 const cellZoneMesh& cellZones = mesh_.cellZones();
481 if (!cellZones.size())
483 FatalErrorIn("void Foam::moleculeCloud::initialiseMolecules")
484 << "No cellZones found in the mesh."
485 << abort(FatalError);
490 const cellZone& zone(cellZones[z]);
494 if (!mdInitialiseDict.found(zone.name()))
496 Info<< "No specification subDictionary for zone "
497 << zone.name() << " found, skipping." << endl;
501 const dictionary& zoneDict =
502 mdInitialiseDict.subDict(zone.name());
504 const scalar temperature
506 readScalar(zoneDict.lookup("temperature"))
509 const vector bulkVelocity(zoneDict.lookup("bulkVelocity"));
511 List<word> latticeIds
513 zoneDict.lookup("latticeIds")
516 List<vector> latticePositions
518 zoneDict.lookup("latticePositions")
521 if (latticeIds.size() != latticePositions.size())
523 FatalErrorIn("Foam::moleculeCloud::initialiseMolecules")
524 << "latticeIds and latticePositions must be the same "
526 << abort(FatalError);
529 diagTensor latticeCellShape
531 zoneDict.lookup("latticeCellShape")
534 scalar latticeCellScale = 0.0;
536 if (zoneDict.found("numberDensity"))
538 scalar numberDensity = readScalar
540 zoneDict.lookup("numberDensity")
543 if (numberDensity < VSMALL)
545 WarningIn("moleculeCloud::initialiseMolecules")
546 << "numberDensity too small, not filling zone "
547 << zone.name() << endl;
552 latticeCellScale = pow
554 latticeIds.size()/(det(latticeCellShape)*numberDensity),
558 else if (zoneDict.found("massDensity"))
560 scalar unitCellMass = 0.0;
562 forAll(latticeIds, i)
564 label id = findIndex(pot_.idList(), latticeIds[i]);
566 const molecule::constantProperties& cP(constProps(id));
568 unitCellMass += cP.mass();
571 scalar massDensity = readScalar
573 zoneDict.lookup("massDensity")
576 if (massDensity < VSMALL)
578 WarningIn("moleculeCloud::initialiseMolecules")
579 << "massDensity too small, not filling zone "
580 << zone.name() << endl;
586 latticeCellScale = pow
588 unitCellMass/(det(latticeCellShape)*massDensity),
594 FatalErrorIn("Foam::moleculeCloud::initialiseMolecules")
595 << "massDensity or numberDensity not specified " << nl
596 << abort(FatalError);
599 latticeCellShape *= latticeCellScale;
601 vector anchor(zoneDict.lookup("anchor"));
603 bool tethered = false;
605 if (zoneDict.found("tetherSiteIds"))
609 List<word>(zoneDict.lookup("tetherSiteIds")).size()
613 const vector orientationAngles
615 zoneDict.lookup("orientationAngles")
618 scalar phi(orientationAngles.x()*pi/180.0);
620 scalar theta(orientationAngles.y()*pi/180.0);
622 scalar psi(orientationAngles.z()*pi/180.0);
626 cos(psi)*cos(phi) - cos(theta)*sin(phi)*sin(psi),
627 cos(psi)*sin(phi) + cos(theta)*cos(phi)*sin(psi),
629 - sin(psi)*cos(phi) - cos(theta)*sin(phi)*cos(psi),
630 - sin(psi)*sin(phi) + cos(theta)*cos(phi)*cos(psi),
633 - sin(theta)*cos(phi),
637 // Find the optimal anchor position. Finding the approximate
638 // mid-point of the zone of cells and snapping to the nearest
641 vector zoneMin = VGREAT*vector::one;
643 vector zoneMax = -VGREAT*vector::one;
647 const point cellCentre = mesh_.cellCentres()[zone[cell]];
649 if (cellCentre.x() > zoneMax.x())
651 zoneMax.x() = cellCentre.x();
653 if (cellCentre.x() < zoneMin.x())
655 zoneMin.x() = cellCentre.x();
657 if (cellCentre.y() > zoneMax.y())
659 zoneMax.y() = cellCentre.y();
661 if (cellCentre.y() < zoneMin.y())
663 zoneMin.y() = cellCentre.y();
665 if (cellCentre.z() > zoneMax.z())
667 zoneMax.z() = cellCentre.z();
669 if (cellCentre.z() < zoneMin.z())
671 zoneMin.z() = cellCentre.z();
675 point zoneMid = 0.5*(zoneMin + zoneMax);
677 point latticeMid = inv(latticeCellShape) & (R.T()
678 & (zoneMid - anchor));
682 label(latticeMid.x() + 0.5*sign(latticeMid.x())),
683 label(latticeMid.y() + 0.5*sign(latticeMid.y())),
684 label(latticeMid.z() + 0.5*sign(latticeMid.z()))
687 anchor += (R & (latticeCellShape & latticeAnchor));
689 // Continue trying to place molecules as long as at
690 // least one molecule is placed in each iteration.
691 // The "|| totalZoneMols == 0" condition means that the
692 // algorithm will continue if the origin is outside the
697 label totalZoneMols = 0;
699 label molsPlacedThisIteration = 0;
703 molsPlacedThisIteration != 0
704 || totalZoneMols == 0
707 label sizeBeforeIteration = this->size();
709 bool partOfLayerInBounds = false;
711 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
712 // start of placement of molecules
713 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
717 // Special treatment is required for the first position,
718 // i.e. iteration zero.
720 labelVector unitCellLatticePosition(0,0,0);
722 forAll(latticePositions, p)
724 label id = findIndex(pot_.idList(), latticeIds[p]);
726 const vector& latticePosition =
729 unitCellLatticePosition.x(),
730 unitCellLatticePosition.y(),
731 unitCellLatticePosition.z()
733 + latticePositions[p];
735 point globalPosition =
737 + (R & (latticeCellShape & latticePosition));
739 partOfLayerInBounds = mesh_.bounds().contains
756 if (findIndex(zone, cell) != -1)
774 // Place top and bottom caps.
776 labelVector unitCellLatticePosition(0,0,0);
780 unitCellLatticePosition.z() = -n;
781 unitCellLatticePosition.z() <= n;
782 unitCellLatticePosition.z() += 2*n
787 unitCellLatticePosition.y() = -n;
788 unitCellLatticePosition.y() <= n;
789 unitCellLatticePosition.y()++
794 unitCellLatticePosition.x() = -n;
795 unitCellLatticePosition.x() <= n;
796 unitCellLatticePosition.x()++
799 forAll(latticePositions, p)
807 const vector& latticePosition =
810 unitCellLatticePosition.x(),
811 unitCellLatticePosition.y(),
812 unitCellLatticePosition.z()
814 + latticePositions[p];
816 point globalPosition =
826 partOfLayerInBounds =
827 mesh_.bounds().contains
844 if (findIndex(zone, cell) != -1)
865 unitCellLatticePosition.z() = -(n-1);
866 unitCellLatticePosition.z() <= (n-1);
867 unitCellLatticePosition.z()++
870 for (label iR = 0; iR <= 2*n -1; iR++)
872 unitCellLatticePosition.x() = n;
874 unitCellLatticePosition.y() = -n + (iR + 1);
876 for (label iK = 0; iK < 4; iK++)
878 forAll(latticePositions, p)
886 const vector& latticePosition =
889 unitCellLatticePosition.x(),
890 unitCellLatticePosition.y(),
891 unitCellLatticePosition.z()
893 + latticePositions[p];
895 point globalPosition =
905 partOfLayerInBounds =
906 mesh_.bounds().contains
923 if (findIndex(zone, cell) != -1)
939 unitCellLatticePosition =
942 - unitCellLatticePosition.y(),
943 unitCellLatticePosition.x(),
944 unitCellLatticePosition.z()
951 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
952 // end of placement of molecules
953 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
958 && !partOfLayerInBounds
961 WarningIn("Foam::moleculeCloud::initialiseMolecules()")
962 << "A whole layer of unit cells was placed "
963 << "outside the bounds of the mesh, but no "
964 << "molecules have been placed in zone '"
966 << "'. This is likely to be because the zone "
969 << " in this case) and no lattice position "
970 << "fell inside them. "
971 << "Aborting filling this zone."
977 molsPlacedThisIteration =
978 this->size() - sizeBeforeIteration;
980 totalZoneMols += molsPlacedThisIteration;
990 void Foam::moleculeCloud::createMolecule
992 const point& position,
999 const vector& bulkVelocity
1004 mesh_.findCellFacePt(position, cell, tetFace, tetPt);
1009 FatalErrorIn("Foam::moleculeCloud::createMolecule")
1010 << "Position specified does not correspond to a mesh cell." << nl
1011 << abort(FatalError);
1014 point specialPosition(vector::zero);
1020 specialPosition = position;
1022 special = molecule::SPECIAL_TETHERED;
1025 const molecule::constantProperties& cP(constProps(id));
1027 vector v = equipartitionLinearVelocity(temperature, cP.mass());
1031 vector pi = vector::zero;
1035 if (!cP.pointMolecule())
1037 pi = equipartitionAngularMomentum(temperature, cP);
1039 scalar phi(rndGen_.scalar01()*twoPi);
1041 scalar theta(rndGen_.scalar01()*twoPi);
1043 scalar psi(rndGen_.scalar01()*twoPi);
1047 cos(psi)*cos(phi) - cos(theta)*sin(phi)*sin(psi),
1048 cos(psi)*sin(phi) + cos(theta)*cos(phi)*sin(psi),
1049 sin(psi)*sin(theta),
1050 - sin(psi)*cos(phi) - cos(theta)*sin(phi)*cos(psi),
1051 - sin(psi)*sin(phi) + cos(theta)*cos(phi)*cos(psi),
1052 cos(psi)*sin(theta),
1053 sin(theta)*sin(phi),
1054 - sin(theta)*cos(phi),
1082 Foam::label Foam::moleculeCloud::nSites() const
1086 forAllConstIter(moleculeCloud, *this, mol)
1088 n += constProps(mol().id()).nSites();
1095 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
1097 Foam::moleculeCloud::moleculeCloud
1099 const polyMesh& mesh,
1100 const potential& pot,
1104 Cloud<molecule>(mesh, "moleculeCloud", false),
1107 cellOccupancy_(mesh_.nCells()),
1108 il_(mesh_, pot_.pairPotentials().rCutMax(), false),
1110 rndGen_(clock::getTime())
1114 molecule::readFields(*this);
1119 setSiteSizesAndPositions();
1121 removeHighEnergyOverlaps();
1127 Foam::moleculeCloud::moleculeCloud
1129 const polyMesh& mesh,
1130 const potential& pot,
1131 const IOdictionary& mdInitialiseDict,
1135 Cloud<molecule>(mesh, "moleculeCloud", false),
1138 il_(mesh_, 0.0, false),
1140 rndGen_(clock::getTime())
1144 molecule::readFields(*this);
1151 initialiseMolecules(mdInitialiseDict);
1155 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
1157 void Foam::moleculeCloud::evolve()
1159 molecule::trackingData td0(*this, 0);
1160 Cloud<molecule>::move(td0, mesh_.time().deltaTValue());
1162 molecule::trackingData td1(*this, 1);
1163 Cloud<molecule>::move(td1, mesh_.time().deltaTValue());
1165 molecule::trackingData td2(*this, 2);
1166 Cloud<molecule>::move(td2, mesh_.time().deltaTValue());
1170 molecule::trackingData td3(*this, 3);
1171 Cloud<molecule>::move(td3, mesh_.time().deltaTValue());
1175 void Foam::moleculeCloud::calculateForce()
1177 buildCellOccupancy();
1179 // Set accumulated quantities to zero
1180 forAllIter(moleculeCloud, *this, mol)
1182 mol().siteForces() = vector::zero;
1184 mol().potentialEnergy() = 0.0;
1186 mol().rf() = tensor::zero;
1189 calculatePairForce();
1191 calculateTetherForce();
1193 calculateExternalForce();
1197 void Foam::moleculeCloud::applyConstraintsAndThermostats
1199 const scalar targetTemperature,
1200 const scalar measuredTemperature
1203 scalar temperatureCorrectionFactor =
1204 sqrt(targetTemperature/max(VSMALL, measuredTemperature));
1206 Info<< "----------------------------------------" << nl
1207 << "Temperature equilibration" << nl
1208 << "Target temperature = "
1209 << targetTemperature << nl
1210 << "Measured temperature = "
1211 << measuredTemperature << nl
1212 << "Temperature correction factor = "
1213 << temperatureCorrectionFactor << nl
1214 << "----------------------------------------"
1217 forAllIter(moleculeCloud, *this, mol)
1219 mol().v() *= temperatureCorrectionFactor;
1221 mol().pi() *= temperatureCorrectionFactor;
1226 void Foam::moleculeCloud::writeXYZ(const fileName& fName) const
1230 os << nSites() << nl << "moleculeCloud site positions in angstroms" << nl;
1232 forAllConstIter(moleculeCloud, *this, mol)
1234 const molecule::constantProperties& cP = constProps(mol().id());
1236 forAll(mol().sitePositions(), i)
1238 const point& sP = mol().sitePositions()[i];
1240 os << pot_.siteIdList()[cP.siteIds()[i]]
1241 << ' ' << sP.x()*1e10
1242 << ' ' << sP.y()*1e10
1243 << ' ' << sP.z()*1e10
1250 // ************************************************************************* //