43 namespace functionObjects
53 { rotationMode::SPECIFIED,
"specified" },
77 if (
dict.readIfPresent(
"rpm",
n_))
80 dict.readIfPresent(
"n",
n_);
84 n_ =
dict.get<scalar>(
"n");
91 const auto* MRFZones =
97 <<
"Unable to find MRFProperties in the database. " 98 <<
"Is this an MRF case?" 101 const auto& mrf = MRFZones->MRFZoneList::getFromName(
MRFName_);
103 dict.readIfPresent(
"originOffset", origin);
104 origin += mrf.origin();
122 if (
dict.readIfPresent(
"alphaAxis", alphaAxis))
127 new coordSystem::cylindrical(origin, axis, alphaAxis)
140 switch (rotationMode_)
142 case rotationMode::SPECIFIED:
149 const auto* MRFZones =
150 mesh_.cfindObject<IOMRFZoneList>(
"MRFProperties");
155 <<
"Unable to find MRFProperties in the database. " 156 <<
"Is this an MRF case?" 160 const auto& mrf = MRFZones->MRFZoneList::getFromName(MRFName_);
169 <<
"Unhandled enumeration " << rotationModeNames_[rotationMode_]
183 if (writePropellerPerformance_ && !propellerPerformanceFilePtr_)
185 propellerPerformanceFilePtr_ =
186 newFileAtStartTime(
"propellerPerformance");
187 auto&
os = propellerPerformanceFilePtr_();
190 writeHeaderValue(
os,
"CofR", coordSysPtr_->origin());
191 writeHeaderValue(
os,
"Radius", radius_);
192 writeHeaderValue(
os,
"Axis", coordSysPtr_->e3());
196 writeCommented(
os,
"Time");
197 writeTabbed(
os,
"n");
198 writeTabbed(
os,
"URef");
199 writeTabbed(
os,
"J");
200 writeTabbed(
os,
"KT");
201 writeTabbed(
os,
"10*KQ");
202 writeTabbed(
os,
"eta0");
206 if (writeWakeFields_)
208 if (!wakeFilePtr_) wakeFilePtr_ = newFileAtStartTime(
"wake");
209 if (!axialWakeFilePtr_) axialWakeFilePtr_ =
210 newFileAtStartTime(
"axialWake");
222 <<
"Unable to find velocity field " << UName_
223 <<
" . Available vector fields are: " 245 label nPoint = nRadius*nTheta;
254 const label nFace = nRadius*nTheta;
256 points.resize_nocopy(nPoint);
260 const scalar zCoord = 0;
262 for (
int radiusi = 0; radiusi <= nRadius; ++radiusi)
264 if (r1 < SMALL && radiusi == 0)
266 points[pointi++] = origin;
270 const scalar r = r1 + radiusi*(r2 - r1)/nRadius;
272 for (label i = 0; i < nTheta; ++i)
287 const List<label> ptIDs(
identity(nTheta));
291 label pointOffset0 = 0;
292 label radiusOffset = 0;
293 DynamicList<label> facePts(4);
294 for (
int radiusi = 0; radiusi < nRadius; ++radiusi)
296 if (r1 < SMALL && radiusi == 0)
302 for (label thetai = 1; thetai <= nTheta; ++thetai)
308 facePts.append(thetai);
309 facePts.append(1 + ptIDs.fcIndex(thetai - 1));
311 faces[facei++] = face(facePts);
316 for (label thetai = 0; thetai < nTheta; ++thetai)
320 label offset = pointOffset0 + (radiusi-radiusOffset)*nTheta;
323 facePts.append(offset + ptIDs.fcIndex(thetai - 1));
324 facePts.append(offset + ptIDs.fcIndex(thetai));
327 facePts.append(offset + nTheta + ptIDs.fcIndex(thetai));
328 facePts.append(offset + nTheta + ptIDs.fcIndex(thetai - 1));
330 faces[facei++] = face(facePts);
339 const dictionary&
dict 344 const scalar r1 = sampleDiskDict.getScalar(
"r1");
345 const scalar r2 = sampleDiskDict.getScalar(
"r2");
347 nTheta_ = sampleDiskDict.getLabel(
"nTheta");
348 nRadial_ = sampleDiskDict.getLabel(
"nRadial");
350 setSampleDiskGeometry
364 if (sampleDiskDict.readIfPresent(
"surfaceWriter", writerType))
378 surfaceWriterPtr_->useTimeDir(
true);
381 errorOnPointNotFound_ =
382 sampleDiskDict.getOrDefault(
"errorOnPointNotFound",
false);
384 updateSampleDiskCells();
390 if (!writeWakeFields_)
399 const auto& meshCells = mesh_.cells();
400 const auto& meshFaces = mesh_.faces();
401 const auto& meshPoints = mesh_.points();
407 for (
const label facei : meshCells[celli])
409 for (
const label fpi : meshFaces[facei])
411 if (bb.contains(meshPoints[fpi]))
420 treeCellIDs.append(celli);
426 indexedOctree<treeDataCell>
tree 435 cellIds_.setSize(points_.size(), -1);
436 pointMask_.setSize(points_.size(),
false);
439 (void)mesh_.tetBasePtIs();
441 const auto& treeData =
tree.shapes();
448 label treeCelli =
tree.findInside(
pos);
452 reduce(proci, maxOp<label>());
454 pointMask_[pointi] = treeCelli != -1;
461 ? treeData.objectIndex(treeCelli)
467 if (errorOnPointNotFound_)
470 <<
"Position " <<
pos <<
" not found in mesh" 476 <<
"Position " <<
pos <<
" not found in mesh" 491 if (
field.size() != points_.size())
494 <<
"Inconsistent field sizes: input:" <<
field.size()
495 <<
" points:" << points_.size()
500 scalar sumFieldArea = 0;
502 for (
const face&
f : faces_)
505 scalar faceValue = 0;
506 for (
const label pti :
f)
509 if (!pointMask_[pti])
514 faceValue +=
field[pti];
519 scalar
area =
f.mag(points_);
521 sumFieldArea += faceValue/
f.size()*
area;
525 return sumFieldArea/(sumArea + ROOTVSMALL);
537 if (!surfaceWriterPtr_)
544 surfaceWriterPtr_->isPointData(
true);
545 surfaceWriterPtr_->beginTime(time_);
546 surfaceWriterPtr_->open
550 (baseFileDir() /
name() /
"surfaces" /
"disk"),
553 surfaceWriterPtr_->nFields(4);
554 surfaceWriterPtr_->write(
"U",
U);
555 surfaceWriterPtr_->write(
"Ur", Ur);
556 surfaceWriterPtr_->write(
"UNorm",
U/URef);
557 surfaceWriterPtr_->write(
"UrNorm", Ur/URef);
558 surfaceWriterPtr_->endTime();
559 surfaceWriterPtr_->clear();
565 if (!writePropellerPerformance_)
571 setRotationalSpeed();
573 const vector sumForce = forceEff();
574 const vector sumMoment = momentEff();
576 const scalar diameter = 2*radius_;
577 const scalar URef = URefPtr_->value(time_.timeOutputValue());
578 const scalar j = -URef/
mag(n_ + ROOTVSMALL)/diameter;
579 const scalar denom = rhoRef_*
sqr(n_)*
pow4(diameter);
580 const scalar kt = (sumForce & coordSysPtr_->e3())/denom;
582 -
sign(n_)*(sumMoment & coordSysPtr_->e3())/(denom*diameter);
587 auto&
os = propellerPerformanceFilePtr_();
589 writeCurrentTime(
os);
602 <<
" Revolutions per second, n : " << n_ <<
nl 603 <<
" Reference velocity, URef : " << URef <<
nl 604 <<
" Advance coefficient, J : " << j <<
nl 605 <<
" Thrust coefficient, Kt : " << kt <<
nl 606 <<
" Torque coefficient, 10*Kq : " << 10*kq <<
nl 607 <<
" Efficiency, etaO : " << etaO <<
nl 613 setResult(
"URef", URef);
617 setResult(
"etaO", etaO);
630 auto&
os = wakeFilePtr_();
632 const pointField propPoints(coordSysPtr_->localPosition(points_));
634 mag(propPoints[1][0] - propPoints[0][0]) < SMALL ? 0 : 1;
635 const scalar rMax = propPoints.last()[0];
637 const scalar UzMean = meanSampleDiskField(
U.component(2));
639 writeHeaderValue(
os,
"Time", time_.timeOutputValue());
640 writeHeaderValue(
os,
"Reference velocity", URef);
641 writeHeaderValue(
os,
"Direction", coordSysPtr_->e3());
642 writeHeaderValue(
os,
"Wake", 1 - UzMean/URef);
644 writeCommented(
os,
"r/R");
645 writeTabbed(
os,
"alpha");
646 writeTabbed(
os,
"(x y z)");
647 writeTabbed(
os,
"(Ur Utheta Uz)");
650 for (label thetai = 0; thetai < nTheta_; ++thetai)
652 const scalar deg = 360*thetai/scalar(nTheta_);
654 for (label radiusi = 0; radiusi <= nRadial_; ++radiusi)
656 label pointi = radiusi*nTheta_ + thetai + offset;
658 if (radiusi == 0 && offset == 1)
664 if (pointMask_[pointi])
666 const scalar rR = propPoints[radiusi*nTheta_][0]/rMax;
669 << points_[pointi] <<
tab <<
U[pointi] <<
nl;
689 auto&
os = axialWakeFilePtr_();
691 const pointField propPoints(coordSysPtr_->localPosition(points_));
693 mag(propPoints[1][0] - propPoints[0][0]) < SMALL ? 0 : 1;
694 const scalar rMax = propPoints.last()[0];
696 writeHeaderValue(
os,
"Time", time_.timeOutputValue());
699 for (label radiusi = 0; radiusi <= nRadial_; ++radiusi)
701 label pointi = radiusi*nTheta_;
702 scalar r = propPoints[pointi][0];
703 os <<
tab <<
"r/R=" << r/rMax;
707 for (label thetai = 0; thetai < nTheta_; ++thetai)
709 os << 360*thetai/scalar(nTheta_);
711 for (label radiusi = 0; radiusi <= nRadial_; ++radiusi)
713 label pointi = radiusi*nTheta_ + thetai + offset;
715 if (radiusi == 0 && offset == 1)
721 if (pointMask_[pointi])
723 os <<
tab << 1 -
U[pointi][2]/URef;
727 os <<
tab <<
"undefined";
742 if (!writeWakeFields_)
748 if (
mag(URef) < ROOTSMALL)
751 <<
"Magnitude of reference velocity should be greater than zero" 759 const vectorField UrDisk(coordSysPtr_->localVector(UDisk));
762 writeSampleDiskSurface(UDisk, UrDisk, URef);
765 writeWake(UrDisk, URef);
766 writeAxialWake(UrDisk, URef);
774 const Type& defaultValue
778 auto&
field = tfield.ref();
787 const label celli = cellIds_[pointi];
789 if (cellIds_[pointi] != -1)
791 const point& position = points_[pointi];
792 field[pointi] = interpolator().interpolate(position, celli);
816 rotationMode_(rotationMode::SPECIFIED),
819 writePropellerPerformance_(true),
820 propellerPerformanceFilePtr_(nullptr),
821 writeWakeFields_(true),
822 surfaceWriterPtr_(nullptr),
826 errorOnPointNotFound_(false),
830 interpolationScheme_(
"cell"),
831 wakeFilePtr_(nullptr),
832 axialWakeFilePtr_(nullptr),
864 radius_ =
dict.getScalar(
"radius");
866 rotationMode_ = rotationModeNames_.get(
"rotationMode",
dict);
868 writePropellerPerformance_ =
869 dict.get<
bool>(
"writePropellerPerformance");
871 writeWakeFields_ =
dict.get<
bool>(
"writeWakeFields");
872 if (writeWakeFields_)
874 dict.readIfPresent(
"interpolationScheme", interpolationScheme_);
876 dict.readIfPresent(
"nanValue", nanValue_);
891 setCoordinateSystem(dict_);
893 if (writeWakeFields_)
895 setSampleDiskSurface(dict_);
905 if (writeWakeFields_)
913 coordSysPtr_->localVector
918 vector::uniform(nanValue_)
922 const scalar UzMean = meanSampleDiskField(UDisk.component(2));
924 setResult(
"UzMean", UzMean);
927 writePropellerPerformance();
935 const scalar URef = URefPtr_->value(time_.timeOutputValue());
936 writeWakeFields(URef);
944 updateSampleDiskCells();
950 updateSampleDiskCells();
Base class for coordinate system specification, the default coordinate system type is cartesian ...
dimensionedScalar sign(const dimensionedScalar &ds)
virtual bool execute()
Execute, currently does nothing.
void createFiles()
Create output files.
Top level data entry class for use in dictionaries. Provides a mechanism to specify a variable as a c...
tmp< Field< Type > > interpolate(const GeometricField< Type, fvPatchField, volMesh > &psi, const Type &defaultValue) const
Interpolate from the mesh onto the sample surface.
point globalPosition(const point &local) const
From local coordinate position to global (cartesian) position.
defineTypeNameAndDebug(ObukhovLength, 0)
void writePropellerPerformance()
Write the wake fields.
static bool initialised_(false)
static void writeHeader(Ostream &os, const word &fieldName)
A cylindrical coordinate system (r-theta-z). The coordinate system angle theta is always in radians...
errorManipArg< error, int > exit(error &err, const int errNo=1)
dimensioned< typename typeOfMag< Type >::type > mag(const dimensioned< Type > &dt)
error FatalError
Error stream (stdout output on all processes), with additional 'FOAM FATAL ERROR' header text and sta...
A list of keyword definitions, which are a keyword followed by a number of values (eg...
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
void setRotationalSpeed()
Set the rotational speed.
propellerInfo(const propellerInfo &)=delete
No copy construct.
bool interpolate(const vector &p1, const vector &p2, const vector &o, vector &n, scalar l)
dimensionedSymmTensor sqr(const dimensionedVector &dv)
constexpr char nl
The newline '\n' character (0x0a)
void movePoints(const polyMesh &mesh)
Update for changes of mesh.
Ostream & endl(Ostream &os)
Add newline and flush stream.
A traits class, which is primarily used for primitives and vector-space.
tmp< DimensionedField< TypeR, GeoMesh > > New(const tmp< DimensionedField< TypeR, GeoMesh >> &tf1, const word &name, const dimensionSet &dimensions, const bool initCopy=false)
Global function forwards to reuseTmpDimensionedField::New.
virtual bool write()
Write the forces.
constexpr char tab
The tab '\t' character(0x09)
void resize_nocopy(const label len)
Adjust allocated size of list without necessarily.
Vector< Cmpt > & normalise(const scalar tol=ROOTVSMALL)
Inplace normalise the vector by its magnitude.
static int myProcNo(const label communicator=worldComm)
Rank of this process in the communicator (starting from masterNo()). Negative if the process is not a...
const Type * cfindObject(const word &name, const bool recursive=false) const
Return const pointer to the object of the given Type.
word MRFName_
Name of MRF zone (if applicable)
static dictionary formatOptions(const dictionary &dict, const word &formatName, const word &entryName="formatOptions")
Same as fileFormats::getFormatOptions.
Class to control time during OpenFOAM simulations that is also the top-level objectRegistry.
virtual bool read(const dictionary &dict)
Read the dictionary.
Class containing mesh-to-mesh mapping information after a change in polyMesh topology.
Macros for easy insertion into run-time selection tables.
void reduce(T &value, [[maybe_unused]] BinaryOp bop, [[maybe_unused]] const int tag=UPstream::msgType(), const int communicator=UPstream::worldComm)
Reduce inplace (cf. MPI Allreduce)
virtual const point & origin() const
Return origin.
static const Enum< rotationMode > rotationModeNames_
#define forAll(list, i)
Loop across all elements in list.
dimensionedScalar pos(const dimensionedScalar &ds)
fileName::Type type(const fileName &name, const bool followLink=true)
Return the file type: DIRECTORY or FILE, normally following symbolic links.
word name(const expressions::valueTypeCode typeCode)
A word representation of a valueTypeCode. Empty for expressions::valueTypeCode::INVALID.
void writeWakeFields(const scalar URef)
Write the wake fields.
void writeAxialWake(const vectorField &U, const scalar URef)
Write the axial wake text file.
labelList identity(const label len, label start=0)
Return an identity map of the given length with (map[i] == i), works like std::iota() but returning a...
void readFields(const typename GeoFieldType::Mesh &mesh, const IOobjectList &objects, const NameMatchPredicate &selectedFields, DynamicList< regIOobject *> &storedObjects)
Read the selected GeometricFields of the templated type and store on the objectRegistry.
constexpr scalar twoPi(2 *M_PI)
const wordList area
Standard area field types (scalar, vector, tensor, etc)
A class for handling words, derived from Foam::string.
void setCoordinateSystem(const dictionary &dict)
Set the coordinate system.
Tree tree(triangles.begin(), triangles.end())
scalar meanSampleDiskField(const scalarField &field) const
Return the area average of a field.
rotationMode rotationMode_
Rotation mode.
label min(const labelHashSet &set, label minValue=labelMax)
Find the min value in labelHashSet, optionally limited by second argument.
void updateSampleDiskCells()
Set the sample cells corresponding to the sample points.
errorManip< error > abort(error &err)
scalar n_
Propeller speed (revolutions per second)
#define DebugInfo
Report an information message using Foam::Info.
const volVectorField & U() const
Return the velocity field.
A Vector of values with scalar precision, where scalar is float/double depending on the compilation f...
void setSampleDiskGeometry(const coordinateSystem &coordSys, const scalar r1, const scalar r2, const scalar nTheta, const label nRadius, faceList &faces, pointField &points) const
Set the faces and points for the sample surface.
Reads fields from the time directories and adds them to the mesh database for further post-processing...
OBJstream os(runTime.globalPath()/outputName)
addToRunTimeSelectionTable(functionObject, ObukhovLength, dictionary)
void writeWake(const vectorField &U, const scalar URef)
Write the wake text file.
void writeSampleDiskSurface(const vectorField &U, const vectorField &Ur, const scalar URef)
Write the sample surface.
static const this_type & null() noexcept
Return a null GeometricField (reference to a nullObject).
autoPtr< coordinateSystem > coordSysPtr_
Coordinate system used when evaluating forces and moments.
vector point
Point is a vector.
void setSampleDiskSurface(const dictionary &dict)
Set the sample surface based on dictionary settings.
#define WarningInFunction
Report a warning using Foam::Warning.
Enum is a wrapper around a list of names/values that represent particular enumeration (or int) values...
#define FatalIOErrorInFunction(ios)
Report an error message using Foam::FatalIOError.
static void listCombineReduce(UList< T > &values, CombineOp cop, const int tag=UPstream::msgType(), const int communicator=UPstream::worldComm)
Forwards to Pstream::listReduce with an in-place cop.
Calculates propeller performance and wake field properties.
static bool master(const label communicator=worldComm)
True if process corresponds to the master rank in the communicator.
dimensionedScalar pow4(const dimensionedScalar &ds)
virtual bool read(const dictionary &)
Read the forces data.
Abstract base class for volume field interpolation.
Standard boundBox with extra functionality for use in octree.
Field< vector > vectorField
Specialisation of Field<T> for vector.
Pointer management similar to std::unique_ptr, with some additional methods and type checking...
const volScalarField & psi
Mesh consisting of general polyhedral cells.
Computes forces and moments over a given list of patches by integrating pressure and viscous forces a...
A class for managing temporary objects.
Registry of regIOobjects.
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...
const fvMesh & mesh_
Reference to the fvMesh.
static autoPtr< surfaceWriter > New(const word &writeType)
Select construct a surfaceWriter.
void UpdateMesh(const mapPolyMesh &mpm)
FlatOutput::OutputAdaptor< Container, Delimiters > flatOutput(const Container &obj, Delimiters delim)
Global flatOutput() function with specified output delimiters.
IOerror FatalIOError
Error stream (stdout output on all processes), with additional 'FOAM FATAL IO ERROR' header text and ...
static constexpr const zero Zero
Global zero (0)