Go to the documentation of this file.
43 namespace functionObjects
53 { rotationMode::SPECIFIED,
"specified" },
68 switch (rotationMode_)
70 case rotationMode::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();
104 <<
"Unhandled enumeration " << rotationModeNames_[rotationMode_]
110 if (!
dict.readIfPresent(
"alphaAxis", alphaAxis))
115 scalar minDot = GREAT;
120 scalar dotp =
mag(test & axis);
128 alphaAxis = axis ^ cand;
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_);
182 if (writePropellerPerformance_ && !propellerPerformanceFilePtr_)
184 propellerPerformanceFilePtr_ = createFile(
"propellerPerformance");
185 auto&
os = propellerPerformanceFilePtr_();
188 writeHeaderValue(
os,
"CofR", coordSysPtr_->origin());
189 writeHeaderValue(
os,
"Radius", radius_);
190 writeHeaderValue(
os,
"Axis", coordSysPtr_->e3());
194 writeCommented(
os,
"Time");
195 writeTabbed(
os,
"n");
196 writeTabbed(
os,
"URef");
197 writeTabbed(
os,
"J");
198 writeTabbed(
os,
"KT");
199 writeTabbed(
os,
"10*KQ");
200 writeTabbed(
os,
"eta0");
204 if (writeWakeFields_)
206 if (!wakeFilePtr_) wakeFilePtr_ = createFile(
"wake");
207 if (!axialWakeFilePtr_) axialWakeFilePtr_ = createFile(
"axialWake");
219 <<
"Unable to find velocity field " << UName_
220 <<
" . Available vector fields are: "
240 label nPoint = nRadius*nTheta;
249 const label nFace = nRadius*nTheta;
255 const scalar zCoord = 0;
257 for (
int radiusi = 0; radiusi <= nRadius; ++radiusi)
259 if (r1 < SMALL && radiusi == 0)
261 points[pointi++] = origin;
265 const scalar r = r1 + radiusi*(r2 - r1)/nRadius;
267 for (label i = 0; i < nTheta; ++i)
286 label pointOffset0 = 0;
287 label radiusOffset = 0;
289 for (
int radiusi = 0; radiusi < nRadius; ++radiusi)
291 if (r1 < SMALL && radiusi == 0)
297 for (label thetai = 1; thetai <= nTheta; ++thetai)
304 facePts.
append(1 + ptIDs.fcIndex(thetai - 1));
306 faces[facei++] =
face(facePts);
311 for (label thetai = 0; thetai < nTheta; ++thetai)
315 label offset = pointOffset0 + (radiusi-radiusOffset)*nTheta;
318 facePts.
append(offset + ptIDs.fcIndex(thetai - 1));
319 facePts.
append(offset + ptIDs.fcIndex(thetai));
322 facePts.
append(offset + nTheta + ptIDs.fcIndex(thetai));
323 facePts.
append(offset + nTheta + ptIDs.fcIndex(thetai - 1));
325 faces[facei++] =
face(facePts);
339 const scalar r1 = sampleDiskDict.
getScalar(
"r1");
340 const scalar r2 = sampleDiskDict.getScalar(
"r2");
342 nTheta_ = sampleDiskDict.getLabel(
"nTheta");
343 nRadial_ = sampleDiskDict.getLabel(
"nRadial");
345 setSampleDiskGeometry
358 if (sampleDiskDict.readIfPresent(
"surfaceWriter", surfWriterType))
360 const auto writeOptions = sampleDiskDict.subOrEmptyDict(
"writeOptions");
365 writeOptions.subOrEmptyDict(surfWriterType)
369 surfaceWriterPtr_->useTimeDir(
true);
372 errorOnPointNotFound_ =
373 sampleDiskDict.getOrDefault(
"errorOnPointNotFound",
false);
375 updateSampleDiskCells();
381 if (!writeWakeFields_)
390 const auto& meshCells = mesh_.cells();
391 const auto& meshFaces = mesh_.faces();
392 const auto& meshPoints = mesh_.points();
398 for (
const label facei : meshCells[celli])
400 for (
const label fpi : meshFaces[facei])
411 treeCellIDs.append(celli);
426 cellIds_.setSize(points_.size(), -1);
427 pointMask_.setSize(points_.size(),
false);
430 (void)mesh_.tetBasePtIs();
432 const auto& cellLabels = tree.
shapes().cellLabels();
445 pointMask_[pointi] = treeCelli != -1;
451 ? cellLabels[treeCelli]
456 if (errorOnPointNotFound_)
459 <<
"Position " <<
pos <<
" not found in mesh"
465 <<
"Position " <<
pos <<
" not found in mesh"
481 if (
field.size() != points_.size())
484 <<
"Inconsistent field sizes: input:" <<
field.size()
485 <<
" points:" << points_.size()
490 scalar sumFieldArea = 0;
493 const face&
f = faces_[facei];
496 scalar faceValue = 0;
497 for (
const label pti :
f)
500 if (!pointMask_[pti])
505 faceValue +=
field[pti];
510 scalar
area =
f.mag(points_);
512 sumFieldArea += faceValue/
f.size()*
area;
516 return sumFieldArea/(sumArea + ROOTVSMALL);
528 if (!surfaceWriterPtr_)
535 surfaceWriterPtr_->isPointData(
true);
536 surfaceWriterPtr_->beginTime(time_);
537 surfaceWriterPtr_->open
541 (baseFileDir() /
name() /
"surfaces" /
"disk"),
544 surfaceWriterPtr_->nFields(4);
545 surfaceWriterPtr_->write(
"U",
U);
546 surfaceWriterPtr_->write(
"Ur", Ur);
547 surfaceWriterPtr_->write(
"UNorm",
U/URef);
548 surfaceWriterPtr_->write(
"UrNorm", Ur/URef);
549 surfaceWriterPtr_->endTime();
550 surfaceWriterPtr_->clear();
556 if (!writePropellerPerformance_)
562 setRotationalSpeed();
564 const vector sumForce(
sum(force_[0]) +
sum(force_[1]) +
sum(force_[2]));
565 const vector sumMoment(
sum(moment_[0]) +
sum(moment_[1]) +
sum(moment_[2]));
567 const scalar diameter = 2*radius_;
568 const scalar URef = URefPtr_->value(time_.timeOutputValue());
569 const scalar j = -URef/
mag(n_ + ROOTVSMALL)/diameter;
570 const scalar denom = rhoRef_*
sqr(n_)*
pow4(diameter);
571 const scalar kt = (sumForce & coordSysPtr_->e3())/denom;
573 -
sign(n_)*(sumMoment & coordSysPtr_->e3())/(denom*diameter);
578 auto&
os = propellerPerformanceFilePtr_();
580 writeCurrentTime(
os);
593 <<
" Revolutions per second, n : " << n_ <<
nl
594 <<
" Reference velocity, URef : " << URef <<
nl
595 <<
" Advance coefficient, J : " << j <<
nl
596 <<
" Thrust coefficient, Kt : " << kt <<
nl
597 <<
" Torque coefficient, 10*Kq : " << 10*kq <<
nl
598 <<
" Efficiency, etaO : " << etaO <<
nl
604 setResult(
"URef", URef);
608 setResult(
"etaO", etaO);
621 auto&
os = wakeFilePtr_();
623 const pointField propPoints(coordSysPtr_->localPosition(points_));
625 mag(propPoints[1][0] - propPoints[0][0]) < SMALL ? 0 : 1;
626 const scalar rMax = propPoints.last()[0];
628 const scalar UzMean = meanSampleDiskField(
U.component(2));
630 writeHeaderValue(
os,
"Time", time_.timeOutputValue());
631 writeHeaderValue(
os,
"Reference velocity", URef);
632 writeHeaderValue(
os,
"Direction", coordSysPtr_->e3());
633 writeHeaderValue(
os,
"Wake", 1 - UzMean/URef);
635 writeCommented(
os,
"r/R");
636 writeTabbed(
os,
"alpha");
637 writeTabbed(
os,
"(x y z)");
638 writeTabbed(
os,
"(Ur Utheta Uz)");
641 for (label thetai = 0; thetai < nTheta_; ++thetai)
643 const scalar deg = 360*thetai/scalar(nTheta_);
645 for (label radiusi = 0; radiusi <= nRadial_; ++radiusi)
647 label pointi = radiusi*nTheta_ + thetai + offset;
649 if (radiusi == 0 && offset == 1)
655 if (pointMask_[pointi])
657 const scalar rR = propPoints[radiusi*nTheta_][0]/rMax;
660 << points_[pointi] <<
tab <<
U[pointi] <<
nl;
680 auto&
os = axialWakeFilePtr_();
682 const pointField propPoints(coordSysPtr_->localPosition(points_));
684 mag(propPoints[1][0] - propPoints[0][0]) < SMALL ? 0 : 1;
685 const scalar rMax = propPoints.last()[0];
687 writeHeaderValue(
os,
"Time", time_.timeOutputValue());
690 for (label radiusi = 0; radiusi <= nRadial_; ++radiusi)
692 label pointi = radiusi*nTheta_;
693 scalar r = propPoints[pointi][0];
694 os <<
tab <<
"r/R=" << r/rMax;
698 for (label thetai = 0; thetai < nTheta_; ++thetai)
700 os << 360*thetai/scalar(nTheta_);
702 for (label radiusi = 0; radiusi <= nRadial_; ++radiusi)
704 label pointi = radiusi*nTheta_ + thetai + offset;
706 if (radiusi == 0 && offset == 1)
712 if (pointMask_[pointi])
714 os <<
tab << 1 -
U[pointi][2]/URef;
718 os <<
tab <<
"undefined";
733 if (!writeWakeFields_)
740 const vectorField UrDisk(coordSysPtr_->localVector(UDisk));
743 writeSampleDiskSurface(UDisk, UrDisk, URef);
746 writeWake(UrDisk, URef);
747 writeAxialWake(UrDisk, URef);
755 const Type& defaultValue
759 auto&
field = tfield.ref();
768 const label celli = cellIds_[pointi];
770 if (cellIds_[pointi] != -1)
772 const point& position = points_[pointi];
773 field[pointi] = interpolator().interpolate(position, celli);
797 rotationMode_(rotationMode::SPECIFIED),
800 writePropellerPerformance_(
true),
801 propellerPerformanceFilePtr_(
nullptr),
802 writeWakeFields_(
true),
803 surfaceWriterPtr_(
nullptr),
807 errorOnPointNotFound_(
false),
811 interpolationScheme_(
"cell"),
812 wakeFilePtr_(
nullptr),
813 axialWakeFilePtr_(
nullptr),
842 radius_ =
dict.getScalar(
"radius");
844 rotationMode_ = rotationModeNames_.get(
"rotationMode",
dict);
847 setCoordinateSystem(
dict);
849 writePropellerPerformance_ =
850 dict.get<
bool>(
"writePropellerPerformance");
852 writeWakeFields_ =
dict.get<
bool>(
"writeWakeFields");
853 if (writeWakeFields_)
855 setSampleDiskSurface(
dict);
857 dict.readIfPresent(
"interpolationScheme", interpolationScheme_);
859 dict.readIfPresent(
"nanValue", nanValue_);
875 if (writeWakeFields_)
883 coordSysPtr_->localVector
892 const scalar UzMean = meanSampleDiskField(UDisk.component(2));
894 setResult(
"UzMean", UzMean);
897 writePropellerPerformance();
905 const scalar URef = URefPtr_->value(time_.timeOutputValue());
906 writeWakeFields(URef);
914 updateSampleDiskCells();
920 updateSampleDiskCells();
point globalPosition(const point &local) const
From local coordinate position to global (cartesian) position.
void movePoints(const polyMesh &mesh)
Update for changes of mesh.
Enum is a wrapper around a list of names/values that represent particular enumeration (or int) values...
Class to control time during OpenFOAM simulations that is also the top-level objectRegistry.
A class for handling words, derived from Foam::string.
static Vector< scalar > uniform(const scalar &s)
Return a VectorSpace with all elements = s.
scalar getScalar(const word &keyword, enum keyType::option matchOpt=keyType::REGEX) const
Same as get< scalar >(const word&, keyType::option)
Calculates the forces and moments by integrating the pressure and skin-friction forces over a given l...
A class for managing temporary objects.
static constexpr const zero Zero
Global zero (0)
void inflate(const scalar s)
Inflate box by factor*mag(span) in all dimensions.
void createFiles()
Create output files.
Standard boundBox with extra functionality for use in octree.
bool read(const char *buf, int32_t &val)
Same as readInt32.
void writePropellerPerformance()
Write the wake fields.
virtual bool execute()
Execute, currently does nothing.
word MRFName_
Name of MRF zone (if applicable)
static bool master(const label communicator=worldComm)
Am I the master process.
const Type & shapes() const
Reference to shape.
Ostream & endl(Ostream &os)
Add newline and flush stream.
static void writeHeader(Ostream &os, const word &fieldName)
Vector< Cmpt > & normalise()
Normalise the vector by its magnitude.
dimensionedScalar sign(const dimensionedScalar &ds)
void clear() noexcept
Clear the addressed list, i.e. set the size to zero.
void writeAxialWake(const vectorField &U, const scalar URef)
Write the axial wake text file.
Top level data entry class for use in dictionaries. Provides a mechanism to specify a variable as a c...
Mesh consisting of general polyhedral cells.
propellerInfo(const propellerInfo &)=delete
No copy construct.
scalar meanSampleDiskField(const scalarField &field) const
Return the area average of a field.
#define forAll(list, i)
Loop across all elements in list.
Registry of regIOobjects.
void writeWakeFields(const scalar URef)
Write the wake fields.
virtual const point & origin() const
Return origin.
void reduce(const List< UPstream::commsStruct > &comms, T &Value, const BinaryOp &bop, const int tag, const label comm)
dimensionedScalar pow4(const dimensionedScalar &ds)
virtual bool write()
Write the forces.
bool interpolate(const vector &p1, const vector &p2, const vector &o, vector &n, scalar l)
Encapsulation of data needed to search in/for cells. Used to find the cell containing a point (e....
const volVectorField & U() const
Return the velocity field.
void setSize(const label n)
Alias for resize()
DynamicList< T, SizeMin > & append(const T &val)
Append an element to the end of this list.
void updateSampleDiskCells()
Set the sample cells corresponding to the sample points.
Non-pointer based hierarchical recursive searching.
Calculates propeller performance and wake field properties.
virtual bool read(const dictionary &)
Read the forces data.
static const Enum< rotationMode > rotationModeNames_
label findInside(const point &) const
Find shape containing point. Only implemented for certain.
A cylindrical coordinate system (r-theta-z). The coordinate system angle theta is always in radians.
constexpr scalar twoPi(2 *M_PI)
Abstract base class for interpolation.
A list of keyword definitions, which are a keyword followed by a number of values (eg,...
OBJstream os(runTime.globalPath()/outputName)
Macros for easy insertion into run-time selection tables.
errorManip< error > abort(error &err)
scalar n_
Propeller speed (revolutions per second)
void writeSampleDiskSurface(const vectorField &U, const vectorField &Ur, const scalar URef)
Write the sample surface.
void setCoordinateSystem(const dictionary &dict)
Set the coordinate system.
errorManipArg< error, int > exit(error &err, const int errNo=1)
tmp< DimensionedField< TypeR, GeoMesh > > New(const tmp< DimensionedField< TypeR, GeoMesh >> &tdf1, const word &name, const dimensionSet &dimensions)
Global function forwards to reuseTmpDimensionedField::New.
static void listCombineGather(const List< commsStruct > &comms, List< T > &Value, const CombineOp &cop, const int tag, const label comm)
Pointer management similar to std::unique_ptr, with some additional methods and type checking.
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
dimensionedSymmTensor sqr(const dimensionedVector &dv)
bool contains(const vector &dir, const point &) const
Contains point (inside or on edge) and moving in direction.
#define DebugInfo
Report an information message using Foam::Info.
const Type * cfindObject(const word &name, const bool recursive=false) const
Return const pointer to the object of the given Type.
static int myProcNo(const label communicator=worldComm)
Number of this process (starting from masterNo() = 0)
rotationMode rotationMode_
Rotation mode.
addToRunTimeSelectionTable(functionObject, ObukhovLength, dictionary)
static autoPtr< surfaceWriter > New(const word &writeType)
Return a reference to the selected surfaceWriter.
A traits class, which is primarily used for primitives.
void writeWake(const vectorField &U, const scalar URef)
Write the wake text file.
dimensioned< typename typeOfMag< Type >::type > mag(const dimensioned< Type > &dt)
fileName::Type type(const fileName &name, const bool followLink=true)
Return the file type: DIRECTORY or FILE, normally following symbolic links.
const fvMesh & mesh_
Reference to the fvMesh.
List of MRF zones with IO functionality. MRF zones are specified by a list of dictionary entries,...
labelList identity(const label len, label start=0)
Create identity map of the given length with (map[i] == i)
dimensioned< Type > sum(const DimensionedField< Type, GeoMesh > &df)
void setSampleDiskSurface(const dictionary &dict)
Set the sample surface based on dictionary settings.
defineTypeNameAndDebug(ObukhovLength, 0)
Reads fields from the time directories and adds them to the mesh database for further post-processing...
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.
void UpdateMesh(const mapPolyMesh &mpm)
const wordList area
Standard area field types (scalar, vector, tensor, etc)
Class containing mesh-to-mesh mapping information after a change in polyMesh topology.
word name(const expressions::valueTypeCode typeCode)
A word representation of a valueTypeCode. Empty for INVALID.
A face is a list of labels corresponding to mesh vertices.
#define FatalIOErrorInFunction(ios)
Report an error message using Foam::FatalIOError.
virtual bool read(const dictionary &)
Read the forces data.
static void listCombineScatter(const List< commsStruct > &comms, List< T > &Value, const int tag, const label comm)
Scatter data. Reverse of combineGather.
tmp< Field< Type > > interpolate(const GeometricField< Type, fvPatchField, volMesh > &psi, const Type &defaultValue) const
Interpolate from the mesh onto the sample surface.
const volScalarField & psi
const Time & time() const noexcept
Return time registry.
void setRotationalSpeed()
Set the rotational speed.
Base class for coordinate system specification, the default coordinate system type is cartesian .
dimensionedScalar pos(const dimensionedScalar &ds)