VM2D 1.14
Vortex methods for 2D flows simulation
Loading...
Searching...
No Matches
Airfoil2D.cpp
Go to the documentation of this file.
1/*--------------------------------*- VM2D -*-----------------*---------------*\
2| ## ## ## ## #### ##### | | Version 1.14 |
3| ## ## ### ### ## ## ## ## | VM2D: Vortex Method | 2026/03/06 |
4| ## ## ## # ## ## ## ## | for 2D Flow Simulation *----------------*
5| #### ## ## ## ## ## | Open Source Code |
6| ## ## ## ###### ##### | https://www.github.com/vortexmethods/VM2D |
7| |
8| Copyright (C) 2017-2026 I. Marchevsky, K. Sokol, E. Ryatina, A. Kolganova |
9*-----------------------------------------------------------------------------*
10| File name: Airfoil2D.cpp |
11| Info: Source code of VM2D |
12| |
13| This file is part of VM2D. |
14| VM2D 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| VM2D 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 VM2D. If not, see <http://www.gnu.org/licenses/>. |
26\*---------------------------------------------------------------------------*/
27
28
40#include "Airfoil2D.h"
41
42#include "Boundary2D.h"
43#include "MeasureVP2D.h"
44#include "Mechanics2D.h"
45#include "nummatrix.h"
46#include "Preprocessor.h"
47#include "StreamParser.h"
48#include "Velocity2D.h"
49#include "World2D.h"
50#include "Wake2D.h"
51#include "Gmres2D.h"
52
53#include "spline/spline.h"
54#ifdef USE_CUDA
55#include "treeKernels.cuh"
56#endif
57
58using namespace VM2D;
59
60// Конструктор
61Airfoil::Airfoil(const World2D& W_, const size_t numberInPassport_)
62 : W(W_)
63 , numberInPassport(numberInPassport_)
64#ifdef USE_CUDA
65#ifdef USE_CUDNN
66 , NN()
67 , vtp(0)
68#endif
69#endif
70{
71
72}
73
74
75//Проверка, идет ли вершина i следом за вершиной j
76bool Airfoil::isAfter(size_t i, size_t j) const
77{
78 return ((i == j + 1) || (i == 0 && j == r_.size() - 1));
79}//isAfter(...)
80
81
82//Определяет, находится ли точка с радиус-вектором r внутри габаритного прямоугольника профиля
84{
85 return (r[0] <= upRight[0] && (r[0] >= lowLeft[0] && r[1] >= lowLeft[1] && r[1] <= upRight[1]));
86}//isInsideGabarits(...)
87
88
89//Определяет, находится ли точка с радиус-вектором r вне габаритного прямоугольника профиля
91{
92 return (r[0] > upRight[0] || (r[0] < lowLeft[0] || r[1] < lowLeft[1] || r[1] > upRight[1]));
93}//isOutsideGabarits(...)
94
95//Вычисление средних значений eps на панелях
97{
98 meanEpsOverPanel.clear();
100
101
102 double midEps;
103
106
107 for (size_t i = 0; i < getNumberOfPanels(); ++i)
108 {
109 midEps = 0.0;
110 for (int j = bnd.vortexBeginEnd[i].first; j < bnd.vortexBeginEnd[i].second; ++j)
111 midEps += virtVortParams.epsastWake[j];
112 //midEps += std::max(virtVortParams.epsastWake[j], 0.5 * len[i] / (bnd.vortexBeginEnd[i].second - bnd.vortexBeginEnd[i].first));
113
114 midEps /= (bnd.vortexBeginEnd[i].second - bnd.vortexBeginEnd[i].first);
115 meanEpsOverPanel[i] = midEps;
116 }//for i
117}//calcMeanEpsOverPanel()
118
119//Тест на "освещенность"
121{
123 {
124
125 //Тестируем на "освещенность"
126 wayToVertex.clear();
127 wayToVertex.resize(getNumberOfPanels(), -1);
128
129 for (size_t i = 0; i < getNumberOfPanels(); ++i)
130 {
131 const Point2D& dest = 0.5 * (getR(i) + getR(i + 1));
132
133 bool flag = false;
134 int wayNumber = -1;
135 size_t j;
136
137 do
138 {
139 flag = false;
140 ++wayNumber;
141 j = 0;
142 for (; j < getNumberOfPanels(); ++j)
143 {
144 const Point2D& aflRj = getR(j);
145 const Point2D& aflRj1 = getR(j + 1);
146
147 auto check = [aflRj, aflRj1](const Point2D& start, const Point2D& finish)
148 {
149 return ((((aflRj - start) ^ (finish - start)) * ((aflRj1 - start) ^ (finish - start)) <= 0) && \
150 (((start - aflRj) ^ (aflRj1 - aflRj)) * ((finish - aflRj) ^ (aflRj1 - aflRj)) <= 0));
151 };
152
153 if (i == j)
154 continue;
155
156 for (size_t q = 0; (!flag) && (q < ((wayNumber == 0) ? 1 : possibleWays[wayNumber - 1].size() + 1)); ++q)
157 {
158 Point2D start = ((q == 0) ? rcm : possibleWays[wayNumber - 1][q - 1]);
159 Point2D finish = (wayNumber == 0 || q == possibleWays[wayNumber - 1].size()) ? dest : possibleWays[wayNumber - 1][q];
160
161 flag = (/*flag ||*/ check(start, finish));
162 }
163
164 if (flag)
165 break;
166 }//for j
167
168 if (j == getNumberOfPanels())
169 {
170 wayToVertex[i] = wayNumber;
171 break;
172 }
173 } while ((wayToVertex[i] == -1) && (wayNumber < possibleWays.size()));
174
175 if (wayToVertex[i] == -1)
176 {
177 std::cout << "Possible way to vertex inside airfoil not found" << std::endl;
178 std::cout << "!!!" << std::endl;
179 std::cout << "dest = " << 0.5 * (getR(i) + getR(i + 1)) << std::endl;
180 std::cout << "j = " << j << std::endl;
181 //std::cout << "q = " << q << ", path[q] = " << path[q] << std::endl;
182 //std::cout << "i = " << i << std::endl;
183 std::cout << "rcm = " << rcm << std::endl;
184 std::cout << "possibleWays.size() = " << possibleWays.size() << std::endl;
185
186 for (size_t w = 0; w < possibleWays.size(); ++w)
187 {
188 std::cout << "Possible way # " << w << std::endl;
189 for (size_t p = 0; p < possibleWays[w].size(); ++p)
190 {
191 std::cout << possibleWays[w][p] << " ";
192 }
193 std::cout << std::endl;
194 }
195
196
197 std::ofstream airfoilFileStep(W.getPassport().dir + "afl" + std::to_string(W.getCurrentStep()));
198 for (size_t s = 0; s < getNumberOfPanels(); ++s)
199 airfoilFileStep << getR(s)[0] << " " << getR(s)[1] << "\n";
200 airfoilFileStep.close();
201
202 //exit(767);
203 }
204 }//for i
205 }
206}//lightningTest()
207
208
209
210
211//Считывание профиля из файла
212void Airfoil::ReadFromFile(const std::string& dir) //загрузка профиля из файла, его поворот и масштабирование
213{
215 std::string filename = dir + param.fileAirfoil;
216 std::ifstream airfoilFile;
217
218 if (fileExistTest(filename, W.getInfo(), true, { "txt", "TXT" }))
219 {
220 std::stringstream airfoilFile(VMlib::Preprocessor(filename).resultString);
221
222 VMlib::StreamParser airfoilParser(W.getInfo(), "airfoil file parser", airfoilFile);
223
224 m = 0.0; //TODO
225 J = 0.0; //TODO
226 rcm = { 0.0, 0.0 }; //TODO
227 phiAfl = 0.0;
228
229 inverse = param.inverse;
230
231
232
233 //*
234
236 //Проверяем отсутствие "задвоенных" подряд идущих точек
237 //airfoilParser.get("r", r_);
238 std::vector<Point2D> rFromFile;
239 airfoilParser.get("r", rFromFile);
240
241 if (rFromFile.size() > 0)
242 {
243 if (param.requiredNPanels <= rFromFile.size()) {
244
245 r_.reserve(rFromFile.size());
246 r_.push_back(rFromFile[0]);
247 for (size_t i = 1; i < rFromFile.size(); ++i)
248 if ((rFromFile[i] - rFromFile[i - 1]).length2() > 1e-12)
249 r_.push_back(rFromFile[i]);
250
251 //Если первая совпадает с последней, то убираем последнюю
252 if ((r_.back() - r_.front()).length2() < 1e-12)
253 r_.resize(r_.size() - 1);
254 }
255 else
256 {
257 W.getInfo('e') << "Airfoil shape is given explicitely, it can not be automatically split!" << std::endl;
258 exit(200);
259 }
260 }
261 else
262 {
263 std::vector<GeomPoint> geomFromFile;
264
265 airfoilParser.get("geometry", geomFromFile);
266
267 if ((geomFromFile.front() - geomFromFile.back()).length2() < 1e-12)
268 geomFromFile.resize(geomFromFile.size() - 1);
269
270 size_t reqN = param.requiredNPanels;
271
272 std::vector<double> L(geomFromFile.size());
273 double totalLength = 0.0;
274 std::vector<size_t> nc = {};
275 std::vector<size_t> ni = {};
276
277 for (size_t i = 0; i < geomFromFile.size(); ++i)
278 {
279 const Point2D& p1 = geomFromFile[i];
280 const Point2D& p2 = geomFromFile[(i + 1) % geomFromFile.size()];
281
282 L[i] = (p2 - p1).length();
283 totalLength += L[i];
284 }
285
286 /*
287 Point2D p1, p2, p3, cc, dd;
288 double ie = 0;
289 int numOfDivPnt = 12;
290 double alpha;
291 double rscale = 0.1;
292 std::vector<Point2D> pnt(numOfDivPnt);
293 std::vector<Point2D> newGeom(numOfDivPnt * geomFromFile.size());
294
295 for (size_t i = 0; i < geomFromFile.size(); ++i)
296 {
297 p1 = (i == 0 ? geomFromFile[(int)geomFromFile.size() - 1].r : geomFromFile[i - 1].r);
298 p2 = geomFromFile[i].r;
299 p3 = geomFromFile[(i + 1) % geomFromFile.size()].r;
300
301 Point2D dc = (p1 - p2).unit() + (p3 - p2).unit();
302 ie = dc.dist2To(dc.proj(p1 - p2));
303 if (ie < 1e-3) ie = 0;
304
305 alpha = (PI - acos(((p3 - p2) & (p1 - p2)) / (sqrt(((p3 - p2) & (p3 - p2)) * ((p1 - p2) & (p1 - p2)))))) / (numOfDivPnt - 1);
306
307 cc = p2 + (rscale * ie) * dc;
308
309 dd = p2 + (cc - p2).proj(p1 - p2) - cc;
310 for (int j = 0; j < numOfDivPnt; ++j)
311 newGeom[i * numOfDivPnt + j] = cc + dd.rotated(j * alpha);
312 }
313 */
314
315 //Ищем первый сплайн
316 size_t i = 0;
317 std::vector<size_t> splineStart, splineFinish;
318
319 while (i < geomFromFile.size())
320 {
321 while (i < geomFromFile.size() && !(geomFromFile[i].type == "c"))
322 ++i;
323
324 if (i < geomFromFile.size())
325 {
326 splineStart.push_back(i);
327 ++i;
328 //ищем второй конец
329 while (i < geomFromFile.size() && !(geomFromFile[i].type == "c"))
330 ++i;
331
332 splineFinish.push_back(i);
333 }
334 }
335
336 if ((splineStart.size() > 0) && (splineStart[0] != 0))
337 splineFinish.back() = splineStart[0];
338
339 if (reqN == 0)
340 {
341 r_.reserve(geomFromFile.size());
342 for (size_t i = 0; i < geomFromFile.size(); ++i)
343 r_.push_back(geomFromFile[i]);
344 }
345 else
346 {
347 double hRef = totalLength / reqN;
348
349 std::vector<int> nPanels;
350
351 bool cyclic = (splineStart.size() == 0);
352
353 if (cyclic)
354 {
355 splineStart.push_back(0);
356 splineFinish.push_back(geomFromFile.size());
357 }
358
359 for (size_t s = 0; s < splineStart.size(); ++s)
360 {
361 //Сплайн
362 std::vector<double> T, X, Y;
363
364 size_t splineLegs = splineFinish[s] - splineStart[s];
365 if (splineFinish[s] <= splineStart[s])
366 splineLegs += geomFromFile.size();
367
368 T.reserve(splineLegs + 1);
369 X.reserve(T.size());
370 Y.reserve(T.size());
371
372 double tbuf = 0.0;
373
374 for (size_t i = splineStart[s]; i <= splineStart[s] + splineLegs; ++i)
375 {
376 const Point2D& pt = geomFromFile[i % geomFromFile.size()];
377
378 T.push_back(tbuf);
379 X.push_back(pt[0]);
380 Y.push_back(pt[1]);
381
382 if ((i == splineStart[s]) && (splineFinish[s] - splineStart[s] == 1))
383 {
384 double halfL = (i < geomFromFile.size()) ? 0.5 * L[i] : 0.5 * (geomFromFile.front() - geomFromFile.back()).length();
385 tbuf += halfL;
386 T.push_back(tbuf);
387 Point2D nextPt = geomFromFile[(i + 1) % geomFromFile.size()];
388
389 X.push_back(0.5 * (pt[0] + nextPt[0]));
390 Y.push_back(0.5 * (pt[1] + nextPt[1]));
391
392 tbuf += halfL;
393 }
394 else
395 tbuf += (i < geomFromFile.size()) ? L[i] : (geomFromFile.front() - geomFromFile.back()).length();
396 }
397
398 tk::spline s1, s2;
399 if (cyclic)
400 {
401 s1.set_boundary(tk::spline::cyclic);
402 s2.set_boundary(tk::spline::cyclic);
403 }
404 else
405 {
406 s1.set_boundary(tk::spline::second_deriv, 0.0, tk::spline::second_deriv, 0.0);
407 s2.set_boundary(tk::spline::second_deriv, 0.0, tk::spline::second_deriv, 0.0);
408 }
409
410 s1.set_points(T, X);
411 s2.set_points(T, Y);
412
413 int NumberOfPanelsSpline = (int)ceil(T.back() / hRef);
414 double hSpline = T.back() / NumberOfPanelsSpline;
415
416 for (int i = 0; i < NumberOfPanelsSpline; ++i)
417 r_.push_back({ s1(hSpline * i), s2(hSpline * i) });
418
419 nPanels.push_back(NumberOfPanelsSpline);
420
421 }
422 }
423 }//else geometry
424
425 if (r_.size() == 0)
426 {
427 W.getInfo('e') << "No points on airfoil contour!" << std::endl;
428 exit(200);
429 }
430
431 if (inverse)
432 std::reverse(r_.begin(), r_.end());
433
434 v_.resize(0);
435 for (size_t q = 0; q < r_.size(); ++q)
436 v_.push_back({ 0.0, 0.0 });
437
438
439 int nPossibleWays;
440 int defaultNPossibleWays = 0;
441 airfoilParser.get("nPossibleWays", nPossibleWays, &defaultNPossibleWays, false);
442
443 possibleWays.resize(nPossibleWays);
444 for (int q = 0; q < nPossibleWays; ++q)
445 airfoilParser.get("possibleWay" + std::to_string(q + 1), possibleWays[q]);
446
447 //определяем начальные габаритные размеры
448 auto xMinMax = std::minmax_element(r_.begin(), r_.end(), Point2D::cmp<'x'>);
449 auto yMinMax = std::minmax_element(r_.begin(), r_.end(), Point2D::cmp<'y'>);
450
452 { Point2D({ (*xMinMax.first)[0], (*yMinMax.first)[1] }), Point2D({ (*xMinMax.second)[0], (*yMinMax.second)[1] }) };
453
454 Move(param.basePoint);
455 Scale(param.scale);
456
457 double rotationAngle = param.angle;
458
459 //Для обдува ветром, когда углы считаются по компасу
460 //if (W.getPassport().geographicalAngles)
461 // rotationAngle = -param.angle - 0.5 * PI;
462
463 Rotate(-rotationAngle);
464 //в конце Rotate нормали, касательные и длины вычисляются сами
465
466 std::string fname = W.getPassport().dir + "/airfoil-" + std::to_string(numberInPassport) + ".pfl";
467 //if (!fileExistTest(fname, W.getInfo()))
468 {
469 std::ofstream of(fname);
470 for (size_t i = 0; i < r_.size(); ++i)
471 of << r_[i][0] << " " << r_[i][1] << std::endl;
472 of.close();
473 }
474
475
476 gammaThrough.clear();
477 gammaThrough.resize(r_.size(), 0.0);
478
479 //Вычисляем площадь
480 area = 0.0;
481 for (size_t q = 0; q < r_.size(); ++q)
482 {
483 const Point2D& cntq = 0.5 * (getR(q) + getR(q + 1));
484 const Point2D& drq = getR(q + 1) - getR(q);
485 area += (cntq[0] * drq[1] - cntq[1] * drq[0]);
486 }
487 area *= 0.5;
488 area = fabs(area);
489
491 }
492}//ReadFromFile(...)
493
494//Вычисление числителей и знаменателей диффузионных скоростей в заданном наборе точек, обусловленных геометрией профиля, и вычисление вязкого трения
495void Airfoil::GetDiffVelocityI0I3ToSetOfPointsAndViscousStresses(const WakeDataBase& pointsDb, std::vector<double>& domainRadius, std::vector<double>& I0, std::vector<Point2D>& I3)
496{
497 std::vector<double> selfI0;
498 std::vector<Point2D> selfI3;
499
500 std::vector<double> currViscousStress;
501 currViscousStress.resize(r_.size(), 0.0);
502
503
504 selfI0.resize(pointsDb.vtx.size(), 0.0);
505 selfI3.resize(pointsDb.vtx.size(), { 0.0, 0.0 });
506
507#pragma warning (push)
508#pragma warning (disable: 4101)
509 //Локальные переменные для цикла
510 Point2D q, xi, xi_m, v0;
511 double lxi, lxi_m, lenj_m;
512
513 Point2D mn;
514 int new_n;
515 Point2D h;
516
517 Point2D vec;
518 double s, d;
519
520 double vs;
521 double iDDomRad, domRad, expon;
522#pragma warning (pop)
523
524#pragma omp parallel for \
525 default(none) \
526 shared(domainRadius, pointsDb, selfI0, selfI3, currViscousStress, PI) \
527 private(xi, xi_m, lxi, lxi_m, lenj_m, v0, q, new_n, mn, h, d, s, vec, vs, expon, domRad, iDDomRad) schedule(dynamic, DYN_SCHEDULE)
528 for (int i = 0; i < pointsDb.vtx.size(); ++i)
529 {
530 const Vortex2D& vtxI = pointsDb.vtx[i];
531
532 domRad = std::max(domainRadius[i], W.getPassport().wakeDiscretizationProperties.getMinEpsAst());
533 iDDomRad = 1.0 / domRad;
534
535 for (size_t j = 0; j < r_.size(); ++j)
536 {
537 vs = 0.0;
538 q = vtxI.r() - 0.5 * (getR(j) + getR(j + 1));
539 vec = tau[j];
540
541 s = q & vec;
542 d = fabs(q & nrm[j]);
543
544 if ((d < 50.0 * len[j]) && (fabs(s) < 50.0 * len[j]))
545 {
546 v0 = vec * len[j];
547
548 if ((d > 5.0 * len[j]) || (fabs(s) > 5.0 * len[j]))
549 {
550 xi = q * iDDomRad;
551 lxi = xi.length();
552
553 expon = exp(-lxi) * len[j];
554 mn = nrm[j] * expon;
555
556 //if (selfI0[i] != -PI * domRad)
557 //{
558 selfI0[i] += (xi & mn) * (lxi + 1.0) / (lxi * lxi);
559 selfI3[i] += mn;
560 //}
561
562 vs = vtxI.g() * expon / (PI * sqr(meanEpsOverPanel[j]));
563 }
564 else if ((d >= 0.1 * len[j]) || (fabs(s) > 0.5 * len[j]))
565 {
566 vs = 0.0;
567 double den = (fabs(s) < 0.5 * len[j]) ? d : (fabs(s) + d - 0.5 * len[j]);
568
569 new_n = std::max(1, static_cast<int>(ceil(5.0 * len[j] / den)));
570 //new_n = 100;
571 h = v0 * (1.0 / new_n);
572
573 for (int m = 0; m < new_n; ++m)
574 {
575 xi_m = (vtxI.r() - (getR(j) + h * (m + 0.5))) * iDDomRad;
576 lxi_m = xi_m.length();
577
578 lenj_m = len[j] / new_n;
579 expon = exp(-lxi_m) * lenj_m;
580
581 mn = nrm[j] * expon;
582 //if (selfI0[i] != -PI * domRad)
583 {
584 selfI0[i] += (xi_m & mn) * (lxi_m + 1.0) / (lxi_m * lxi_m);
585 selfI3[i] += mn;
586 }
587 vs += expon;
588 }//for m
589 vs *= vtxI.g() / (PI * sqr(meanEpsOverPanel[j]));
590 }
591 else
592 {
593
594 selfI0[i] += -PI * domRad;
595 double mnog = 2.0 * domRad * (1.0 - exp(-len[j] * iDDomRad / 2.0) * cosh(fabs(s) * iDDomRad));
596 selfI3[i] += nrm[j] * mnog;
597 vs = mnog * vtxI.g() / (PI * sqr(meanEpsOverPanel[j]));
598 }
599 }//if d<50 len
600
601#pragma omp atomic
602 currViscousStress[j] += vs;
603 }//for j
604 }//for i
605
606
607 if (&pointsDb == &(W.getWake()))
608 for (size_t i = 0; i < viscousStress.size(); ++i)
609 {
610 viscousStress[i] += currViscousStress[i];
611 }
612
613 for (size_t i = 0; i < I0.size(); ++i)
614 {
615 domRad = std::max(domainRadius[i], W.getPassport().wakeDiscretizationProperties.getMinEpsAst());
616
617 if (I0[i] != -PI * domRad)
618 {
619 if (selfI0[i] == -PI * domRad)
620 {
621 I0[i] = selfI0[i];
622 I3[i] = selfI3[i];
623 }
624 else
625 {
626 I0[i] += selfI0[i];
627 I3[i] += selfI3[i];
628 }
629 }
630 }
631}; //GetDiffVelocityI0I3ToSetOfPointsAndViscousStresses(...)
632
633
634#if defined(USE_CUDA)
635
636#ifndef USE_CUDNN
637void Airfoil::GPUGetDiffVelocityI0I3ToSetOfPointsAndViscousStresses(std::vector<double>& domainRadius, std::vector<double>& I0, std::vector<Point2D>& I3)
638{
639 std::vector<double> newViscousStress;
640
641 //Обнуление вязких напряжений
642 viscousStress.clear();
643 viscousStress.resize(r_.size(), 0.0);
644
645 //CUDA-ядро вызывается 1 раз и учитывает влияние сразу всех профилей
646 if (numberInPassport == 0)
647 {
648 size_t npt = W.getWake().vtx.size();
649 const size_t nr = r_.size();
650
651 float*& dev_ptr_i0f = W.getWake().devI0fPtr;
652 float*& dev_ptr_i3f = W.getWake().devI3fPtr;
653
654 size_t nTotPan = 0;
655 for (size_t s = 0; s < W.getNumberOfAirfoil(); ++s)
656 nTotPan += W.getAirfoil(s).getNumberOfPanels();
657
658
659 std::vector<Point2D> newI3(npt);
660 std::vector<double> newI0(npt);
661 std::vector<Point2Df> newI3f(npt);
662 std::vector<float> newI0f(npt);
663
664 newViscousStress.resize(nTotPan);
665
666 if ((npt > 0) && (nr > 0))
667 {
668 double*& dev_ptr_visstr = devViscousStressesPtr;
669
670 std::vector<double> locvisstr(nTotPan);
671
672 std::vector<double> zeroVec(nTotPan, 0.0);
673 cuCopyFixedArray(dev_ptr_visstr, zeroVec.data(), nTotPan * sizeof(double), 101);
674
676 {
677 double* dev_ptr_pt = W.getWake().devVtxPtr;
678 double* dev_ptr_i0 = W.getWake().devI0Ptr;
679 double* dev_ptr_i3 = W.getWake().devI3Ptr;
680 double* dev_ptr_rad = W.getWake().devRadPtr;
681
682 cuCalculateSurfDiffVeloWake(npt, dev_ptr_pt, nTotPan, devRPtr, dev_ptr_i0, dev_ptr_i3, dev_ptr_rad, devMeanEpsOverPanelPtr, W.getPassport().wakeDiscretizationProperties.getMinEpsAst(), dev_ptr_visstr);
683 W.getCuda().CopyMemFromDev<double, 2>(npt, dev_ptr_i3, (double*)newI3.data());
684 W.getCuda().CopyMemFromDev<double, 1>(npt, dev_ptr_i0, newI0.data());
685 W.getCuda().CopyMemFromDev<double, 1>(nTotPan, dev_ptr_visstr, newViscousStress.data());
686 //if (&pointsDb == &(W.getWake()))
687 {
688 for (size_t q = 0; q < I3.size(); ++q)
689 {
690 I0[q] += newI0[q];
691 I3[q] += newI3[q];
692 }
693 }
694 }
695 else
696 {
697 //FAST 31-05
698 //*
699 auto& treeWake = *W.getCuda().inflTreeWake;
700
701 double time = treeWake.I0I3CalculationWrapper(W.getPassport().wakeDiscretizationProperties.getMinEpsAst(), dev_ptr_i0f, (Point2Df*)dev_ptr_i3f, W.getWake().devRadPtr, devMeanEpsOverPanelPtr, (int)nTotPan, devRPtr, dev_ptr_visstr);
702
703 W.getCuda().CopyMemFromDev<float, 2>(npt, dev_ptr_i3f, (float*)newI3f.data());
704 W.getCuda().CopyMemFromDev<float, 1>(npt, dev_ptr_i0f, newI0f.data());
705 W.getCuda().CopyMemFromDev<double, 1>(nTotPan, dev_ptr_visstr, newViscousStress.data());
706
707 for (size_t q = 0; q < I3.size(); ++q)
708 {
709 I0[q] += newI0f[q];
710 I3[q] += newI3f[q];
711 }
712
713 size_t curGlobPnl = 0;
714 for (size_t s = 0; s < W.getNumberOfAirfoil(); ++s)
715 {
716 std::vector<double>& tmpVisStress = W.getNonConstAirfoil(s).tmpViscousStresses;
717 const size_t& np = W.getAirfoil(s).getNumberOfPanels();
718 tmpVisStress.resize(0);
719 tmpVisStress.insert(tmpVisStress.end(), newViscousStress.begin() + curGlobPnl, newViscousStress.begin() + curGlobPnl + np);
720 curGlobPnl += np;
721 }
722 }
723 }
724 }//if numberInPassport==0
725
726 for (size_t i = 0; i < viscousStress.size(); ++i)
727 viscousStress[i] += tmpViscousStresses[i];
728
729}//GPUGetDiffVelocityI0I3ToSetOfPointsAndViscousStresses
730#endif
731
732
733#ifdef USE_CUDNN
734void Airfoil::GPUGetDiffVelocityI0I3ToSetOfPointsAndViscousStresses(std::vector<double>& domainRadius, std::vector<double>& I0, std::vector<Point2D>& I3)
735{
736 const WakeDataBase& pointsDb = W.getWake();
737
738 std::vector<double> newViscousStress;
739
740 //Обнуление вязких напряжений
741 if (&pointsDb == &(W.getWake()))
742 {
743 viscousStress.clear();
744 viscousStress.resize(r_.size(), 0.0);
745 }
746
747 if (W.getCurrentStep() == 0)
748 return;
749
750 //CUDA-ядро вызывается 1 раз и учитывает влияние сразу всех профилей
751 if ((numberInPassport == 0) && ((&pointsDb == &(W.getWake())) || (&pointsDb == &(W.getBoundary(0).virtualWake))))
752 {
753 size_t npt = pointsDb.vtx.size();
754
755 if (&pointsDb == &(W.getBoundary(0).virtualWake))
756 {
757 for (size_t q = 1; q < W.getNumberOfAirfoil(); ++q)
758 npt += W.getBoundary(q).virtualWake.vtx.size();
759 }
760
761 double*& dev_ptr_pt = pointsDb.devVtxPtr;
762 double*& dev_ptr_rad = pointsDb.devRadPtr;
763 double*& dev_ptr_meanEps = devMeanEpsOverPanelPtr;
764 const size_t nr = r_.size();
765 double*& dev_ptr_r = devRPtr;
766
767 double*& dev_ptr_i0 = pointsDb.devI0Ptr;
768 double*& dev_ptr_i3 = pointsDb.devI3Ptr;
769
770 float*& dev_ptr_i0f = pointsDb.devI0fPtr;
771 float*& dev_ptr_i3f = pointsDb.devI3fPtr;
772
774
775 size_t nTotPan = 0;
776 for (size_t s = 0; s < W.getNumberOfAirfoil(); ++s)
777 nTotPan += W.getAirfoil(s).getNumberOfPanels();
778
779 double tNEIB = -omp_get_wtime();
780
781
782 bool isMovable = false;
783 for (size_t afl = 0; afl < W.getNumberOfAirfoil(); ++afl)
784 isMovable = isMovable || W.getMechanics(afl).isMoves || W.getMechanics(afl).isDeform;
785
786 std::vector<int> nearestPanIdx(npt);
787 int* nearestPanIdx_dev;
788 cudaMalloc(&nearestPanIdx_dev, npt * sizeof(int));
789 BHcu::treeClosestPanelToPointsCalculationWrapper(*W.getCuda().auxTreePnl, *W.getCuda().cntrTreeWake, nearestPanIdx_dev, false);
790 cudaMemcpy(nearestPanIdx.data(), nearestPanIdx_dev, npt * sizeof(int), cudaMemcpyDeviceToHost);
791 cudaFree(nearestPanIdx_dev);
792
793 /*
794 std::ofstream vortexFile(W.getPassport().dir + "vortexFile.txt");
795 for (size_t i = 0; i < W.getWake().vtx.size(); ++i)
796 vortexFile << W.getWake().vtx[i].r()[0] << " " << W.getWake().vtx[i].r()[1] << " " << nearestPanIdx[i] << '\n';
797 vortexFile.close();
798
799 std::ofstream panelFile(W.getPassport().dir + "panelFile.txt");
800 for (size_t i = 0; i < W.getAirfoil(0).getNumberOfPanels(); ++i)
801 panelFile << W.getAirfoil(0).getR(i)[0] << " " << W.getAirfoil(0).getR(i)[1] << " " << \
802 W.getAirfoil(0).getR(i+1)[0] << " " << W.getAirfoil(0).getR(i+1)[1] << " " << '\n';
803 panelFile.close();
804 */
805
806 std::vector<double> dist2ToNearestPan(npt);
807#pragma omp parallel for
808 for (int i = 0; i < (int)npt; ++i)
809 {
810 Point2D pnlCen = 0.5 * (W.getAirfoil(0).getR(nearestPanIdx[i]) + W.getAirfoil(0).getR(nearestPanIdx[i] + 1));
811 double prod = (pointsDb.vtx[i].r() - pnlCen) & W.getAirfoil(0).tau[nearestPanIdx[i]];
812
813 if (fabs(prod) <= 0.5 * W.getAirfoil(0).len[nearestPanIdx[i]])
814 dist2ToNearestPan[i] = sqr((pointsDb.vtx[i].r() - pnlCen) & W.getAirfoil(0).nrm[nearestPanIdx[i]]);
815 else if (prod < -0.5 * W.getAirfoil(0).len[nearestPanIdx[i]])
816 dist2ToNearestPan[i] = (pointsDb.vtx[i].r() - W.getAirfoil(0).getR(nearestPanIdx[i])).length2();
817 else // (prod > 0.5 * W.getAirfoil(0).len[nearestPanIdx[i]])
818 dist2ToNearestPan[i] = (pointsDb.vtx[i].r() - W.getAirfoil(0).getR(nearestPanIdx[i] + 1)).length2();
819 }
820 tNEIB += omp_get_wtime();
821
823
824 double tSelectVortex = -omp_get_wtime();
825
826 if (vorticesToProcess.size() < npt)
827 {
828 size_t curSize = vorticesToProcess.size();
829 do
830 {
831 curSize += 1024;
832 } while (curSize < npt);
833 vorticesToProcess.resize(curSize);
834 }
835
836 vtp = 0;
837//#pragma omp parallel for
838 for (int q = 0; q < (int)npt; ++q)
839 {
840 //double dist2 = (W.getWake().vtx[q].r() - 0.5 * (W.getAirfoil(0).getR(nearestPan[q]) + W.getAirfoil(0).getR(nearestPan[q] + 1))).length2();
841 //if ((dist2 < 9.0 * sqr(W.getAirfoil(0).len[nearestPan[q]])))
842
843 if (dist2ToNearestPan[q] < 9.0 * sqr(W.getAirfoil(0).len[nearestPanIdx[q]]))
844//#pragma omp critical
845 vorticesToProcess[vtp++] = q;
846 }
847
848 if (batch.size() < vtp * 3 * 5) // 3 - число входов 5 - число панелей
849 {
850 size_t curSize = batch.size() / (3*5);
851 do
852 {
853 curSize += 1024;
854 } while (curSize < vtp);
855
856 batch.resize(curSize * 3 * 5); //входы
857 signy.resize(curSize * 5); //частица над или под панелью
858 nnout.resize(curSize * 2 * 5); //выходы
859 batchPanels.resize(curSize * 5);//номера панелей
860 }
861
862#pragma omp parallel for
863 for (int v = 0; v < (int)vtp; ++v)
864 {
865 const Vortex2D& vrt = W.getWake().vtx[vorticesToProcess[v]];
866
867 for (int s = 0; s < 5; ++s)
868 {
869 int panIndex = nearestPanIdx[vorticesToProcess[v]] + s - 2;
870
871 if (panIndex < 0)
872 panIndex += (int)W.getAirfoil(0).getNumberOfPanels();
873
874 if (panIndex >= (int)W.getAirfoil(0).getNumberOfPanels())
875 panIndex -= (int)W.getAirfoil(0).getNumberOfPanels();
876
877 Point2D panCenter = 0.5 * (W.getAirfoil(0).getR(panIndex) + W.getAirfoil(0).getR(panIndex + 1));
878
879 Point2D relPos = (1.0 / W.getAirfoil(0).len[panIndex]) *
880 Point2D{
881 Point2D({vrt.r() - panCenter }) & W.getAirfoil(0).tau[panIndex], //h
882 Point2D({vrt.r() - panCenter }) & W.getAirfoil(0).nrm[panIndex] //d
883 };
884
885 batch[v * 15 + 3 * s + 0] = fabsf((float)relPos[0]);
886 batch[v * 15 + 3 * s + 1] = fabsf((float)relPos[1]);
887 signy[v * 5 + s] = (relPos[1] > 0) ? 1 : -1;
888
889 double domrad = std::max(W.getVelocity().wakeVortexesParams.epsastWake[vorticesToProcess[v]], W.getPassport().wakeDiscretizationProperties.getMinEpsAst());
890 batch[v * 15 + 3 * s + 2] = (float)(domrad / W.getAirfoil(0).len[panIndex]);
891
892 batchPanels[v * 5 + s] = panIndex;
893 }
894 }
895 tSelectVortex += omp_get_wtime();
896
897
898 double tNN2 = -omp_get_wtime();
899 NN.setup_layers(vtp * 5);
900 tNN2 += omp_get_wtime();
901
902 double tNN3 = -omp_get_wtime();
903 NN.start((int)(vtp * 5), batch.data(), nnout.data(), signy.data());
904 tNN3 += omp_get_wtime();
905
906 double tNN4 = -omp_get_wtime();
907 NN.destroy_layers();
908 tNN4 += omp_get_wtime();
909
910 //std::cout << "tNEIB = " << tNEIB << std::endl;
911 //std::cout << "tSelectVortex = " << tSelectVortex << std::endl;
912 //std::cout << "tNN2 = " << tNN2 << std::endl;
913 //std::cout << "tNN3 = " << tNN3 << std::endl;
914 //std::cout << "tNN4 = " << tNN4 << std::endl;
915 //std::cout << "tSum = " << tNEIB + tSelectVortex + tNN2 + tNN3 + tNN4 << std::endl;
916
917
918 //
919 std::vector<double> nnI0(npt, 0.0);
920 std::vector<Point2D> nnI3(npt, {0.0, 0.0});
921
922 newViscousStress.resize(nTotPan);
923
924#pragma omp parallel for
925 for (int v = 0; v < vtp; ++v)
926 {
927 double domrad = std::max(W.getVelocity().wakeVortexesParams.epsastWake[vorticesToProcess[v]], W.getPassport().wakeDiscretizationProperties.getMinEpsAst());
928
929 for (int s = 0; s < 5; ++s)
930 {
931 float absx = batch[v * 15 + 3 * s + 0];
932 float absy = batch[v * 15 + 3 * s + 1];
933 if ((absx < 0.5f) && (absy < 1e-3))
934 {
935 nnI0[vorticesToProcess[v]] += -PI * domrad * domrad;
936 //todo
937 nnI3[vorticesToProcess[v]] += (nnout[v * 10 + s * 2 + 0] * W.getAirfoil(0).len[batchPanels[v * 5 + s]]) * W.getAirfoil(0).nrm[batchPanels[v * 5 + s]];
938 }
939 else
940 {
941 nnI0[vorticesToProcess[v]] += (-nnout[v * 10 + s * 2 + 1] * sqr(W.getAirfoil(0).len[batchPanels[v * 5 + s]]));
942 nnI3[vorticesToProcess[v]] += (nnout[v * 10 + s * 2 + 0] * W.getAirfoil(0).len[batchPanels[v * 5 + s]]) * W.getAirfoil(0).nrm[batchPanels[v * 5 + s]];
943 }
944
945 int curPnl = batchPanels[v * 5 + s];
946 const Vortex2D& vrt = W.getWake().vtx[vorticesToProcess[v]];
947
948#pragma omp atomic
949 newViscousStress[curPnl] += len[curPnl] * vrt.g() * nnout[v * 10 + s * 2 + 0] / (PI * sqr(meanEpsOverPanel[curPnl]));
950
951 }
952 nnI0[vorticesToProcess[v]] /= domrad;
953 }
954 //
955
956 //std::vector<Point2D> newI3(npt); //(I3.size());
957 //std::vector<double> newI0(npt); //(I0.size());
958 //std::vector<Point2Df> newI3f(npt); //(I3.size());
959 //std::vector<float> newI0f(npt); //(I0.size());
960
961
962
963
964 if ((npt > 0) && (nr > 0))
965 {
966 double*& dev_ptr_visstr = devViscousStressesPtr;
967 std::vector<double> locvisstr(nTotPan);
968 cuCopyFixedArray(dev_ptr_visstr, newViscousStress.data(), nTotPan * sizeof(double), 101);
969
970 //cuCalculateSurfDiffVeloWake(npt, dev_ptr_pt, nTotPan, dev_ptr_r, dev_ptr_i0, dev_ptr_i3, dev_ptr_rad, dev_ptr_meanEps, minRad, dev_ptr_visstr);
971 //W.getCuda().CopyMemFromDev<double, 2>(npt, dev_ptr_i3, (double*)newI3.data());
972 //W.getCuda().CopyMemFromDev<double, 1>(npt, dev_ptr_i0, newI0.data());
973 //W.getCuda().CopyMemFromDev<double, 1>(nTotPan, dev_ptr_visstr, newViscousStress.data());
974 //if (&pointsDb == &(W.getWake()))
975 //{
976 // for (size_t q = 0; q < I3.size(); ++q)
977 // {
978 // I0[q] += newI0[q];
979 // I3[q] += newI3[q];
980 // }
981 //}
982
983
984 //FAST 31-05
985 /*
986 double timings[7];
987
988 double tOLD = -omp_get_wtime();
989 BHcu::wrapperDiffusiveVeloI0I3((Vortex2D*)dev_ptr_pt, dev_ptr_i0f, (Point2Df*)dev_ptr_i3f, dev_ptr_rad, dev_ptr_r, W.getNonConstCuda().CUDAptrs, true, (int)npt, nTotPan, dev_ptr_visstr, timings, dev_ptr_meanEps, minRad, \
990 W.getNonConstCuda().n_CUDA_bodies, (int)W.getNonConstCuda().n_CUDA_wake, 8);
991 W.getCuda().CopyMemFromDev<float, 2>(npt, dev_ptr_i3f, (float*)newI3f.data());
992 W.getCuda().CopyMemFromDev<float, 1>(npt, dev_ptr_i0f, newI0f.data());
993 W.getCuda().CopyMemFromDev<double, 1>(nTotPan, dev_ptr_visstr, newViscousStress.data());
994
995 tOLD += omp_get_wtime();
996 std::cout << "tOLD = " << tOLD << std::endl;
997
998 if (&pointsDb == &(W.getWake()))
999 {
1000 for (size_t q = 0; q < I3.size(); ++q)
1001 {
1002 //I0[q] += newI0f[q];
1003 //I3[q] += newI3f[q];
1004 }
1005 }
1006 */
1007
1008 //ЗАТЫЧКА
1009 if (&pointsDb == &(W.getWake()))
1010 {
1011 for (size_t q = 0; q < I3.size(); ++q)
1012 {
1013 I0[q] += nnI0[q];
1014 I3[q] += nnI3[q];
1015 }
1016 }
1017
1018 //std::ofstream testFile(W.getPassport().dir + "/testFile.txt");
1019 //for (int q = 0; q < W.getWake().vtx.size(); ++q)
1020 //{
1021 // double domrad = std::max(W.getVelocity().wakeVortexesParams.epsastWake[q], W.getPassport().wakeDiscretizationProperties.getMinEpsAst());
1022 // testFile << q << " " << W.getWake().vtx[q].r()[0] << " " << W.getWake().vtx[q].r()[1] << " " << nearestPan[q].first << " " << \
1023 // I0[q] << " " << I3[q][0] << " " << I3[q][1] << " " << nnI0[q] << " " << nnI3[q][0] << " " << nnI3[q][1] << " " << domrad << "\n";
1024 //}
1025 //testFile.close();
1026 //exit(-1);
1027
1028 size_t curGlobPnl = 0;
1029 for (size_t s = 0; s < W.getNumberOfAirfoil(); ++s)
1030 {
1031 std::vector<double>& tmpVisStress = W.getNonConstAirfoil(s).tmpViscousStresses;
1032 const size_t& np = W.getAirfoil(s).getNumberOfPanels();
1033 tmpVisStress.resize(0);
1034 tmpVisStress.insert(tmpVisStress.end(), newViscousStress.begin() + curGlobPnl, newViscousStress.begin() + curGlobPnl + np);
1035 curGlobPnl += np;
1036 }
1037
1038 }
1039 }//if numberInPassport==0
1040
1041
1042 if ((&pointsDb == &(W.getWake())) || (&pointsDb == &(W.getBoundary(0).virtualWake)))
1043 for (size_t i = 0; i < viscousStress.size(); ++i)
1044 viscousStress[i] += tmpViscousStresses[i];
1045 //exit(-100);
1046}//GPUGetDiffVelocityI0I3ToSetOfPointsAndViscousStresses
1047#endif
1048
1049
1050#endif
1051
1052bool Airfoil::IsPointInAirfoil(const Point2D& point) const
1053{
1054 double sumAngle = 0.0;
1055
1056 Point2D v1, v2;
1057
1058 for (size_t i = 0; i < r_.size(); ++i)
1059 {
1060 v1 = getR(i) - point;
1061 v2 = getR(i + 1) - point;
1062 sumAngle += atan2(v1 ^ v2, v1 & v2);
1063 }
1064
1065 if (fabs(sumAngle) < 0.1)
1066 return false;
1067 else
1068 return true;
1069}//IsPointInAirfoil(...)
1070
1071//Вычисляет габаритный прямоугольник профиля
1072void Airfoil::GetGabarits(double gap) //определение габаритного прямоугольника
1073{
1074 lowLeft = { 1E+10, 1E+10 };
1075 upRight = { -1E+10, -1E+10 };
1076
1077 for (size_t i = 0; i < r_.size(); ++i)
1078 {
1079 lowLeft[0] = std::min(lowLeft[0], r_[i][0]);
1080 lowLeft[1] = std::min(lowLeft[1], r_[i][1]);
1081
1082 upRight[0] = std::max(upRight[0], r_[i][0]);
1083 upRight[1] = std::max(upRight[1], r_[i][1]);
1084 }
1085
1086 Point2D size = upRight - lowLeft;
1087 lowLeft -= size * gap;
1088 upRight += size * gap;
1089}//GetGabarits(...)
1090
1091// Вычисление нормалей, касательных и длин панелей по текущему положению вершин
1093{
1094 if (nrm.size() != r_.size())
1095 {
1096 nrm.resize(r_.size());
1097 psn.resize(r_.size());
1098 tau.resize(r_.size());
1099 len.resize(r_.size());
1100 }
1101
1102 Point2D rpan;
1103
1104 //считаем длины, касательные и нормали
1105#pragma omp parallel for private(rpan)
1106 for (int i = 0; i < (int)r_.size(); ++i)
1107 {
1108 rpan = (getR(i + 1) - getR(i));
1109 len[i] = rpan.length();
1110 tau[i] = rpan.unit();
1111 nrm[i] = { tau[i][1], -tau[i][0] };
1112 }
1113
1114 //считаем псевдонормали
1115#pragma omp parallel for
1116 for (int i = 0; i < (int)r_.size(); ++i)
1117 {
1118 int next = (i == (int)r_.size() - 1 ? 0 : i + 1);
1119 int prev = (i == 0 ? (int)r_.size() - 1 : i - 1);
1120
1121 psn[i] = std::make_pair( (nrm[prev] + nrm[i]).unit(), (nrm[i] + nrm[next]).unit() );
1122 }
1123
1124 //закольцовываем
1125 //nrm.push_back(nrm[0]);
1126 //tau.push_back(tau[0]);
1127 //len.push_back(len[0]);
1128}//CalcNrmTauLen()
1129
1130//Перемещение профиля
1131void Airfoil::Move(const Point2D& dr) //перемещение профиля как единого целого на вектор dr
1132{
1133 for (size_t i = 0; i < r_.size(); ++i)
1134 r_[i] += dr;
1135 rcm += dr;
1136
1137 for (size_t q = 0; q < possibleWays.size(); ++q)
1138 for (Point2D& pts : possibleWays[q])
1139 pts += dr;
1140
1141 CalcNrmTauLen();
1142 GetGabarits();
1143}//Move(...)
1144
1145//Поворот профиля
1146void Airfoil::Rotate(double alpha) //поворот профиля на угол alpha вокруг центра масс
1147{
1148 phiAfl += alpha;
1149 nummatrix<double, 2, 2> rotMatrix = { { cos(alpha), -sin(alpha) }, { sin(alpha), cos(alpha) } };
1150
1151 for (size_t i = 0; i < r_.size(); ++i)
1152 r_[i] = rcm + (rotMatrix & (r_[i] - rcm));
1153
1154 for (size_t q = 0; q < possibleWays.size(); ++q)
1155 for (Point2D& pts : possibleWays[q])
1156 {
1157 Point2D oldPts = pts;
1158 pts = rcm + (rotMatrix & (oldPts - rcm));
1159 }
1160
1161 CalcNrmTauLen();
1162 GetGabarits();
1163}//Rotate(...)
1164
1165
1166//Масштабирование профиля
1167void Airfoil::Scale(const Point2D& factor) //масштабирование профиля на коэффициент factor относительно центра масс
1168{
1169 for (size_t i = 0; i < r_.size(); ++i)
1170 r_[i] = rcm + Point2D{ factor[0] * (r_[i] - rcm)[0], factor[1] * (r_[i] - rcm)[1] };
1171
1172 for (size_t q = 0; q < possibleWays.size(); ++q)
1173 for (Point2D& pts : possibleWays[q])
1174 {
1175 Point2D oldPts = pts;
1176 pts = rcm + Point2D{ factor[0] * (oldPts - rcm)[0], factor[1] * (oldPts - rcm)[1] };
1177 }
1178
1179 CalcNrmTauLen();
1180 GetGabarits();
1181}//Scale(...)
1182
1183//Вычисление коэффициентов матрицы A для расчета влияния панели на панель
1184std::vector<double> Airfoil::getA(size_t p, size_t i, const Airfoil& airfoil, size_t j) const
1185{
1186 std::vector<double> res(p * p, 0.0);
1187
1188 if ((i == j) && (&airfoil == this))
1189 {
1190 res[0] = 0.5 * (tau[i] ^ nrm[i]);
1191 if (p == 1)
1192 return res;
1193
1194 res[3] = (1.0 / 12.0) * res[0];
1195 if (p == 2)
1196 return res;
1197
1198 if (p > 2)
1199 throw (-42);
1200
1201 }//if i==j
1202
1203 const auto& miq = W.getIQ(numberInPassport, airfoil.numberInPassport);
1204
1205 res[0] = miq.first(i, j);
1206
1207 if (p == 1)
1208 return res;
1209
1210 res[1] = miq.first(i, airfoil.getNumberOfPanels() + j);
1211 res[2] = miq.first(getNumberOfPanels() + i, j);
1212 res[3] = miq.first(getNumberOfPanels() + i, airfoil.getNumberOfPanels() + j);
1213
1214 if (p == 2)
1215 return res;
1216
1217 if (p > 2)
1218 throw (-42);
1219
1220 //Хотя сюда никогда и не попадем
1221 return res;
1222}//getA(...)
1223
1224//Вычисление коэффициентов матрицы A для расчета влияния профиля самого на себя
1225void Airfoil::calcIQ(size_t p, const Airfoil& otherAirfoil, std::pair<Eigen::MatrixXd, Eigen::MatrixXd>& matrPair) const
1226{
1227 bool self = (&otherAirfoil == this);
1228
1229 size_t aflNSelf = numberInPassport;
1230 size_t aflNOther = otherAirfoil.numberInPassport;
1231
1232 size_t npI;
1233 size_t npJ;
1234
1235 numvector<double, 3> alpha, lambda;
1236
1237 //auxillary vectors
1238 Point2D p1, s1, p2, s2, di, dj, i00, i01, i10, i11;
1239 numvector<Point2D, 3> v00, v11;
1240 numvector<Point2D, 2> v01, v10;
1241
1242#pragma omp parallel for \
1243 default(none) \
1244 shared(otherAirfoil, self, aflNSelf, aflNOther, matrPair, p, IDPI, IQPI) \
1245 private(npI, npJ, alpha, lambda, p1, s1, p2, s2, di, dj, i00, i01, i10, i11, v00, v11, v01, v10) schedule(dynamic, DYN_SCHEDULE)
1246 for (int i = 0; i < (int)getNumberOfPanels(); ++i)
1247 for (int j = 0; j < (int)otherAirfoil.getNumberOfPanels(); ++j)
1248 {
1249 npI = getNumberOfPanels();
1250 npJ = otherAirfoil.getNumberOfPanels();
1251
1252 if ((i == j) && self)
1253 {
1254
1255 matrPair.first(i, j) = 0.0;
1256 matrPair.second(i, j) = 0.0;
1257
1258 if (p == 2)
1259 {
1260 matrPair.first(i, npJ + j) = 0.0;
1261 matrPair.first(npI + i, j) = 0.0;
1262
1263 matrPair.second(i, npJ + j) = -IQPI;
1264 matrPair.second(npI + i, j) = IQPI;
1265
1266 matrPair.first(npI + i, npJ + j) = 0.0;
1267 matrPair.second(npI + i, npJ + j) = 0.0;
1268
1269 }
1270 if (p > 2)
1271 throw (-42);
1272 }//if i==j
1273 else
1274 {
1275 const Point2D& taui = tau[i];
1276 const Point2D& tauj = otherAirfoil.tau[j];
1277
1278 p1 = getR(i + 1) - otherAirfoil.getR(j + 1);
1279 s1 = getR(i + 1) - otherAirfoil.getR(j);
1280 p2 = getR(i) - otherAirfoil.getR(j + 1);
1281 s2 = getR(i) - otherAirfoil.getR(j);
1282 di = getR(i + 1) - getR(i);
1283 dj = otherAirfoil.getR(j + 1) - otherAirfoil.getR(j);
1284
1285 alpha = { \
1286 (self && isAfter(j, i)) ? 0.0 : VMlib::Alpha(s2, s1), \
1287 VMlib::Alpha(s2, p1), \
1288 (self && isAfter(i, j)) ? 0.0 : VMlib::Alpha(p1, p2) \
1289 };
1290
1291 lambda = { \
1292 (self && isAfter(j, i)) ? 0.0 : VMlib::Lambda(s2, s1), \
1293 VMlib::Lambda(s2, p1), \
1294 (self && isAfter(i, j)) ? 0.0 : VMlib::Lambda(p1, p2) \
1295 };
1296
1297 v00 = {
1298 VMlib::Omega(s1, taui, tauj),
1299 -VMlib::Omega(di, taui, tauj),
1300 VMlib::Omega(p2, taui, tauj)
1301 };
1302
1303 i00 = IDPI / len[i] * (-(alpha[0] * v00[0] + alpha[1] * v00[1] + alpha[2] * v00[2]).kcross() \
1304 + (lambda[0] * v00[0] + lambda[1] * v00[1] + lambda[2] * v00[2]));
1305
1306 matrPair.first(i, j) = i00 & nrm[i];
1307 matrPair.second(i, j) = i00 & taui;
1308
1309
1310 if (p == 2)
1311 {
1312 v01 = {
1313 0.5 / (dj.length()) * (((p1 + s1) & tauj) * VMlib::Omega(s1, taui, tauj) - s1.length2() * taui),
1314 -0.5 * di.length() / dj.length() * VMlib::Omega(s1 + p2, tauj, tauj)
1315 };
1316
1317 i01 = IDPI / len[i] * (-((alpha[0] + alpha[2]) * v01[0] + (alpha[1] + alpha[2]) * v01[1]).kcross() \
1318 + ((lambda[0] + lambda[2]) * v01[0] + (lambda[1] + lambda[2]) * v01[1]) - 0.5 * di.length() * tauj);
1319
1320 matrPair.first(i, npJ + j) = i01 & nrm[i];
1321 matrPair.second(i, npJ + j) = i01 & taui;
1322
1323
1324 v10 = {
1325 -0.5 / di.length() * (((s1 + s2) & taui) * VMlib::Omega(s1, taui, tauj) - s1.length2() * tauj),
1326 0.5 * dj.length() / di.length() * VMlib::Omega(s1 + p2, taui, taui)
1327 };
1328
1329 i10 = IDPI / len[i] * (-((alpha[0] + alpha[2]) * v10[0] + alpha[2] * v10[1]).kcross() \
1330 + ((lambda[0] + lambda[2]) * v10[0] + lambda[2] * v10[1]) + 0.5 * dj.length() * taui);
1331
1332 matrPair.first(npI + i, j) = i10 & nrm[i];
1333 matrPair.second(npI + i, j) = i10 & taui;
1334
1335
1336 v11 = {
1337 1.0 / (12.0 * di.length() * dj.length()) * (2.0 * (s1 & VMlib::Omega(s1 - 3.0 * p2, taui, tauj)) * VMlib::Omega(s1, taui, tauj) - s1.length2() * (s1 - 3.0 * p2)) - 0.25 * VMlib::Omega(s1, taui, tauj),
1338 -di.length() / (12.0 * dj.length()) * VMlib::Omega(di, tauj, tauj),
1339 -dj.length() / (12.0 * di.length()) * VMlib::Omega(dj, taui, taui)
1340 };
1341
1342 i11 = IDPI / len[i] * (-((alpha[0] + alpha[2]) * v11[0] + (alpha[1] + alpha[2]) * v11[1] + alpha[2] * v11[2]).kcross()\
1343 + (lambda[0] + lambda[2]) * v11[0] + (lambda[1] + lambda[2]) * v11[1] + lambda[2] * v11[2] \
1344 + 1.0 / 12.0 * (dj.length() * taui + di.length() * tauj - 2.0 * VMlib::Omega(s1, taui, tauj)));
1345
1346 matrPair.first(npI + i, npJ + j) = i11 & nrm[i];
1347 matrPair.second(npI + i, npJ + j) = i11 & taui;
1348 }
1349
1350
1351 if (p > 2)
1352 throw (-42);
1353 }//else(i == j)
1354
1355 }//for(...)
1356}//getIQ(...)
1357
1358void Airfoil::GetInfluenceFromVorticesToPanel(size_t panel, const Vortex2D* ptr, ptrdiff_t count, std::vector<double>& panelRhs) const
1359{
1361}//GetInfluenceFromVorticesToPanel(...)
1362
1363
1364//Вычисление влияния части подряд идущих источников из области течения на панель для правой части
1365void Airfoil::GetInfluenceFromSourcesToPanel(size_t panel, const Vortex2D* ptr, ptrdiff_t count, std::vector<double>& panelRhs) const
1366{
1368}//GetInfluenceFromSourcesToPanel(...)
1369
1370//Вычисление влияния слоя источников конкретной прямолинейной панели на вихрь в области течения
1372{
1374}//GetInfluenceFromSourceSheetToVortex(...)
1375
1376//Вычисление влияния вихревых слоев (свободный + присоединенный) конкретной прямолинейной панели на вихрь в области течения
1378{
1380}//GetInfluenceFromVortexSheetToVortex(...)
1381
1382void Airfoil::GetInfluenceFromVInfToPanel(std::vector<double>& vInfRhs) const
1383{
1385}//GetInfluenceFromVInfToPanel(...)
1386
1387
Заголовочный файл с описанием класса Airfoil.
Заголовочный файл с описанием класса Boundary.
Заголовочный файл с функциями для метода GMRES.
Заголовочный файл с описанием класса MeasureVP.
Заголовочный файл с описанием класса Mechanics.
Заголовочный файл с описанием класса Preprocessor.
Заголовочный файл с описанием класса StreamParser.
const double PI
Число .
Definition defs.h:76
const double IQPI
Число .
Definition defs.h:85
const double IDPI
Число .
Definition defs.h:79
Заголовочный файл с описанием класса Velocity.
Заголовочный файл с описанием класса Wake.
Заголовочный файл с описанием класса World2D.
std::vector< Point2D > r_
Координаты начал панелей
Definition Airfoil2D.h:69
std::vector< Point2D > v_
Скорости начал панелей
Definition Airfoil2D.h:72
double phiAfl
Поворот профиля
Definition Airfoil2D.h:100
std::vector< double > len
Длины панелей профиля
Definition Airfoil2D.h:94
std::vector< std::pair< Point2D, Point2D > > psn
Псевдонормали к панелям профиля
Definition Airfoil2D.h:86
const Point2D & getR(size_t q) const
Возврат константной ссылки на вершину профиля
Definition Airfoil2D.h:113
double area
Площадь профиля
Definition Airfoil2D.h:103
std::vector< Point2D > nrm
Нормали к панелям профиля
Definition Airfoil2D.h:81
std::vector< Point2D > tau
Касательные к панелям профиля
Definition Airfoil2D.h:91
bool inverse
Признак разворота нормалей (для расчета внутренних течений)
Definition Airfoil2D.h:76
Point2D rcm
Положение центра масс профиля
Definition Airfoil2D.h:97
size_t getNumberOfPanels() const
Возврат количества панелей на профиле
Definition Airfoil2D.h:163
Абстрактный класс, определяющий обтекаемый профиль
Definition Airfoil2D.h:182
const World2D & W
Константная ссылка на решаемую задачу
Definition Airfoil2D.h:185
void calcMeanEpsOverPanel()
Вычисление средних значений eps на панелях
Definition Airfoil2D.cpp:96
virtual void GetInfluenceFromSourcesToPanel(size_t panel, const Vortex2D *ptr, ptrdiff_t count, std::vector< double > &panelRhs) const
Вычисление влияния части подряд идущих источников из области течения на панель для правой части
virtual void GetInfluenceFromSourceSheetToVortex(size_t panel, const Vortex2D &vtx, Point2D &vel) const
Вычисление влияния слоя источников конкретной прямолинейной панели на вихрь в области течения
virtual void Scale(const Point2D &)
Масштабирование профиля
bool isAfter(size_t i, size_t j) const
Проверка, идет ли вершина i следом за вершиной j.
Definition Airfoil2D.cpp:76
bool isOutsideGabarits(const Point2D &r) const
Определяет, находится ли точка с радиус-вектором вне габаритного прямоугольника профиля
Definition Airfoil2D.cpp:90
Point2D upRight
Правый верхний угол габаритного прямоугольника профиля
Definition Airfoil2D.h:271
std::vector< double > gammaThrough
Суммарные циркуляции вихрей, пересекших панели профиля на прошлом шаге
Definition Airfoil2D.h:276
std::vector< int > wayToVertex
Номера путей к вершинам
Definition Airfoil2D.h:202
virtual void GetGabarits(double gap=0.02)
Вычисляет габаритный прямоугольник профиля
bool isInsideGabarits(const Point2D &r) const
Определяет, находится ли точка с радиус-вектором внутри габаритного прямоугольника профиля
Definition Airfoil2D.cpp:83
double J
Полярный момент инерции профиля относительно центра масс
Definition Airfoil2D.h:196
virtual void GetInfluenceFromVorticesToPanel(size_t panel, const Vortex2D *ptr, ptrdiff_t count, std::vector< double > &panelRhs) const
Вычисление влияния части подряд идущих вихрей из вихревого следа на панель для правой части
virtual void Move(const Point2D &dr)
Перемещение профиля
virtual void GetInfluenceFromVInfToPanel(std::vector< double > &vInfRhs) const
Вычисление влияния набегающего потока на панель для правой части
void CalcNrmTauLen()
Вычисление нормалей, касательных и длин панелей по текущему положению вершин
virtual bool IsPointInAirfoil(const Point2D &point) const
Определяет, находится ли точка с радиус-вектором внутри профиля
void lightningTest()
Тест на "отвещенность".
Point2D lowLeft
Левый нижний угол габаритного прямоугольника профиля
Definition Airfoil2D.h:270
std::vector< double > viscousStress
Нейросеть для коэффициентов I0 и I3 диффузионной скорости
Definition Airfoil2D.h:268
virtual void calcIQ(size_t p, const Airfoil &otherAirfoil, std::pair< Eigen::MatrixXd, Eigen::MatrixXd > &matrPair) const
Вычисление коэффициентов матрицы, состоящей из интегралов от (r-xi)/|r-xi|^2.
double m
Масса профиля
Definition Airfoil2D.h:193
virtual void GetInfluenceFromVortexSheetToVortex(size_t panel, const Vortex2D &vtx, Point2D &vel) const
Вычисление влияния вихревых слоев (свободный + присоединенный) конкретной прямолинейной панели на вих...
virtual void GetDiffVelocityI0I3ToSetOfPointsAndViscousStresses(const WakeDataBase &pointsDb, std::vector< double > &domainRadius, std::vector< double > &I0, std::vector< Point2D > &I3)
Вычисление числителей и знаменателей диффузионных скоростей в заданном наборе точек,...
Airfoil(const World2D &W_, const size_t numberInPassport_)
Definition Airfoil2D.cpp:61
const size_t numberInPassport
Номер профиля в паспорте
Definition Airfoil2D.h:188
std::vector< std::vector< Point2D > > possibleWays
Возможные пути внутри профиля от точки (0, 0) к центрам всех панелей
Definition Airfoil2D.h:199
virtual void Rotate(double alpha)
Поворот профиля
virtual std::vector< double > getA(size_t p, size_t i, const Airfoil &airfoil, size_t j) const
Вычисление коэффициентов матрицы A для расчета влияния панели на панель
std::vector< double > meanEpsOverPanel
Средние значения Eps на панелях
Definition Airfoil2D.h:209
virtual void ReadFromFile(const std::string &dir)
Считывание профиля из файла
Абстрактный класс, определяющий способ удовлетворения граничного условия на обтекаемом профиле
Definition Boundary2D.h:65
virtual void GetInfluenceFromVorticesToRectPanel(size_t panel, const Vortex2D *ptr, ptrdiff_t count, std::vector< double > &wakeRhs) const =0
Вычисление влияния части подряд идущих вихрей из вихревого следа на прямолинейную панель для правой ч...
virtual void GetInfluenceFromSourcesToRectPanel(size_t panel, const Vortex2D *ptr, ptrdiff_t count, std::vector< double > &wakeRhs) const =0
Вычисление влияния части подряд источников из области течения на прямолинейную панель для правой част...
virtual void GetInfluenceFromSourceSheetAtRectPanelToVortex(size_t panel, const Vortex2D &vtx, Point2D &vel) const =0
Вычисление влияния слоя источников конкретной прямолинейной панели на вихрь в области течения
virtual void GetInfluenceFromVInfToRectPanel(std::vector< double > &vInfRhs) const =0
Вычисление влияния набегающего потока на прямолинейную панель для правой части
std::vector< std::pair< int, int > > vortexBeginEnd
Номера первого и последнего вихрей, рождаемых на каждой панели профиля (формируется после решения СЛА...
Definition Boundary2D.h:83
virtual void GetInfluenceFromVortexSheetAtRectPanelToVortex(size_t panel, const Vortex2D &vtx, Point2D &vel) const =0
Вычисление влияния вихревых слоев (свободный + присоединенный) конкретной прямолинейной панели на вих...
VirtualWake virtualWake
Виртуальный вихревой след конкретного профиля
Definition Boundary2D.h:86
const bool isMoves
Переменная, отвечающая за то, двигается профиль или нет
const bool isDeform
Переменная, отвечающая за то, деформируется профиль или нет
WakeDiscretizationProperties wakeDiscretizationProperties
Структура с параметрами дискретизации вихревого следа
Definition Passport2D.h:304
std::vector< AirfoilParams > airfoilParams
Список структур с параметрами профилей
Definition Passport2D.h:276
NumericalSchemes numericalSchemes
Структура с используемыми численными схемами
Definition Passport2D.h:307
std::vector< VortexesParams > virtualVortexesParams
Вектор струтур, определяющий параметры виртуальных вихрей для профилей
Definition Velocity2D.h:115
VortexesParams wakeVortexesParams
Струтура, определяющая параметры вихрей в следе
Definition Velocity2D.h:112
Класс, опеделяющий набор вихрей
std::vector< Vortex2D > vtx
Список вихревых элементов
Класс, опеделяющий текущую решаемую задачу
Definition World2D.h:77
size_t getNumberOfAirfoil() const
Возврат количества профилей в задаче
Definition World2D.h:180
const Wake & getWake() const
Возврат константной ссылки на вихревой след
Definition World2D.h:232
const Airfoil & getAirfoil(size_t i) const
Возврат константной ссылки на объект профиля
Definition World2D.h:163
const Mechanics & getMechanics(size_t i) const
Возврат константной ссылки на объект механики
Definition World2D.h:219
const std::pair< Eigen::MatrixXd, Eigen::MatrixXd > & getIQ(size_t i, size_t j) const
Возврат константной ссылки на объект, связанный с матрицей интегралов от (r-xi)/|r-xi|^2.
Definition World2D.h:283
const Velocity & getVelocity() const
Возврат константной ссылки на объект для вычисления скоростей
Definition World2D.h:253
const Gpu & getCuda() const
Возврат константной ссылки на объект, связанный с видеокартой (GPU)
Definition World2D.h:273
const Passport & getPassport() const
Возврат константной ссылки на паспорт
Definition World2D.h:263
Passport & getNonConstPassport() const
Возврат неконстантной ссылки на паспорт
Definition World2D.h:268
const Boundary & getBoundary(size_t i) const
Возврат константной ссылки на объект граничного условия
Definition World2D.h:186
Airfoil & getNonConstAirfoil(size_t i) const
Возврат неконстантной ссылки на объект профиля
Definition World2D.h:175
TimeDiscretizationProperties timeDiscretizationProperties
Структура с параметрами процесса интегрирования по времени
std::string dir
Рабочий каталог задачи
Класс, опеделяющий двумерный вектор
Definition Point2D.h:77
Класс, позволяющий выполнять предварительную обработку файлов
Класс, позволяющий выполнять разбор файлов и строк с настройками и параметрами
bool get(const std::string &name, std::vector< Point2D > &res, const std::vector< Point2D > *defValue=nullptr, bool echoDefault=true) const
Считывание вектора из двумерных точек из базы данных
Класс, опеделяющий двумерный вихревой элемент
Definition Vortex2D.h:59
HD Point2D & r()
Функция для доступа к радиус-вектору вихря
Definition Vortex2D.h:92
HD double & g()
Функция для доступа к циркуляции вихря
Definition Vortex2D.h:100
VMlib::LogStream & getInfo() const
Возврат ссылки на объект LogStream Используется в техничеcких целях для организации вывода
Definition WorldGen.h:82
size_t getCurrentStep() const
Возврат константной ссылки на параметры распараллеливания по MPI.
Definition WorldGen.h:99
Шаблонный класс, определяющий матрицу фиксированного размера Фактически представляет собой массив,...
Definition nummatrix.h:66
Шаблонный класс, определяющий вектор фиксированной длины Фактически представляет собой массив,...
Definition numvector.h:99
auto length2() const -> typename std::remove_const< typename std::remove_reference< decltype(this->data[0])>::type >::type
Вычисление квадрата нормы (длины) вектора
Definition numvector.h:386
size_t size() const
Definition numvector.h:114
auto unit(P newlen=1) const -> numvector< typename std::remove_const< decltype(this->data[0] *newlen)>::type, n >
Вычисление орта вектора или вектора заданной длины, коллинеарного данному
Definition numvector.h:402
P length() const
Вычисление 2-нормы (длины) вектора
Definition numvector.h:374
double Lambda(const Point2D &p, const Point2D &s)
Вспомогательная функция вычисления логарифма отношения норм векторов
Definition defs.cpp:268
T sqr(T x)
Возведение числа в квадрат
Definition defs.h:455
double Alpha(const Point2D &p, const Point2D &s)
Вспомогательная функция вычисления угла между векторами
Definition defs.cpp:262
Point2D Omega(const Point2D &a, const Point2D &b, const Point2D &c)
Вспомогательная функция вычисления величины .
Definition defs.cpp:274
Описание класса nummatrix.
Структура, задающая параметры профиля
Definition Passport2D.h:205
Point2D basePoint
Смещение центра масс (перенос профиля)
Definition Passport2D.h:216
std::string fileAirfoil
Имя файла с начальным состоянием профилей (без полного пути)
Definition Passport2D.h:207
double angle
Угол поворота (угол атаки)
Definition Passport2D.h:225
Point2D scale
Коэффициент масштабирования
Definition Passport2D.h:219
size_t requiredNPanels
Желаемое число панелей для разбиения геометрии
Definition Passport2D.h:213
bool inverse
Признак разворота нормалей (для расчета внутреннего течения)
Definition Passport2D.h:231
std::pair< std::string, int > velocityComputation
Definition Passport2D.h:180
Структура, определяющая параметры виртуальных вихрей для отдельного профиля
Definition Velocity2D.h:70
std::vector< double > epsastWake
Вектор характерных радиусов вихревых доменов (eps*)
Definition Velocity2D.h:90
double getMinEpsAst() const
Функция минимально возможного значения для epsAst.
Definition Passport2D.h:153
int saveVPstep
Шаг вычисления и сохранения скорости и давления
Definition PassportGen.h:86