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
981 label& nLocateBoundaryHits,
985 const vector& bulkVelocity
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.
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 move(TrackCloudType &cloud, typename ParticleType::trackingData &td)
Move the particles.
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(lagrangian::Cloud< molecule > &mC)
Mesh consisting of general polyhedral cells.
Foam::fvMesh mesh(Foam::IOobject(regionName, runTime.name(), runTime, Foam::IOobject::MUST_READ), false)
#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)
point position(const polyMesh &mesh, const barycentric &coordinates, const label celli, const label facei, const label faceTrii, const scalar stepFraction)
Return the position given the coordinates and tet topology.
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.
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.
void det(LagrangianPatchField< scalar > &f, const LagrangianPatchField< tensor > &f1)
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)
void pow(LagrangianPatchField< typename powProduct< Type, r >::type > &f, const LagrangianPatchField< Type > &f1)
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)
word name(const LagrangianState state)
Return a string representation of a Lagrangian state enumeration.
label findIndex(const ListType &, typename ListType::const_reference, const label start=0)
Find first occurrence of given element and return index,.
void inv(LagrangianPatchField< tensor > &f, const LagrangianPatchField< tensor > &f1)
void sqrt(LagrangianPatchField< scalar > &f, const LagrangianPatchField< scalar > &f1)
dimensionedScalar cos(const dimensionedScalar &ds)
const word constProp(initialConditions.lookup("constantProperty"))