rotorDiskSource.C
Go to the documentation of this file.
1/*---------------------------------------------------------------------------*\
2 ========= |
3 \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4 \\ / O peration |
5 \\ / A nd | www.openfoam.com
6 \\/ M anipulation |
7-------------------------------------------------------------------------------
8 Copyright (C) 2011-2016 OpenFOAM Foundation
9 Copyright (C) 2018-2022 OpenCFD Ltd.
10-------------------------------------------------------------------------------
11License
12 This file is part of OpenFOAM.
13
14 OpenFOAM is free software: you can redistribute it and/or modify it
15 under the terms of the GNU General Public License as published by
16 the Free Software Foundation, either version 3 of the License, or
17 (at your option) any later version.
18
19 OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
20 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
21 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22 for more details.
23
24 You should have received a copy of the GNU General Public License
25 along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
26
27\*---------------------------------------------------------------------------*/
28
29#include "rotorDiskSource.H"
31#include "trimModel.H"
32#include "fvMatrices.H"
33#include "geometricOneField.H"
34#include "syncTools.H"
35#include "unitConversion.H"
36
37using namespace Foam::constant;
38
39// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
40
41namespace Foam
42{
43 namespace fv
44 {
47 }
48}
49
50
51const Foam::Enum
52<
54>
56({
57 { geometryModeType::gmAuto, "auto" },
58 { geometryModeType::gmSpecified, "specified" },
59});
60
61
62const Foam::Enum
63<
65>
67({
68 { inletFlowType::ifFixed, "fixed" },
69 { inletFlowType::ifSurfaceNormal, "surfaceNormal" },
70 { inletFlowType::ifLocal, "local" },
71});
72
73
74// * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
75
77{
78 // Set inflow type
79 switch (selectionMode())
80 {
81 case smCellSet:
82 case smCellZone:
83 case smAll:
84 {
85 // Set the profile ID for each blade section
87 switch (inletFlow_)
88 {
89 case ifFixed:
90 {
91 coeffs_.readEntry("inletVelocity", inletVelocity_);
92 break;
93 }
94 case ifSurfaceNormal:
95 {
96 scalar UIn(coeffs_.get<scalar>("inletNormalVelocity"));
98 break;
99 }
100 case ifLocal:
101 {
102 break;
103 }
104 default:
105 {
107 << "Unknown inlet velocity type" << abort(FatalError);
108 }
109 }
110
111 break;
112 }
113 default:
114 {
116 << "Source cannot be used with '"
118 << "' mode. Please use one of: " << nl
122 << exit(FatalError);
123 }
124 }
125}
126
127
129{
130 area_ = 0.0;
131
132 static const scalar tol = 0.8;
133
134 const label nInternalFaces = mesh_.nInternalFaces();
135 const polyBoundaryMesh& pbm = mesh_.boundaryMesh();
136 const vectorField& Sf = mesh_.Sf();
137 const scalarField& magSf = mesh_.magSf();
138
139 vector n = Zero;
140
141 // Calculate cell addressing for selected cells
142 labelList cellAddr(mesh_.nCells(), -1);
143 labelUIndList(cellAddr, cells_) = identity(cells_.size());
144 labelList nbrFaceCellAddr(mesh_.nBoundaryFaces(), -1);
145 forAll(pbm, patchi)
146 {
147 const polyPatch& pp = pbm[patchi];
148
149 if (pp.coupled())
150 {
151 forAll(pp, i)
152 {
153 label facei = pp.start() + i;
154 label nbrFacei = facei - nInternalFaces;
155 label own = mesh_.faceOwner()[facei];
156 nbrFaceCellAddr[nbrFacei] = cellAddr[own];
157 }
158 }
159 }
160
161 // Correct for parallel running
162 syncTools::swapBoundaryFaceList(mesh_, nbrFaceCellAddr);
163
164 // Add internal field contributions
165 for (label facei = 0; facei < nInternalFaces; facei++)
166 {
167 const label own = cellAddr[mesh_.faceOwner()[facei]];
168 const label nbr = cellAddr[mesh_.faceNeighbour()[facei]];
169
170 if ((own != -1) && (nbr == -1))
171 {
172 vector nf = Sf[facei]/magSf[facei];
173
174 if ((nf & axis) > tol)
175 {
176 area_[own] += magSf[facei];
177 n += Sf[facei];
178 }
179 }
180 else if ((own == -1) && (nbr != -1))
181 {
182 vector nf = Sf[facei]/magSf[facei];
183
184 if ((-nf & axis) > tol)
185 {
186 area_[nbr] += magSf[facei];
187 n -= Sf[facei];
188 }
189 }
190 }
191
192
193 // Add boundary contributions
194 forAll(pbm, patchi)
195 {
196 const polyPatch& pp = pbm[patchi];
197 const vectorField& Sfp = mesh_.Sf().boundaryField()[patchi];
198 const scalarField& magSfp = mesh_.magSf().boundaryField()[patchi];
199
200 if (pp.coupled())
201 {
202 forAll(pp, j)
203 {
204 const label facei = pp.start() + j;
205 const label own = cellAddr[mesh_.faceOwner()[facei]];
206 const label nbr = nbrFaceCellAddr[facei - nInternalFaces];
207 const vector nf = Sfp[j]/magSfp[j];
208
209 if ((own != -1) && (nbr == -1) && ((nf & axis) > tol))
210 {
211 area_[own] += magSfp[j];
212 n += Sfp[j];
213 }
214 }
215 }
216 else
217 {
218 forAll(pp, j)
219 {
220 const label facei = pp.start() + j;
221 const label own = cellAddr[mesh_.faceOwner()[facei]];
222 const vector nf = Sfp[j]/magSfp[j];
223
224 if ((own != -1) && ((nf & axis) > tol))
225 {
226 area_[own] += magSfp[j];
227 n += Sfp[j];
228 }
229 }
230 }
231 }
232
233 if (correct)
234 {
236 axis = n/mag(n);
237 }
238
239 if (debug)
240 {
241 volScalarField area
242 (
244 (
245 name_ + ":area",
246 mesh_.time().timeName(),
247 mesh_,
250 ),
251 mesh_,
253 );
254 UIndirectList<scalar>(area.primitiveField(), cells_) = area_;
255
256 Info<< type() << ": " << name_ << " writing field " << area.name()
257 << endl;
258
259 area.write();
260 }
261}
262
263
265{
266 // Construct the local rotor coordinate system
267 vector origin(Zero);
268 vector axis(Zero);
269 vector refDir(Zero);
270
272 geometryModeTypeNames_.get("geometryMode", coeffs_);
273
274 switch (gm)
275 {
276 case gmAuto:
277 {
278 // Determine rotation origin (cell volume weighted)
279 scalar sumV = 0.0;
280 const scalarField& V = mesh_.V();
281 const vectorField& C = mesh_.C();
282 forAll(cells_, i)
283 {
284 const label celli = cells_[i];
285 sumV += V[celli];
286 origin += V[celli]*C[celli];
287 }
288 reduce(origin, sumOp<vector>());
289 reduce(sumV, sumOp<scalar>());
290 origin /= sumV;
291
292 // Determine first radial vector
293 vector dx1(Zero);
294 scalar magR = -GREAT;
295 forAll(cells_, i)
296 {
297 const label celli = cells_[i];
298 vector test = C[celli] - origin;
299 if (mag(test) > magR)
300 {
301 dx1 = test;
302 magR = mag(test);
303 }
304 }
306 magR = mag(dx1);
307
308 // Determine second radial vector and cross to determine axis
309 forAll(cells_, i)
310 {
311 const label celli = cells_[i];
312 vector dx2 = C[celli] - origin;
313 if (mag(dx2) > 0.5*magR)
314 {
315 axis = dx1 ^ dx2;
316 if (mag(axis) > SMALL)
317 {
318 break;
319 }
320 }
321 }
323 axis.normalise();
324
325 // Correct the axis direction using a point above the rotor
326 {
327 vector pointAbove(coeffs_.get<vector>("pointAbove"));
328 vector dir = pointAbove - origin;
329 dir.normalise();
330 if ((dir & axis) < 0)
331 {
332 axis *= -1.0;
333 }
334 }
335
336 coeffs_.readEntry("refDirection", refDir);
337
338 // Set the face areas and apply correction to calculated axis
339 // e.g. if cellZone is more than a single layer in thickness
340 setFaceArea(axis, true);
341
342 break;
343 }
344 case gmSpecified:
345 {
346 coeffs_.readEntry("origin", origin);
347 coeffs_.readEntry("axis", axis);
348 coeffs_.readEntry("refDirection", refDir);
349
350 setFaceArea(axis, false);
351
352 break;
353 }
354 default:
355 {
357 << "Unknown geometryMode " << geometryModeTypeNames_[gm]
358 << ". Available geometry modes include "
359 << geometryModeTypeNames_
360 << exit(FatalError);
361 }
362 }
363
364 coordSys_ = coordSystem::cylindrical(origin, axis, refDir);
365
366 const scalar sumArea = gSum(area_);
367 const scalar diameter = Foam::sqrt(4.0*sumArea/mathematical::pi);
368 Info<< " Rotor gometry:" << nl
369 << " - disk diameter = " << diameter << nl
370 << " - disk area = " << sumArea << nl
371 << " - origin = " << coordSys_.origin() << nl
372 << " - r-axis = " << coordSys_.e1() << nl
373 << " - psi-axis = " << coordSys_.e2() << nl
374 << " - z-axis = " << coordSys_.e3() << endl;
375}
376
377
379{
380 const pointUIndList cc(mesh_.C(), cells_);
381
382 // Optional: for later transform(), invTransform()
384
385 forAll(cells_, i)
386 {
387 if (area_[i] > ROOTVSMALL)
388 {
389 // Position in (planar) rotor coordinate system
390 x_[i] = coordSys_.localPosition(cc[i]);
391
392 // Cache max radius
393 rMax_ = max(rMax_, x_[i].x());
394
395 // Swept angle relative to rDir axis [radians] in range 0 -> 2*pi
396 scalar psi = x_[i].y();
397
398 // Blade flap angle [radians]
399 scalar beta =
400 flap_.beta0 - flap_.beta1c*cos(psi) - flap_.beta2s*sin(psi);
401
402 // Determine rotation tensor to convert from planar system into the
403 // rotor cone system
404 scalar c = cos(beta);
405 scalar s = sin(beta);
406 Rcone_[i] = tensor(c, 0, -s, 0, 1, 0, s, 0, c);
407 }
408 }
409}
410
411
413(
414 const volVectorField& U
415) const
416{
417 switch (inletFlow_)
418 {
419 case ifFixed:
420 case ifSurfaceNormal:
421 {
422 return tmp<vectorField>::New(mesh_.nCells(), inletVelocity_);
423
424 break;
425 }
426 case ifLocal:
427 {
428 return U.primitiveField();
429
430 break;
431 }
432 default:
433 {
435 << "Unknown inlet flow specification" << abort(FatalError);
436 }
437 }
438
439 return tmp<vectorField>::New(mesh_.nCells(), Zero);
440}
441
442
443// * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * * //
444
446(
447 const word& name,
448 const word& modelType,
449 const dictionary& dict,
450 const fvMesh& mesh
451
452)
453:
454 fv::cellSetOption(name, modelType, dict, mesh),
455 rhoRef_(1.0),
456 omega_(0.0),
457 nBlades_(0),
458 inletFlow_(ifLocal),
459 inletVelocity_(Zero),
460 tipEffect_(1.0),
461 flap_(),
462 x_(cells_.size(), Zero),
463 Rcone_(cells_.size(), I),
464 area_(cells_.size(), Zero),
465 coordSys_(),
466 rMax_(0.0),
467 trim_(trimModel::New(*this, coeffs_)),
468 blade_(coeffs_.subDict("blade")),
469 profiles_(coeffs_.subDict("profiles"))
470{
471 read(dict);
472}
473
474
475// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
476
478(
479 fvMatrix<vector>& eqn,
480 const label fieldi
481)
482{
483 volVectorField force
484 (
486 (
487 name_ + ":rotorForce",
488 mesh_.time().timeName(),
489 mesh_
490 ),
491 mesh_,
493 );
494
495 // Read the reference density for incompressible flow
496 coeffs_.readEntry("rhoRef", rhoRef_);
497
498 const vectorField Uin(inflowVelocity(eqn.psi()));
499 trim_->correct(Uin, force);
500 calculate(geometricOneField(), Uin, trim_->thetag(), force);
501
502 // Add source to rhs of eqn
503 eqn -= force;
504
505 if (mesh_.time().writeTime())
506 {
507 force.write();
508 }
509}
510
511
513(
514 const volScalarField& rho,
515 fvMatrix<vector>& eqn,
516 const label fieldi
517)
518{
519 volVectorField force
520 (
522 (
523 name_ + ":rotorForce",
524 mesh_.time().timeName(),
525 mesh_
526 ),
527 mesh_,
529 );
530
531 const vectorField Uin(inflowVelocity(eqn.psi()));
532 trim_->correct(rho, Uin, force);
533 calculate(rho, Uin, trim_->thetag(), force);
534
535 // Add source to rhs of eqn
536 eqn -= force;
537
538 if (mesh_.time().writeTime())
539 {
540 force.write();
541 }
542}
543
544
546{
548 {
549 coeffs_.readEntry("fields", fieldNames_);
551
552 // Read coordinate system/geometry invariant properties
553 omega_ = rpmToRads(coeffs_.get<scalar>("rpm"));
554
555 coeffs_.readEntry("nBlades", nBlades_);
556
557 inletFlowTypeNames_.readEntry("inletFlowType", coeffs_, inletFlow_);
558
559 coeffs_.readEntry("tipEffect", tipEffect_);
560
561 const dictionary& flapCoeffs(coeffs_.subDict("flapCoeffs"));
562 flap_.beta0 = degToRad(flapCoeffs.get<scalar>("beta0"));
563 flap_.beta1c = degToRad(flapCoeffs.get<scalar>("beta1c"));
564 flap_.beta2s = degToRad(flapCoeffs.get<scalar>("beta2s"));
565
566
567 // Create coordinate system
568 createCoordinateSystem();
569
570 // Read coordinate system dependent properties
571 checkData();
572
573 constructGeometry();
574
575 trim_->read(coeffs_);
576
577 if (debug)
578 {
579 writeField("thetag", trim_->thetag()(), true);
580 writeField("faceArea", area_, true);
581 }
582
583 return true;
584 }
585
586 return false;
587}
588
589
590// ************************************************************************* //
label n
Macros for easy insertion into run-time selection tables.
#define addToRunTimeSelectionTable(baseType, thisType, argNames)
Add to construction table with typeName as the key.
Graphite solid properties.
Definition: C.H:53
C()
Construct null.
Definition: C.C:43
Enum is a wrapper around a list of names/values that represent particular enumeration (or int) values...
Definition: Enum.H:61
Defines the attributes of an object for which implicit objectRegistry management is supported,...
Definition: IOobject.H:170
virtual bool read()
Re-read model coefficients if they have changed.
static autoPtr< Time > New()
Construct (dummy) Time - no functionObjects or libraries.
Definition: Time.C:717
A List with indirect addressing. Like IndirectList but does not store addressing.
Definition: IndirectList.H:79
Vector< Cmpt > & normalise(const scalar tol=ROOTVSMALL)
Inplace normalise the vector by its magnitude.
Definition: VectorI.H:123
const List< label > & profileID() const
Return const access to the profile ID list.
Definition: bladeModel.C:146
const List< word > & profileName() const
Return const access to the profile name list.
Definition: bladeModel.C:140
A cylindrical coordinate system (r-theta-z). The coordinate system angle theta is always in radians.
Definition: cylindricalCS.H:74
virtual const vector e3() const
The local Cartesian z-axis in global coordinates.
A list of keyword definitions, which are a keyword followed by a number of values (eg,...
Definition: dictionary.H:126
T get(const word &keyword, enum keyType::option matchOpt=keyType::REGEX) const
bool readEntry(const word &keyword, T &val, enum keyType::option matchOpt=keyType::REGEX, bool mandatory=true) const
A special matrix type and solver, designed for finite volume solutions of scalar equations....
Definition: fvMatrix.H:121
const dimensionSet & dimensions() const noexcept
Definition: fvMatrix.H:453
const GeometricField< Type, fvPatchField, volMesh > & psi(const label i=0) const
Return psi.
Definition: fvMatrix.H:412
Mesh data needed to do the Finite Volume discretisation.
Definition: fvMesh.H:91
Intermediate abstract class for handling cell-set options for the derived fvOptions.
static const Enum< selectionModeType > selectionModeTypeNames_
List of selection mode type names.
selectionModeType selectionMode() const noexcept
Return the cell selection mode.
Base abstract class for handling finite volume options (i.e. fvOption).
Definition: fvOption.H:127
dictionary coeffs_
Dictionary containing source coefficients.
Definition: fvOption.H:145
void resetApplied()
Resize/reset applied flag list for all fieldNames_ entries.
Definition: fvOption.C:48
Applies cell-based momentum sources on velocity (i.e. U) within a specified cylindrical region to app...
static const Enum< geometryModeType > geometryModeTypeNames_
Names for geometryModeType.
void setFaceArea(vector &axis, const bool correct)
Set the face areas per cell, and optionally correct the rotor axis.
void checkData()
Check data.
inletFlowType inletFlow_
Inlet flow type.
void constructGeometry()
Construct geometry.
coordSystem::cylindrical coordSys_
Rotor local cylindrical coordinate system (r-theta-z)
virtual bool read(const dictionary &dict)
Read source dictionary.
bladeModel blade_
Blade data.
tmp< vectorField > inflowVelocity(const volVectorField &U) const
Return the inlet flow field.
void createCoordinateSystem()
Create the coordinate system.
vector inletVelocity_
Inlet velocity for specified inflow.
inletFlowType
Options for the inlet flow type specification.
virtual void addSup(fvMatrix< vector > &eqn, const label fieldi)
Add explicit contribution to momentum equation.
profileModelList profiles_
Profile data.
static const Enum< inletFlowType > inletFlowTypeNames_
Names for inletFlowType.
geometryModeType
Options for the geometry type specification.
A class representing the concept of a GeometricField of 1 used to avoid unnecessary manipulations for...
A polyBoundaryMesh is a polyPatch list with additional search methods and registered IO.
A patch is a list of labels that address the faces in the global face list.
Definition: polyPatch.H:75
virtual bool coupled() const
Return true if this patch is geometrically coupled (i.e. faces and.
Definition: polyPatch.H:380
label start() const
Return start label of this patch in the polyMesh face list.
Definition: polyPatch.H:364
void connectBlades(const List< word > &names, List< label > &addr) const
Set blade->profile addressing.
virtual bool write(const bool valid=true) const
Write using setting from DB.
static void swapBoundaryFaceList(const polyMesh &mesh, UList< T > &faceValues)
Swap coupled boundary face values. Uses eqOp.
Definition: syncTools.H:445
A class for managing temporary objects.
Definition: tmp.H:65
Base class for trim models for handling blade characteristics and thrust-torque relations.
Definition: trimModel.H:112
A class for handling words, derived from Foam::string.
Definition: word.H:68
#define defineTypeNameAndDebug(Type, DebugSwitch)
Define the typeName and debug information.
Definition: className.H:121
U
Definition: pEqn.H:72
thermo correct()
const volScalarField & psi
dynamicFvMesh & mesh
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:453
A special matrix type and solver, designed for finite volume solutions of scalar equations.
gmvFile<< "tracers "<< particles.size()<< nl;for(const passiveParticle &p :particles){ gmvFile<< p.position().x()<< " ";}gmvFile<< nl;for(const passiveParticle &p :particles){ gmvFile<< p.position().y()<< " ";}gmvFile<< nl;for(const passiveParticle &p :particles){ gmvFile<< p.position().z()<< " ";}gmvFile<< nl;forAll(lagrangianScalarNames, i){ word name=lagrangianScalarNames[i];IOField< scalar > s(IOobject(name, runTime.timeName(), cloud::prefix, mesh, IOobject::MUST_READ, IOobject::NO_WRITE))
constexpr scalar pi(M_PI)
Different types of constants.
Namespace for OpenFOAM.
label max(const labelHashSet &set, label maxValue=labelMin)
Find the max value in labelHashSet, optionally limited by second argument.
Definition: hashSets.C:47
Type gSum(const FieldField< Field, Type > &f)
labelList identity(const label len, label start=0)
Return an identity map of the given length with (map[i] == i)
Definition: labelList.C:38
const dimensionSet dimArea(sqr(dimLength))
Definition: dimensionSets.H:59
dimensionedScalar sin(const dimensionedScalar &ds)
messageStream Info
Information stream (stdout output on master, null elsewhere)
static const Identity< scalar > I
Definition: Identity.H:94
Tensor< scalar > tensor
Definition: symmTensor.H:61
fileName::Type type(const fileName &name, const bool followLink=true)
Return the file type: DIRECTORY or FILE, normally following symbolic links.
Definition: MSwindows.C:598
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:372
constexpr scalar rpmToRads() noexcept
Multiplication factor for revolutions/minute to radians/sec.
constexpr scalar degToRad() noexcept
Multiplication factor for degrees to radians conversion.
dimensionedScalar sqrt(const dimensionedScalar &ds)
dimensioned< typename typeOfMag< Type >::type > mag(const dimensioned< Type > &dt)
void reduce(const List< UPstream::commsStruct > &comms, T &value, const BinaryOp &bop, const int tag, const label comm)
errorManip< error > abort(error &err)
Definition: errorManip.H:144
static constexpr const zero Zero
Global zero (0)
Definition: zero.H:131
tmp< DimensionedField< TypeR, GeoMesh > > New(const tmp< DimensionedField< TypeR, GeoMesh > > &tdf1, const word &name, const dimensionSet &dimensions)
Global function forwards to reuseTmpDimensionedField::New.
error FatalError
label checkData(const fvMesh &mesh, const instantList &timeDirs, wordList &objectNames)
Check if fields are good to use (available at all times)
const dimensionSet dimVolume(pow3(dimLength))
Definition: dimensionSets.H:60
word name(const expressions::valueTypeCode typeCode)
A word representation of a valueTypeCode. Empty for INVALID.
Definition: exprTraits.C:59
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:130
UIndirectList< label > labelUIndList
UIndirectList of labels.
Definition: IndirectList.H:68
dimensionedScalar cos(const dimensionedScalar &ds)
constexpr char nl
The newline '\n' character (0x0a)
Definition: Ostream.H:53
labelList fv(nPoints)
dictionary dict
dimensionedScalar beta("beta", dimless/dimTemperature, laminarTransport)
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:333
Unit conversion functions.