DAFvSourceActuatorLine.C
Go to the documentation of this file.
1 /*---------------------------------------------------------------------------*\
2 
3  DAFoam : Discrete Adjoint with OpenFOAM
4  Version : v3
5 
6 \*---------------------------------------------------------------------------*/
7 
9 
10 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
11 
12 namespace Foam
13 {
14 
15 defineTypeNameAndDebug(DAFvSourceActuatorLine, 0);
16 addToRunTimeSelectionTable(DAFvSource, DAFvSourceActuatorLine, dictionary);
17 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
18 
20  const word modelType,
21  const fvMesh& mesh,
22  const DAOption& daOption,
23  const DAModel& daModel,
24  const DAIndex& daIndex)
25  : DAFvSource(modelType, mesh, daOption, daModel, daIndex)
26 {
27  printIntervalUnsteady_ = daOption.getOption<label>("printIntervalUnsteady");
28 }
29 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
30 
32 {
33  /*
34  Description:
35  Compute the actuator line source term.
36  Reference: Stokkermans et al. Validation and comparison of RANS propeller
37  modeling methods for tip-mounted applications
38  NOTE: rotDir = right means propeller rotates clockwise viewed from
39  the tail of the aircraft looking forward
40  */
41 
42  const scalar& pi = Foam::constant::mathematical::pi;
43 
44  scalar t = mesh_.time().timeOutputValue();
45 
46  forAll(fvSource, idxI)
47  {
48  fvSource[idxI] = vector::zero;
49  }
50 
51  dictionary fvSourceSubDict = daOption_.getAllOptions().subDict("fvSource");
52 
53  // loop over all the cell indices for all actuator lines
54  forAll(fvSourceSubDict.toc(), idxI)
55  {
56  // name of this line model
57  word lineName = fvSourceSubDict.toc()[idxI];
58 
59  // sub dictionary with all parameters for this disk
60  dictionary lineSubDict = fvSourceSubDict.subDict(lineName);
61 
62  // now read in all parameters for this actuator line
63  // center of the actuator line
64  scalarList center1;
65  lineSubDict.readEntry<scalarList>("center", center1);
66  vector center = {center1[0], center1[1], center1[2]};
67  // thrust direction
68  scalarList direction1;
69  lineSubDict.readEntry<scalarList>("direction", direction1);
70  vector direction = {direction1[0], direction1[1], direction1[2]};
71  direction = direction / mag(direction);
72  // initial vector for the actuator line
73  scalarList initial1;
74  lineSubDict.readEntry<scalarList>("initial", initial1);
75  vector initial = {initial1[0], initial1[1], initial1[2]};
76  initial = initial / mag(initial);
77  if (fabs(direction & initial) > 1.0e-10)
78  {
79  FatalErrorIn(" ") << "direction and initial need to be orthogonal!" << abort(FatalError);
80  }
81  // rotation direction, can be either right or left
82  word rotDir = lineSubDict.getWord("rotDir");
83  // inner and outer radius of the lines
84  scalar innerRadius = lineSubDict.get<scalar>("innerRadius");
85  scalar outerRadius = lineSubDict.get<scalar>("outerRadius");
86  // rotation speed in rpm
87  scalar rpm = lineSubDict.get<scalar>("rpm");
88  scalar radPerS = rpm / 60.0 * 2.0 * pi;
89  // smooth factor which should be of grid size
90  scalar eps = lineSubDict.get<scalar>("eps");
91  // scaling factor to ensure a desired integrated thrust
92  scalar scale = lineSubDict.get<scalar>("scale");
93  // phase (rad) of the rotation positive value means rotates ahead of time for phase rad
94  scalar phase = lineSubDict.get<scalar>("phase");
95  // number of blades for this line model
96  label nBlades = lineSubDict.get<label>("nBlades");
97  scalar POD = lineSubDict.getScalar("POD");
98  scalar expM = lineSubDict.getScalar("expM");
99  scalar expN = lineSubDict.getScalar("expN");
100  // Now we need to compute normalized eps in the radial direction, i.e. epsRStar this is because
101  // we need to smooth the radial distribution of the thrust, here the radial location is
102  // normalized as rStar = (r - rInner) / (rOuter - rInner), so to make epsRStar consistent with this
103  // we need to normalize eps with the demoninator of rStar, i.e. Outer - rInner
104  scalar epsRStar = eps / (outerRadius - innerRadius);
105  scalar rStarMin = epsRStar;
106  scalar rStarMax = 1.0 - epsRStar;
107  scalar fRMin = pow(rStarMin, expM) * pow(1.0 - rStarMin, expN);
108  scalar fRMax = pow(rStarMax, expM) * pow(1.0 - rStarMax, expN);
109 
110  scalar thrustTotal = 0.0;
111  scalar torqueTotal = 0.0;
112  forAll(mesh_.C(), cellI)
113  {
114  // the cell center coordinates of this cellI
115  vector cellC = mesh_.C()[cellI];
116  // cell center to disk center vector
117  vector cellC2AVec = cellC - center;
118  // tmp tensor for calculating the axial/radial components of cellC2AVec
119  tensor cellC2AVecE(tensor::zero);
120  cellC2AVecE.xx() = cellC2AVec.x();
121  cellC2AVecE.yy() = cellC2AVec.y();
122  cellC2AVecE.zz() = cellC2AVec.z();
123  // now we need to decompose cellC2AVec into axial and radial components
124  // the axial component of cellC2AVec vector
125  vector cellC2AVecA = cellC2AVecE & direction;
126  // the radial component of cellC2AVec vector
127  vector cellC2AVecR = cellC2AVec - cellC2AVecA;
128  // now we can use the cross product to compute the tangential
129  // (circ) direction of cellI
130  vector cellC2AVecC(vector::zero);
131  if (rotDir == "left")
132  {
133  // propeller rotates counter-clockwise viewed from the tail of the aircraft looking forward
134  cellC2AVecC = cellC2AVecR ^ direction; // circ
135  }
136  else if (rotDir == "right")
137  {
138  // propeller rotates clockwise viewed from the tail of the aircraft looking forward
139  cellC2AVecC = direction ^ cellC2AVecR; // circ
140  }
141  else
142  {
143  FatalErrorIn(" ") << "rotDir not valid" << abort(FatalError);
144  }
145  // the magnitude of radial component of cellC2AVecR
146  scalar cellC2AVecRLen = mag(cellC2AVecR);
147  // the magnitude of tangential component of cellC2AVecR
148  scalar cellC2AVecCLen = mag(cellC2AVecC);
149  // the magnitude of axial component of cellC2AVecA
150  scalar cellC2AVecALen = mag(cellC2AVecA);
151  // the normalized cellC2AVecC (tangential) vector
152  vector cellC2AVecCNorm = cellC2AVecC / (cellC2AVecCLen + SMALL);
153  // smooth coefficient in the axial direction
154  scalar etaAxial = exp(-sqr(cellC2AVecALen / eps));
155 
156  scalar etaTheta = 0.0;
157  for (label bb = 0; bb < nBlades; bb++)
158  {
159  scalar thetaBlade = bb * 2.0 * pi / nBlades + radPerS * t + phase;
160  if (daOption_.getOption<word>("runStatus") == "solvePrimal")
161  {
162  if (cellI == 0)
163  {
164  if (mesh_.time().timeIndex() % printIntervalUnsteady_ == 0
165  || mesh_.time().timeIndex() == 1)
166  {
167  scalar twoPi = 2.0 * pi;
168  Info << "blade " << bb << " theta: "
169 #if defined(CODI_AD_FORWARD) || defined(CODI_AD_REVERSE)
170  << fmod(thetaBlade.getValue(), twoPi.getValue()) * 180.0 / pi.getValue()
171 #else
172  << fmod(thetaBlade, twoPi) * 180.0 / pi
173 #endif
174  << " deg" << endl;
175  }
176  }
177  }
178  // compute the rotated vector of initial by thetaBlade degree
179  // We use a simplified version of Rodrigues rotation formulation
180  vector rotatedVec = vector::zero;
181  if (rotDir == "right")
182  {
183  rotatedVec = initial * cos(thetaBlade)
184  + (direction ^ initial) * sin(thetaBlade);
185  }
186  else if (rotDir == "left")
187  {
188  rotatedVec = initial * cos(thetaBlade)
189  + (initial ^ direction) * sin(thetaBlade);
190  }
191  else
192  {
193  FatalErrorIn(" ") << "rotDir not valid" << abort(FatalError);
194  }
195  // scale the rotated vector to have the same length as cellC2AVecR
196  rotatedVec *= cellC2AVecRLen;
197  // now we can compute the distance between the cellC2AVecR and the rotatedVec
198  scalar dS_Theta = mag(cellC2AVecR - rotatedVec);
199  // smooth coefficient in the theta direction
200  etaTheta += exp(-sqr(dS_Theta / eps));
201  }
202 
203  // now we can use Hoekstra's formulation to compute radial thrust distribution
204  scalar rPrime = cellC2AVecRLen / outerRadius;
205  scalar rPrimeHub = innerRadius / outerRadius;
206  // rStar is normalized radial location
207  scalar rStar = (rPrime - rPrimeHub) / (1.0 - rPrimeHub);
208 
209  scalar fAxial = 0.0;
210  if (rStar < rStarMin)
211  {
212  scalar dR2 = (rStar - rStarMin) * (rStar - rStarMin);
213  fAxial = fRMin * exp(-dR2 / epsRStar / epsRStar);
214  }
215  else if (rStar >= rStarMin && rStar <= rStarMax)
216  {
217  fAxial = pow(rStar, expM) * pow(1.0 - rStar, expN);
218  }
219  else
220  {
221  scalar dR2 = (rStar - rStarMax) * (rStar - rStarMax);
222  fAxial = fRMax * exp(-dR2 / epsRStar / epsRStar);
223  }
224  // we use Hoekstra's method to calculate the fCirc based on fAxial
225  // here we add 0.01*eps/outerRadius to avoid diving a zero rPrime
226  // this might happen if a cell center is very close to actuator center
227  scalar fCirc = fAxial * POD / pi / (rPrime + 0.01 * eps / outerRadius);
228 
229  vector sourceVec = (fAxial * direction + fCirc * cellC2AVecCNorm);
230 
231  fvSource[cellI] += scale * etaAxial * etaTheta * sourceVec;
232 
233  thrustTotal += scale * etaAxial * etaTheta * fAxial * mesh_.V()[cellI];
234  torqueTotal += scale * etaAxial * etaTheta * fCirc * mesh_.V()[cellI];
235  }
236  reduce(thrustTotal, sumOp<scalar>());
237  reduce(torqueTotal, sumOp<scalar>());
238 
239  if (daOption_.getOption<word>("runStatus") == "solvePrimal")
240  {
241  if (mesh_.time().timeIndex() % printIntervalUnsteady_ == 0 || mesh_.time().timeIndex() == 1)
242  {
243  Info << "Actuator line source: " << lineName << endl;
244  Info << "Total thrust source: " << thrustTotal << endl;
245  Info << "Total torque source: " << torqueTotal << endl;
246  }
247  }
248  }
249 
250  fvSource.correctBoundaryConditions();
251 }
252 
253 } // End namespace Foam
254 
255 // ************************************************************************* //
Foam::DAFvSource
Definition: DAFvSource.H:34
Foam::DAFvSourceActuatorLine::DAFvSourceActuatorLine
DAFvSourceActuatorLine(const word modelType, const fvMesh &mesh, const DAOption &daOption, const DAModel &daModel, const DAIndex &daIndex)
Definition: DAFvSourceActuatorLine.C:19
forAll
forAll(pseudoP.boundaryField(), patchI)
Definition: solvePseudoPEqn.H:10
DAFvSourceActuatorLine.H
Foam::DAFvSource::daOption_
const DAOption & daOption_
DAOption object.
Definition: DAFvSource.H:53
Foam::DAOption
Definition: DAOption.H:29
daOption
DAOption daOption(mesh, pyOptions_)
Foam::DAOption::getOption
classType getOption(const word key) const
get an option from subDict and key
Definition: DAOption.H:92
fvSource
volScalarField & fvSource
Definition: createRefsHeatTransfer.H:7
Foam::DAOption::getAllOptions
const dictionary & getAllOptions() const
return a reference of allOptions_ dictionary
Definition: DAOption.H:56
mesh
fvMesh & mesh
Definition: createRefsHeatTransfer.H:4
Foam::DAFvSourceActuatorLine::calcFvSource
virtual void calcFvSource(volVectorField &fvSource)
compute the FvSource term
Definition: DAFvSourceActuatorLine.C:31
Foam::DAIndex
Definition: DAIndex.H:32
Foam::DAModel
Definition: DAModel.H:59
Foam
Definition: multiFreqScalarFvPatchField.C:144
Foam::defineTypeNameAndDebug
defineTypeNameAndDebug(DAFvSource, 0)
Foam::DAFvSource::mesh_
const fvMesh & mesh_
fvMesh
Definition: DAFvSource.H:50
Foam::addToRunTimeSelectionTable
addToRunTimeSelectionTable(DAFvSource, DAFvSourceActuatorDisk, dictionary)
daModel
DAModel daModel(mesh, daOption)
Foam::DAFvSourceActuatorLine::printIntervalUnsteady_
label printIntervalUnsteady_
print interval
Definition: DAFvSourceActuatorLine.H:33
daIndex
DAIndex daIndex(mesh, daOption, daModel)