42 void Foam::moleculeCloud::buildConstProps()
44 Info<<
nl <<
"Reading moleculeProperties dictionary." <<
endl;
48 constPropList_.setSize(idList.size());
50 const List<word>& siteIdList(pot_.siteIdList());
52 IOdictionary moleculePropertiesDict
57 mesh_.time().constant(),
67 const word&
id = idList[i];
68 const dictionary& molDict = moleculePropertiesDict.subDict(
id);
70 List<word> siteIdNames = molDict.lookup(
"siteIds");
76 const word& siteId = siteIdNames[sI];
78 siteIds[sI] =
findIndex(siteIdList, siteId);
80 if (siteIds[sI] == -1)
83 << siteId <<
" site not found."
88 molecule::constantProperties& constProp = constPropList_[i];
90 constProp = molecule::constantProperties(molDict);
92 constProp.siteIds() = siteIds;
97 void Foam::moleculeCloud::setSiteSizesAndPositions()
101 const molecule::constantProperties& cP = constProps(mol().
id());
103 mol().setSiteSizes(cP.nSites());
105 mol().setSitePositions(
mesh(), cP);
110 void Foam::moleculeCloud::buildCellOccupancy()
112 forAll(cellOccupancy_, cO)
114 cellOccupancy_[cO].clear();
119 cellOccupancy_[mol().cell()].append(&mol());
122 forAll(cellOccupancy_, cO)
124 cellOccupancy_[cO].shrink();
129 void Foam::moleculeCloud::calculatePairForce()
137 molecule* molI =
nullptr;
138 molecule* molJ =
nullptr;
147 forAll(cellOccupancy_[d],cellIMols)
149 molI = cellOccupancy_[d][cellIMols];
151 forAll(dil[d], interactingCells)
154 cellOccupancy_[dil[d][interactingCells]];
158 molJ = cellJ[cellJMols];
160 evaluatePair(*molI, *molJ);
164 forAll(cellOccupancy_[d], cellIOtherMols)
166 molJ = cellOccupancy_[d][cellIOtherMols];
170 evaluatePair(*molI, *molJ);
178 il_.receiveReferredData(pBufs, startOfRequests);
191 IDLList<molecule>& refMols = referredMols[r];
206 molI = celli[cellIMols];
208 evaluatePair(*molI, refMol());
217 void Foam::moleculeCloud::calculateTetherForce()
219 const tetherPotentialList& tetherPot(pot_.tetherPotentials());
223 if (mol().tethered())
225 vector rIT = mol().position(
mesh()) - mol().specialPosition();
227 label idI = mol().id();
229 scalar massI = constProps(idI).mass();
231 vector fIT = tetherPot.force(idI, rIT);
233 mol().a() += fIT/massI;
235 mol().potentialEnergy() += tetherPot.energy(idI, rIT);
244 void Foam::moleculeCloud::calculateExternalForce()
248 mol().a() += pot_.gravity();
253 void Foam::moleculeCloud::removeHighEnergyOverlaps()
255 Info<<
nl <<
"Removing high energy overlaps, limit = "
256 << pot_.potentialEnergyLimit()
257 <<
nl <<
"Removal order:";
259 forAll(pot_.removalOrder(), rO)
261 Info<<
' ' << pot_.idList()[pot_.removalOrder()[rO]];
266 label initialSize = this->size();
268 buildCellOccupancy();
272 molecule* molI =
nullptr;
273 molecule* molJ =
nullptr;
276 DynamicList<molecule*> molsToDelete;
282 forAll(cellOccupancy_[d],cellIMols)
284 molI = cellOccupancy_[d][cellIMols];
286 forAll(dil[d], interactingCells)
289 cellOccupancy_[dil[d][interactingCells]];
293 molJ = cellJ[cellJMols];
295 if (evaluatePotentialLimit(*molI, *molJ))
297 label idI = molI->id();
299 label idJ = molJ->id();
310 molsToDelete.append(molJ);
313 else if (
findIndex(molsToDelete, molI) == -1)
315 molsToDelete.append(molI);
322 forAll(cellOccupancy_[d], cellIOtherMols)
324 molJ = cellOccupancy_[d][cellIOtherMols];
328 if (evaluatePotentialLimit(*molI, *molJ))
330 label idI = molI->id();
332 label idJ = molJ->id();
343 molsToDelete.append(molJ);
346 else if (
findIndex(molsToDelete, molI) == -1)
348 molsToDelete.append(molI);
357 deleteParticle(*(molsToDelete[mTD]));
361 buildCellOccupancy();
371 il_.receiveReferredData(pBufs, startOfRequests);
376 DynamicList<molecule*> molsToDelete;
384 IDLList<molecule>& refMols = referredMols[r];
399 label celli = realCells[rC];
405 molI = cellIMols[cIM];
407 if (evaluatePotentialLimit(*molI, *molJ))
409 label idI = molI->id();
411 label idJ = molJ->id();
421 molsToDelete.append(molI);
435 if (molI->origId() > molJ->origId())
439 molsToDelete.append(molI);
451 deleteParticle(*(molsToDelete[mTD]));
455 buildCellOccupancy();
463 il_.receiveReferredData(pBufs, startOfRequests);
465 label molsRemoved = initialSize - this->size();
469 reduce(molsRemoved, sumOp<label>());
472 Info<<
tab << molsRemoved <<
" molecules removed" <<
endl;
476 void Foam::moleculeCloud::initialiseMolecules
478 const IOdictionary& mdInitialiseDict
482 <<
"Initialising molecules in each zone specified in mdInitialiseDict."
485 const cellZoneList& cellZones = mesh_.cellZones();
489 if (!cellZones.size())
492 <<
"No cellZones found in the mesh."
496 label nLocateBoundaryHits = 0;
500 const cellZone& zone(cellZones[z]);
504 if (!mdInitialiseDict.found(zone.name()))
506 Info<<
"No specification subDictionary for zone "
507 << zone.name() <<
" found, skipping." <<
endl;
511 const dictionary& zoneDict =
512 mdInitialiseDict.subDict(zone.name());
516 zoneDict.template lookup<scalar>(
"temperature")
519 const vector bulkVelocity(zoneDict.lookup(
"bulkVelocity"));
523 zoneDict.lookup(
"latticeIds")
528 zoneDict.lookup(
"latticePositions")
531 if (latticeIds.size() != latticePositions.size())
534 <<
"latticeIds and latticePositions must be the same "
541 zoneDict.lookup(
"latticeCellShape")
544 scalar latticeCellScale = 0.0;
546 if (zoneDict.found(
"numberDensity"))
548 scalar numberDensity =
549 zoneDict.lookup<scalar>(
"numberDensity");
551 if (numberDensity < vSmall)
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;
574 const molecule::constantProperties& cP(constProps(
id));
576 unitCellMass += cP.mass();
579 scalar massDensity = zoneDict.lookup<scalar>(
"massDensity");
581 if (massDensity < vSmall)
584 <<
"massDensity too small, not filling zone "
585 << zone.name() <<
endl;
591 latticeCellScale =
pow
593 unitCellMass/(
det(latticeCellShape)*massDensity),
600 <<
"massDensity or numberDensity not specified " <<
nl
604 latticeCellShape *= latticeCellScale;
606 vector anchor(zoneDict.lookup(
"anchor"));
608 bool tethered =
false;
610 if (zoneDict.found(
"tetherSiteIds"))
614 List<word>(zoneDict.lookup(
"tetherSiteIds")).size()
618 const vector orientationAngles
620 zoneDict.lookup(
"orientationAngles")
623 scalar phi(orientationAngles.x()*
pi/180.0);
625 scalar theta(orientationAngles.y()*
pi/180.0);
627 scalar
psi(orientationAngles.z()*
pi/180.0);
652 const point cellCentre = mesh_.cellCentres()[zone[
cell]];
654 if (cellCentre.x() > zoneMax.x())
656 zoneMax.
x() = cellCentre.x();
658 if (cellCentre.x() < zoneMin.x())
660 zoneMin.x() = cellCentre.x();
662 if (cellCentre.y() > zoneMax.y())
664 zoneMax.y() = cellCentre.y();
666 if (cellCentre.y() < zoneMin.y())
668 zoneMin.y() = cellCentre.y();
670 if (cellCentre.z() > zoneMax.z())
672 zoneMax.z() = cellCentre.z();
674 if (cellCentre.z() < zoneMin.z())
676 zoneMin.z() = cellCentre.z();
680 point zoneMid = 0.5*(zoneMin + zoneMax);
682 point latticeMid =
inv(latticeCellShape) & (
R.T()
683 & (zoneMid - anchor));
687 label(latticeMid.x() + 0.5*
sign(latticeMid.x())),
688 label(latticeMid.y() + 0.5*
sign(latticeMid.y())),
689 label(latticeMid.z() + 0.5*
sign(latticeMid.z()))
692 anchor += (
R & (latticeCellShape & latticeAnchor));
702 label totalZoneMols = 0;
704 label molsPlacedThisIteration = 0;
708 molsPlacedThisIteration != 0
709 || totalZoneMols == 0
712 label sizeBeforeIteration = this->size();
714 bool partOfLayerInBounds =
false;
731 const vector& latticePosition =
734 unitCellLatticePosition.x(),
735 unitCellLatticePosition.y(),
736 unitCellLatticePosition.z()
738 + latticePositions[
p];
740 point globalPosition =
742 + (
R & (latticeCellShape & latticePosition));
744 partOfLayerInBounds = mesh_.bounds().contains
750 searchEngine.findCell(globalPosition);
776 unitCellLatticePosition.z() = -
n;
777 unitCellLatticePosition.z() <=
n;
778 unitCellLatticePosition.z() += 2*
n
783 unitCellLatticePosition.y() = -
n;
784 unitCellLatticePosition.y() <=
n;
785 unitCellLatticePosition.y()++
790 unitCellLatticePosition.x() = -
n;
791 unitCellLatticePosition.x() <=
n;
792 unitCellLatticePosition.x()++
803 const vector& latticePosition =
806 unitCellLatticePosition.x(),
807 unitCellLatticePosition.y(),
808 unitCellLatticePosition.z()
810 + latticePositions[
p];
812 point globalPosition =
822 partOfLayerInBounds =
823 mesh_.bounds().contains
829 searchEngine.findCell
855 unitCellLatticePosition.z() = -(
n-1);
856 unitCellLatticePosition.z() <= (
n-1);
857 unitCellLatticePosition.z()++
860 for (
label iR = 0; iR <= 2*
n -1; iR++)
862 unitCellLatticePosition.x() =
n;
864 unitCellLatticePosition.y() = -
n + (iR + 1);
866 for (
label iK = 0; iK < 4; iK++)
876 const vector& latticePosition =
879 unitCellLatticePosition.x(),
880 unitCellLatticePosition.y(),
881 unitCellLatticePosition.z()
883 + latticePositions[
p];
885 point globalPosition =
895 partOfLayerInBounds =
896 mesh_.bounds().contains
902 searchEngine.findCell
923 unitCellLatticePosition =
926 - unitCellLatticePosition.y(),
927 unitCellLatticePosition.x(),
928 unitCellLatticePosition.z()
942 && !partOfLayerInBounds
946 <<
"A whole layer of unit cells was placed "
947 <<
"outside the bounds of the mesh, but no "
948 <<
"molecules have been placed in zone '"
950 <<
"'. This is likely to be because the zone "
953 <<
" in this case) and no lattice position "
954 <<
"fell inside them. "
955 <<
"Aborting filling this zone."
961 molsPlacedThisIteration =
962 this->size() - sizeBeforeIteration;
964 totalZoneMols += molsPlacedThisIteration;
972 reduce(nLocateBoundaryHits, sumOp<label>());
973 if (nLocateBoundaryHits != 0)
976 <<
"Initialisation of cloud " << this->
name()
977 <<
" did not accurately locate " << nLocateBoundaryHits
978 <<
" particles" <<
endl;
983 void Foam::moleculeCloud::createMolecule
985 const meshSearch& searchEngine,
988 label& nLocateBoundaryHits,
992 const vector& bulkVelocity
1006 const molecule::constantProperties& cP(constProps(
id));
1016 if (!cP.pointMolecule())
1020 scalar phi(rndGen_.scalar01()*
twoPi);
1022 scalar theta(rndGen_.scalar01()*
twoPi);
1024 scalar
psi(rndGen_.scalar01()*
twoPi);
1047 nLocateBoundaryHits,
1068 n += constProps(mol().
id()).nSites();
1087 cellOccupancy_(mesh_.nCells()),
1088 il_(mesh_, pot_.pairPotentials().rCutMax(), false),
1090 rndGen_(
clock::getTime()),
1091 stdNormal_(rndGen_.generator())
1100 setSiteSizesAndPositions();
1102 removeHighEnergyOverlaps();
1119 il_(mesh_, 0.0, false),
1121 rndGen_(
clock::getTime()),
1122 stdNormal_(rndGen_.generator())
1133 initialiseMolecules(mdInitialiseDict);
1161 buildCellOccupancy();
1166 mol().siteForces() =
Zero;
1168 mol().potentialEnergy() = 0.0;
1173 calculatePairForce();
1175 calculateTetherForce();
1177 calculateExternalForce();
1183 const scalar targetTemperature,
1184 const scalar measuredTemperature
1187 scalar temperatureCorrectionFactor =
1188 sqrt(targetTemperature/
max(vSmall, measuredTemperature));
1190 Info<<
"----------------------------------------" <<
nl
1191 <<
"Temperature equilibration" <<
nl
1192 <<
"Target temperature = "
1193 << targetTemperature <<
nl
1194 <<
"Measured temperature = "
1195 << measuredTemperature <<
nl
1196 <<
"Temperature correction factor = "
1197 << temperatureCorrectionFactor <<
nl
1198 <<
"----------------------------------------"
1203 mol().v() *= temperatureCorrectionFactor;
1205 mol().pi() *= temperatureCorrectionFactor;
1214 os << nSites() <<
nl <<
"moleculeCloud site positions in angstroms" <<
nl;
1220 forAll(mol().sitePositions(), i)
1222 const point& sP = mol().sitePositions()[i];
1224 os << pot_.siteIdList()[cP.
siteIds()[i]]
1225 <<
' ' << sP.
x()*1e10
1226 <<
' ' << sP.
y()*1e10
1227 <<
' ' << 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.
static const meshSearch & New(const polyMesh &mesh, const pointInCellShapes=pointInCellShapes::tets)
Lookup or construct from mesh and cell decomposition option.
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)
Motion of the mesh specified as a list of pointMeshMovers.
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)
const dimensionSet temperature
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.
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 inv(pointPatchField< tensor > &, const pointPatchField< tensor > &)
List< labelList > labelListList
A List of labelList.
static scalar R(const scalar a, const scalar x)
word name(const LagrangianState state)
Return a string representation of a Lagrangian state enumeration.
void det(pointPatchField< scalar > &, const pointPatchField< tensor > &)
tmp< DimensionedField< typename powProduct< Type, r >::type, GeoMesh, Field > > pow(const DimensionedField< Type, GeoMesh, PrimitiveField > &df, typename powProduct< Type, r >::type)
label findIndex(const ListType &, typename ListType::const_reference, const label start=0)
Find first occurrence of given element and return index,.
void sqrt(LagrangianPatchField< scalar > &f, const LagrangianPatchField< scalar > &f1)
defineTypeNameAndDebug(atmosphericBoundaryLayer, 0)
dimensionedScalar cos(const dimensionedScalar &ds)
dimensioned< Type > max(const DimensionedField< Type, GeoMesh, PrimitiveField > &df)