43 namespace functionObjects
53 { rotationMode::SPECIFIED,
"specified" },
76 n_ =
dict.get<scalar>(
"n");
82 const auto* MRFZones =
88 <<
"Unable to find MRFProperties in the database. " 89 <<
"Is this an MRF case?" 92 const auto& mrf = MRFZones->MRFZoneList::getFromName(
MRFName_);
93 vector offset =
dict.getOrDefault(
"originOffset", vector::zero);
94 origin = offset + mrf.origin();
110 if (!
dict.readIfPresent(
"alphaAxis", alphaAxis))
115 scalar minDot = GREAT;
120 scalar dotp =
mag(test & axis);
128 alphaAxis = axis ^ cand;
131 alphaAxis.normalise();
139 switch (rotationMode_)
141 case rotationMode::SPECIFIED:
148 const auto* MRFZones =
154 <<
"Unable to find MRFProperties in the database. " 155 <<
"Is this an MRF case?" 159 const auto& mrf = MRFZones->MRFZoneList::getFromName(MRFName_);
168 <<
"Unhandled enumeration " << rotationModeNames_[rotationMode_]
182 if (writePropellerPerformance_ && !propellerPerformanceFilePtr_)
184 propellerPerformanceFilePtr_ =
185 newFileAtStartTime(
"propellerPerformance");
186 auto&
os = propellerPerformanceFilePtr_();
189 writeHeaderValue(
os,
"CofR", coordSysPtr_->origin());
190 writeHeaderValue(
os,
"Radius", radius_);
191 writeHeaderValue(
os,
"Axis", coordSysPtr_->e3());
195 writeCommented(
os,
"Time");
196 writeTabbed(
os,
"n");
197 writeTabbed(
os,
"URef");
198 writeTabbed(
os,
"J");
199 writeTabbed(
os,
"KT");
200 writeTabbed(
os,
"10*KQ");
201 writeTabbed(
os,
"eta0");
205 if (writeWakeFields_)
207 if (!wakeFilePtr_) wakeFilePtr_ = newFileAtStartTime(
"wake");
208 if (!axialWakeFilePtr_) axialWakeFilePtr_ =
209 newFileAtStartTime(
"axialWake");
221 <<
"Unable to find velocity field " << UName_
222 <<
" . Available vector fields are: " 242 label nPoint = nRadius*nTheta;
251 const label nFace = nRadius*nTheta;
257 const scalar zCoord = 0;
259 for (
int radiusi = 0; radiusi <= nRadius; ++radiusi)
261 if (r1 < SMALL && radiusi == 0)
263 points[pointi++] = origin;
267 const scalar r = r1 + radiusi*(r2 - r1)/nRadius;
269 for (label i = 0; i < nTheta; ++i)
284 const List<label> ptIDs(
identity(nTheta));
288 label pointOffset0 = 0;
289 label radiusOffset = 0;
290 DynamicList<label> facePts(4);
291 for (
int radiusi = 0; radiusi < nRadius; ++radiusi)
293 if (r1 < SMALL && radiusi == 0)
299 for (label thetai = 1; thetai <= nTheta; ++thetai)
305 facePts.append(thetai);
306 facePts.append(1 + ptIDs.fcIndex(thetai - 1));
308 faces[facei++] = face(facePts);
313 for (label thetai = 0; thetai < nTheta; ++thetai)
317 label offset = pointOffset0 + (radiusi-radiusOffset)*nTheta;
320 facePts.append(offset + ptIDs.fcIndex(thetai - 1));
321 facePts.append(offset + ptIDs.fcIndex(thetai));
324 facePts.append(offset + nTheta + ptIDs.fcIndex(thetai));
325 facePts.append(offset + nTheta + ptIDs.fcIndex(thetai - 1));
327 faces[facei++] = face(facePts);
336 const dictionary&
dict 339 const dictionary& sampleDiskDict(
dict.subDict(
"sampleDisk"));
341 const scalar r1 = sampleDiskDict.getScalar(
"r1");
342 const scalar r2 = sampleDiskDict.getScalar(
"r2");
344 nTheta_ = sampleDiskDict.getLabel(
"nTheta");
345 nRadial_ = sampleDiskDict.getLabel(
"nRadial");
347 setSampleDiskGeometry
361 if (sampleDiskDict.readIfPresent(
"surfaceWriter", writerType))
375 surfaceWriterPtr_->useTimeDir(
true);
378 errorOnPointNotFound_ =
379 sampleDiskDict.getOrDefault(
"errorOnPointNotFound",
false);
381 updateSampleDiskCells();
387 if (!writeWakeFields_)
396 const auto& meshCells = mesh_.cells();
397 const auto& meshFaces = mesh_.faces();
398 const auto& meshPoints = mesh_.points();
404 for (
const label facei : meshCells[celli])
406 for (
const label fpi : meshFaces[facei])
408 if (bb.contains(meshPoints[fpi]))
417 treeCellIDs.append(celli);
423 indexedOctree<treeDataCell>
tree 432 cellIds_.setSize(points_.size(), -1);
433 pointMask_.setSize(points_.size(),
false);
436 (void)mesh_.tetBasePtIs();
438 const auto& treeData =
tree.shapes();
445 label treeCelli =
tree.findInside(
pos);
449 reduce(proci, maxOp<label>());
451 pointMask_[pointi] = treeCelli != -1;
458 ? treeData.objectIndex(treeCelli)
464 if (errorOnPointNotFound_)
467 <<
"Position " <<
pos <<
" not found in mesh" 473 <<
"Position " <<
pos <<
" not found in mesh" 488 if (
field.size() != points_.size())
491 <<
"Inconsistent field sizes: input:" <<
field.size()
492 <<
" points:" << points_.size()
497 scalar sumFieldArea = 0;
500 const face&
f = faces_[facei];
503 scalar faceValue = 0;
504 for (
const label pti :
f)
507 if (!pointMask_[pti])
512 faceValue +=
field[pti];
517 scalar
area =
f.mag(points_);
519 sumFieldArea += faceValue/
f.size()*
area;
523 return sumFieldArea/(sumArea + ROOTVSMALL);
535 if (!surfaceWriterPtr_)
542 surfaceWriterPtr_->isPointData(
true);
543 surfaceWriterPtr_->beginTime(time_);
544 surfaceWriterPtr_->open
548 (baseFileDir() /
name() /
"surfaces" /
"disk"),
551 surfaceWriterPtr_->nFields(4);
552 surfaceWriterPtr_->write(
"U",
U);
553 surfaceWriterPtr_->write(
"Ur", Ur);
554 surfaceWriterPtr_->write(
"UNorm",
U/URef);
555 surfaceWriterPtr_->write(
"UrNorm", Ur/URef);
556 surfaceWriterPtr_->endTime();
557 surfaceWriterPtr_->clear();
563 if (!writePropellerPerformance_)
569 setRotationalSpeed();
571 const vector sumForce = forceEff();
572 const vector sumMoment = momentEff();
574 const scalar diameter = 2*radius_;
575 const scalar URef = URefPtr_->value(time_.timeOutputValue());
576 const scalar j = -URef/
mag(n_ + ROOTVSMALL)/diameter;
577 const scalar denom = rhoRef_*
sqr(n_)*
pow4(diameter);
578 const scalar kt = (sumForce & coordSysPtr_->e3())/denom;
580 -
sign(n_)*(sumMoment & coordSysPtr_->e3())/(denom*diameter);
585 auto&
os = propellerPerformanceFilePtr_();
587 writeCurrentTime(
os);
600 <<
" Revolutions per second, n : " << n_ <<
nl 601 <<
" Reference velocity, URef : " << URef <<
nl 602 <<
" Advance coefficient, J : " << j <<
nl 603 <<
" Thrust coefficient, Kt : " << kt <<
nl 604 <<
" Torque coefficient, 10*Kq : " << 10*kq <<
nl 605 <<
" Efficiency, etaO : " << etaO <<
nl 611 setResult(
"URef", URef);
615 setResult(
"etaO", etaO);
628 auto&
os = wakeFilePtr_();
630 const pointField propPoints(coordSysPtr_->localPosition(points_));
632 mag(propPoints[1][0] - propPoints[0][0]) < SMALL ? 0 : 1;
633 const scalar rMax = propPoints.last()[0];
635 const scalar UzMean = meanSampleDiskField(
U.component(2));
637 writeHeaderValue(
os,
"Time", time_.timeOutputValue());
638 writeHeaderValue(
os,
"Reference velocity", URef);
639 writeHeaderValue(
os,
"Direction", coordSysPtr_->e3());
640 writeHeaderValue(
os,
"Wake", 1 - UzMean/URef);
642 writeCommented(
os,
"r/R");
643 writeTabbed(
os,
"alpha");
644 writeTabbed(
os,
"(x y z)");
645 writeTabbed(
os,
"(Ur Utheta Uz)");
648 for (label thetai = 0; thetai < nTheta_; ++thetai)
650 const scalar deg = 360*thetai/scalar(nTheta_);
652 for (label radiusi = 0; radiusi <= nRadial_; ++radiusi)
654 label pointi = radiusi*nTheta_ + thetai + offset;
656 if (radiusi == 0 && offset == 1)
662 if (pointMask_[pointi])
664 const scalar rR = propPoints[radiusi*nTheta_][0]/rMax;
667 << points_[pointi] <<
tab <<
U[pointi] <<
nl;
687 auto&
os = axialWakeFilePtr_();
689 const pointField propPoints(coordSysPtr_->localPosition(points_));
691 mag(propPoints[1][0] - propPoints[0][0]) < SMALL ? 0 : 1;
692 const scalar rMax = propPoints.last()[0];
694 writeHeaderValue(
os,
"Time", time_.timeOutputValue());
697 for (label radiusi = 0; radiusi <= nRadial_; ++radiusi)
699 label pointi = radiusi*nTheta_;
700 scalar r = propPoints[pointi][0];
701 os <<
tab <<
"r/R=" << r/rMax;
705 for (label thetai = 0; thetai < nTheta_; ++thetai)
707 os << 360*thetai/scalar(nTheta_);
709 for (label radiusi = 0; radiusi <= nRadial_; ++radiusi)
711 label pointi = radiusi*nTheta_ + thetai + offset;
713 if (radiusi == 0 && offset == 1)
719 if (pointMask_[pointi])
721 os <<
tab << 1 -
U[pointi][2]/URef;
725 os <<
tab <<
"undefined";
740 if (!writeWakeFields_)
746 if (
mag(URef) < ROOTSMALL)
749 <<
"Magnitude of reference velocity should be greater than zero" 757 const vectorField UrDisk(coordSysPtr_->localVector(UDisk));
760 writeSampleDiskSurface(UDisk, UrDisk, URef);
763 writeWake(UrDisk, URef);
764 writeAxialWake(UrDisk, URef);
772 const Type& defaultValue
776 auto&
field = tfield.ref();
785 const label celli = cellIds_[pointi];
787 if (cellIds_[pointi] != -1)
789 const point& position = points_[pointi];
790 field[pointi] = interpolator().interpolate(position, celli);
813 rotationMode_(rotationMode::SPECIFIED),
816 writePropellerPerformance_(true),
817 propellerPerformanceFilePtr_(nullptr),
818 writeWakeFields_(true),
819 surfaceWriterPtr_(nullptr),
823 errorOnPointNotFound_(false),
827 interpolationScheme_(
"cell"),
828 wakeFilePtr_(nullptr),
829 axialWakeFilePtr_(nullptr),
858 radius_ =
dict.getScalar(
"radius");
860 rotationMode_ = rotationModeNames_.get(
"rotationMode",
dict);
863 setCoordinateSystem(
dict);
865 writePropellerPerformance_ =
866 dict.get<
bool>(
"writePropellerPerformance");
868 writeWakeFields_ =
dict.get<
bool>(
"writeWakeFields");
869 if (writeWakeFields_)
871 setSampleDiskSurface(
dict);
873 dict.readIfPresent(
"interpolationScheme", interpolationScheme_);
875 dict.readIfPresent(
"nanValue", nanValue_);
891 if (writeWakeFields_)
899 coordSysPtr_->localVector
904 vector::uniform(nanValue_)
908 const scalar UzMean = meanSampleDiskField(UDisk.component(2));
910 setResult(
"UzMean", UzMean);
913 writePropellerPerformance();
921 const scalar URef = URefPtr_->value(time_.timeOutputValue());
922 writeWakeFields(URef);
930 updateSampleDiskCells();
936 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 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.
virtual bool write()
Write the forces.
constexpr char tab
The tab '\t' character(0x09)
static int myProcNo(const label communicator=worldComm)
Number of this process (starting from masterNo() = 0)
tmp< DimensionedField< TypeR, GeoMesh > > New(const tmp< DimensionedField< TypeR, GeoMesh >> &tdf1, const word &name, const dimensionSet &dimensions, const bool initCopy=false)
Global function forwards to reuseTmpDimensionedField::New.
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.
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.
void setSize(const label n)
Alias for resize()
word name(const expressions::valueTypeCode typeCode)
A word representation of a valueTypeCode. Empty for 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)
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.
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.
Calculates propeller performance and wake field properties.
static bool master(const label communicator=worldComm)
Am I the master rank.
dimensionedScalar pow4(const dimensionedScalar &ds)
virtual bool read(const dictionary &)
Read the forces data.
void reduce(const List< UPstream::commsStruct > &comms, T &value, const BinaryOp &bop, const int tag, const label comm)
Reduce inplace (cf. MPI Allreduce) using specified communication schedule.
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.
void readFields(const typename GeoFieldType::Mesh &mesh, const IOobjectList &objects, const wordHashSet &selectedFields, LIFOStack< regIOobject *> &storedObjects)
Read the selected GeometricFields of the templated type.
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.
List of MRF zones with IO functionality. MRF zones are specified by a list of dictionary entries...
const fvMesh & mesh_
Reference to the fvMesh.
static autoPtr< surfaceWriter > New(const word &writeType)
Return a reference to the selected surfaceWriter.
static void listCombineReduce(List< T > &values, const CombineOp &cop, const int tag=UPstream::msgType(), const label comm=UPstream::worldComm)
After completion all processors have the same data.
void UpdateMesh(const mapPolyMesh &mpm)
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)