1 /*---------------------------------------------------------------------------*\
3 \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
5 \\ / A nd | Copyright (C) 2008-2009 OpenCFD Ltd.
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 the
13 Free Software Foundation; either version 2 of the License, or (at your
14 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, write to the Free Software Foundation,
23 Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
25 \*----------------------------------------------------------------------------*/
27 #include "moleculeCloud.H"
30 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
34 defineParticleTypeNameAndDebug(molecule, 0);
35 defineTemplateTypeNameAndDebug(Cloud<molecule>, 0);
38 Foam::scalar Foam::moleculeCloud::kb = 1.380650277e-23;
40 Foam::scalar Foam::moleculeCloud::elementaryCharge = 1.602176487e-19;
42 Foam::scalar Foam::moleculeCloud::vacuumPermittivity = 8.854187817e-12;
45 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
47 void Foam::moleculeCloud::buildConstProps()
49 Info<< nl << "Reading moleculeProperties dictionary." << endl;
51 const List<word>& idList(pot_.idList());
53 constPropList_.setSize(idList.size());
55 const List<word>& siteIdList(pot_.siteIdList());
57 IOdictionary moleculePropertiesDict
62 mesh_.time().constant(),
72 const word& id(idList[i]);
74 const dictionary& molDict(moleculePropertiesDict.subDict(id));
76 List<word> siteIdNames = molDict.lookup("siteIds");
78 List<label> siteIds(siteIdNames.size());
80 forAll(siteIdNames, sI)
82 const word& siteId = siteIdNames[sI];
84 siteIds[sI] = findIndex(siteIdList, siteId);
86 if (siteIds[sI] == -1)
88 FatalErrorIn("moleculeCloud.C") << nl
89 << siteId << " site not found."
90 << nl << abort(FatalError);
94 molecule::constantProperties& constProp = constPropList_[i];
96 constProp = molecule::constantProperties(molDict);
98 constProp.siteIds() = siteIds;
103 void Foam::moleculeCloud::setSiteSizesAndPositions()
105 iterator mol(this->begin());
107 for (mol = this->begin(); mol != this->end(); ++mol)
109 const molecule::constantProperties& cP = constProps(mol().id());
111 mol().setSiteSizes(cP.nSites());
113 mol().setSitePositions(cP);
118 void Foam::moleculeCloud::buildCellOccupancy()
120 forAll(cellOccupancy_, cO)
122 cellOccupancy_[cO].clear();
125 iterator mol(this->begin());
134 cellOccupancy_[mol().cell()].append(&mol());
137 forAll(cellOccupancy_, cO)
139 cellOccupancy_[cO].shrink();
142 il_.ril().referMolecules(cellOccupancy());
146 void Foam::moleculeCloud::calculatePairForce()
148 iterator mol(this->begin());
151 // Real-Real interactions
153 molecule* molI = &mol();
155 molecule* molJ = &mol();
157 const directInteractionList& dil(il_.dil());
161 forAll(cellOccupancy_[d],cellIMols)
163 molI = cellOccupancy_[d][cellIMols];
165 forAll(dil[d], interactingCells)
167 List< molecule* > cellJ =
168 cellOccupancy_[dil[d][interactingCells]];
170 forAll(cellJ, cellJMols)
172 molJ = cellJ[cellJMols];
174 evaluatePair(molI, molJ);
178 forAll(cellOccupancy_[d],cellIOtherMols)
180 molJ = cellOccupancy_[d][cellIOtherMols];
184 evaluatePair(molI, molJ);
192 // Real-Referred interactions
194 molecule* molK = &mol();
196 referredCellList& ril(il_.ril());
200 const List<label>& realCells = ril[r].realCellsForInteraction();
202 forAll(ril[r], refMols)
204 referredMolecule* molL(&(ril[r][refMols]));
206 forAll(realCells, rC)
208 List<molecule*> cellK = cellOccupancy_[realCells[rC]];
210 forAll(cellK, cellKMols)
212 molK = cellK[cellKMols];
214 evaluatePair(molK, molL);
223 void Foam::moleculeCloud::calculateTetherForce()
225 const tetherPotentialList& tetherPot(pot_.tetherPotentials());
227 iterator mol(this->begin());
229 for (mol = this->begin(); mol != this->end(); ++mol)
231 if (mol().tethered())
233 vector rIT = mol().position() - mol().specialPosition();
235 label idI = mol().id();
237 scalar massI = constProps(idI).mass();
239 vector fIT = tetherPot.force(idI, rIT);
241 mol().a() += fIT/massI;
243 mol().potentialEnergy() += tetherPot.energy(idI, rIT);
246 // mol().rf() += rIT*fIT;
252 void Foam::moleculeCloud::calculateExternalForce()
254 iterator mol(this->begin());
256 for (mol = this->begin(); mol != this->end(); ++mol)
258 mol().a() += pot_.gravity();
263 void Foam::moleculeCloud::removeHighEnergyOverlaps()
265 Info<< nl << "Removing high energy overlaps, limit = "
266 << pot_.potentialEnergyLimit()
267 << nl << "Removal order:";
269 forAll(pot_.removalOrder(), rO)
271 Info<< ' ' << pot_.idList()[pot_.removalOrder()[rO]];
276 label initialSize = this->size();
278 buildCellOccupancy();
280 // Real-Real interaction
281 iterator mol(this->begin());
286 molecule* molI = &mol();
288 molecule* molJ = &mol();
290 DynamicList<molecule*> molsToDelete;
292 const directInteractionList& dil(il_.dil());
296 forAll(cellOccupancy_[d],cellIMols)
298 molI = cellOccupancy_[d][cellIMols];
300 forAll(dil[d], interactingCells)
302 List< molecule* > cellJ =
303 cellOccupancy_[dil[d][interactingCells]];
305 forAll(cellJ, cellJMols)
307 molJ = cellJ[cellJMols];
309 if (evaluatePotentialLimit(molI, molJ))
311 label idI = molI->id();
313 label idJ = molJ->id();
318 || findIndex(pot_.removalOrder(), idJ)
319 < findIndex(pot_.removalOrder(), idI)
322 if (findIndex(molsToDelete, molJ) == -1)
324 molsToDelete.append(molJ);
327 else if (findIndex(molsToDelete, molI) == -1)
329 molsToDelete.append(molI);
336 forAll(cellOccupancy_[d],cellIOtherMols)
338 molJ = cellOccupancy_[d][cellIOtherMols];
342 if (evaluatePotentialLimit(molI, molJ))
344 label idI = molI->id();
346 label idJ = molJ->id();
351 || findIndex(pot_.removalOrder(), idJ)
352 < findIndex(pot_.removalOrder(), idI)
355 if (findIndex(molsToDelete, molJ) == -1)
357 molsToDelete.append(molJ);
360 else if (findIndex(molsToDelete, molI) == -1)
362 molsToDelete.append(molI);
369 forAll (molsToDelete, mTD)
371 deleteParticle(*(molsToDelete[mTD]));
375 buildCellOccupancy();
377 // Real-Referred interaction
380 molecule* molK = &mol();
382 DynamicList<molecule*> molsToDelete;
384 referredCellList& ril(il_.ril());
388 referredCell& refCell = ril[r];
390 forAll(refCell, refMols)
392 referredMolecule* molL = &(refCell[refMols]);
394 List <label> realCells = refCell.realCellsForInteraction();
396 forAll(realCells, rC)
398 label cellK = realCells[rC];
400 List<molecule*> cellKMols = cellOccupancy_[cellK];
402 forAll(cellKMols, cKM)
404 molK = cellKMols[cKM];
406 if (evaluatePotentialLimit(molK, molL))
408 label idK = molK->id();
410 label idL = molL->id();
414 findIndex(pot_.removalOrder(), idK)
415 < findIndex(pot_.removalOrder(), idL)
418 if (findIndex(molsToDelete, molK) == -1)
420 molsToDelete.append(molK);
426 findIndex(pot_.removalOrder(), idK)
427 == findIndex(pot_.removalOrder(), idL)
432 Pstream::myProcNo() == refCell.sourceProc()
433 && cellK <= refCell.sourceCell()
436 if (findIndex(molsToDelete, molK) == -1)
438 molsToDelete.append(molK);
444 Pstream::myProcNo() < refCell.sourceProc()
447 if (findIndex(molsToDelete, molK) == -1)
449 molsToDelete.append(molK);
459 forAll (molsToDelete, mTD)
461 deleteParticle(*(molsToDelete[mTD]));
465 buildCellOccupancy();
467 label molsRemoved = initialSize - this->size();
469 if (Pstream::parRun())
471 reduce(molsRemoved, sumOp<label>());
474 Info<< tab << molsRemoved << " molecules removed" << endl;
478 void Foam::moleculeCloud::initialiseMolecules
480 const IOdictionary& mdInitialiseDict
484 << "Initialising molecules in each zone specified in mdInitialiseDict."
487 const cellZoneMesh& cellZones = mesh_.cellZones();
489 if (!cellZones.size())
491 FatalErrorIn("void Foam::moleculeCloud::initialiseMolecules")
492 << "No cellZones found in the mesh."
493 << abort(FatalError);
496 forAll (cellZones, z)
498 const cellZone& zone(cellZones[z]);
502 if (!mdInitialiseDict.found(zone.name()))
504 Info<< "No specification subDictionary for zone "
505 << zone.name() << " found, skipping." << endl;
509 const dictionary& zoneDict =
510 mdInitialiseDict.subDict(zone.name());
512 const scalar temperature
514 readScalar(zoneDict.lookup("temperature"))
517 const vector bulkVelocity(zoneDict.lookup("bulkVelocity"));
519 List<word> latticeIds
521 zoneDict.lookup("latticeIds")
524 List<vector> latticePositions
526 zoneDict.lookup("latticePositions")
529 if(latticeIds.size() != latticePositions.size())
531 FatalErrorIn("Foam::moleculeCloud::initialiseMolecules")
532 << "latticeIds and latticePositions must be the same "
534 << abort(FatalError);
537 diagTensor latticeCellShape
539 zoneDict.lookup("latticeCellShape")
542 scalar latticeCellScale = 0.0;
544 if (zoneDict.found("numberDensity"))
546 scalar numberDensity = readScalar
548 zoneDict.lookup("numberDensity")
551 if (numberDensity < VSMALL)
553 WarningIn("moleculeCloud::initialiseMolecules")
554 << "numberDensity too small, not filling zone "
555 << zone.name() << endl;
560 latticeCellScale = pow
562 latticeIds.size()/(det(latticeCellShape)*numberDensity),
566 else if (zoneDict.found("massDensity"))
568 scalar unitCellMass = 0.0;
570 forAll(latticeIds, i)
572 label id = findIndex(pot_.idList(), latticeIds[i]);
574 const molecule::constantProperties& cP(constProps(id));
576 unitCellMass += cP.mass();
579 scalar massDensity = readScalar
581 zoneDict.lookup("massDensity")
584 if (massDensity < VSMALL)
586 WarningIn("moleculeCloud::initialiseMolecules")
587 << "massDensity too small, not filling zone "
588 << zone.name() << endl;
594 latticeCellScale = pow
596 unitCellMass/(det(latticeCellShape)*massDensity),
602 FatalErrorIn("Foam::moleculeCloud::initialiseMolecules")
603 << "massDensity or numberDensity not specified " << nl
604 << abort(FatalError);
607 latticeCellShape *= latticeCellScale;
609 vector anchor(zoneDict.lookup("anchor"));
611 bool tethered = false;
613 if (zoneDict.found("tetherSiteIds"))
617 List<word>(zoneDict.lookup("tetherSiteIds")).size()
621 const vector orientationAngles
623 zoneDict.lookup("orientationAngles")
628 orientationAngles.x()*mathematicalConstant::pi/180.0
633 orientationAngles.y()*mathematicalConstant::pi/180.0
638 orientationAngles.z()*mathematicalConstant::pi/180.0
643 cos(psi)*cos(phi) - cos(theta)*sin(phi)*sin(psi),
644 cos(psi)*sin(phi) + cos(theta)*cos(phi)*sin(psi),
646 - sin(psi)*cos(phi) - cos(theta)*sin(phi)*cos(psi),
647 - sin(psi)*sin(phi) + cos(theta)*cos(phi)*cos(psi),
650 - sin(theta)*cos(phi),
654 // Find the optimal anchor position. Finding the approximate
655 // mid-point of the zone of cells and snapping to the nearest
658 vector zoneMin = VGREAT*vector::one;
660 vector zoneMax = -VGREAT*vector::one;
664 const point cellCentre = mesh_.cellCentres()[zone[cell]];
666 if (cellCentre.x() > zoneMax.x())
668 zoneMax.x() = cellCentre.x();
670 if (cellCentre.x() < zoneMin.x())
672 zoneMin.x() = cellCentre.x();
674 if (cellCentre.y() > zoneMax.y())
676 zoneMax.y() = cellCentre.y();
678 if (cellCentre.y() < zoneMin.y())
680 zoneMin.y() = cellCentre.y();
682 if (cellCentre.z() > zoneMax.z())
684 zoneMax.z() = cellCentre.z();
686 if (cellCentre.z() < zoneMin.z())
688 zoneMin.z() = cellCentre.z();
692 point zoneMid = 0.5*(zoneMin + zoneMax);
694 point latticeMid = inv(latticeCellShape) & (R.T()
695 & (zoneMid - anchor));
699 label(latticeMid.x() + 0.5*sign(latticeMid.x())),
700 label(latticeMid.y() + 0.5*sign(latticeMid.y())),
701 label(latticeMid.z() + 0.5*sign(latticeMid.z()))
704 anchor += (R & (latticeCellShape & latticeAnchor));
706 // Continue trying to place molecules as long as at
707 // least one molecule is placed in each iteration.
708 // The "|| totalZoneMols == 0" condition means that the
709 // algorithm will continue if the origin is outside the
714 label totalZoneMols = 0;
716 label molsPlacedThisIteration = 0;
720 molsPlacedThisIteration != 0
721 || totalZoneMols == 0
724 label sizeBeforeIteration = this->size();
726 bool partOfLayerInBounds = false;
728 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
729 // start of placement of molecules
730 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
734 // Special treatment is required for the first position,
735 // i.e. iteration zero.
737 labelVector unitCellLatticePosition(0,0,0);
739 forAll(latticePositions, p)
741 label id = findIndex(pot_.idList(), latticeIds[p]);
743 const vector& latticePosition =
746 unitCellLatticePosition.x(),
747 unitCellLatticePosition.y(),
748 unitCellLatticePosition.z()
750 + latticePositions[p];
752 point globalPosition =
753 anchor + (R & (latticeCellShape & latticePosition));
755 partOfLayerInBounds =
756 mesh_.bounds().contains(globalPosition);
758 label cell = mesh_.findCell(globalPosition);
760 if (findIndex(zone, cell) != -1)
776 // Place top and bottom caps.
778 labelVector unitCellLatticePosition(0,0,0);
782 unitCellLatticePosition.z() = -n;
783 unitCellLatticePosition.z() <= n;
784 unitCellLatticePosition.z() += 2*n
789 unitCellLatticePosition.y() = -n;
790 unitCellLatticePosition.y() <= n;
791 unitCellLatticePosition.y()++
796 unitCellLatticePosition.x() = -n;
797 unitCellLatticePosition.x() <= n;
798 unitCellLatticePosition.x()++
801 forAll(latticePositions, p)
809 const vector& latticePosition =
812 unitCellLatticePosition.x(),
813 unitCellLatticePosition.y(),
814 unitCellLatticePosition.z()
816 + latticePositions[p];
818 point globalPosition = anchor
820 &(latticeCellShape & latticePosition));
822 partOfLayerInBounds =
823 mesh_.bounds().contains(globalPosition);
825 label cell = mesh_.findCell
830 if (findIndex(zone, cell) != -1)
849 unitCellLatticePosition.z() = -(n-1);
850 unitCellLatticePosition.z() <= (n-1);
851 unitCellLatticePosition.z()++
854 for (label iR = 0; iR <= 2*n -1; iR++)
856 unitCellLatticePosition.x() = n;
858 unitCellLatticePosition.y() = -n + (iR + 1);
860 for (label iK = 0; iK < 4; iK++)
862 forAll(latticePositions, p)
870 const vector& latticePosition =
873 unitCellLatticePosition.x(),
874 unitCellLatticePosition.y(),
875 unitCellLatticePosition.z()
877 + latticePositions[p];
879 point globalPosition = anchor
881 &(latticeCellShape & latticePosition));
883 partOfLayerInBounds =
884 mesh_.bounds().contains(globalPosition);
886 label cell = mesh_.findCell
891 if (findIndex(zone, cell) != -1)
905 unitCellLatticePosition =
908 - unitCellLatticePosition.y(),
909 unitCellLatticePosition.x(),
910 unitCellLatticePosition.z()
917 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
918 // end of placement of molecules
919 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
924 && !partOfLayerInBounds
927 WarningIn("Foam::moleculeCloud::initialiseMolecules()")
928 << "A whole layer of unit cells was placed "
929 << "outside the bounds of the mesh, but no "
930 << "molecules have been placed in zone '"
932 << "'. This is likely to be because the zone "
935 << " in this case) and no lattice position "
936 << "fell inside them. "
937 << "Aborting filling this zone."
943 molsPlacedThisIteration =
944 this->size() - sizeBeforeIteration;
946 totalZoneMols += molsPlacedThisIteration;
956 void Foam::moleculeCloud::createMolecule
958 const point& position,
963 const vector& bulkVelocity
966 const Cloud<molecule>& cloud = *this;
970 cell = mesh_.findCell(position);
975 FatalErrorIn("Foam::moleculeCloud::createMolecule")
976 << "Position specified does not correspond to a mesh cell." << nl
977 << abort(FatalError);
980 point specialPosition(vector::zero);
986 specialPosition = position;
988 special = molecule::SPECIAL_TETHERED;
991 const molecule::constantProperties& cP(constProps(id));
993 vector v = equipartitionLinearVelocity(temperature, cP.mass());
997 vector pi = vector::zero;
1001 if (!cP.pointMolecule())
1003 pi = equipartitionAngularMomentum(temperature, cP);
1005 scalar phi(rndGen_.scalar01()*mathematicalConstant::twoPi);
1007 scalar theta(rndGen_.scalar01()*mathematicalConstant::twoPi);
1009 scalar psi(rndGen_.scalar01()*mathematicalConstant::twoPi);
1013 cos(psi)*cos(phi) - cos(theta)*sin(phi)*sin(psi),
1014 cos(psi)*sin(phi) + cos(theta)*cos(phi)*sin(psi),
1015 sin(psi)*sin(theta),
1016 - sin(psi)*cos(phi) - cos(theta)*sin(phi)*cos(psi),
1017 - sin(psi)*sin(phi) + cos(theta)*cos(phi)*cos(psi),
1018 cos(psi)*sin(theta),
1019 sin(theta)*sin(phi),
1020 - sin(theta)*cos(phi),
1046 Foam::label Foam::moleculeCloud::nSites() const
1050 const_iterator mol(this->begin());
1052 for (mol = this->begin(); mol != this->end(); ++mol)
1054 n += constProps(mol().id()).nSites();
1061 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
1063 Foam::moleculeCloud::moleculeCloud
1065 const polyMesh& mesh,
1066 const potential& pot
1069 Cloud<molecule>(mesh, "moleculeCloud", false),
1072 cellOccupancy_(mesh_.nCells()),
1073 il_(mesh_, pot_.pairPotentials().rCutMaxSqr(), false),
1075 rndGen_(clock::getTime())
1077 molecule::readFields(*this);
1081 setSiteSizesAndPositions();
1083 removeHighEnergyOverlaps();
1089 Foam::moleculeCloud::moleculeCloud
1091 const polyMesh& mesh,
1092 const potential& pot,
1093 const IOdictionary& mdInitialiseDict
1096 Cloud<molecule>(mesh, "moleculeCloud", false),
1101 rndGen_(clock::getTime())
1103 molecule::readFields(*this);
1109 initialiseMolecules(mdInitialiseDict);
1113 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
1115 void Foam::moleculeCloud::evolve()
1117 molecule::trackData td0(*this, 0);
1118 Cloud<molecule>::move(td0);
1120 molecule::trackData td1(*this, 1);
1121 Cloud<molecule>::move(td1);
1123 molecule::trackData td2(*this, 2);
1124 Cloud<molecule>::move(td2);
1128 molecule::trackData td3(*this, 3);
1129 Cloud<molecule>::move(td3);
1133 void Foam::moleculeCloud::calculateForce()
1135 buildCellOccupancy();
1137 iterator mol(this->begin());
1139 // Set accumulated quantities to zero
1140 for (mol = this->begin(); mol != this->end(); ++mol)
1142 mol().siteForces() = vector::zero;
1144 mol().potentialEnergy() = 0.0;
1146 mol().rf() = tensor::zero;
1149 calculatePairForce();
1151 calculateTetherForce();
1153 calculateExternalForce();
1157 void Foam::moleculeCloud::applyConstraintsAndThermostats
1159 const scalar targetTemperature,
1160 const scalar measuredTemperature
1163 scalar temperatureCorrectionFactor =
1164 sqrt(targetTemperature/measuredTemperature);
1166 Info<< "----------------------------------------" << nl
1167 << "Temperature equilibration" << nl
1168 << "Target temperature = "
1169 << targetTemperature << nl
1170 << "Measured temperature = "
1171 << measuredTemperature << nl
1172 << "Temperature correction factor = "
1173 << temperatureCorrectionFactor << nl
1174 << "----------------------------------------"
1177 iterator mol(this->begin());
1179 for (mol = this->begin(); mol != this->end(); ++mol)
1181 mol().v() *= temperatureCorrectionFactor;
1183 mol().pi() *= temperatureCorrectionFactor;
1188 void Foam::moleculeCloud::writeFields() const
1190 molecule::writeFields(*this);
1194 void Foam::moleculeCloud::writeXYZ(const fileName& fName) const
1196 OFstream str(fName);
1198 str << nSites() << nl << "moleculeCloud site positions in angstroms" << nl;
1200 const_iterator mol(this->begin());
1202 for (mol = this->begin(); mol != this->end(); ++mol)
1204 const molecule::constantProperties& cP = constProps(mol().id());
1206 forAll(mol().sitePositions(), i)
1208 const point& sP = mol().sitePositions()[i];
1210 str << pot_.siteIdList()[cP.siteIds()[i]]
1211 << ' ' << sP.x()*1e10
1212 << ' ' << sP.y()*1e10
1213 << ' ' << sP.z()*1e10
1220 // ************************************************************************* //