60 { outputFormatType::PLAIN,
"plain" },
61 { outputFormatType::DICTIONARY,
"dictionary" },
71 { scalingType::LENGTH,
"length" },
72 { scalingType::FORCE,
"force" },
73 { scalingType::MOMENT,
"moment" },
87 return os << v.x() <<
' ' << v.y() <<
' ' << v.z();
97 const label len = list.size();
100 os << header.c_str() <<
nl;
106 for (label i=0; i < len; ++i)
134 inputName_(
"positions.in"),
135 outputName_(
"forces.out"),
136 logName_(
"movement.log"),
137 inputFormat_(lumpedPointState::inputFormatType::DICTIONARY),
138 outputFormat_(outputFormatType::DICTIONARY),
162 inputName_(
"positions.in"),
163 outputName_(
"forces.out"),
164 logName_(
"movement.log"),
178 return (
timeIndex >= lastTrigger_ + calcFrequency_);
194 auto&
points0 = tpoints0.ref();
199 originalIds_.
clear();
200 controllers_.clear();
201 patchControls_.clear();
212 <<
"Incorrect number of pointLabels. Had "
213 << originalIds_.size() <<
" for " <<
points0.
size() <<
" points"
222 for (
const label
id : originalIds_)
224 if (!pointIdMap.insert(
id, pointi))
232 if (!duplicates.empty())
235 <<
"Found duplicate point ids "
249 (*iter)->remapPointLabels(
points0.
size(), pointIdMap);
257 controllers_.clear();
266 coupler_.readDict(commDict);
268 calcFrequency_ = commDict.
getOrDefault<label>(
"calcFrequency", 1);
272 commDict.readEntry(
"inputName", inputName_);
273 commDict.readEntry(
"outputName", outputName_);
274 commDict.readIfPresent(
"logName", logName_);
277 outputFormat_ = formatNames.get(
"outputFormat", commDict);
284 if ((scaleDict = commDict.findDict(
"scaleInput")) !=
nullptr)
286 for (
int i=0; i < scaleInput_.size(); ++i)
288 const word&
key = scalingNames[scalingType(i)];
292 scaleDict->readIfPresent(key, scaleInput_[i])
293 && scaleInput_[i] > 0
296 Info<<
"Using input " <<
key <<
" multiplier: "
297 << scaleInput_[i] <<
nl;
302 if ((scaleDict = commDict.findDict(
"scaleOutput")) !=
nullptr)
304 for (
int i=0; i < scaleOutput_.size(); ++i)
306 const word&
key = scalingNames[scalingType(i)];
310 scaleDict->readIfPresent(key, scaleOutput_[i])
311 && scaleOutput_[i] > 0
314 Info<<
"Using output " <<
key <<
" multiplier: "
315 << scaleOutput_[i] <<
nl;
332 state0_.scalePoints(scaleInput_[scalingType::LENGTH]);
343 return hasPatchControl(
pp.index());
352 return hasInterpolator(fpatch.index());
361 const auto ctrlIter = patchControls_.cfind(
pp.index());
363 if (!ctrlIter.good())
366 <<
"No controllers for patch " <<
pp.name()
370 const patchControl& ctrl = *ctrlIter;
372 for (
const word& ctrlName : ctrl.names_)
374 const auto iter = controllers_.cfind(ctrlName);
379 <<
"No controller: " << ctrlName <<
nl
380 <<
" For patch " <<
pp.name()
395 const pointField& lumpedCentres0 = state0().points();
397 const label patchIndex =
pp.index();
402 patchControl& ctrl = patchControls_(patchIndex);
403 ctrl.names_ = ctrlNames;
408 checkPatchControl(
pp);
410 const faceList& faces =
pp.boundaryMesh().mesh().faces();
415 for (
const word& ctrlName : ctrl.names_)
417 const auto iter = controllers_.cfind(ctrlName);
422 <<
"No controller: " << ctrlName <<
nl
431 if (ctrl.names_.size() && subsetPointIds.empty())
434 <<
"Controllers specified, but without any points" <<
nl
447 subsetPointIds.sortedToc(),
448 subsetPointIds.size()
455 const auto& treePoints = ppTree.shapes();
457 const scalar searchDistSqr(
sqr(GREAT));
459 const label patchStart =
pp.start();
462 const point fc(faces[patchStart + patchFacei].centre(
points0));
466 treePoints.objectIndex
468 ppTree.findNearest(fc, searchDistSqr).index()
474 Pout<<
"Added face mapping for patch: " << patchIndex <<
endl;
486 const pointField& lumpedCentres0 = state0().points();
488 const label patchIndex = fpatch.index();
490 patchControl& ctrl = patchControls_(patchIndex);
501 for (
const word& ctrlName : ctrl.names_)
503 const auto iter = controllers_.cfind(ctrlName);
508 <<
"No controller: " << ctrlName <<
nl
524 adjacency(prev).insert(curr);
525 adjacency(curr).insert(prev);
527 else if (!adjacency.found(curr))
529 adjacency(curr).clear();
534 if (ctrl.names_.empty())
537 const label len = state0().size();
539 for (label i=0; i < len; ++i)
541 const label curr = i;
545 const label prev = i-1;
546 adjacency(prev).insert(curr);
547 adjacency(curr).insert(prev);
549 else if (!adjacency.found(curr))
551 adjacency(curr).clear();
556 if (ctrl.names_.size() && subsetPointIds.empty())
559 <<
"Controllers specified, but without any points" <<
nl
579 const label nearest = iter.key();
581 labelList neighbours(iter.val().sortedToc());
583 const label len = neighbours.size();
588 const point& nearPt = lumpedCentres0[nearest];
590 for (label j=1; j < len; ++j)
592 for (label i=0; i < j; ++i)
594 labelPair neiPair(neighbours[i], neighbours[j]);
599 lumpedCentres0[neiPair.first()],
600 lumpedCentres0[neiPair.second()]
604 if (tri.pointToBarycentric(tri.a(), bary) > SMALL)
607 pairs.append(neiPair);
615 acuteAngles.append(-(ab & ac));
620 if (pairs.size() > 1)
627 adjacencyPairs.insert(nearest, pairs);
633 Info<<
"Adjacency table for patch: " << fpatch.name() <<
nl;
635 for (
const label own : adjacency.sortedToc())
638 for (
const label nei : adjacency[own].
sortedToc())
643 if (originalIds_.size())
645 Info<<
" # " << originalIds_[own] <<
" =>";
646 for (
const label nei : adjacency[own].
sortedToc())
648 Info<<
' ' << originalIds_[nei];
665 subsetPointIds.sortedToc(),
666 subsetPointIds.size()
673 const auto& treePoints = ppTree.shapes();
678 const scalar searchDistSqr(
sqr(GREAT));
680 const labelList& meshPoints = fpatch.meshPoints();
682 interpList.resize(meshPoints.size());
687 forAll(meshPoints, pointi)
692 const label nearest =
693 treePoints.objectIndex
695 ppTree.findNearest(ptOnMesh, searchDistSqr).index()
698 interpList[pointi].nearest(nearest);
715 const point& nearPt = lumpedCentres0[nearest];
719 const labelPairList& adjacentPairs = adjacencyPairs[nearest];
721 unsortedNeighbours = adjacency[nearest].toc();
722 unsortedNeiWeightDist.resize(unsortedNeighbours.size());
724 forAll(unsortedNeighbours, nbri)
726 unsortedNeiWeightDist[nbri] =
727 magSqr(ptOnMesh - lumpedCentres0[unsortedNeighbours[nbri]]);
736 for (
const label nbri : distOrder)
738 const label nextPointi = unsortedNeighbours[nbri];
740 const point& nextPt = lumpedCentres0[nextPointi];
744 const scalar weight =
745 (toMeshPt.vec() & toNextPt.unitVec()) / toNextPt.mag();
755 unsortedNeiWeightDist[nbri] = weight;
758 distOrder[ngood] = nbri;
763 distOrder.resize(ngood);
765 if (distOrder.size() < 1)
773 bool useFirst =
true;
775 if (neighbours.size() > 1 && adjacentPairs.size())
780 neiPointid.
set(neighbours);
782 for (
const labelPair& ends : adjacentPairs)
784 label nei1 = ends.first();
785 label nei2 = ends.second();
787 if (!neiPointid.test(nei1) || !neiPointid.test(nei2))
792 else if (neighbours.find(nei2) < neighbours.find(nei1))
796 std::swap(nei1, nei2);
800 triFace triF(nearest, nei1, nei2);
804 triF.tri(lumpedCentres0).pointToBarycentric(ptOnMesh, bary)
810 interpList[pointi].set(triF, bary);
821 interpList[pointi].next(neighbours.first(), neiWeight.first());
832 const label nLumpedPoints = state0().
size();
836 if (patchControls_.empty())
839 <<
"Attempted to calculate areas without setMapping()"
857 const label patchIndex = iter.key();
858 const patchControl& ctrl = iter.val();
876 zoneAreas[pointIndex] +=
mag(patchSf[patchIndex][patchFacei]);
894 const label nLumpedPoints = state0().size();
896 forces.resize(nLumpedPoints);
897 moments.resize(nLumpedPoints);
899 if (patchControls_.empty())
902 <<
"Attempted to calculate forces without setMapping()"
905 forces.resize(nLumpedPoints,
Zero);
906 moments.resize(nLumpedPoints,
Zero);
915 const pointField& lumpedCentres = state().points();
919 const word pName(forcesDict_.getOrDefault<
word>(
"p",
"p"));
920 scalar pRef = forcesDict_.getOrDefault<scalar>(
"pRef", 0);
921 scalar rhoRef = forcesDict_.getOrDefault<scalar>(
"rhoRef", 1);
952 const label patchIndex = iter.key();
953 const patchControl& ctrl = iter.val();
957 if (!forceOnPatches.found(patchIndex))
967 * patchSf[patchIndex] * (patchPress[patchIndex] - pRef)
972 const vectorField& forceOnPatch = *forceOnPatches[patchIndex];
988 forces[pointIndex] += forceOnPatch[patchFacei];
995 patchCf[patchIndex][patchFacei]
996 - lumpedCentres[pointIndex]
1000 moments[pointIndex] += lever ^ forceOnPatch[patchFacei];
1016Foam::tmp<Foam::pointField>
1023 return pointsDisplacement(state(), fpatch,
points0);
1027Foam::tmp<Foam::pointField>
1035 const label patchIndex = fpatch.index();
1038 const pointField& lumpedCentres0 = state0().points();
1041 const pointField& lumpedCentres = state.points();
1044 const tensorField& localToGlobal = state.rotations();
1046 const labelList& meshPoints = fpatch.meshPoints();
1051 auto& disp = tdisp.ref();
1055 = patchControls_[patchIndex].interp_;
1057 forAll(meshPoints, pointi)
1063 const vector origin0 = interp.interpolate(lumpedCentres0);
1064 const vector origin = interp.interpolate(lumpedCentres);
1065 const tensor rotTensor = interp.interpolate(localToGlobal);
1067 disp[pointi] = (rotTensor & (
p0 - origin0)) + origin -
p0;
1074Foam::tmp<Foam::pointField>
1082 const label patchIndex = fpatch.index();
1085 const pointField& lumpedCentres0 = state0().points();
1088 const pointField& lumpedCentres = state.points();
1091 const tensorField& localToGlobal = state.rotations();
1093 const labelList& meshPoints = fpatch.meshPoints();
1098 auto& disp = tdisp.ref();
1102 patchControls_[patchIndex].interp_;
1104 forAll(meshPoints, pointi)
1110 const vector origin0 = interp.interpolate(lumpedCentres0);
1111 const vector origin = interp.interpolate(lumpedCentres);
1112 const tensor rotTensor = interp.interpolate(localToGlobal);
1114 disp[pointi] = (rotTensor & (
p0 - origin0)) + origin;
1132 const bool status = state_.readData
1135 coupler().resolveFile(inputName_),
1136 state0().rotationOrder(),
1140 scalePoints(state_);
1142 state_.relax(relax_, prev);
1153 const outputFormatType fmt,
1157 const bool writeMoments = (moments.size() == forces.size());
1159 if (fmt == outputFormatType::PLAIN)
1161 os <<
"########" <<
nl;
1164 os <<
"# Time value=" << timesWritten->first() <<
nl
1165 <<
"# Time prev=" << timesWritten->second() <<
nl;
1167 os <<
"# size=" << this->size() <<
nl
1168 <<
"# columns (points) (forces)";
1177 bool report =
false;
1178 scalar scaleLength = scaleOutput_[scalingType::LENGTH];
1179 scalar scaleForce = scaleOutput_[scalingType::FORCE];
1180 scalar scaleMoment = scaleOutput_[scalingType::MOMENT];
1182 if (scaleLength > 0)
1202 if (scaleMoment > 0)
1214 os <<
"# scaling points=" << scaleLength
1215 <<
" forces=" << scaleForce;
1219 os <<
" moments=" << scaleMoment;
1225 os <<
"########" <<
nl;
1229 const point& pt = state0().points()[i];
1231 putPlain(
os, scaleLength * pt) <<
' ';
1233 if (i < forces.size())
1235 const vector val(scaleForce * forces[i]);
1246 if (i < moments.size())
1248 const vector val(scaleMoment * moments[i]);
1266 os <<
"////////" <<
nl;
1303 coupler().resolveFile(outputName_)
1306 writeData(
os, forces, moments, outputFormat_, timesWritten);
1313 coupler().resolveFile(logName_),
1318 writeData(
os, forces, moments, outputFormatType::PLAIN, timesWritten);
Inter-processor communication reduction functions.
uindirectPrimitivePatch pp(UIndirectList< face >(mesh.faces(), faceLabels), mesh.points())
const dimensionSet & dimensions() const noexcept
Return dimensions.
A 1D vector of objects of type <T> that resizes itself as necessary to accept the new objects.
Enum is a wrapper around a list of names/values that represent particular enumeration (or int) values...
GeometricBoundaryField< vector, fvsPatchField, surfaceMesh > Boundary
const Boundary & boundaryField() const noexcept
Return const-reference to the boundary field.
A HashTable of pointers to objects of type <T>, with deallocation management of the pointers.
A simple container for options an IOstream can normally have.
@ APPEND_ATE
append (seek end after open)
label size() const noexcept
The number of elements in the list.
A 1D array of objects of type <T>, where the size of the vector is known and used for subscript bound...
void clear()
Clear the list, i.e. set size to zero.
A HashTable to objects of type <T> with a label key.
Output to file stream as an OSstream, normally using std::ofstream for the actual output.
An Ostream is an abstract base class for all output systems (streams, files, token lists,...
Ostream & writeEntry(const keyType &key, const T &value)
Write a keyword/value entry.
virtual Ostream & endEntry()
Write end entry (';') followed by newline.
static void listReduce(UList< T > &values, BinaryOp bop, const int tag=UPstream::msgType(), const int communicator=UPstream::worldComm)
Reduce list elements (list must be equal size on all ranks), applying bop to each list element.
A HashTable of pointers to objects of type <T> with a label key.
A 2-tuple for storing two objects of dissimilar types. The container is similar in purpose to std::pa...
A List with indirect addressing. Like IndirectList but does not store addressing.
A 1D vector of objects of type <T>, where the size of the vector is known and can be used for subscri...
void size(const label n)
Older name for setAddressableSize.
static bool master(const label communicator=worldComm)
True if process corresponds to the master rank in the communicator.
A bitSet stores bits (elements with only two states) in packed internal format and supports a variety...
void set(const bitSet &bitset)
Set specified bits from another bitset.
A list of keyword definitions, which are a keyword followed by a number of values (eg,...
dictionary subOrEmptyDict(const word &keyword, enum keyType::option matchOpt=keyType::REGEX, const bool mandatory=false) const
Find and return a sub-dictionary as a copy, otherwise return an empty dictionary.
const dictionary * findDict(const word &keyword, enum keyType::option matchOpt=keyType::REGEX) const
Find and return a sub-dictionary pointer if present (and it is a dictionary) otherwise return nullptr...
const dictionary & subDict(const word &keyword, enum keyType::option matchOpt=keyType::REGEX) const
Find and return a sub-dictionary.
bool readEntry(const word &keyword, T &val, enum keyType::option matchOpt=keyType::REGEX, IOobjectOption::readOption readOpt=IOobjectOption::MUST_READ) const
Find entry and assign to T val. FatalIOError if it is found and the number of tokens is incorrect,...
T getOrDefault(const word &keyword, const T &deflt, enum keyType::option matchOpt=keyType::REGEX) const
Find and return a T, or return the given default value. FatalIOError if it is found and the number of...
bool readIfPresent(const word &keyword, T &val, enum keyType::option matchOpt=keyType::REGEX) const
Find an entry if present, and assign to T val. FatalIOError if it is found and the number of tokens i...
A topoSetPointSource to select all points based on usage in given faceSet(s).
const surfaceVectorField & Cf() const
Return face centres as surfaceVectorField.
const surfaceVectorField & Sf() const
Return cell face area vectors.
Non-pointer based hierarchical recursive searching.
A simple linear interpolator between two locations, which are referenced by index.
bool hasPatchControl(const label patchIndex) const
Check if patch control exists for specified patch.
static const word canonicalName
The canonical name ("lumpedPointMovement") for the dictionary.
tmp< pointField > pointsDisplacement(const pointPatch &fpatch, const pointField &points0) const
Displace points according to the current state.
static const Enum< outputFormatType > formatNames
Names for the output format types.
tmp< pointField > pointsPosition(const lumpedPointState &state, const pointPatch &fpatch, const pointField &points0) const
The points absolute position according to specified state.
void readDict(const dictionary &dict)
Update settings from dictionary.
void setInterpolator(const pointPatch &fpatch, const pointField &points0)
Check if patch control exists for specified patch.
void setPatchControl(const polyPatch &pp, const wordList &ctrlNames, const pointField &points0)
Define pressure-zones mapping for faces in the specified patches.
bool readState()
Read state from file, applying relaxation as requested.
void couplingCompleted(const label timeIndex) const
Register that coupling is completed at this calcFrequency.
bool couplingPending(const label timeIndex) const
Check if coupling is pending (according to the calcFrequency).
static const Enum< scalingType > scalingNames
Names for the scaling types.
void checkPatchControl(const polyPatch &pp) const
Check if patch control exists for specified patch.
outputFormatType
Output format types.
bool writeData(Ostream &os, const UList< vector > &forces, const UList< vector > &moments, const outputFormatType fmt=outputFormatType::PLAIN, const Tuple2< scalar, scalar > *timesWritten=nullptr) const
Write points, forces, moments. Only call from the master process.
void writeDict(Ostream &os) const
Write axis, locations, division as a dictionary.
static int debug
Debug switch.
bool hasInterpolator(const pointPatch &fpatch) const
Check if patch control exists for specified patch.
lumpedPointMovement()
Default construct.
bool forcesAndMoments(const polyMesh &pmesh, List< vector > &forces, List< vector > &moments) const
The forces and moments acting on each pressure-zone.
scalingType
Output format types.
List< scalar > areas(const polyMesh &pmesh) const
The areas for each pressure-zone.
The state of lumped points corresponds to positions and rotations.
static const Enum< inputFormatType > formatNames
Names for the input format types.
bool readData(Istream &is, const quaternion::eulerOrder rotOrder=quaternion::eulerOrder::ZXZ, const bool degrees=false)
Read input as dictionary content.
Basic pointPatch represents a set of points from the mesh.
A polyBoundaryMesh is a polyPatch list with registered IO, a reference to the associated polyMesh,...
Mesh consisting of general polyhedral cells.
A patch is a list of labels that address the faces in the global face list.
static const Enum< eulerOrder > eulerOrderNames
The names for Euler-angle and Tait-Bryan angles, including "rollPitchYaw" and "yawPitchRoll" aliases.
static tmp< T > New(Args &&... args)
Construct tmp with forwarding arguments.
@ BEGIN_LIST
Begin list [isseparator].
@ END_LIST
End list [isseparator].
Standard boundBox with extra functionality for use in octree.
Holds (reference to) pointField. Encapsulation of data needed for octree searches....
A triangular face using a FixedList of labels corresponding to mesh vertices.
A class for handling words, derived from Foam::string.
Represents 0/1 range or concept. Used for tagged dispatch or clamping.
const volScalarField & p0
const polyBoundaryMesh & patches
#define FatalIOErrorInFunction(ios)
Report an error message using Foam::FatalIOError.
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
OBJstream os(runTime.globalPath()/outputName)
#define WarningInFunction
Report a warning using Foam::Warning.
List< label > sortedToc(const UList< bool > &bools)
Return the (sorted) values corresponding to 'true' entries.
Namespace for handling debugging switches.
int debugSwitch(const char *name, const int deflt=0)
Lookup debug switch or add default value.
constexpr auto key(const Type &t) noexcept
Helper function to return the enum value.
void writeList(vtk::formatter &fmt, const UList< uint8_t > &values)
Write a list of uint8_t values.
const dimensionSet dimPressure
Type & refCast(U &obj)
A dynamic_cast (for references) to Type reference.
Pair< label > labelPair
A pair of labels.
List< word > wordList
List of word.
List< labelPair > labelPairList
List of labelPair.
List< label > labelList
A List of labels.
dimensionedSymmTensor sqr(const dimensionedVector &dv)
quaternion normalised(const quaternion &q)
Return the normalised (unit) quaternion of the given quaternion.
HashSet< label, Hash< label > > labelHashSet
A HashSet of labels, uses label hasher.
GeometricField< scalar, fvPatchField, volMesh > volScalarField
Barycentric2D< scalar > barycentric2D
A scalar version of the templated Barycentric2D.
messageStream Info
Information stream (stdout output on master, null elsewhere).
dimensionSet clamp(const dimensionSet &a, const dimensionSet &range)
List< face > faceList
List of faces.
Ostream & endl(Ostream &os)
Add newline and flush stream.
void inplaceReorder(const labelUList &oldToNew, ListType &input, const bool prune=false)
Inplace reorder the elements of a list.
const Type * isA(const U &obj)
Attempt dynamic_cast to Type.
dimensioned< typename typeOfMag< Type >::type > mag(const dimensioned< Type > &dt)
labelList sortedOrder(const UList< T > &input)
Return the (stable) sort order for the list.
line< point, const point & > linePointRef
A line using referred points.
FlatOutput::OutputAdaptor< Container, Delimiters > flatOutput(const Container &obj, Delimiters delim)
Global flatOutput() function with specified output delimiters.
Field< vector > vectorField
Specialisation of Field<T> for vector.
IOerror FatalIOError
Error stream (stdout output on all processes), with additional 'FOAM FATAL IO ERROR' header text and ...
vector point
Point is a vector.
static constexpr const zero Zero
Global zero (0).
Field< tensor > tensorField
Specialisation of Field<T> for tensor.
triangle< point, const point & > triPointRef
A triangle using referred points.
error FatalError
Error stream (stdout output on all processes), with additional 'FOAM FATAL ERROR' header text and sta...
prefixOSstream Pout
OSstream wrapped stdout (std::cout) with parallel prefix.
vectorField pointField
pointField is a vectorField.
static void writeData(Ostream &os, const Type &val)
errorManipArg< error, int > exit(error &err, const int errNo=1)
dimensioned< typename typeOfMag< Type >::type > magSqr(const dimensioned< Type > &dt)
constexpr char nl
The newline '\n' character (0x0a).
labelList pointLabels(nPoints, -1)
#define forAll(list, i)
Loop across all elements in list.
#define forAllIters(container, iter)
Iterate across all elements in the container object.
#define forAllConstIters(container, iter)
Iterate across all elements of the container object with const access.
pointField points0(pointIOField(IOobject("points", mesh.time().constant(), polyMesh::meshSubDir, mesh, IOobject::MUST_READ, IOobject::NO_WRITE, IOobject::NO_REGISTER)))