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(),
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());
75 const word& siteId = siteIdNames[sI];
77 siteIds[sI] =
findIndex(siteIdList, siteId);
79 if (siteIds[sI] == -1)
82 << siteId <<
" site not found."
87 molecule::constantProperties&
constProp = constPropList_[i];
89 constProp = molecule::constantProperties(molDict);
96 void Foam::moleculeCloud::setSiteSizesAndPositions()
100 const molecule::constantProperties& cP = constProps(mol().
id());
102 mol().setSiteSizes(cP.nSites());
104 mol().setSitePositions(mesh(), cP);
109 void Foam::moleculeCloud::buildCellOccupancy()
111 forAll(cellOccupancy_, cO)
113 cellOccupancy_[cO].clear();
118 cellOccupancy_[mol().cell()].append(&mol());
121 forAll(cellOccupancy_, cO)
123 cellOccupancy_[cO].shrink();
128 void Foam::moleculeCloud::calculatePairForce()
136 molecule* molI =
nullptr;
137 molecule* molJ =
nullptr;
146 forAll(cellOccupancy_[d],cellIMols)
148 molI = cellOccupancy_[d][cellIMols];
150 forAll(dil[d], interactingCells)
152 List<molecule*> cellJ =
153 cellOccupancy_[dil[d][interactingCells]];
157 molJ = cellJ[cellJMols];
159 evaluatePair(*molI, *molJ);
163 forAll(cellOccupancy_[d], cellIOtherMols)
165 molJ = cellOccupancy_[d][cellIOtherMols];
169 evaluatePair(*molI, *molJ);
177 il_.receiveReferredData(pBufs, startOfRequests);
184 List<IDLList<molecule>>& referredMols = il_.referredParticles();
188 const List<label>& realCells = ril[r];
190 IDLList<molecule>& refMols = referredMols[r];
201 List<molecule*> celli = cellOccupancy_[realCells[rC]];
205 molI = celli[cellIMols];
207 evaluatePair(*molI, refMol());
216 void Foam::moleculeCloud::calculateTetherForce()
218 const tetherPotentialList& tetherPot(pot_.tetherPotentials());
222 if (mol().tethered())
224 vector rIT = mol().position(mesh()) - mol().specialPosition();
226 label idI = mol().id();
228 scalar massI = constProps(idI).mass();
230 vector fIT = tetherPot.force(idI, rIT);
232 mol().a() += fIT/massI;
234 mol().potentialEnergy() += tetherPot.energy(idI, rIT);
243 void Foam::moleculeCloud::calculateExternalForce()
247 mol().a() += pot_.gravity();
252 void Foam::moleculeCloud::removeHighEnergyOverlaps()
254 Info<<
nl <<
"Removing high energy overlaps, limit = "
255 << pot_.potentialEnergyLimit()
256 <<
nl <<
"Removal order:";
258 forAll(pot_.removalOrder(), rO)
260 Info<<
' ' << pot_.idList()[pot_.removalOrder()[rO]];
265 label initialSize = this->size();
267 buildCellOccupancy();
271 molecule* molI =
nullptr;
272 molecule* molJ =
nullptr;
275 DynamicList<molecule*> molsToDelete;
281 forAll(cellOccupancy_[d],cellIMols)
283 molI = cellOccupancy_[d][cellIMols];
285 forAll(dil[d], interactingCells)
287 List<molecule*> cellJ =
288 cellOccupancy_[dil[d][interactingCells]];
292 molJ = cellJ[cellJMols];
294 if (evaluatePotentialLimit(*molI, *molJ))
296 label idI = molI->id();
298 label idJ = molJ->id();
309 molsToDelete.append(molJ);
312 else if (
findIndex(molsToDelete, molI) == -1)
314 molsToDelete.append(molI);
321 forAll(cellOccupancy_[d], cellIOtherMols)
323 molJ = cellOccupancy_[d][cellIOtherMols];
327 if (evaluatePotentialLimit(*molI, *molJ))
329 label idI = molI->id();
331 label idJ = molJ->id();
342 molsToDelete.append(molJ);
345 else if (
findIndex(molsToDelete, molI) == -1)
347 molsToDelete.append(molI);
356 deleteParticle(*(molsToDelete[mTD]));
360 buildCellOccupancy();
370 il_.receiveReferredData(pBufs, startOfRequests);
375 DynamicList<molecule*> molsToDelete;
379 List<IDLList<molecule>>& referredMols = il_.referredParticles();
383 IDLList<molecule>& refMols = referredMols[r];
394 const List<label>& realCells = ril[r];
398 label celli = realCells[rC];
400 List<molecule*> cellIMols = cellOccupancy_[celli];
404 molI = cellIMols[cIM];
406 if (evaluatePotentialLimit(*molI, *molJ))
408 label idI = molI->id();
410 label idJ = molJ->id();
420 molsToDelete.append(molI);
434 if (molI->origId() > molJ->origId())
438 molsToDelete.append(molI);
450 deleteParticle(*(molsToDelete[mTD]));
454 buildCellOccupancy();
462 il_.receiveReferredData(pBufs, startOfRequests);
464 label molsRemoved = initialSize - this->size();
468 reduce(molsRemoved, sumOp<label>());
471 Info<<
tab << molsRemoved <<
" molecules removed" <<
endl;
475 void Foam::moleculeCloud::initialiseMolecules
477 const IOdictionary& mdInitialiseDict
481 <<
"Initialising molecules in each zone specified in mdInitialiseDict."
484 const cellZoneList& cellZones = mesh_.cellZones();
486 if (!cellZones.size())
489 <<
"No cellZones found in the mesh."
493 label nLocateBoundaryHits = 0;
497 const cellZone& zone(cellZones[z]);
501 if (!mdInitialiseDict.found(zone.name()))
503 Info<<
"No specification subDictionary for zone "
504 << zone.name() <<
" found, skipping." <<
endl;
508 const dictionary& zoneDict =
509 mdInitialiseDict.subDict(zone.name());
511 const scalar temperature
513 zoneDict.template lookup<scalar>(
"temperature")
516 const vector bulkVelocity(zoneDict.lookup(
"bulkVelocity"));
518 List<word> latticeIds
520 zoneDict.lookup(
"latticeIds")
523 List<vector> latticePositions
525 zoneDict.lookup(
"latticePositions")
528 if (latticeIds.size() != latticePositions.size())
531 <<
"latticeIds and latticePositions must be the same "
538 zoneDict.lookup(
"latticeCellShape")
541 scalar latticeCellScale = 0.0;
543 if (zoneDict.found(
"numberDensity"))
545 scalar numberDensity =
546 zoneDict.lookup<scalar>(
"numberDensity");
548 if (numberDensity < vSmall)
551 <<
"numberDensity too small, not filling zone "
552 << zone.name() <<
endl;
557 latticeCellScale =
pow
559 latticeIds.size()/(
det(latticeCellShape)*numberDensity),
563 else if (zoneDict.found(
"massDensity"))
565 scalar unitCellMass = 0.0;
571 const molecule::constantProperties& cP(constProps(
id));
573 unitCellMass += cP.mass();
576 scalar massDensity = zoneDict.lookup<scalar>(
"massDensity");
578 if (massDensity < vSmall)
581 <<
"massDensity too small, not filling zone "
582 << zone.name() <<
endl;
588 latticeCellScale =
pow
590 unitCellMass/(
det(latticeCellShape)*massDensity),
597 <<
"massDensity or numberDensity not specified " <<
nl
601 latticeCellShape *= latticeCellScale;
603 vector anchor(zoneDict.lookup(
"anchor"));
605 bool tethered =
false;
607 if (zoneDict.found(
"tetherSiteIds"))
611 List<word>(zoneDict.lookup(
"tetherSiteIds")).size()
615 const vector orientationAngles
617 zoneDict.lookup(
"orientationAngles")
620 scalar phi(orientationAngles.x()*
pi/180.0);
622 scalar theta(orientationAngles.y()*
pi/180.0);
624 scalar
psi(orientationAngles.z()*
pi/180.0);
649 const point cellCentre = mesh_.cellCentres()[zone[cell]];
651 if (cellCentre.x() > zoneMax.x())
653 zoneMax.
x() = cellCentre.x();
655 if (cellCentre.x() < zoneMin.x())
657 zoneMin.x() = cellCentre.x();
659 if (cellCentre.y() > zoneMax.y())
661 zoneMax.y() = cellCentre.y();
663 if (cellCentre.y() < zoneMin.y())
665 zoneMin.y() = cellCentre.y();
667 if (cellCentre.z() > zoneMax.z())
669 zoneMax.z() = cellCentre.z();
671 if (cellCentre.z() < zoneMin.z())
673 zoneMin.z() = cellCentre.z();
677 point zoneMid = 0.5*(zoneMin + zoneMax);
679 point latticeMid =
inv(latticeCellShape) & (
R.T()
680 & (zoneMid - anchor));
684 label(latticeMid.x() + 0.5*
sign(latticeMid.x())),
685 label(latticeMid.y() + 0.5*
sign(latticeMid.y())),
686 label(latticeMid.z() + 0.5*
sign(latticeMid.z()))
689 anchor += (
R & (latticeCellShape & latticeAnchor));
699 label totalZoneMols = 0;
701 label molsPlacedThisIteration = 0;
705 molsPlacedThisIteration != 0
706 || totalZoneMols == 0
709 label sizeBeforeIteration = this->size();
711 bool partOfLayerInBounds =
false;
728 const vector& latticePosition =
731 unitCellLatticePosition.x(),
732 unitCellLatticePosition.y(),
733 unitCellLatticePosition.z()
735 + latticePositions[
p];
737 point globalPosition =
739 + (
R & (latticeCellShape & latticePosition));
741 partOfLayerInBounds = mesh_.bounds().contains
747 mesh_.cellTree().findInside(globalPosition);
772 unitCellLatticePosition.z() = -
n;
773 unitCellLatticePosition.z() <=
n;
774 unitCellLatticePosition.z() += 2*
n
779 unitCellLatticePosition.y() = -
n;
780 unitCellLatticePosition.y() <=
n;
781 unitCellLatticePosition.y()++
786 unitCellLatticePosition.x() = -
n;
787 unitCellLatticePosition.x() <=
n;
788 unitCellLatticePosition.x()++
799 const vector& latticePosition =
802 unitCellLatticePosition.x(),
803 unitCellLatticePosition.y(),
804 unitCellLatticePosition.z()
806 + latticePositions[
p];
808 point globalPosition =
818 partOfLayerInBounds =
819 mesh_.bounds().contains
825 mesh_.cellTree().findInside
850 unitCellLatticePosition.z() = -(
n-1);
851 unitCellLatticePosition.z() <= (
n-1);
852 unitCellLatticePosition.z()++
855 for (
label iR = 0; iR <= 2*
n -1; iR++)
857 unitCellLatticePosition.x() =
n;
859 unitCellLatticePosition.y() = -
n + (iR + 1);
861 for (
label iK = 0; iK < 4; iK++)
871 const vector& latticePosition =
874 unitCellLatticePosition.x(),
875 unitCellLatticePosition.y(),
876 unitCellLatticePosition.z()
878 + latticePositions[
p];
880 point globalPosition =
890 partOfLayerInBounds =
891 mesh_.bounds().contains
897 mesh_.cellTree().findInside
917 unitCellLatticePosition =
920 - unitCellLatticePosition.y(),
921 unitCellLatticePosition.x(),
922 unitCellLatticePosition.z()
936 && !partOfLayerInBounds
940 <<
"A whole layer of unit cells was placed "
941 <<
"outside the bounds of the mesh, but no "
942 <<
"molecules have been placed in zone '"
944 <<
"'. This is likely to be because the zone "
947 <<
" in this case) and no lattice position "
948 <<
"fell inside them. "
949 <<
"Aborting filling this zone."
955 molsPlacedThisIteration =
956 this->size() - sizeBeforeIteration;
958 totalZoneMols += molsPlacedThisIteration;
966 reduce(nLocateBoundaryHits, sumOp<label>());
967 if (nLocateBoundaryHits != 0)
970 <<
"Initialisation of cloud " << this->
name()
971 <<
" did not accurately locate " << nLocateBoundaryHits
972 <<
" particles" <<
endl;
977 void Foam::moleculeCloud::createMolecule
979 const point& position,
981 label& nLocateBoundaryHits,
985 const vector& bulkVelocity
994 specialPosition = position;
999 const molecule::constantProperties& cP(constProps(
id));
1001 vector v = equipartitionLinearVelocity(temperature, cP.mass());
1009 if (!cP.pointMolecule())
1011 pi = equipartitionAngularMomentum(temperature, cP);
1013 scalar phi(rndGen_.scalar01()*
twoPi);
1015 scalar theta(rndGen_.scalar01()*
twoPi);
1017 scalar
psi(rndGen_.scalar01()*
twoPi);
1040 nLocateBoundaryHits,
1061 n += constProps(mol().
id()).nSites();
1080 cellOccupancy_(mesh_.nCells()),
1081 il_(mesh_, pot_.pairPotentials().rCutMax(), false),
1083 rndGen_(
clock::getTime()),
1084 stdNormal_(rndGen_.generator())
1093 setSiteSizesAndPositions();
1095 removeHighEnergyOverlaps();
1112 il_(mesh_, 0.0, false),
1114 rndGen_(
clock::getTime()),
1115 stdNormal_(rndGen_.generator())
1126 initialiseMolecules(mdInitialiseDict);
1154 buildCellOccupancy();
1159 mol().siteForces() =
Zero;
1161 mol().potentialEnergy() = 0.0;
1166 calculatePairForce();
1168 calculateTetherForce();
1170 calculateExternalForce();
1176 const scalar targetTemperature,
1177 const scalar measuredTemperature
1180 scalar temperatureCorrectionFactor =
1181 sqrt(targetTemperature/
max(vSmall, measuredTemperature));
1183 Info<<
"----------------------------------------" <<
nl
1184 <<
"Temperature equilibration" <<
nl
1185 <<
"Target temperature = "
1186 << targetTemperature <<
nl
1187 <<
"Measured temperature = "
1188 << measuredTemperature <<
nl
1189 <<
"Temperature correction factor = "
1190 << temperatureCorrectionFactor <<
nl
1191 <<
"----------------------------------------"
1196 mol().v() *= temperatureCorrectionFactor;
1198 mol().pi() *= temperatureCorrectionFactor;
1207 os << nSites() <<
nl <<
"moleculeCloud site positions in angstroms" <<
nl;
1213 forAll(mol().sitePositions(), i)
1215 const point& sP = mol().sitePositions()[i];
1217 os << pot_.siteIdList()[cP.
siteIds()[i]]
1218 <<
' ' << sP.
x()*1e10
1219 <<
' ' << sP.
y()*1e10
1220 <<
' ' << sP.
z()*1e10
#define forAll(list, i)
Loop across all elements in list.
#define forAllIter(Container, container, iter)
Iterate across all elements in the container object of type.
#define forAllConstIter(Container, container, iter)
Iterate across all elements in the container object of type.
const List< DynamicList< molecule * > > & cellOccupancy
Base cloud calls templated on particle type.
void move(TrackCloudType &cloud, typename ParticleType::trackingData &td)
Move the particles.
IOdictionary is derived from dictionary and IOobject to give the dictionary automatic IO functionalit...
static label nRequests()
Get number of outstanding requests.
static bool & parRun()
Is this a parallel run?
Read access to the system clock with formatting.
A class for handling file names.
void writeXYZ(const fileName &fName) const
Write molecule sites in XYZ format.
void evolve()
Evolve the molecules (move, calculate forces, control state etc)
void applyConstraintsAndThermostats(const scalar targetTemperature, const scalar measuredTemperature)
moleculeCloud(const polyMesh &mesh, const potential &pot, bool readFields=true)
Construct given mesh and potential references.
Class to hold molecule constant properties.
const List< label > & siteIds() const
Class used to pass tracking data to the trackToFace function.
trackPart part() const
Return the part of the tracking operation taking place.
static void readFields(Cloud< molecule > &mC)
Mesh consisting of general polyhedral cells.
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
const volScalarField & psi
#define WarningInFunction
Report a warning using Foam::Warning.
const scalar twoPi(2 *pi)
dimensionedScalar det(const dimensionedSphericalTensor &dt)
dimensionedScalar sign(const dimensionedScalar &ds)
intWM_LABEL_SIZE_t label
A label is an int32_t or int64_t as specified by the pre-processor macro WM_LABEL_SIZE.
Tensor< scalar > tensor
Tensor of scalars.
Ostream & endl(Ostream &os)
Add newline and flush stream.
word name(const bool)
Return a word representation of a bool.
void readFields(const typename GeoFieldType::Mesh &mesh, const IOobjectList &objects, const HashSet< word > &selectedFields, LIFOStack< regIOobject * > &storedObjects)
Read the selected GeometricFields of the specified type.
Vector< label > labelVector
Vector of labels.
errorManip< error > abort(error &err)
dimensionedScalar sin(const dimensionedScalar &ds)
static const Identity< scalar > I
vector point
Point is a vector.
dimensionedScalar pow(const dimensionedScalar &ds, const dimensionedScalar &expt)
DiagTensor< scalar > diagTensor
A scalar version of the templated DiagTensor.
Vector< scalar > vector
A scalar version of the templated Vector.
void reduce(const List< UPstream::commsStruct > &comms, T &Value, const BinaryOp &bop, const int tag, const label comm)
dimensionedScalar sqrt(const dimensionedScalar &ds)
List< labelList > labelListList
A List of labelList.
defineTypeNameAndDebug(combustionModel, 0)
layerAndWeight max(const layerAndWeight &a, const layerAndWeight &b)
static scalar R(const scalar a, const scalar x)
label findIndex(const ListType &, typename ListType::const_reference, const label start=0)
Find first occurrence of given element and return index,.
dimensionedSphericalTensor inv(const dimensionedSphericalTensor &dt)
dimensionedScalar cos(const dimensionedScalar &ds)
const word constProp(initialConditions.lookup("constantProperty"))