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 std::vector<double> newRhs(nTotPan), newRhsLin(nTotPan);
780
781 W.getCuda().CopyMemFromDev<double, 1>(nTotPan, dev_ptr_rhs, newRhs.data(), 22);
782 if (shDim != 1)
783 W.getCuda().CopyMemFromDev<double, 1>(nTotPan, dev_ptr_rhsLin, newRhsLin.data(), 22);
784
785 size_t curGlobPnl = 0;
786 for (size_t s = 0; s < W.getNumberOfAirfoil(); ++s)
787 {
788 std::vector<double>& tmpRhs = W.getNonConstAirfoil(s).tmpRhs;
789 const size_t& np = W.getAirfoil(s).getNumberOfPanels();
790 tmpRhs.resize(0);
791 tmpRhs.insert(tmpRhs.end(), newRhs.begin() + curGlobPnl, newRhs.begin() + curGlobPnl + np);
792 if (shDim != 1)
793 tmpRhs.insert(tmpRhs.end(), newRhsLin.begin() + curGlobPnl, newRhsLin.begin() + curGlobPnl + np);
794
795 curGlobPnl += np;
796 }
797 }
798 }
799
800 if ((nvt > 0) || (nsr > 0))
801 wakeVelo = std::move(afl.tmpRhs);
802 else
803 wakeVelo.resize(afl.getNumberOfPanels() * (W.getPassport().numericalSchemes.boundaryCondition.second), 0.0);
804}//GPUGetWakeInfluenceToRhsFAST(...)
805#endif
806
807
808
809void Velocity::FillRhs(Eigen::VectorXd& rhsReord) const
810{
811 Eigen::VectorXd locRhs;
812 std::vector<double> lastRhs(W.getNumberOfBoundary());
813
814 size_t currentRow = 0;
815 size_t currentSkosRow = 0;
816
817 size_t nAllVars = 0;
818 for (size_t bou = 0; bou < W.getNumberOfBoundary(); ++bou)
819 nAllVars += W.getBoundary(bou).GetUnknownsSize();
820
821 //Временные массивы для хранения правой части по быстрому методу (размеры задаются внутри CPUGetFASTWakeInfluenceToRhs)
822 std::vector<double> fastWakeRhs;
823 std::vector<double> fastWakeRhsLin;
824
825#ifndef USE_CUDA
826 if (W.getPassport().numericalSchemes.velocityComputation.second == 1) //Быстрый метод на CPU
827 CPUGetFASTWakeInfluenceToRhs(fastWakeRhs, fastWakeRhsLin);
828#endif
829
830 size_t curGlobPnl = 0;
831
832 W.timerRhs.reset();
833
834 for (size_t bou = 0; bou < W.getNumberOfBoundary(); ++bou)
835 {
836 const Airfoil& afl = W.getAirfoil(bou);
837 size_t np = afl.getNumberOfPanels();
838
839 size_t nVars;
840
841 nVars = W.getBoundary(bou).GetUnknownsSize();
842 locRhs.resize(nVars);
843
844 std::vector<double> wakeRhs;
845
846 double tt1 = omp_get_wtime();
847
848#if (defined(__CUDACC__) || defined(USE_CUDA)) && (defined(CU_RHS))
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
884 std::vector<double> vInfRhs;
885 afl.GetInfluenceFromVInfToPanel(vInfRhs);
886
887#pragma omp parallel for \
888 default(none) \
889 shared(locRhs, afl, bou, wakeRhs, vInfRhs, np) \
890 schedule(dynamic, DYN_SCHEDULE)
891 for (int i = 0; i < (int)afl.getNumberOfPanels(); ++i)
892 {
893 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] - прямолинейные
894 if (W.getBoundary(bou).sheetDim > 1)
895 locRhs(np + i) = -vInfRhs[np + i] - wakeRhs[np + i];
896
898 //Если включен быстрый метод и есть подвижные тела, то пока что все равно вычисляем IQ
900 {
901 for (size_t q = 0; q < W.getNumberOfBoundary(); ++q)
902 {
903 const auto& sht = W.getBoundary(q).sheets;
904 const auto& iq = W.getIQ(bou, q);
905
906 const Airfoil& aflOther = W.getAirfoil(q);
907 if (W.getMechanics(q).isMoves)
908 {
909 for (size_t j = 0; j < aflOther.getNumberOfPanels(); ++j)
910 {
911 if ((i != j) || (bou != q))
912 {
913 locRhs(i) += -iq.first(i, j) * sht.attachedVortexSheet(j, 0);
914 locRhs(i) += -iq.second(i, j) * sht.attachedSourceSheet(j, 0);
915 }//if (i != j)
916
917 }//for j
918 }
919 }//for q
920 }
921 }//for i
922
923 lastRhs[bou] = 0.0;
924
925#pragma omp for
926 for (int q = 0; q < (int)afl.gammaThrough.size(); ++q)
927 lastRhs[bou] += afl.gammaThrough[q];
928
930
931 const double currT = W.getCurrentTime();
932 const double dt = W.getPassport().timeDiscretizationProperties.dt;
933
934 lastRhs[bou] += (W.getMechanics(bou).circulation - W.getMechanics(bou).circulationOld) * (W.getAirfoil(bou).inverse ? 1.0 : -1.0);
935
937
938 //размазываем правую часть
939 for (size_t i = 0; i < nVars; ++i)
940 rhsReord(i + currentSkosRow) = locRhs(i);
941
942 rhsReord(nAllVars + bou) = lastRhs[bou];
943
944 currentRow += nVars + 1;
945 currentSkosRow += nVars;
946 }// for bou
947
948
950 /*
951 std::ofstream treeTimeFile;
952 if (W.getCurrentStep() == 0)
953 {
954 treeTimeFile.open(W.getPassport().dir + "/dbg/rhsTime.csv");
955 treeTimeFile << "step,time,Nvtx,Npnl,tDNW,tDNWgpu\n";
956 }
957 else
958 treeTimeFile.open(W.getPassport().dir + "/dbg/rhsTime.csv", std::ios::app);
959
960
961 treeTimeFile << W.getCurrentStep() << ',' << W.getCurrentTime() << ',' << W.getInflTreeWake().object.size() << ',' << W.getCntrTreePnl().object.size() << ',' \
962 << timerRhsTest.duration() << ',' << W.timerRhs.duration() << '\n';
963
964 treeTimeFile.close();
965 //*/
966}
967
968
969
970
971//Вычисление конвективных скоростей вихрей и виртуальных вихрей в вихревом следе, а также в точках wakeVP
973{
974 W.getTimers().start("ConvVel");
975
976 //Влияние следа на след
977#if (defined(__CUDACC__) || defined(USE_CUDA)) && (defined(CU_CONV_TOWAKE))
978 //Вызов виртуальной функции
979
982
983 VMlib::vmTimer timerAgpu, timerBgpu;
984
985 std::unique_ptr<BHcu::CudaTreeInfo>& cntrTree = W.getNonConstCuda().cntrTreeWake;
986 auto& treeWake = *W.getNonConstCuda().inflTreeWake;
988 {
989 cntrTree->MemoryAllocate((int)W.getCuda().n_CUDA_wake);
990 cntrTree->Update((int)W.getWake().vtx.size(), W.getWake().devVtxPtr);
991 cntrTree->Build();
992
993 //Перестроение дерева для вихрей //todo //временно для корректного расчета epsast
994 treeWake.MemoryAllocate((int)W.getCuda().n_CUDA_wake);
995 treeWake.Update((int)W.getWake().vtx.size(), W.getWake().devVtxPtr);
996 timerAgpu.start();
997 treeWake.Build();
998 timerAgpu.stop();
999
1000 timerBgpu.start();
1001 treeWake.UpwardTraversal(W.getPassport().numericalSchemes.nbodyMultipoleOrder);
1002 timerBgpu.stop();
1003 }
1004
1005 double timerCgpuduration = GPUCalcConvVeloToSetOfPointsFromWake(W.getNonConstCuda().cntrTreeWake, W.getWake(), wakeVortexesParams.convVelo, wakeVortexesParams.epsastWake, true, true);
1006
1007
1009
1011 /*
1012 std::vector<fmm::particle2d> sources(W.getWake().vtx.size());
1013 #pragma omp parallel for
1014 for (int i = 0; i < (int)sources.size(); ++i)
1015 {
1016 const auto& w = W.getWake().vtx[i];
1017
1018 sources[i].center = fmm::point2d(w.r()[0], w.r()[1]);
1019 sources[i].q = w.g();
1020 sources[i].morton_code = 0;
1021 }
1022
1023 auto targets = sources;
1024 for (int i = 0; i < (int)sources.size(); ++i)
1025 targets[i].q = 0;
1026
1027 double t = omp_get_wtime();
1028 //fmm::ComputeExact<fmm::InteractionType::Potential2d>(sources, targets); // or fmm::gpu::ComputeExact... for gpu version
1029 fmm::ComputeExact<fmm::InteractionType::Force2d>(sources, targets); // or fmm::gpu::ComputeExact... for gpu version
1030 std::cout << "Compute exact time = " << omp_get_wtime() - t << std::endl;
1031
1032 t = omp_get_wtime();
1033
1034 fmm::FastMultipole fmm(sources, targets, 1.e-8, fmm::FMM_AUTO, fmm::FMM_AUTO); // or fmm::gpu::FastMultipole... for gpu version
1035 fmm::ReadError<fmm::InteractionType::Force2d>(fmm.forces);
1036 //fmm::ReadError<fmm::InteractionType::Potential2d>(fmm.potentials);
1037 std::cout << "Total time = " << omp_get_wtime() - t << std::endl;
1038 */
1039
1040
1041
1043 /*
1044 std::ofstream treeTimeFile;
1045 if (W.getCurrentStep() == 0)
1046 {
1047 treeTimeFile.open(W.getPassport().dir + "/dbg/treeTime.csv");
1048 treeTimeFile << "step,time,N,lev,averPoints,tBUI,tUPW,tDNW,tBUIgpu,tUPWgpu,tDNWgpu\n";
1049 }
1050 else
1051 treeTimeFile.open(W.getPassport().dir + "/dbg/treeTime.csv", std::ios::app);
1052
1053
1054 int estLevel = std::max(4, (int)(log2(W.getWake().vtx.size())));
1055
1056 for (int controlLevel = std::max(estLevel - 2, 1); controlLevel <= estLevel + 2; ++controlLevel)
1057 {
1058 VMlib::vmTimer timerA, timerB;
1059 if (W.getWake().vtx.size() > 0)
1060 {
1061 if (W.getPassport().numericalSchemes.velocityComputation.second == 1)
1062 {
1063 W.getInflTreeWake().Update(W.getWake().vtx, controlLevel);
1064 timerA.start();
1065 W.getInflTreeWake().Build();
1066 timerA.stop();
1067 timerB.start();
1068 W.getInflTreeWake().UpwardTraversal(W.getPassport().numericalSchemes.nbodyMultipoleOrder);
1069 timerB.stop();
1070
1071 W.getCntrTreeWake().Update(W.getWake().vtx, controlLevel);
1072 W.getCntrTreeWake().Build();
1073 W.getCntrTreeWake().UpwardTraversal(W.getPassport().numericalSchemes.nbodyMultipoleOrder);
1074 }
1075
1076 std::vector<Point2D> tempVelo(wakeVortexesParams.convVelo.size());
1077 std::vector<double> tempRadius(wakeVortexesParams.epsastWake.size());
1078
1079 //Вызов виртуальной функции
1080 double timerCduration = CalcConvVeloToSetOfPointsFromWake(W.getWake(), tempVelo, tempRadius, true, false);
1081
1082 int nObjInContr = 0;
1083 for (int c = 0; c < W.getCntrTreeWake().indexControlCells.size(); ++c)
1084 {
1085 const auto& idx = W.getCntrTreeWake().indexControlCells[c];
1086 if (idx > W.getCntrTreeWake().object.size())
1087 nObjInContr += 1;
1088 else
1089 {
1090 nObjInContr += W.getCntrTreeWake().range[idx].second - W.getCntrTreeWake().range[idx].first + 1;
1091 }
1092 }
1093
1094 treeTimeFile << W.getCurrentStep() << ',' << W.getCurrentTime() << ',' << W.getInflTreeWake().object.size() << ',' \
1095 << controlLevel << ',' << (double)nObjInContr / W.getCntrTreeWake().indexControlCells.size() << ',' \
1096 << timerA.duration() << ',' << timerB.duration() << ',' << timerCduration << ',' \
1097 << timerAgpu.duration() << ',' << timerBgpu.duration() << ',' << timerCgpuduration << '\n';
1098 }
1099 }
1100
1101 treeTimeFile.close();
1102 //*/
1103#else
1104 float timeUpd1=0, timeBld1=0, timeUpw1=0, timeUpd2=0, timeBld2=0, timeUpw2=0, timeDnw=0;
1107
1108 VMlib::vmTimer fullTimer;
1109 fullTimer.reset();
1110 fullTimer.start();
1111
1112 if (W.getWake().vtx.size() > 0)
1113 {
1115 {
1116 int estOptLevel = std::max(4, (int)(log2(W.getWake().vtx.size())) - 2);
1117
1118 timeUpd1 = W.getInflTreeWake().Update(W.getWake().vtx, estOptLevel);
1119 timeBld1 = W.getInflTreeWake().Build();
1121
1122 //timeUpd2 = W.getCntrTreeWake().Update(W.getWake().vtx, estOptLevel);
1123 //timeBld2 = W.getCntrTreeWake().Build();
1124 //timeUpw2 = W.getCntrTreeWake().UpwardTraversal(W.getPassport().numericalSchemes.nbodyMultipoleOrder);
1125
1126 //std::cout << "timeUpd = " << timeUpd1 << " " << timeUpd2 << '\n';
1127 //std::cout << "timeBld = " << timeBld1 << " " << timeBld2 << '\n';
1128 //std::cout << "timeUpw = " << timeUpw1 << " " << timeUpw2 << '\n';
1129
1130 }
1131
1132 //Вызов виртуальной функции
1133
1135
1136 //std::string vname = VMlib::fileNameStep("VeloBH", 0, wakeVortexesParams.convVelo.size(), "txt");
1137 //std::ofstream velofile;
1138 //velofile.open(W.getPassport().dir + vname);
1139
1140 //for (int i = 0; i < wakeVortexesParams.convVelo.size(); ++i)
1141 // velofile << wakeVortexesParams.convVelo[i][0] << " " << wakeVortexesParams.convVelo[i][1] << "\n";
1142 //velofile.close();
1143 //exit(-124);
1144
1145 //std::cout << "timeDnw = " << timeDnw << '\n';
1146 }
1147 fullTimer.stop();
1148
1150 //std::cout << "theta = " << W.getPassport().numericalSchemes.nbodyTheta << "\n";
1151
1153 /*
1154 VMlib::vmTimer timerExact;
1155
1156 if (W.getWake().vtx.size() > 0)
1157 {
1158 std::vector<fmm::particle2d> sources(W.getWake().vtx.size());
1159 #pragma omp parallel for
1160 for (int i = 0; i < (int)sources.size(); ++i)
1161 {
1162 const auto& w = W.getWake().vtx[i];
1163
1164 sources[i].center = fmm::point2d(w.r()[0], w.r()[1]);
1165 sources[i].q = w.g();
1166 sources[i].morton_code = 0;
1167 }
1168
1169 auto targets = sources;
1170 for (int i = 0; i < (int)sources.size(); ++i)
1171 targets[i].q = 0;
1172
1173 //double t = omp_get_wtime();
1174 //fmm::ComputeExact<fmm::InteractionType::Potential2d>(sources, targets); // or fmm::gpu::ComputeExact... for gpu version
1175 timerExact.start();
1176 fmm::ComputeExact<fmm::InteractionType::Force2d>(sources, targets); // or fmm::gpu::ComputeExact... for gpu version
1177 timerExact.stop();
1178 //std::cout << "Compute exact time = " << omp_get_wtime() - t << std::endl;
1179
1180 //t = omp_get_wtime();
1181
1182 fmm::FastMultipole fmm(sources, targets, 1.e-8, fmm::FMM_AUTO, fmm::FMM_AUTO); // or fmm::gpu::FastMultipole... for gpu version
1183 fmm::ReadError<fmm::InteractionType::Force2d>(fmm.forces);
1184 //fmm::ReadError<fmm::InteractionType::Potential2d>(fmm.potentials);
1185 //std::cout << "Total time = " << omp_get_wtime() - t << std::endl;
1186
1187 std::cout << "Hybrid time ConvVelo Wake = " << fullTimer.duration() <<
1188 //W.timerConvVelo.duration() <<
1189 " ( " << timeUpd1 + timeUpd2 + timeBld1 + timeBld2 + timeUpw1 + timeUpw2 + timeDnw << " )" << '\n';
1190
1191 fmm::ReadErrorHyb(*((std::vector<std::array<double, 2>>*) &wakeVortexesParams.convVelo));
1192 std::cout << "Exact time = " << timerExact.duration() << '\n';
1193 }
1194 //*/
1195
1196
1197
1198#endif
1199
1200 std::vector<Point2D> nullVector(0);
1201 //вычисление конвективных скоростей по закону Био-Савара от виртуальных вихрей (точнее, от слоев)
1202 for (size_t bou = 0; bou < W.getNumberOfBoundary(); ++bou)
1203 {
1204 //Влияние на след от панелей
1205#if (defined(__CUDACC__) || defined(USE_CUDA)) && (defined(CU_CONVVIRT))
1206
1208 {
1209 case 0:
1210 W.getBoundary(bou).GPUCalcConvVelocityToSetOfPointsFromSheets(W.getWake(), wakeVortexesParams.convVelo);
1211 break;
1212 case 1:
1213 if (bou == 0)
1214 GPUCalcConvVelocityToSetOfPointsFromSheets(W.getNonConstCuda().cntrTreeWake, W.getWake(), wakeVortexesParams.convVelo);
1215 break;
1216 }
1217 //
1218
1219#else
1221#endif
1222 }
1223
1224 //Скорости только что рожденных вихрей
1225 {
1226 size_t nVirtVortices = 0;
1227 for (size_t bou = 0; bou < W.getNumberOfBoundary(); ++bou)
1228 nVirtVortices += W.getBoundary(bou).virtualWake.vtx.size();
1229
1230 size_t counter = wakeVortexesParams.convVelo.size() - nVirtVortices;
1231
1232 for (size_t bou = 0; bou < W.getNumberOfBoundary(); ++bou)
1233 for (size_t v = 0; v < W.getBoundary(bou).virtualWake.vtx.size(); ++v)
1234 {
1237 - W.getV0();
1238 ++counter;
1239 }
1240 }
1241
1242 //Заполнение структур данных виртуальных вихрей
1243 {
1244 size_t nVirtVortices = 0;
1245 for (size_t bou = 0; bou < W.getNumberOfBoundary(); ++bou)
1246 nVirtVortices += W.getBoundary(bou).virtualWake.vtx.size();
1247
1248 size_t counter = wakeVortexesParams.convVelo.size() - nVirtVortices;
1249
1250 for (size_t bou = 0; bou < W.getNumberOfBoundary(); ++bou)
1251 for (size_t v = 0; v < W.getBoundary(bou).virtualWake.vtx.size(); ++v)
1252 {
1253 virtualVortexesParams[bou].convVelo[v] = wakeVortexesParams.convVelo[counter];
1254 virtualVortexesParams[bou].epsastWake[v] = wakeVortexesParams.epsastWake[counter];
1255 ++counter;
1256 }
1257 }
1258
1259 W.getTimers().stop("ConvVel");
1260 W.getTimers().start("VelPres");
1261
1262 //вычисление скоростей в заданных точках только на соответствующем шаге по времени
1264#ifdef OPTIMIZER
1265 if ((W.getCurrentStep() >= OPTIMIZER_START_STEP && W.getCurrentStep() <= OPTIMIZER_STOP_STEP) || W.getCurrentStep() == 0)
1267#else
1268
1269 //t1 = -omp_get_wtime();
1271 //t1 += omp_get_wtime();
1272 //std::cout << "time ConvVelo VP = " << t1 << std::endl;
1273#endif
1274
1276 /*if ((W.getPassport().timeDiscretizationProperties.saveVPstep > 0) && (!(W.getCurrentStep() % W.getPassport().timeDiscretizationProperties.saveVPstep)))
1277 {
1278 int currentStep = W.getCurrentStep();
1279 if (currentStep >= 1100 && currentStep <= 1300)
1280 {
1281 CalcVeloToWakeVP();
1282 optimizedVelocity.AddVelocities(W.getNonConstMeasureVP().getNonConstVelocity());
1283 }
1284
1285 if (currentStep == 1200)
1286 {
1287 optimizedVelocity.PerformAveragingVelo(W.getPassport().dir);
1288 }
1289 }*/
1291
1292 W.getTimers().stop("VelPres");
1293}//CalcConvVelo()
1294
1295
1296
1297//Вычисление конвективных скоростей вихрей в точках wakeVP
1299{
1300 std::vector<Point2D> velConvWake;
1301 std::vector<std::vector<Point2D>> velConvBou;
1302
1303 int addWSize = (int)W.getMeasureVP().getWakeVP().vtx.size();
1304
1305 velConvWake.resize(addWSize, { 0.0, 0.0 });
1306
1307 velConvBou.resize(W.getNumberOfBoundary());
1308 for (size_t i = 0; i < W.getNumberOfBoundary(); ++i)
1309 velConvBou[i].resize(addWSize, { 0.0, 0.0 });
1310
1311 std::vector<Point2D>& velocityRef = W.getNonConstMeasureVP().getNonConstVelocity();
1312 velocityRef.assign(addWSize, W.getV0());
1313
1314#if (defined(USE_CUDA))
1315 W.getNonConstCuda().RefreshVP();
1316#endif
1317
1318#if (defined(__CUDACC__) || defined(USE_CUDA)) && (defined(CU_VP))
1319 std::unique_ptr<BHcu::CudaTreeInfo>& cntrTree = W.getNonConstCuda().cntrTreeVP;
1320
1322 {
1323 case 1:
1325 {
1326 cntrTree->MemoryAllocate((int)W.getCuda().n_CUDA_velVP);
1327 cntrTree->Update((int)W.getMeasureVP().getWakeVP().vtx.size(), W.getMeasureVP().getWakeVP().devVtxPtr);
1328 cntrTree->Build();
1329 GPUCalcConvVeloToSetOfPointsFromWake(cntrTree, W.getMeasureVP().getWakeVP(), velocityRef, W.getNonConstMeasureVP().getNonConstDomainRadius(), true, false);
1330 }
1331 break;
1332 case 0:
1333 GPUCalcConvVeloToSetOfPointsFromWake(cntrTree, W.getMeasureVP().getWakeVP(), velocityRef, W.getNonConstMeasureVP().getNonConstDomainRadius(), true, false);
1334 break;
1335 }
1336
1337
1338#else
1340 {
1341 case 0:
1343 break;
1344 case 1:
1346 double theta = W.getPassport().numericalSchemes.nbodyTheta;
1347
1349 W.getCntrTreeVP().Build();
1352 }
1353#endif
1354
1355
1356 for (size_t i = 0; i < W.getNumberOfBoundary(); ++i)
1357#if (defined(__CUDACC__) || defined(USE_CUDA)) && (defined(CU_VP))
1359 {
1360 case 0:
1361 W.getNonConstBoundary(i).GPUCalcConvVelocityToSetOfPointsFromSheets(W.getMeasureVP().getWakeVP(), velConvBou[i]);
1362
1363 for (int s = 0; s < addWSize; ++s)
1364 velocityRef[s] += velConvWake[s];
1365
1366 for (size_t bou = 0; bou < velConvBou.size(); ++bou)
1367 for (int j = 0; j < addWSize; ++j)
1368 velocityRef[j] += velConvBou[bou][j];
1369 break;
1370 case 1:
1371 if (i == 0)
1372 GPUCalcConvVelocityToSetOfPointsFromSheets(cntrTree, W.getMeasureVP().getWakeVP(), velocityRef);
1373 break;
1374 }
1375#else
1377 for (int i = 0; i < addWSize; ++i)
1378 velocityRef[i] += velConvWake[i];
1379
1380 for (size_t bou = 0; bou < velConvBou.size(); ++bou)
1381 for (int j = 0; j < addWSize; ++j)
1382 velocityRef[j] += velConvBou[bou][j];
1383#endif
1384}// CalcVeloToWakeVP()
1385
1386
1387void Velocity::CalcDiffVeloI1I2ToWakeFromWake(const WakeDataBase& pointsDb, const std::vector<double>& domainRadius, const WakeDataBase& vorticesDb, std::vector<double>& I1, std::vector<Point2D>& I2)
1388{
1389 CalcDiffVeloI1I2ToSetOfPointsFromWake(pointsDb, domainRadius, vorticesDb, I1, I2);
1390}//CalcDiffVeloI1I2ToWakeFromWake(...)
1391
1392void Velocity::CalcDiffVeloI1I2ToWakeFromSheets(const WakeDataBase& pointsDb, const std::vector<double>& domainRadius, const Boundary& bnd, std::vector<double>& I1, std::vector<Point2D>& I2)
1393{
1394 CalcDiffVeloI1I2ToSetOfPointsFromSheets(pointsDb, domainRadius, bnd, I1, I2);
1395}//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, double coefEpsAst)
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:304
WakeDiscretizationProperties wakeDiscretizationProperties
Структура с параметрами дискретизации вихревого следа
Definition Passport2D.h:307
NumericalSchemes numericalSchemes
Структура с используемыми численными схемами
Definition Passport2D.h:310
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:2370
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:447
std::string fileNameStep(const std::string &name, int length, size_t number, const std::string &ext)
Формирование имени файла
Definition defs.h:387
std::pair< std::string, int > boundaryCondition
Метод аппроксимации граничных условий
Definition Passport2D.h:193
std::pair< std::string, int > velocityComputation
Definition Passport2D.h:183
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:156
double coefEpsAst
Коэффициент для вычислекния eps_ast.
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