43 const scalar& pi = Foam::constant::mathematical::pi;
45 scalar t =
mesh_.time().timeOutputValue();
59 forAll(fvSourceSubDict.toc(), idxI)
62 word lineName = fvSourceSubDict.toc()[idxI];
65 dictionary lineSubDict = fvSourceSubDict.subDict(lineName);
68 actuatorLinePars[lineName][0],
69 actuatorLinePars[lineName][1],
70 actuatorLinePars[lineName][2]};
73 actuatorLinePars[lineName][3],
74 actuatorLinePars[lineName][4],
75 actuatorLinePars[lineName][5]};
76 direction = direction / mag(direction);
79 actuatorLinePars[lineName][6],
80 actuatorLinePars[lineName][7],
81 actuatorLinePars[lineName][8]};
82 initial = initial / mag(initial);
83 if (fabs(direction & initial) > 1.0e-10)
85 FatalErrorIn(
" ") <<
"direction and initial need to be orthogonal!" << abort(FatalError);
88 word rotDir = lineSubDict.getWord(
"rotDir");
90 scalar innerRadius = actuatorLinePars[lineName][9];
91 scalar outerRadius = actuatorLinePars[lineName][10];
93 scalar rpm = actuatorLinePars[lineName][15];
94 scalar radPerS = rpm / 60.0 * 2.0 * pi;
96 scalar eps = actuatorLinePars[lineName][17];
98 scalar scale = actuatorLinePars[lineName][11];
100 scalar phase = actuatorLinePars[lineName][16];
102 label nBlades = lineSubDict.get<label>(
"nBlades");
103 scalar POD = actuatorLinePars[lineName][12];
104 scalar expM = actuatorLinePars[lineName][13];
105 scalar expN = actuatorLinePars[lineName][14];
110 scalar epsRStar = eps / (outerRadius - innerRadius);
111 scalar rStarMin = epsRStar;
112 scalar rStarMax = 1.0 - epsRStar;
113 scalar fRMin = pow(rStarMin, expM) * pow(1.0 - rStarMin, expN);
114 scalar fRMax = pow(rStarMax, expM) * pow(1.0 - rStarMax, expN);
116 scalar thrustTotal = 0.0;
117 scalar torqueTotal = 0.0;
121 vector cellC =
mesh_.C()[cellI];
123 vector cellC2AVec = cellC - center;
125 tensor cellC2AVecE(tensor::zero);
126 cellC2AVecE.xx() = cellC2AVec.x();
127 cellC2AVecE.yy() = cellC2AVec.y();
128 cellC2AVecE.zz() = cellC2AVec.z();
131 vector cellC2AVecA = cellC2AVecE & direction;
133 vector cellC2AVecR = cellC2AVec - cellC2AVecA;
136 vector cellC2AVecC(vector::zero);
137 if (rotDir ==
"left")
140 cellC2AVecC = cellC2AVecR ^ direction;
142 else if (rotDir ==
"right")
145 cellC2AVecC = direction ^ cellC2AVecR;
149 FatalErrorIn(
" ") <<
"rotDir not valid" << abort(FatalError);
152 scalar cellC2AVecRLen = mag(cellC2AVecR);
154 scalar cellC2AVecCLen = mag(cellC2AVecC);
156 scalar cellC2AVecALen = mag(cellC2AVecA);
158 vector cellC2AVecCNorm = cellC2AVecC / (cellC2AVecCLen + SMALL);
160 scalar etaAxial = exp(-sqr(cellC2AVecALen / eps));
162 scalar etaTheta = 0.0;
163 for (label bb = 0; bb < nBlades; bb++)
165 scalar thetaBlade = bb * 2.0 * pi / nBlades + radPerS * t + phase;
170 ||
mesh_.time().timeIndex() == 1)
172 scalar twoPi = 2.0 * pi;
173 Info <<
"blade " << bb <<
" theta: "
175 << fmod(thetaBlade, twoPi) * 180.0 / pi
182 vector rotatedVec = vector::zero;
183 if (rotDir ==
"right")
185 rotatedVec = initial * cos(thetaBlade)
186 + (direction ^ initial) * sin(thetaBlade);
188 else if (rotDir ==
"left")
190 rotatedVec = initial * cos(thetaBlade)
191 + (initial ^ direction) * sin(thetaBlade);
195 FatalErrorIn(
" ") <<
"rotDir not valid" << abort(FatalError);
198 rotatedVec *= cellC2AVecRLen;
200 scalar dS_Theta = mag(cellC2AVecR - rotatedVec);
202 etaTheta += exp(-sqr(dS_Theta / eps));
206 scalar rPrime = cellC2AVecRLen / outerRadius;
207 scalar rPrimeHub = innerRadius / outerRadius;
209 scalar rStar = (rPrime - rPrimeHub) / (1.0 - rPrimeHub);
212 if (rStar < rStarMin)
214 scalar dR2 = (rStar - rStarMin) * (rStar - rStarMin);
215 fAxial = fRMin * exp(-dR2 / epsRStar / epsRStar);
217 else if (rStar >= rStarMin && rStar <= rStarMax)
219 fAxial = pow(rStar, expM) * pow(1.0 - rStar, expN);
223 scalar dR2 = (rStar - rStarMax) * (rStar - rStarMax);
224 fAxial = fRMax * exp(-dR2 / epsRStar / epsRStar);
229 scalar fCirc = fAxial * POD / pi / (rPrime + 0.01 * eps / outerRadius);
231 vector sourceVec = (fAxial * direction + fCirc * cellC2AVecCNorm);
233 fvSource[cellI] += scale * etaAxial * etaTheta * sourceVec;
235 thrustTotal += scale * etaAxial * etaTheta * fAxial *
mesh_.V()[cellI];
236 torqueTotal += scale * etaAxial * etaTheta * fCirc *
mesh_.V()[cellI];
238 reduce(thrustTotal, sumOp<scalar>());
239 reduce(torqueTotal, sumOp<scalar>());
244 Info <<
"Actuator line source: " << lineName << endl;
245 Info <<
"Total thrust source: " << thrustTotal << endl;
246 Info <<
"Total torque source: " << torqueTotal << endl;
251 fvSource.correctBoundaryConditions();
265 forAll(fvSourceSubDict.toc(), idxI)
267 word diskName = fvSourceSubDict.toc()[idxI];
269 dictionary lineSubDict = fvSourceSubDict.subDict(diskName);
270 word type = lineSubDict.getWord(
"type");
272 if (type ==
"actuatorLine")
276 scalarList centerList;
277 lineSubDict.readEntry<scalarList>(
"center", centerList);
280 lineSubDict.readEntry<scalarList>(
"direction", dirList);
283 lineSubDict.readEntry<scalarList>(
"initial", initList);
286 scalarList dvList(18);
287 dvList[0] = centerList[0];
288 dvList[1] = centerList[1];
289 dvList[2] = centerList[2];
290 dvList[3] = dirList[0];
291 dvList[4] = dirList[1];
292 dvList[5] = dirList[2];
293 dvList[6] = initList[0];
294 dvList[7] = initList[1];
295 dvList[8] = initList[2];
296 dvList[9] = lineSubDict.getScalar(
"innerRadius");
297 dvList[10] = lineSubDict.getScalar(
"outerRadius");
298 dvList[11] = lineSubDict.getScalar(
"scale");
299 dvList[12] = lineSubDict.getScalar(
"POD");
300 dvList[13] = lineSubDict.getScalar(
"expM");
301 dvList[14] = lineSubDict.getScalar(
"expN");
302 dvList[15] = lineSubDict.getScalar(
"rpm");
303 dvList[16] = lineSubDict.getScalar(
"phase");
304 dvList[17] = lineSubDict.getScalar(
"eps");