42 const scalar& pi = Foam::constant::mathematical::pi;
44 scalar t =
mesh_.time().timeOutputValue();
54 forAll(fvSourceSubDict.toc(), idxI)
57 word lineName = fvSourceSubDict.toc()[idxI];
60 dictionary lineSubDict = fvSourceSubDict.subDict(lineName);
65 lineSubDict.readEntry<scalarList>(
"center", center1);
66 vector center = {center1[0], center1[1], center1[2]};
68 scalarList direction1;
69 lineSubDict.readEntry<scalarList>(
"direction", direction1);
70 vector direction = {direction1[0], direction1[1], direction1[2]};
71 direction = direction / mag(direction);
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)
79 FatalErrorIn(
" ") <<
"direction and initial need to be orthogonal!" << abort(FatalError);
82 word rotDir = lineSubDict.getWord(
"rotDir");
84 scalar innerRadius = lineSubDict.get<scalar>(
"innerRadius");
85 scalar outerRadius = lineSubDict.get<scalar>(
"outerRadius");
87 scalar rpm = lineSubDict.get<scalar>(
"rpm");
88 scalar radPerS = rpm / 60.0 * 2.0 * pi;
90 scalar eps = lineSubDict.get<scalar>(
"eps");
92 scalar scale = lineSubDict.get<scalar>(
"scale");
94 scalar phase = lineSubDict.get<scalar>(
"phase");
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");
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);
110 scalar thrustTotal = 0.0;
111 scalar torqueTotal = 0.0;
115 vector cellC =
mesh_.C()[cellI];
117 vector cellC2AVec = cellC - center;
119 tensor cellC2AVecE(tensor::zero);
120 cellC2AVecE.xx() = cellC2AVec.x();
121 cellC2AVecE.yy() = cellC2AVec.y();
122 cellC2AVecE.zz() = cellC2AVec.z();
125 vector cellC2AVecA = cellC2AVecE & direction;
127 vector cellC2AVecR = cellC2AVec - cellC2AVecA;
130 vector cellC2AVecC(vector::zero);
131 if (rotDir ==
"left")
134 cellC2AVecC = cellC2AVecR ^ direction;
136 else if (rotDir ==
"right")
139 cellC2AVecC = direction ^ cellC2AVecR;
143 FatalErrorIn(
" ") <<
"rotDir not valid" << abort(FatalError);
146 scalar cellC2AVecRLen = mag(cellC2AVecR);
148 scalar cellC2AVecCLen = mag(cellC2AVecC);
150 scalar cellC2AVecALen = mag(cellC2AVecA);
152 vector cellC2AVecCNorm = cellC2AVecC / (cellC2AVecCLen + SMALL);
154 scalar etaAxial = exp(-sqr(cellC2AVecALen / eps));
156 scalar etaTheta = 0.0;
157 for (label bb = 0; bb < nBlades; bb++)
159 scalar thetaBlade = bb * 2.0 * pi / nBlades + radPerS * t + phase;
165 ||
mesh_.time().timeIndex() == 1)
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()
172 << fmod(thetaBlade, twoPi) * 180.0 / pi
180 vector rotatedVec = vector::zero;
181 if (rotDir ==
"right")
183 rotatedVec = initial * cos(thetaBlade)
184 + (direction ^ initial) * sin(thetaBlade);
186 else if (rotDir ==
"left")
188 rotatedVec = initial * cos(thetaBlade)
189 + (initial ^ direction) * sin(thetaBlade);
193 FatalErrorIn(
" ") <<
"rotDir not valid" << abort(FatalError);
196 rotatedVec *= cellC2AVecRLen;
198 scalar dS_Theta = mag(cellC2AVecR - rotatedVec);
200 etaTheta += exp(-sqr(dS_Theta / eps));
204 scalar rPrime = cellC2AVecRLen / outerRadius;
205 scalar rPrimeHub = innerRadius / outerRadius;
207 scalar rStar = (rPrime - rPrimeHub) / (1.0 - rPrimeHub);
210 if (rStar < rStarMin)
212 scalar dR2 = (rStar - rStarMin) * (rStar - rStarMin);
213 fAxial = fRMin * exp(-dR2 / epsRStar / epsRStar);
215 else if (rStar >= rStarMin && rStar <= rStarMax)
217 fAxial = pow(rStar, expM) * pow(1.0 - rStar, expN);
221 scalar dR2 = (rStar - rStarMax) * (rStar - rStarMax);
222 fAxial = fRMax * exp(-dR2 / epsRStar / epsRStar);
227 scalar fCirc = fAxial * POD / pi / (rPrime + 0.01 * eps / outerRadius);
229 vector sourceVec = (fAxial * direction + fCirc * cellC2AVecCNorm);
231 fvSource[cellI] += scale * etaAxial * etaTheta * sourceVec;
233 thrustTotal += scale * etaAxial * etaTheta * fAxial *
mesh_.V()[cellI];
234 torqueTotal += scale * etaAxial * etaTheta * fCirc *
mesh_.V()[cellI];
236 reduce(thrustTotal, sumOp<scalar>());
237 reduce(torqueTotal, sumOp<scalar>());
243 Info <<
"Actuator line source: " << lineName << endl;
244 Info <<
"Total thrust source: " << thrustTotal << endl;
245 Info <<
"Total torque source: " << torqueTotal << endl;
250 fvSource.correctBoundaryConditions();