VM2D 1.14
Vortex methods for 2D flows simulation
Loading...
Searching...
No Matches
Velocity2D.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: Velocity2D.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 "Velocity2D.h"
41
42#include "Airfoil2D.h"
43#include "Boundary2D.h"
44#include "MeasureVP2D.h"
45#include "Mechanics2D.h"
46#include "StreamParser.h"
47#include "Wake2D.h"
48#include "World2D.h"
49#include "Gmres2D.h"
50
51
52// Fast Multipole Method
53#ifdef USE_FMM
54#include "multipole.h"
55#include "multipole3d.h"
56#include "special_functions.h"
57#endif
58
59using namespace VM2D;
60
61//Вычисление диффузионных скоростей вихрей и виртуальных вихрей в вихревом следе
63{
64 // !!! пелена на пелену
65#if (defined(__CUDACC__) || defined(USE_CUDA)) && (defined(CU_I1I2))
67 GPUCalcDiffVeloI1I2ToSetOfPointsFromWake(W.getWake(), wakeVortexesParams.epsastWake, W.getWake(), wakeVortexesParams.I1, wakeVortexesParams.I2);
68 else
70#else
72#endif
73
74
75 for (size_t bou = 0; bou < W.getNumberOfBoundary(); ++bou)
76 {
77 //не нужно, т.к. сделано выше перед началом вычисления скоростей
78 //W.getNonConstBoundary(bou).virtualWake.WakeSynchronize();
79
80
81 //виртуальные на границе на след
82#if (defined(__CUDACC__) || defined(USE_CUDA)) && (defined(CU_I1I2))
83 GPUCalcDiffVeloI1I2ToSetOfPointsFromSheets(W.getWake(), wakeVortexesParams.epsastWake, W.getBoundary(bou), wakeVortexesParams.I1, wakeVortexesParams.I2);
84#else
86#endif
87 } //for bou
88
89 Point2D I2;
90 double I1;
91
92#pragma omp parallel for private(I1, I2)
93 for (int vt = 0; vt < (int)wakeVortexesParams.diffVelo.size(); ++vt)
94 {
95 I2 = wakeVortexesParams.I2[vt];
96 I1 = wakeVortexesParams.I1[vt];
97
98 if (fabs(I1) < 1.e-8)
99 wakeVortexesParams.diffVelo[vt] = { 0.0, 0.0 };
100 else
102 }
103}//CalcDiffVeloI1I2()
104
105
107{
108 for (size_t afl = 0; afl < W.getNumberOfAirfoil(); ++afl)
109 {
110#if (defined(__CUDACC__) || defined(USE_CUDA)) && (defined(CU_I0I3))
111 W.getNonConstAirfoil(afl).GPUGetDiffVelocityI0I3ToSetOfPointsAndViscousStresses(wakeVortexesParams.epsastWake, wakeVortexesParams.I0, wakeVortexesParams.I3);
112#else
114#endif
115 }
116
117
118 //влияние поверхности
119 Point2D I3;
120 double I0;
121
122 double domrad = 0.0;
123
124#pragma omp parallel for private(I0, I3, domrad)
125 for (int vt = 0; vt < (int)wakeVortexesParams.diffVelo.size(); ++vt)
126 {
128
129 wakeVortexesParams.I0[vt] *= domrad;
130 wakeVortexesParams.I0[vt] += DPI * sqr(domrad);
131
132
133 I3 = wakeVortexesParams.I3[vt];
134 I0 = wakeVortexesParams.I0[vt];
135
136
137 if (fabs(I0) > 1.e-8)
138 wakeVortexesParams.diffVelo[vt] += I3 * (1.0 / I0);
139 }
140}//CalcDiffVeloI0I3()
141
142
143void Velocity::LimitDiffVelo(std::vector<Point2D>& diffVel)
144{
145 for (size_t i = 0; i < diffVel.size(); ++i)
146 {
147 Point2D& diffV = diffVel[i];
148
149 if (diffV.length() > 1.5 * W.getPassport().physicalProperties.vRef)
151 }
152}
153
154// Вычисление диффузионных скоростей
156{
157 W.getTimers().start("DiffVel");
158
159 double t12start, t12finish;
160 double t03start, t03finish;
161 double tOtherstart, tOtherfinish;
162
164 {
165 t12start = omp_get_wtime();
167 t12finish = omp_get_wtime();
168
169 t03start = omp_get_wtime();
171 t03finish = omp_get_wtime();
172
173 tOtherstart = omp_get_wtime();
174 for (Point2D& diffV : wakeVortexesParams.diffVelo)
176
177 //контроль застрелов диффузионной скорости
179
180 for (size_t afl = 0; afl < W.getNumberOfAirfoil(); ++afl)
181 for (size_t i = 0; i < W.getAirfoil(afl).viscousStress.size(); ++i)
183
184 //SaveVisStress();
185
186
187 //Заполнение структуры данных виртуальных вихрей
188 size_t nVirtVortices = 0;
189 for (size_t bou = 0; bou < W.getNumberOfBoundary(); ++bou)
190 nVirtVortices += W.getBoundary(bou).virtualWake.vtx.size();
191
192 size_t counter = wakeVortexesParams.convVelo.size() - nVirtVortices;
193
194 for (size_t bou = 0; bou < W.getNumberOfBoundary(); ++bou)
195 for (size_t v = 0; v < W.getBoundary(bou).virtualWake.vtx.size(); ++v)
196 {
197 virtualVortexesParams[bou].diffVelo[v] = wakeVortexesParams.diffVelo[counter];
198 virtualVortexesParams[bou].I0[v] = wakeVortexesParams.I0[counter];
199 virtualVortexesParams[bou].I1[v] = wakeVortexesParams.I1[counter];
200 virtualVortexesParams[bou].I2[v] = wakeVortexesParams.I2[counter];
201 virtualVortexesParams[bou].I3[v] = wakeVortexesParams.I3[counter];
202 ++counter;
203 }
204
205 tOtherfinish = omp_get_wtime();
206 }
207 W.getTimers().stop("DiffVel");
208
209}// CalcDiffVelo()
210
211
212//Вычисление числителей и знаменателей диффузионных скоростей в заданном наборе точек
213void Velocity::CalcDiffVeloI1I2ToSetOfPointsFromWake(const WakeDataBase& pointsDb, const std::vector<double>& domainRadius, const WakeDataBase& vorticesDb, std::vector<double>& I1, std::vector<Point2D>& I2)
214{
215 std::vector<double> selfI1(pointsDb.vtx.size(), 0.0);
216 std::vector<Point2D> selfI2(pointsDb.vtx.size(), { 0.0, 0.0 });
217
218#pragma warning (push)
219#pragma warning (disable: 4101)
220 //Локальные переменные для цикла
221 Point2D Rij;
222 double rij, expr;
223 double diffRadius, domRad;
224 double left;
225 double right;
226 double posJx;
227#pragma warning (pop)
228
229#pragma omp parallel for default(none) shared(selfI1, selfI2, domainRadius, vorticesDb, pointsDb) private(Rij, rij, expr, diffRadius, domRad, left, right, posJx)
230 for (int i = 0; i < pointsDb.vtx.size(); ++i)
231 {
232 const Vortex2D& vtxI = pointsDb.vtx[i];
233
234 domRad = std::max(domainRadius[i], W.getPassport().wakeDiscretizationProperties.getMinEpsAst());
235
237 diffRadius = 8.0 * domRad;
238
239 left = vtxI.r()[0] - diffRadius;
240 right = vtxI.r()[0] + diffRadius;
241
242 for (size_t j = 0; j < vorticesDb.vtx.size(); ++j)
243 {
244 const Vortex2D& vtxJ = vorticesDb.vtx[j];
245 posJx = vtxJ.r()[0];
246
247 if ((left < posJx) && (posJx < right))
248 {
249 Rij = vtxI.r() - vtxJ.r();
250 rij = Rij.length();
251 if (rij < diffRadius && rij > 1.e-10)
252 {
253 expr = exp(-rij / domRad);
254 selfI2[i] += (vtxJ.g()* expr / rij) * Rij;
255 selfI1[i] += vtxJ.g()*expr;
256 }
257 }//if (rij>1e-6)
258 }//for j
259 } // for r
260
261
262 for (size_t i = 0; i < I1.size(); ++i)
263 {
264 I1[i] += selfI1[i];
265 I2[i] += selfI2[i];
266 }
267}//CalcDiffVeloI1I2ToSetOfPointsFromWake(...)
268
269
270//Вычисление числителей и знаменателей диффузионных скоростей в заданном наборе точек
271void Velocity::CalcDiffVeloI1I2ToSetOfPointsFromSheets(const WakeDataBase& pointsDb, const std::vector<double>& domainRadius, const Boundary& bnd, std::vector<double>& I1, std::vector<Point2D>& I2)
272{
273 double tCPUSTART, tCPUEND;
274
275 tCPUSTART = omp_get_wtime();
276
277 std::vector<double> selfI1(pointsDb.vtx.size(), 0.0);
278 std::vector<Point2D> selfI2(pointsDb.vtx.size(), { 0.0, 0.0 });
279
280#pragma warning (push)
281#pragma warning (disable: 4101)
282 //Локальные переменные для цикла
283 Point2D Rij;
284 double rij, expr;
285 double diffRadius;
286 double left;
287 double right;
288 double posJx;
289 double domRad;
290#pragma warning (pop)
291
292
293#pragma omp parallel for default(none) shared(selfI1, selfI2, domainRadius, bnd, pointsDb, std::cout) private(Rij, rij, expr, domRad, diffRadius, left, right, posJx)
294 for (int i = 0; i < pointsDb.vtx.size(); ++i)
295 {
296 const Vortex2D& vtxI = pointsDb.vtx[i];
297
298 domRad = std::max(domainRadius[i], W.getPassport().wakeDiscretizationProperties.getMinEpsAst());
299
301 diffRadius = 8.0 * domRad;
302
303 left = vtxI.r()[0] - diffRadius;
304 right = vtxI.r()[0] + diffRadius;
305
306 for (size_t j = 0; j < bnd.afl.getNumberOfPanels(); ++j)
307 {
309 const int nQuadPt = 3;
310
312 const double ptG = bnd.sheets.freeVortexSheet(j, 0) * bnd.afl.len[j] / nQuadPt;
313
314 for (int q = 0; q < nQuadPt; ++q)
315 {
316 const Point2D& ptJ = bnd.afl.getR(j) + bnd.afl.tau[j] * (q + 0.5) * bnd.afl.len[j] * (1.0 / nQuadPt); // vorticesDb.vtx[j];
317 posJx = ptJ[0];
318
319 if ((left < posJx) && (posJx < right))
320 {
321 Rij = vtxI.r() - ptJ;
322 rij = Rij.length();
323 if (rij < diffRadius && rij > 1.e-10)
324 {
325 expr = exp(-rij / domRad);
326 selfI2[i] += (ptG * expr / rij) * Rij;
327 selfI1[i] += ptG * expr;
328 }
329 }//if (rij>1e-6)
330 }
331 }//for j
332 } // for r
333
334 for (size_t i = 0; i < I1.size(); ++i)
335 {
336 I1[i] += selfI1[i];
337 I2[i] += selfI2[i];
338 }
339
340 tCPUEND = omp_get_wtime();
341 //W.getInfo('t') << "DIFF_CPU: " << tCPUEND - tCPUSTART << std::endl;
342}//CalcDiffVeloI1I2ToSetOfPointsFromSheets(...)
343
344
345
346#if defined (USE_CUDA)
347//Вычисление числителей и знаменателей диффузионных скоростей в заданном наборе точек
348void Velocity::GPUCalcDiffVeloI1I2ToSetOfPointsFromWake(const WakeDataBase& pointsDb, const std::vector<double>& domainRadius, const WakeDataBase& vorticesDb, std::vector<double>& I1, std::vector<Point2D>& I2, bool useMesh)
349{
350 if ( (&pointsDb == &W.getWake()) || (&pointsDb == &W.getBoundary(0).virtualWake) )
351 {
352 size_t npt = pointsDb.vtx.size();
353
354 if ((W.getNumberOfBoundary() > 0) && (&pointsDb == &W.getBoundary(0).virtualWake))
355 {
356 for (size_t q = 1; q < W.getNumberOfBoundary(); ++q)
357 npt += W.getBoundary(q).virtualWake.vtx.size();
358 }
359
360 double*& dev_ptr_pt = pointsDb.devVtxPtr;
361 double*& dev_ptr_dr = pointsDb.devRadPtr;
362
363 const size_t nvt = vorticesDb.vtx.size();
364 double*& dev_ptr_vt = vorticesDb.devVtxPtr;
365
366 std::vector<Point2D> newI2(npt); // I2.size());
367 std::vector<double> newI1(npt); // I1.size());
368
369 double*& dev_ptr_i1 = pointsDb.devI1Ptr;
370 double*& dev_ptr_i2 = pointsDb.devI2Ptr;
372
373 if ((nvt > 0) && (npt > 0))
374 {
375 //СЕТКА
376 if (!useMesh)
377 cuCalculateDiffVeloWake(npt, dev_ptr_pt, nvt, dev_ptr_vt, dev_ptr_i1, dev_ptr_i2, dev_ptr_dr, minRad);
378 else
379 cuCalculateDiffVeloWakeMesh(npt, dev_ptr_pt, nvt, dev_ptr_vt, W.getWake().devMeshPtr, W.getPassport().wakeDiscretizationProperties.epscol, dev_ptr_i1, dev_ptr_i2, dev_ptr_dr);
380
381 W.getCuda().CopyMemFromDev<double, 2>(npt, dev_ptr_i2, (double*)newI2.data(), 10);
382 W.getCuda().CopyMemFromDev<double, 1>(npt, dev_ptr_i1, newI1.data(), 11);
383
384 if (&pointsDb == &W.getWake())
385 {
386 for (size_t q = 0; q < I2.size(); ++q)
387 {
388 I1[q] += newI1[q];
389 I2[q] += newI2[q];
390 }
391 }
392
393 if ((W.getNumberOfBoundary() > 0) && (&pointsDb == &W.getBoundary(0).virtualWake))
394 {
395 size_t curGlobPnl = 0;
396
397 for (size_t s = 0; s < W.getNumberOfAirfoil(); ++s) //W.getNumberOfAirfoil()
398 {
399 size_t nv = W.getBoundary(s).virtualWake.vtx.size();
400 for (size_t q = 0; q < nv; ++q)
401 {
402 W.getNonConstVelocity().virtualVortexesParams[s].I1[q] += newI1[curGlobPnl + q];
403 W.getNonConstVelocity().virtualVortexesParams[s].I2[q] += newI2[curGlobPnl + q];
404 }
405 curGlobPnl += nv;
406 }
407 }
408 }
409 }
410}//GPUCalcDiffVeloI1I2ToSetOfPointsFromWake(...)
411
412
413//Вычисление числителей и знаменателей диффузионных скоростей в заданном наборе точек
414void Velocity::GPUCalcDiffVeloI1I2ToSetOfPointsFromSheets(const WakeDataBase& pointsDb, const std::vector<double>& domainRadius, const Boundary& bou, std::vector<double>& I1, std::vector<Point2D>& I2, bool useMesh)
415{
416 if ((&pointsDb == &W.getWake()) || (&pointsDb == &W.getBoundary(0).virtualWake))
417 {
418 if (bou.afl.numberInPassport == 0)
419 {
420 size_t npt = pointsDb.vtx.size();
421 double*& dev_ptr_pt = pointsDb.devVtxPtr;
422 double*& dev_ptr_dr = pointsDb.devRadPtr;
423
424 if ((W.getNumberOfBoundary() > 0) && (&pointsDb == &W.getBoundary(0).virtualWake))
425 {
426 for (size_t q = 1; q < W.getNumberOfBoundary(); ++q)
427 npt += W.getBoundary(q).virtualWake.vtx.size();
428 }
429
430 size_t npnl = bou.afl.getNumberOfPanels(); //vorticesDb.vtx.size();
431 double*& dev_ptr_r = bou.afl.devRPtr;
432 double*& dev_ptr_freeVortexSheet = bou.afl.devFreeVortexSheetPtr;
433 double*& dev_ptr_freeVortexSheetLin = bou.afl.devFreeVortexSheetLinPtr;
434
435 for (size_t q = 1; q < W.getNumberOfBoundary(); ++q)
436 npnl += W.getBoundary(q).afl.getNumberOfPanels();
437
438 std::vector<Point2D> newI2(npt);
439 std::vector<double> newI1(npt);
440
441 double*& dev_ptr_i1 = pointsDb.devI1Ptr;
442 double*& dev_ptr_i2 = pointsDb.devI2Ptr;
444
445 if ((npnl > 0) && (npt > 0))
446 {
448 //if (!useMesh)
449 cuCalculateDiffVeloWakeFromPanels(npt, dev_ptr_pt, npnl, dev_ptr_r, dev_ptr_freeVortexSheet, dev_ptr_freeVortexSheetLin, dev_ptr_i1, dev_ptr_i2, dev_ptr_dr, minRad);
450 //else
451 // cuCalculateDiffVeloWakeMesh(npt, dev_ptr_pt, nvt, dev_ptr_vt, W.getWake().devMeshPtr, W.getPassport().wakeDiscretizationProperties.epscol, dev_ptr_i1, dev_ptr_i2, dev_ptr_dr);
452
453 W.getCuda().CopyMemFromDev<double, 2>(npt, dev_ptr_i2, (double*)newI2.data(), 12);
454 W.getCuda().CopyMemFromDev<double, 1>(npt, dev_ptr_i1, newI1.data(), 13);
455
456 if (&pointsDb == &W.getWake())
457 {
458 for (size_t q = 0; q < I2.size(); ++q)
459 {
460 I1[q] += newI1[q];
461 I2[q] += newI2[q];
462 }
463 }
464
465 if ((W.getNumberOfBoundary() > 0) && (&pointsDb == &W.getBoundary(0).virtualWake))
466 {
467 size_t curGlobPnl = 0;
468
469 for (size_t s = 0; s < W.getNumberOfAirfoil(); ++s) //W.getNumberOfAirfoil()
470 {
471 size_t nv = W.getBoundary(s).virtualWake.vtx.size();
472 for (size_t q = 0; q < nv; ++q)
473 {
474 W.getNonConstVelocity().virtualVortexesParams[s].I1[q] += newI1[curGlobPnl + q];
475 W.getNonConstVelocity().virtualVortexesParams[s].I2[q] += newI2[curGlobPnl + q];
476 }
477 curGlobPnl += nv;
478 }
479 }
480 }
481 }
482 }
483}//GPUCalcDiffVeloI1I2ToSetOfPointsFromSheets(...)
484#endif
485
486#if defined(USE_CUDA)
487void Velocity::GPUDiffVeloFAST(const std::vector<double>& domainRadius, std::vector<double>& I1, std::vector<Point2D>& I2)
488{
489 size_t nvt = W.getWake().vtx.size();
490
491 std::vector<Point2D> newI2(nvt);
492 std::vector<double> newI1(nvt);
493
494 double*& dev_ptr_i1 = W.getWake().devI1Ptr;
495 double*& dev_ptr_i2 = W.getWake().devI2Ptr;
496
497 size_t* const& dev_nVortices = W.getCuda().dev_ptr_nVortices;
498
499 if (nvt > 0)
500 {
501 W.getNonConstCuda().RefreshWake(4);
502 auto& treeWake = *W.getCuda().inflTreeWake;
503 treeWake.MemoryAllocate((int)W.getCuda().n_CUDA_wake);
504 treeWake.Update((int)W.getWake().vtx.size(), W.getWake().devVtxPtr);
505 treeWake.Build();
506 treeWake.UpwardTraversal(W.getPassport().numericalSchemes.nbodyMultipoleOrder);
507
508 double time = treeWake.I1I2CalculationWrapper(W.getPassport().wakeDiscretizationProperties.getMinEpsAst(), dev_ptr_i1, (Point2D*)dev_ptr_i2, W.getWake().devRadPtr);
509
510 W.getCuda().CopyMemFromDev<double, 2>(nvt, dev_ptr_i2, (double*)newI2.data(), 10);
511 W.getCuda().CopyMemFromDev<double, 1>(nvt, dev_ptr_i1, newI1.data(), 11);
512
513 for (size_t q = 0; q < I2.size(); ++q)
514 {
515 I1[q] += newI1[q];
516 I2[q] += newI2[q];
517 }
518 }
519}
520#endif
521
522// Очистка старых массивов под хранение скоростей, выделение новой памяти и обнуление
524{
525 W.getTimers().start("ConvVel");
527 wakeVortexesParams.convVelo.resize(W.getWake().vtx.size(), { 0.0, 0.0 });
528 W.getTimers().stop("ConvVel");
529
530 W.getTimers().start("DiffVel");
531 wakeVortexesParams.I0.clear();
532 wakeVortexesParams.I0.resize(W.getWake().vtx.size(), 0.0);
533
534 wakeVortexesParams.I1.clear();
535 wakeVortexesParams.I1.resize(W.getWake().vtx.size(), 0.0);
536
537 wakeVortexesParams.I2.clear();
538 wakeVortexesParams.I2.resize(W.getWake().vtx.size(), { 0.0, 0.0 });
539
540 wakeVortexesParams.I3.clear();
541 wakeVortexesParams.I3.resize(W.getWake().vtx.size(), { 0.0, 0.0 });
542
544 wakeVortexesParams.diffVelo.resize(W.getWake().vtx.size(), { 0.0, 0.0 });
545
547 wakeVortexesParams.epsastWake.resize(W.getWake().vtx.size(), 0.0);
548 W.getTimers().stop("DiffVel");
549
550 //Создаем массивы под виртуальные вихри
551 W.getTimers().start("ConvVel");
552 virtualVortexesParams.clear();
554 W.getTimers().stop("ConvVel");
555
556 for (size_t bou = 0; bou < W.getNumberOfBoundary(); ++bou)
557 {
558 W.getTimers().start("ConvVel");
559 virtualVortexesParams[bou].convVelo.clear();
560 virtualVortexesParams[bou].convVelo.resize(W.getBoundary(bou).virtualWake.vtx.size(), { 0.0, 0.0 });
561 W.getTimers().stop("ConvVel");
562
563 W.getTimers().start("DiffVel");
564 virtualVortexesParams[bou].I0.clear();
565 virtualVortexesParams[bou].I0.resize(W.getBoundary(bou).virtualWake.vtx.size(), 0.0);
566
567 virtualVortexesParams[bou].I1.clear();
568 virtualVortexesParams[bou].I1.resize(W.getBoundary(bou).virtualWake.vtx.size(), 0.0);
569
570 virtualVortexesParams[bou].I2.clear();
571 virtualVortexesParams[bou].I2.resize(W.getBoundary(bou).virtualWake.vtx.size(), { 0.0, 0.0 });
572
573 virtualVortexesParams[bou].I3.clear();
574 virtualVortexesParams[bou].I3.resize(W.getBoundary(bou).virtualWake.vtx.size(), { 0.0, 0.0 });
575
576 virtualVortexesParams[bou].diffVelo.clear();
577 virtualVortexesParams[bou].diffVelo.resize(W.getBoundary(bou).virtualWake.vtx.size(), { 0.0, 0.0 });
578
579 virtualVortexesParams[bou].epsastWake.clear();
580 virtualVortexesParams[bou].epsastWake.resize(W.getBoundary(bou).virtualWake.vtx.size(), 0.0);
581
582 W.getTimers().stop("DiffVel");
583 }
584}//ResizeAndZero()
585
586
588{
590 // if ((W.getPassport().timeDiscretizationProperties.saveVTK > 0) && (W.ifDivisible(10)) && (W.getNumberOfAirfoil() > 0))
591 {
592 for (size_t q = 0; q < W.getNumberOfAirfoil(); ++q)
593 {
594 if (q == 0)
596
597 std::stringstream ss;
598 ss << "VisStress_" << q << "-";
600 std::ofstream outfile;
601 outfile.open(W.getPassport().dir + "visStress/" + fname);
602
603 outfile << W.getAirfoil(q).viscousStress.size() << std::endl; //Сохранение числа вихрей в пелене
604
605 for (size_t i = 0; i < W.getAirfoil(q).viscousStress.size(); ++i)
606 {
607 const Point2D& r = 0.5 * (W.getAirfoil(q).getR(i + 1) + W.getAirfoil(q).getR(i));
608 double gi = W.getAirfoil(q).viscousStress[i];
609 outfile << static_cast<int>(i) << " " << r[0] << " " << r[1] << " " << gi << std::endl;
610 }//for i
611 outfile.close();
612 }
613 }
614}//SaveVisStress()
615
616
617
618//Генерация вектора влияния вихревого следа на профиль
619void Velocity::GetWakeInfluenceToRhs(const Airfoil& afl, std::vector<double>& wakeRhs) const
620{
621 size_t np = afl.getNumberOfPanels();
622 size_t shDim = W.getBoundary(afl.numberInPassport).sheetDim;
623
624 wakeRhs.resize(W.getBoundary(afl.numberInPassport).GetUnknownsSize());
625
626 //локальные переменные для цикла
627 std::vector<double> velI(shDim, 0.0);
628
629#pragma omp parallel for default(none) shared(shDim, afl, np, wakeRhs, IDPI) private(velI)
630 for (int i = 0; i < np; ++i)
631 {
632 velI.assign(shDim, 0.0);
633
634 if (W.getWake().vtx.size() > 0)
635 {
636 //Учет влияния следа
637 afl.GetInfluenceFromVorticesToPanel(i, W.getWake().vtx.data(), W.getWake().vtx.size(), velI);
638 }
639
640 if (W.getSource().vtx.size() > 0)
641 {
642 //Учет влияния источников
643 afl.GetInfluenceFromSourcesToPanel(i, W.getSource().vtx.data(), W.getSource().vtx.size(), velI);
644 }
645
646 for (size_t j = 0; j < shDim; ++j)
647 velI[j] *= IDPI / afl.len[i];
648
649 wakeRhs[i] = velI[0];
650
651 if (shDim != 1)
652 wakeRhs[np + i] = velI[1];
653 }//for i
654}//GetWakeInfluenceToRhs(...)
655
656void Velocity::CPUGetFASTWakeInfluenceToRhs(std::vector<double>& wakeRhs, std::vector<double>& wakeRhsLin) const
657{
658 const size_t nvt = W.getWake().vtx.size();
659
660
661 size_t nTotPan = 0;
662
663 for (size_t s = 0; s < W.getNumberOfAirfoil(); ++s)
664 nTotPan += W.getAirfoil(s).getNumberOfPanels();
665
666 wakeRhs.assign(nTotPan, 0.0);
667 wakeRhsLin.assign(nTotPan, 0.0);
668
669 if (nvt > 0)
670 {
671 auto& inflTree = W.getInflTreeWake();
672
673 auto& cntrTree = W.getCntrTreePnl();
674
676 cntrTree,
677 wakeRhs,
678 wakeRhsLin,
681 }
682}//CPUGetFASTWakeInfluenceToRhs(...)
683
684#if defined(USE_CUDA)
685//Генерация вектора влияния вихревого следа на профиль
686void Velocity::GPUGetWakeInfluenceToRhs(const Airfoil& afl, std::vector<double>& wakeVelo) const
687{
688 size_t shDim = W.getBoundary(afl.numberInPassport).sheetDim;
689
690 const size_t& nvt = W.getWake().vtx.size();
691 const size_t& nsr = W.getSource().vtx.size();
692
693 if (afl.numberInPassport == 0)
694 {
695 size_t nTotPan = 0;
696 for (size_t s = 0; s < W.getNumberOfAirfoil(); ++s)
697 nTotPan += W.getAirfoil(s).getNumberOfPanels();
698
699 double*& dev_ptr_pt = afl.devRPtr;
700 double*& dev_ptr_vt = W.getWake().devVtxPtr;
701 double*& dev_ptr_sr = W.getSource().devVtxPtr;
702 double*& dev_ptr_rhs = afl.devRhsPtr;
703 double*& dev_ptr_rhsLin = afl.devRhsLinPtr;
704
705 std::vector<double> locrhs(nTotPan);
706 std::vector<double> locrhsLin(nTotPan);
707
708 if ((nvt > 0) || (nsr > 0))
709 {
710 cuCalculateRhs(nTotPan, dev_ptr_pt, nvt, dev_ptr_vt, nsr, dev_ptr_sr, dev_ptr_rhs, dev_ptr_rhsLin);
711
712 std::vector<double> newRhs(nTotPan), newRhsLin(nTotPan);
713
714 W.getCuda().CopyMemFromDev<double, 1>(nTotPan, dev_ptr_rhs, newRhs.data(), 22);
715 if (shDim != 1)
716 W.getCuda().CopyMemFromDev<double, 1>(nTotPan, dev_ptr_rhsLin, newRhsLin.data(), 22);
717
718 size_t curGlobPnl = 0;
719 for (size_t s = 0; s < W.getNumberOfAirfoil(); ++s)
720 {
721 std::vector<double>& tmpRhs = W.getNonConstAirfoil(s).tmpRhs;
722 const size_t& np = W.getAirfoil(s).getNumberOfPanels();
723 tmpRhs.resize(0);
724 tmpRhs.insert(tmpRhs.end(), newRhs.begin() + curGlobPnl, newRhs.begin() + curGlobPnl + np);
725 if (shDim != 1)
726 tmpRhs.insert(tmpRhs.end(), newRhsLin.begin() + curGlobPnl, newRhsLin.begin() + curGlobPnl + np);
727
728 curGlobPnl += np;
729 }
730 }
731 }
732
733 if ((nvt > 0) || (nsr > 0))
734 wakeVelo = std::move(afl.tmpRhs);
735 else
736 wakeVelo.resize(afl.getNumberOfPanels() * (W.getPassport().numericalSchemes.boundaryCondition.second), 0.0);
737
738}//GPUGetWakeInfluenceToRhs(...)
739#endif
740
741#if defined(USE_CUDA)
742//Генерация вектора влияния вихревого следа на профиль
743void Velocity::GPUFASTGetWakeInfluenceToRhs(const Airfoil & afl, std::vector<double>&wakeVelo) const
744{
745 size_t shDim = W.getBoundary(afl.numberInPassport).sheetDim;
746
747 const size_t& nvt = W.getWake().vtx.size();
748 const size_t& nsr = W.getSource().vtx.size();
749
750 auto& inflTree = *W.getCuda().inflTreeWake;
752
753 if (afl.numberInPassport == 0)
754 {
755 size_t nTotPan = 0;
756 for (size_t s = 0; s < W.getNumberOfAirfoil(); ++s)
757 nTotPan += W.getAirfoil(s).getNumberOfPanels();
758
759 double*& dev_ptr_pt = afl.devRPtr;
760 double*& dev_ptr_vt = W.getWake().devVtxPtr;
761 double*& dev_ptr_sr = W.getSource().devVtxPtr;
762 double*& dev_ptr_rhs = afl.devRhsPtr;
763 double*& dev_ptr_rhsLin = afl.devRhsLinPtr;
764 std::vector<double> locrhs(nTotPan);
765 std::vector<double> locrhsLin(nTotPan);
766
767 if ((nvt > 0) || (nsr > 0))
768 {
769 //double timingsToRHS[7];
770
771 double* linPtr = (double*)((shDim == 1) ? nullptr : dev_ptr_rhsLin);
772
773 auto& inflTree = *W.getCuda().inflTreeWake;
774 auto& cntrTree = *W.getCuda().cntrTreePnl;
775
776 inflTree.DownwardTraversalVorticesToPanels(cntrTree, (double*)dev_ptr_rhs,
778
779
780 std::vector<double> newRhs(nTotPan), newRhsLin(nTotPan);
781
782 W.getCuda().CopyMemFromDev<double, 1>(nTotPan, dev_ptr_rhs, newRhs.data(), 22);
783 if (shDim != 1)
784 W.getCuda().CopyMemFromDev<double, 1>(nTotPan, dev_ptr_rhsLin, newRhsLin.data(), 22);
785
786 size_t curGlobPnl = 0;
787 for (size_t s = 0; s < W.getNumberOfAirfoil(); ++s)
788 {
789 std::vector<double>& tmpRhs = W.getNonConstAirfoil(s).tmpRhs;
790 const size_t& np = W.getAirfoil(s).getNumberOfPanels();
791 tmpRhs.resize(0);
792 tmpRhs.insert(tmpRhs.end(), newRhs.begin() + curGlobPnl, newRhs.begin() + curGlobPnl + np);
793 if (shDim != 1)
794 tmpRhs.insert(tmpRhs.end(), newRhsLin.begin() + curGlobPnl, newRhsLin.begin() + curGlobPnl + np);
795
796 curGlobPnl += np;
797 }
798 }
799 }
800
801 if ((nvt > 0) || (nsr > 0))
802 wakeVelo = std::move(afl.tmpRhs);
803 else
804 wakeVelo.resize(afl.getNumberOfPanels() * (W.getPassport().numericalSchemes.boundaryCondition.second), 0.0);
805
806}//GPUGetWakeInfluenceToRhsFAST(...)
807#endif
808
809
810
811void Velocity::FillRhs(Eigen::VectorXd& rhsReord) const
812{
813 Eigen::VectorXd locRhs;
814 std::vector<double> lastRhs(W.getNumberOfBoundary());
815
816 size_t currentRow = 0;
817 size_t currentSkosRow = 0;
818
819 size_t nAllVars = 0;
820 for (size_t bou = 0; bou < W.getNumberOfBoundary(); ++bou)
821 nAllVars += W.getBoundary(bou).GetUnknownsSize();
822
823 //Временные массивы для хранения правой части по быстрому методу (размеры задаются внутри CPUGetFASTWakeInfluenceToRhs)
824 std::vector<double> fastWakeRhs;
825 std::vector<double> fastWakeRhsLin;
826
827#ifndef USE_CUDA
828 if (W.getPassport().numericalSchemes.velocityComputation.second == 1) //Быстрый метод на CPU
829 CPUGetFASTWakeInfluenceToRhs(fastWakeRhs, fastWakeRhsLin);
830#endif
831 size_t curGlobPnl = 0;
832
833 for (size_t bou = 0; bou < W.getNumberOfBoundary(); ++bou)
834 {
835 const Airfoil& afl = W.getAirfoil(bou);
836 size_t np = afl.getNumberOfPanels();
837
838 size_t nVars;
839
840 nVars = W.getBoundary(bou).GetUnknownsSize();
841 locRhs.resize(nVars);
842
843 std::vector<double> wakeRhs;
844
845 double tt1 = omp_get_wtime();
846
847#if (defined(__CUDACC__) || defined(USE_CUDA)) && (defined(CU_RHS))
848 W.timerRhs.reset();
849 W.timerRhs.start();
851 GPUFASTGetWakeInfluenceToRhs(afl, wakeRhs);
852 else
853 GPUGetWakeInfluenceToRhs(afl, wakeRhs);
854 W.timerRhs.stop();
855#else
856 if (W.getPassport().numericalSchemes.velocityComputation.second == 1) //Собираем результат из быстрого метода
857 {
858 size_t shDim = W.getBoundary(afl.numberInPassport).sheetDim;
859
860 wakeRhs.assign(np * shDim, 0.0);
861
862 for (size_t i = 0; i < np; ++i)
863 {
864 wakeRhs[i] = fastWakeRhs[curGlobPnl + i];
865
866 if (shDim != 1)
867 wakeRhs[np + i] = fastWakeRhsLin[curGlobPnl + i];
868 }
869 curGlobPnl += np;
870 }
871 else
872 GetWakeInfluenceToRhs(afl, wakeRhs); //Прямой расчет
873
874 //std::string vname = VMlib::fileNameStep("rhsLinBH", 0, wakeRhs.size(), "txt");
875 //std::ofstream velofile;
876 //velofile.open(W.getPassport().dir + vname);
877
878 //for (int i = 0; i < wakeRhs.size(); ++i)
879 // velofile << wakeRhs[i] << "\n";
880 //velofile.close();
881 //exit(-124);
882#endif
883 std::vector<double> vInfRhs;
884 afl.GetInfluenceFromVInfToPanel(vInfRhs);
885
886#pragma omp parallel for \
887 default(none) \
888 shared(locRhs, afl, bou, wakeRhs, vInfRhs, np) \
889 schedule(dynamic, DYN_SCHEDULE)
890 for (int i = 0; i < (int)afl.getNumberOfPanels(); ++i)
891 {
892 locRhs(i) = -vInfRhs[i] - wakeRhs[i] + 0.25 * ((afl.getV(i) + afl.getV(i + 1)) & afl.tau[i]); //0.25 * (afl.getV(i) + afl.getV(i + 1))*afl.tau[i] - прямолинейные
893 if (W.getBoundary(bou).sheetDim > 1)
894 locRhs(np + i) = -vInfRhs[np + i] - wakeRhs[np + i];
895
897 //Если включен быстрый метод и есть подвижные тела, то пока что все равно вычисляем IQ
899 {
900 for (size_t q = 0; q < W.getNumberOfBoundary(); ++q)
901 {
902 const auto& sht = W.getBoundary(q).sheets;
903 const auto& iq = W.getIQ(bou, q);
904
905 const Airfoil& aflOther = W.getAirfoil(q);
906 if (W.getMechanics(q).isMoves)
907 {
908 for (size_t j = 0; j < aflOther.getNumberOfPanels(); ++j)
909 {
910 if ((i != j) || (bou != q))
911 {
912 locRhs(i) += -iq.first(i, j) * sht.attachedVortexSheet(j, 0);
913 locRhs(i) += -iq.second(i, j) * sht.attachedSourceSheet(j, 0);
914 }//if (i != j)
915
916 }//for j
917 }
918 }//for q
919 }
920 }//for i
921
922 lastRhs[bou] = 0.0;
923
924#pragma omp for
925 for (int q = 0; q < (int)afl.gammaThrough.size(); ++q)
926 lastRhs[bou] += afl.gammaThrough[q];
927
929
930 const double currT = W.getCurrentTime();
931 const double dt = W.getPassport().timeDiscretizationProperties.dt;
932
933 lastRhs[bou] += (W.getMechanics(bou).circulation - W.getMechanics(bou).circulationOld) * (W.getAirfoil(bou).inverse ? 1.0 : -1.0);
934
936
937 //размазываем правую часть
938 for (size_t i = 0; i < nVars; ++i)
939 rhsReord(i + currentSkosRow) = locRhs(i);
940
941 rhsReord(nAllVars + bou) = lastRhs[bou];
942
943 currentRow += nVars + 1;
944 currentSkosRow += nVars;
945 }// for bou
946}
947
948
949
950
951//Вычисление конвективных скоростей вихрей и виртуальных вихрей в вихревом следе, а также в точках wakeVP
953{
954 W.getTimers().start("ConvVel");
955
956 //Влияние следа на след
957#if (defined(__CUDACC__) || defined(USE_CUDA)) && (defined(CU_CONV_TOWAKE))
958 //Вызов виртуальной функции
959
962
963 VMlib::vmTimer timerAgpu, timerBgpu;
964
965 std::unique_ptr<BHcu::CudaTreeInfo>& cntrTree = W.getNonConstCuda().cntrTreeWake;
966 auto& treeWake = *W.getNonConstCuda().inflTreeWake;
968 {
969 cntrTree->MemoryAllocate((int)W.getCuda().n_CUDA_wake);
970 cntrTree->Update((int)W.getWake().vtx.size(), W.getWake().devVtxPtr);
971 cntrTree->Build();
972
973 //Перестроение дерева для вихрей //todo //временно для корректного расчета epsast
974 treeWake.MemoryAllocate((int)W.getCuda().n_CUDA_wake);
975 treeWake.Update((int)W.getWake().vtx.size(), W.getWake().devVtxPtr);
976 timerAgpu.start();
977 treeWake.Build();
978 timerAgpu.stop();
979
980 timerBgpu.start();
981 treeWake.UpwardTraversal(W.getPassport().numericalSchemes.nbodyMultipoleOrder);
982 timerBgpu.stop();
983 }
984
985 double timerCgpuduration = GPUCalcConvVeloToSetOfPointsFromWake(W.getNonConstCuda().cntrTreeWake, W.getWake(), wakeVortexesParams.convVelo, wakeVortexesParams.epsastWake, true, true);
986
987
989
991 /*
992 std::vector<fmm::particle2d> sources(W.getWake().vtx.size());
993 #pragma omp parallel for
994 for (int i = 0; i < (int)sources.size(); ++i)
995 {
996 const auto& w = W.getWake().vtx[i];
997
998 sources[i].center = fmm::point2d(w.r()[0], w.r()[1]);
999 sources[i].q = w.g();
1000 sources[i].morton_code = 0;
1001 }
1002
1003 auto targets = sources;
1004 for (int i = 0; i < (int)sources.size(); ++i)
1005 targets[i].q = 0;
1006
1007 double t = omp_get_wtime();
1008 //fmm::ComputeExact<fmm::InteractionType::Potential2d>(sources, targets); // or fmm::gpu::ComputeExact... for gpu version
1009 fmm::ComputeExact<fmm::InteractionType::Force2d>(sources, targets); // or fmm::gpu::ComputeExact... for gpu version
1010 std::cout << "Compute exact time = " << omp_get_wtime() - t << std::endl;
1011
1012 t = omp_get_wtime();
1013
1014 fmm::FastMultipole fmm(sources, targets, 1.e-8, fmm::FMM_AUTO, fmm::FMM_AUTO); // or fmm::gpu::FastMultipole... for gpu version
1015 fmm::ReadError<fmm::InteractionType::Force2d>(fmm.forces);
1016 //fmm::ReadError<fmm::InteractionType::Potential2d>(fmm.potentials);
1017 std::cout << "Total time = " << omp_get_wtime() - t << std::endl;
1018 */
1019
1020
1021
1023 /*
1024 std::ofstream treeTimeFile;
1025 if (W.getCurrentStep() == 0)
1026 {
1027 treeTimeFile.open(W.getPassport().dir + "/dbg/treeTime.csv");
1028 treeTimeFile << "step,time,N,lev,averPoints,tBUI,tUPW,tDNW,tBUIgpu,tUPWgpu,tDNWgpu\n";
1029 }
1030 else
1031 treeTimeFile.open(W.getPassport().dir + "/dbg/treeTime.csv", std::ios::app);
1032
1033
1034 int estLevel = std::max(4, (int)(log2(W.getWake().vtx.size())));
1035
1036 for (int controlLevel = std::max(estLevel - 2, 1); controlLevel <= estLevel + 2; ++controlLevel)
1037 {
1038 VMlib::vmTimer timerA, timerB;
1039 if (W.getWake().vtx.size() > 0)
1040 {
1041 if (W.getPassport().numericalSchemes.velocityComputation.second == 1)
1042 {
1043 W.getInflTreeWake().Update(W.getWake().vtx, controlLevel);
1044 timerA.start();
1045 W.getInflTreeWake().Build();
1046 timerA.stop();
1047 timerB.start();
1048 W.getInflTreeWake().UpwardTraversal(W.getPassport().numericalSchemes.nbodyMultipoleOrder);
1049 timerB.stop();
1050
1051 W.getCntrTreeWake().Update(W.getWake().vtx, controlLevel);
1052 W.getCntrTreeWake().Build();
1053 W.getCntrTreeWake().UpwardTraversal(W.getPassport().numericalSchemes.nbodyMultipoleOrder);
1054 }
1055
1056 std::vector<Point2D> tempVelo(wakeVortexesParams.convVelo.size());
1057 std::vector<double> tempRadius(wakeVortexesParams.epsastWake.size());
1058
1059 //Вызов виртуальной функции
1060 double timerCduration = CalcConvVeloToSetOfPointsFromWake(W.getWake(), tempVelo, tempRadius, true, false);
1061
1062 int nObjInContr = 0;
1063 for (int c = 0; c < W.getCntrTreeWake().indexControlCells.size(); ++c)
1064 {
1065 const auto& idx = W.getCntrTreeWake().indexControlCells[c];
1066 if (idx > W.getCntrTreeWake().object.size())
1067 nObjInContr += 1;
1068 else
1069 {
1070 nObjInContr += W.getCntrTreeWake().range[idx].second - W.getCntrTreeWake().range[idx].first + 1;
1071 }
1072 }
1073
1074 treeTimeFile << W.getCurrentStep() << ',' << W.getCurrentTime() << ',' << W.getInflTreeWake().object.size() << ',' \
1075 << controlLevel << ',' << (double)nObjInContr / W.getCntrTreeWake().indexControlCells.size() << ',' \
1076 << timerA.duration() << ',' << timerB.duration() << ',' << timerCduration << ',' \
1077 << timerAgpu.duration() << ',' << timerBgpu.duration() << ',' << timerCgpuduration << '\n';
1078 }
1079 }
1080
1081 treeTimeFile.close();
1082 */
1083#else
1084 float timeUpd1=0, timeBld1=0, timeUpw1=0, timeUpd2=0, timeBld2=0, timeUpw2=0, timeDnw=0;
1087
1088 VMlib::vmTimer fullTimer;
1089 fullTimer.reset();
1090 fullTimer.start();
1091
1092 if (W.getWake().vtx.size() > 0)
1093 {
1095 {
1096 int estOptLevel = std::max(4, (int)(log2(W.getWake().vtx.size())) - 2);
1097
1098 timeUpd1 = W.getInflTreeWake().Update(W.getWake().vtx, estOptLevel);
1099 timeBld1 = W.getInflTreeWake().Build();
1101
1102 //timeUpd2 = W.getCntrTreeWake().Update(W.getWake().vtx, estOptLevel);
1103 //timeBld2 = W.getCntrTreeWake().Build();
1104 //timeUpw2 = W.getCntrTreeWake().UpwardTraversal(W.getPassport().numericalSchemes.nbodyMultipoleOrder);
1105
1106 //std::cout << "timeUpd = " << timeUpd1 << " " << timeUpd2 << '\n';
1107 //std::cout << "timeBld = " << timeBld1 << " " << timeBld2 << '\n';
1108 //std::cout << "timeUpw = " << timeUpw1 << " " << timeUpw2 << '\n';
1109
1110 }
1111
1112 //Вызов виртуальной функции
1113
1115
1116 //std::string vname = VMlib::fileNameStep("VeloBH", 0, wakeVortexesParams.convVelo.size(), "txt");
1117 //std::ofstream velofile;
1118 //velofile.open(W.getPassport().dir + vname);
1119
1120 //for (int i = 0; i < wakeVortexesParams.convVelo.size(); ++i)
1121 // velofile << wakeVortexesParams.convVelo[i][0] << " " << wakeVortexesParams.convVelo[i][1] << "\n";
1122 //velofile.close();
1123 //exit(-124);
1124
1125 //std::cout << "timeDnw = " << timeDnw << '\n';
1126 }
1127 fullTimer.stop();
1128
1130 //std::cout << "theta = " << W.getPassport().numericalSchemes.nbodyTheta << "\n";
1131
1133 /*
1134 VMlib::vmTimer timerExact;
1135
1136 if (W.getWake().vtx.size() > 0)
1137 {
1138 std::vector<fmm::particle2d> sources(W.getWake().vtx.size());
1139 #pragma omp parallel for
1140 for (int i = 0; i < (int)sources.size(); ++i)
1141 {
1142 const auto& w = W.getWake().vtx[i];
1143
1144 sources[i].center = fmm::point2d(w.r()[0], w.r()[1]);
1145 sources[i].q = w.g();
1146 sources[i].morton_code = 0;
1147 }
1148
1149 auto targets = sources;
1150 for (int i = 0; i < (int)sources.size(); ++i)
1151 targets[i].q = 0;
1152
1153 //double t = omp_get_wtime();
1154 //fmm::ComputeExact<fmm::InteractionType::Potential2d>(sources, targets); // or fmm::gpu::ComputeExact... for gpu version
1155 timerExact.start();
1156 fmm::ComputeExact<fmm::InteractionType::Force2d>(sources, targets); // or fmm::gpu::ComputeExact... for gpu version
1157 timerExact.stop();
1158 //std::cout << "Compute exact time = " << omp_get_wtime() - t << std::endl;
1159
1160 //t = omp_get_wtime();
1161
1162 fmm::FastMultipole fmm(sources, targets, 1.e-8, fmm::FMM_AUTO, fmm::FMM_AUTO); // or fmm::gpu::FastMultipole... for gpu version
1163 fmm::ReadError<fmm::InteractionType::Force2d>(fmm.forces);
1164 //fmm::ReadError<fmm::InteractionType::Potential2d>(fmm.potentials);
1165 //std::cout << "Total time = " << omp_get_wtime() - t << std::endl;
1166
1167 std::cout << "Hybrid time ConvVelo Wake = " << fullTimer.duration() <<
1168 //W.timerConvVelo.duration() <<
1169 " ( " << timeUpd1 + timeUpd2 + timeBld1 + timeBld2 + timeUpw1 + timeUpw2 + timeDnw << " )" << '\n';
1170
1171 fmm::ReadErrorHyb(*((std::vector<std::array<double, 2>>*) &wakeVortexesParams.convVelo));
1172 std::cout << "Exact time = " << timerExact.duration() << '\n';
1173 }
1174 //*/
1175
1176
1177
1178#endif
1179
1180 std::vector<Point2D> nullVector(0);
1181 //вычисление конвективных скоростей по закону Био-Савара от виртуальных вихрей (точнее, от слоев)
1182 for (size_t bou = 0; bou < W.getNumberOfBoundary(); ++bou)
1183 {
1184 //Влияние на след от панелей
1185#if (defined(__CUDACC__) || defined(USE_CUDA)) && (defined(CU_CONVVIRT))
1186
1188 {
1189 case 0:
1190 W.getBoundary(bou).GPUCalcConvVelocityToSetOfPointsFromSheets(W.getWake(), wakeVortexesParams.convVelo);
1191 break;
1192 case 1:
1193 if (bou == 0)
1194 GPUCalcConvVelocityToSetOfPointsFromSheets(W.getNonConstCuda().cntrTreeWake, W.getWake(), wakeVortexesParams.convVelo);
1195 break;
1196 }
1197 //
1198
1199#else
1201#endif
1202 }
1203
1204 //Скорости только что рожденных вихрей
1205 {
1206 size_t nVirtVortices = 0;
1207 for (size_t bou = 0; bou < W.getNumberOfBoundary(); ++bou)
1208 nVirtVortices += W.getBoundary(bou).virtualWake.vtx.size();
1209
1210 size_t counter = wakeVortexesParams.convVelo.size() - nVirtVortices;
1211
1212 for (size_t bou = 0; bou < W.getNumberOfBoundary(); ++bou)
1213 for (size_t v = 0; v < W.getBoundary(bou).virtualWake.vtx.size(); ++v)
1214 {
1217 - W.getV0();
1218 ++counter;
1219 }
1220 }
1221
1222 //Заполнение структур данных виртуальных вихрей
1223 {
1224 size_t nVirtVortices = 0;
1225 for (size_t bou = 0; bou < W.getNumberOfBoundary(); ++bou)
1226 nVirtVortices += W.getBoundary(bou).virtualWake.vtx.size();
1227
1228 size_t counter = wakeVortexesParams.convVelo.size() - nVirtVortices;
1229
1230 for (size_t bou = 0; bou < W.getNumberOfBoundary(); ++bou)
1231 for (size_t v = 0; v < W.getBoundary(bou).virtualWake.vtx.size(); ++v)
1232 {
1233 virtualVortexesParams[bou].convVelo[v] = wakeVortexesParams.convVelo[counter];
1234 virtualVortexesParams[bou].epsastWake[v] = wakeVortexesParams.epsastWake[counter];
1235 ++counter;
1236 }
1237 }
1238
1239 W.getTimers().stop("ConvVel");
1240 W.getTimers().start("VelPres");
1241
1242 //вычисление скоростей в заданных точках только на соответствующем шаге по времени
1244#ifdef OPTIMIZER
1245 if ((W.getCurrentStep() >= OPTIMIZER_START_STEP && W.getCurrentStep() <= OPTIMIZER_STOP_STEP) || W.getCurrentStep() == 0)
1247#else
1248
1249 //t1 = -omp_get_wtime();
1251 //t1 += omp_get_wtime();
1252 //std::cout << "time ConvVelo VP = " << t1 << std::endl;
1253#endif
1254
1256 /*if ((W.getPassport().timeDiscretizationProperties.saveVPstep > 0) && (!(W.getCurrentStep() % W.getPassport().timeDiscretizationProperties.saveVPstep)))
1257 {
1258 int currentStep = W.getCurrentStep();
1259 if (currentStep >= 1100 && currentStep <= 1300)
1260 {
1261 CalcVeloToWakeVP();
1262 optimizedVelocity.AddVelocities(W.getNonConstMeasureVP().getNonConstVelocity());
1263 }
1264
1265 if (currentStep == 1200)
1266 {
1267 optimizedVelocity.PerformAveragingVelo(W.getPassport().dir);
1268 }
1269 }*/
1271
1272 W.getTimers().stop("VelPres");
1273}//CalcConvVelo()
1274
1275
1276
1277//Вычисление конвективных скоростей вихрей в точках wakeVP
1279{
1280 std::vector<Point2D> velConvWake;
1281 std::vector<std::vector<Point2D>> velConvBou;
1282
1283 int addWSize = (int)W.getMeasureVP().getWakeVP().vtx.size();
1284
1285 velConvWake.resize(addWSize, { 0.0, 0.0 });
1286
1287 velConvBou.resize(W.getNumberOfBoundary());
1288 for (size_t i = 0; i < W.getNumberOfBoundary(); ++i)
1289 velConvBou[i].resize(addWSize, { 0.0, 0.0 });
1290
1291 std::vector<Point2D>& velocityRef = W.getNonConstMeasureVP().getNonConstVelocity();
1292 velocityRef.assign(addWSize, W.getV0());
1293
1294#if (defined(USE_CUDA))
1295 W.getNonConstCuda().RefreshVP();
1296#endif
1297
1298#if (defined(__CUDACC__) || defined(USE_CUDA)) && (defined(CU_VP))
1299 std::unique_ptr<BHcu::CudaTreeInfo>& cntrTree = W.getNonConstCuda().cntrTreeVP;
1300
1302 {
1303 case 1:
1305 {
1306 cntrTree->MemoryAllocate((int)W.getCuda().n_CUDA_velVP);
1307 cntrTree->Update((int)W.getMeasureVP().getWakeVP().vtx.size(), W.getMeasureVP().getWakeVP().devVtxPtr);
1308 cntrTree->Build();
1309 GPUCalcConvVeloToSetOfPointsFromWake(cntrTree, W.getMeasureVP().getWakeVP(), velocityRef, W.getNonConstMeasureVP().getNonConstDomainRadius(), true, false);
1310 }
1311 break;
1312 case 0:
1313 GPUCalcConvVeloToSetOfPointsFromWake(cntrTree, W.getMeasureVP().getWakeVP(), velocityRef, W.getNonConstMeasureVP().getNonConstDomainRadius(), true, false);
1314 break;
1315 }
1316
1317
1318#else
1320 {
1321 case 0:
1323 break;
1324 case 1:
1326 double theta = W.getPassport().numericalSchemes.nbodyTheta;
1327
1329 W.getCntrTreeVP().Build();
1332 }
1333#endif
1334
1335
1336 for (size_t i = 0; i < W.getNumberOfBoundary(); ++i)
1337#if (defined(__CUDACC__) || defined(USE_CUDA)) && (defined(CU_VP))
1339 {
1340 case 0:
1341 W.getNonConstBoundary(i).GPUCalcConvVelocityToSetOfPointsFromSheets(W.getMeasureVP().getWakeVP(), velConvBou[i]);
1342
1343 for (int s = 0; s < addWSize; ++s)
1344 velocityRef[s] += velConvWake[s];
1345
1346 for (size_t bou = 0; bou < velConvBou.size(); ++bou)
1347 for (int j = 0; j < addWSize; ++j)
1348 velocityRef[j] += velConvBou[bou][j];
1349 break;
1350 case 1:
1351 if (i == 0)
1352 GPUCalcConvVelocityToSetOfPointsFromSheets(cntrTree, W.getMeasureVP().getWakeVP(), velocityRef);
1353 break;
1354 }
1355#else
1357 for (int i = 0; i < addWSize; ++i)
1358 velocityRef[i] += velConvWake[i];
1359
1360 for (size_t bou = 0; bou < velConvBou.size(); ++bou)
1361 for (int j = 0; j < addWSize; ++j)
1362 velocityRef[j] += velConvBou[bou][j];
1363#endif
1364}// CalcVeloToWakeVP()
1365
1366
1367void Velocity::CalcDiffVeloI1I2ToWakeFromWake(const WakeDataBase& pointsDb, const std::vector<double>& domainRadius, const WakeDataBase& vorticesDb, std::vector<double>& I1, std::vector<Point2D>& I2)
1368{
1369 CalcDiffVeloI1I2ToSetOfPointsFromWake(pointsDb, domainRadius, vorticesDb, I1, I2);
1370}//CalcDiffVeloI1I2ToWakeFromWake(...)
1371
1372void Velocity::CalcDiffVeloI1I2ToWakeFromSheets(const WakeDataBase& pointsDb, const std::vector<double>& domainRadius, const Boundary& bnd, std::vector<double>& I1, std::vector<Point2D>& I2)
1373{
1374 CalcDiffVeloI1I2ToSetOfPointsFromSheets(pointsDb, domainRadius, bnd, I1, I2);
1375}//CalcDiffVeloI1I2ToWakeFromSheets(...)
Заголовочный файл с описанием класса Airfoil.
Заголовочный файл с описанием класса Boundary.
Заголовочный файл с функциями для метода GMRES.
#define CU_VP
Definition Gpudefs.h:92
Заголовочный файл с описанием класса MeasureVP.
Заголовочный файл с описанием класса Mechanics.
Заголовочный файл с описанием класса StreamParser.
const double IDPI
Число .
Definition defs.h:79
const double DPI
Число .
Definition defs.h:82
Заголовочный файл с описанием класса Velocity.
Заголовочный файл с описанием класса Wake.
Заголовочный файл с описанием класса World2D.
std::vector< double > len
Длины панелей профиля
Definition Airfoil2D.h:94
const Point2D & getR(size_t q) const
Возврат константной ссылки на вершину профиля
Definition Airfoil2D.h:113
const Point2D & getV(size_t q) const
Возврат константной ссылки на скорость вершины профиля
Definition Airfoil2D.h:137
std::vector< Point2D > tau
Касательные к панелям профиля
Definition Airfoil2D.h:91
bool inverse
Признак разворота нормалей (для расчета внутренних течений)
Definition Airfoil2D.h:76
size_t getNumberOfPanels() const
Возврат количества панелей на профиле
Definition Airfoil2D.h:163
Абстрактный класс, определяющий обтекаемый профиль
Definition Airfoil2D.h:182
virtual void GetInfluenceFromSourcesToPanel(size_t panel, const Vortex2D *ptr, ptrdiff_t count, std::vector< double > &panelRhs) const
Вычисление влияния части подряд идущих источников из области течения на панель для правой части
std::vector< double > gammaThrough
Суммарные циркуляции вихрей, пересекших панели профиля на прошлом шаге
Definition Airfoil2D.h:276
virtual void GetInfluenceFromVorticesToPanel(size_t panel, const Vortex2D *ptr, ptrdiff_t count, std::vector< double > &panelRhs) const
Вычисление влияния части подряд идущих вихрей из вихревого следа на панель для правой части
virtual void GetInfluenceFromVInfToPanel(std::vector< double > &vInfRhs) const
Вычисление влияния набегающего потока на панель для правой части
std::vector< double > viscousStress
Нейросеть для коэффициентов I0 и I3 диффузионной скорости
Definition Airfoil2D.h:268
virtual void GetDiffVelocityI0I3ToSetOfPointsAndViscousStresses(const WakeDataBase &pointsDb, std::vector< double > &domainRadius, std::vector< double > &I0, std::vector< Point2D > &I3)
Вычисление числителей и знаменателей диффузионных скоростей в заданном наборе точек,...
const size_t numberInPassport
Номер профиля в паспорте
Definition Airfoil2D.h:188
Абстрактный класс, определяющий способ удовлетворения граничного условия на обтекаемом профиле
Definition Boundary2D.h:65
size_t GetUnknownsSize() const
Возврат размерности вектора решения
size_t sheetDim
Размерность параметров каждого из слоев на каждой из панелей
Definition Boundary2D.h:93
const Airfoil & afl
Definition Boundary2D.h:77
Sheet sheets
Слои на профиле
Definition Boundary2D.h:96
VirtualWake virtualWake
Виртуальный вихревой след конкретного профиля
Definition Boundary2D.h:86
virtual void CalcConvVelocityToSetOfPointsFromSheets(const WakeDataBase &pointsDb, std::vector< Point2D > &velo) const =0
Вычисление конвективных скоростей в наборе точек, вызываемых наличием слоев вихрей и источников на пр...
float Update(const std::vector< Vortex2D > &vtx, int cntrLev=0)
float DownwardTraversalVorticesToPanels(CpuTreeInfo &cntrTree, std::vector< double > &rhs, std::vector< double > &rhsLin, double theta, int order)
float UpwardTraversal(int order)
float DownwardTraversalVorticesToPoints(CpuTreeInfo &cntrTree, std::vector< Point2D > &vel, std::vector< double > &epsast, double theta, int order, bool calcRadius)
const WakeDataBase & getWakeVP() const
Возврат wakeVP.
std::vector< Point2D > & getNonConstVelocity()
Возврат velocity.
std::vector< double > & getNonConstDomainRadius()
Возврат domainRadius.
const bool isMoves
Переменная, отвечающая за то, двигается профиль или нет
double circulationOld
Циркуляция скорости по границе профиля с предыдущего шага
double circulation
Текущая циркуляция скорости по границе профиля
PhysicalProperties physicalProperties
Структура с физическими свойствами задачи
Definition Passport2D.h:301
WakeDiscretizationProperties wakeDiscretizationProperties
Структура с параметрами дискретизации вихревого следа
Definition Passport2D.h:304
NumericalSchemes numericalSchemes
Структура с используемыми численными схемами
Definition Passport2D.h:307
const double & freeVortexSheet(size_t n, size_t moment) const
Definition Sheet2D.h:100
void CalcConvVelo()
Вычисление конвективных скоростей вихрей и виртуальных вихрей в вихревом следе, а также в точках wake...
void CalcDiffVelo()
Вычисление диффузионных скоростей
void LimitDiffVelo(std::vector< Point2D > &diffVel)
Контроль больших значений диффузионных скоростей
void GetWakeInfluenceToRhs(const Airfoil &afl, std::vector< double > &wakeRhs) const
Генерация вектора влияния вихревого следа на профиль
void CalcDiffVeloI1I2ToWakeFromSheets(const WakeDataBase &pointsDb, const std::vector< double > &domainRadius, const Boundary &bnd, std::vector< double > &I1, std::vector< Point2D > &I2)
void CalcDiffVeloI1I2ToWakeFromWake(const WakeDataBase &pointsDb, const std::vector< double > &domainRadius, const WakeDataBase &vorticesDb, std::vector< double > &I1, std::vector< Point2D > &I2)
virtual float CalcConvVeloToSetOfPointsFromWake(const WakeDataBase &pointsDb, std::vector< Point2D > &velo, std::vector< double > &domainRadius, bool calcVelo, bool calcRadius)=0
Вычисление конвективных скоростей и радиусов вихревых доменов в заданном наборе точек от следа
std::vector< VortexesParams > virtualVortexesParams
Вектор струтур, определяющий параметры виртуальных вихрей для профилей
Definition Velocity2D.h:115
VortexesParams wakeVortexesParams
Струтура, определяющая параметры вихрей в следе
Definition Velocity2D.h:112
virtual void CalcVeloToWakeVP()
Вычисление скоростей в точках wakeVP.
void FillRhs(Eigen::VectorXd &rhsReord) const
void CalcDiffVeloI0I3()
void CalcDiffVeloI1I2ToSetOfPointsFromWake(const WakeDataBase &pointsDb, const std::vector< double > &domainRadius, const WakeDataBase &vorticesDb, std::vector< double > &I1, std::vector< Point2D > &I2)
Вычисление числителей и знаменателей диффузионных скоростей в заданном наборе точек
void CalcDiffVeloI1I2ToSetOfPointsFromSheets(const WakeDataBase &pointsDb, const std::vector< double > &domainRadius, const Boundary &bnd, std::vector< double > &I1, std::vector< Point2D > &I2)
void ResizeAndZero()
Очистка старых массивов под хранение скоростей, выделение новой памяти и обнуление
void SaveVisStress()
Сохранение вязких напряжений
void CalcDiffVeloI1I2()
Вычисление диффузионных скоростей вихрей и виртуальных вихрей в вихревом следе
const World2D & W
Константная ссылка на решаемую задачу
Definition Velocity2D.h:108
void CPUGetFASTWakeInfluenceToRhs(std::vector< double > &wakeRhs, std::vector< double > &wakeRhsLin) const
std::vector< Point2D > vecHalfGamma
Скорость вихрей виртуального следа конкретного профиля (равна Gamma/2) используется для расчета давле...
std::vector< std::pair< size_t, size_t > > aflPan
Пара чисел: номер профиля и номер панели, на которой рожден виртуальный вихрь
Класс, опеделяющий набор вихрей
std::vector< Vortex2D > vtx
Список вихревых элементов
VMlib::vmTimer timerConvVelo
Definition World2D.h:371
size_t getNumberOfAirfoil() const
Возврат количества профилей в задаче
Definition World2D.h:180
bool isAnyMovableOrDeformable() const
Возврат признака того, что хотя бы один из профилей подвижный или деформируемый
Definition World2D.cpp:2349
const Wake & getWake() const
Возврат константной ссылки на вихревой след
Definition World2D.h:232
const Airfoil & getAirfoil(size_t i) const
Возврат константной ссылки на объект профиля
Definition World2D.h:163
const WakeDataBase & getSource() const
Возврат константной ссылки на источники в области течения
Definition World2D.h:248
CpuTreeInfo & getCntrTreePnl() const
Definition World2D.h:243
Gpu & getNonConstCuda() const
Возврат неконстантной ссылки на объект, связанный с видеокартой (GPU)
Definition World2D.h:278
Velocity & getNonConstVelocity() const
Возврат неконстантной ссылки на объект для вычисления скоростей
Definition World2D.h:258
MeasureVP & getNonConstMeasureVP() const
Возврат неконстантной ссылки на measureVP.
Definition World2D.h:213
VMlib::TimersGen & getTimers() const
Возврат ссылки на временную статистику выполнения шага расчета по времени
Definition World2D.h:288
const Mechanics & getMechanics(size_t i) const
Возврат константной ссылки на объект механики
Definition World2D.h:219
Point2D getV0() const
Возврат текущей скорости набегающего потока
Definition World2D.h:148
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 Gpu & getCuda() const
Возврат константной ссылки на объект, связанный с видеокартой (GPU)
Definition World2D.h:273
VMlib::vmTimer timerRhs
Definition World2D.h:367
const Passport & getPassport() const
Возврат константной ссылки на паспорт
Definition World2D.h:263
Boundary & getNonConstBoundary(size_t i) const
Возврат неконстантной ссылки на объект граничного условия
Definition World2D.h:192
CpuTreeInfo & getCntrTreeVP() const
Definition World2D.h:242
const Boundary & getBoundary(size_t i) const
Возврат константной ссылки на объект граничного условия
Definition World2D.h:186
bool ifDivisible(int val) const
Definition World2D.h:290
size_t getNumberOfBoundary() const
Возврат количества граничных условий в задаче
Definition World2D.h:197
Airfoil & getNonConstAirfoil(size_t i) const
Возврат неконстантной ссылки на объект профиля
Definition World2D.h:175
const MeasureVP & getMeasureVP() const
Возврат константной ссылки на measureVP.
Definition World2D.h:208
CpuTreeInfo & getInflTreeWake() const
Definition World2D.h:239
TimeDiscretizationProperties timeDiscretizationProperties
Структура с параметрами процесса интегрирования по времени
std::string dir
Рабочий каталог задачи
void stop(const std::string &timerLabel)
Останов счетчика
Definition TimesGen.cpp:68
void start(const std::string &timerLabel)
Запуск счетчика
Definition TimesGen.cpp:55
Класс, опеделяющий двумерный вихревой элемент
Definition Vortex2D.h:59
HD Point2D & r()
Функция для доступа к радиус-вектору вихря
Definition Vortex2D.h:92
HD double & g()
Функция для доступа к циркуляции вихря
Definition Vortex2D.h:100
double getCurrentTime() const
Definition WorldGen.h:100
size_t getCurrentStep() const
Возврат константной ссылки на параметры распараллеливания по MPI.
Definition WorldGen.h:99
P length() const
Вычисление 2-нормы (длины) вектора
Definition numvector.h:374
void normalize(P newlen=1.0)
Нормирование вектора на заданную длину
Definition numvector.h:419
Класс засекания времени
Definition TimesGen.h:59
const vmTimer & stop() const
Останов работающего счетчика времени
Definition TimesGen.h:125
const vmTimer & start() const
Запуск (первый или повторный) счетчика времени
Definition TimesGen.h:109
const vmTimer & reset() const
Сброс счетчика времени
Definition TimesGen.h:101
void CreateUserDirectory(const std::string &dir, const std::string &name)
Создание каталога
Definition defs.h:439
std::string fileNameStep(const std::string &name, int length, size_t number, const std::string &ext)
Формирование имени файла
Definition defs.h:379
std::pair< std::string, int > boundaryCondition
Метод аппроксимации граничных условий
Definition Passport2D.h:190
std::pair< std::string, int > velocityComputation
Definition Passport2D.h:180
double vRef
Референсная скорость
Definition Passport2D.h:81
double nu
Коэффициент кинематической вязкости среды
Definition Passport2D.h:99
std::vector< Point2D > convVelo
Вектор конвективных скоростей вихрей
Definition Velocity2D.h:72
std::vector< double > epsastWake
Вектор характерных радиусов вихревых доменов (eps*)
Definition Velocity2D.h:90
std::vector< Point2D > I2
Вектор числителей (I2) диффузионных скоростей вихрей (обусловленных завихренностью)
Definition Velocity2D.h:84
std::vector< double > I0
Вектор знаменателей (I0) диффузионных скоростей вихрей (обусловленных профилем)
Definition Velocity2D.h:78
std::vector< Point2D > diffVelo
Вектор диффузионных скоростей вихрей
Definition Velocity2D.h:75
std::vector< Point2D > I3
Вектор числителей (I3) диффузионных скоростей вихрей (обусловленных профилем)
Definition Velocity2D.h:87
std::vector< double > I1
Вектор знаменателей (I1) диффузионных скоростей вихрей (обусловленных завихренностью)
Definition Velocity2D.h:81
double getMinEpsAst() const
Функция минимально возможного значения для epsAst.
Definition Passport2D.h:153
double epscol
Радиус коллапса
Definition Passport2D.h:132
double dt
Шаг по времени
Definition PassportGen.h:67
int saveVPstep
Шаг вычисления и сохранения скорости и давления
Definition PassportGen.h:86
int saveVtxStep
Шаг сохранения кадров в бинарные файлы
Definition PassportGen.h:81
int saveVisStress
Шаг вычисления и сохранения скорости и давления
Definition PassportGen.h:89
int nameLength
Число разрядов в имени файла
Definition PassportGen.h:76