VM2D 1.14
Vortex methods for 2D flows simulation
Loading...
Searching...
No Matches
cpuTreeInfo.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: cpuTreeInfo.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 "cpuTreeInfo.h"
41#include "Gpudefs.h"
42#include <algorithm>
43#include <omp.h>
44#include <stdio.h>
45#include "defs.h"
46#include "TimesGen.h"
47
48namespace VM2D
49{
50 //"Разрежение" двоичного представления беззнакового целого, вставляя по одному нулику между всеми битами
51 inline unsigned int ExpandBits(unsigned int v)
52 {
53 // вставит 1 нуль
54 v = (v | (v << 8)) & 0x00FF00FF; // 00000000`00000000`abcdefgh`ijklmnop
55 // | 00000000`abcdefgh`ijklmnop`00000000
56 // = 00000000`abcdefgh`XXXXXXXX`ijklmnop
57 // & 00000000`11111111`00000000`11111111
58 // = 00000000`abcdefgh`00000000`ijklmnop
59
60 v = (v | (v << 4)) & 0x0F0F0F0F; // 00000000`abcdefgh`00000000`ijklmnop
61 // | 0000abcd`efgh0000`0000ijkl`mnop0000
62 // = 0000abcd`XXXXefgh`0000ijkl`XXXXmnop
63 // & 00001111`00001111`00001111`00001111
64 // = 0000abcd`0000efgh`0000ijkl`0000mnop
65
66 v = (v | (v << 2)) & 0x33333333; // 0000abcd`0000efgh`0000ijkl`0000mnop
67 // | 00abcd00`00efgh00`00ijkl00`00mnop00
68 // = 00abXXcd`00efXXgh`00ijXXkl`00mnXXop
69 // & 00110011`00110011`00110011`00110011
70 // = 00ab00cd`00ef00gh`00ij00kl`00mn00op
71
72 v = (v | (v << 1)) & 0x55555555; // 00ab00cd`00ef00gh`00ij00kl`00mn00op
73 // | 0ab00cd0`0ef00gh0`0ij00kl0`0mn00op0
74 // = 0aXb0cXd`0eXf0gXh`0iXj0kXl`0mXn0oXp
75 // & 01010101`01010101`01010101`01010101
76 // = 0a0b0c0d`0e0f0g0h`0i0j0k0l`0m0n0o0p
77 return v;
78 }
79
80 //Округление "в потолок" результата деления на степень двойки
81 inline int ceilpow2(int x, int p) // =ceil(x / 2^p)
82 {
83 return (x >> p) + !!(x & ((1 << p) - 1));
84 }
85
86 //Округление "в потолок" результата деления пополам
87 inline int ceilhalf(int x) // =ceil(x / 2), т.е. предыдущая функция при p=1
88 {
89 return (x >> 1) + (x & 1);
90 }
91
92 int CpuTreeInfo::Delta(int i, int j) const
93 {
94 if ((j < 0) || (j > (int)object.size() - 1))
95 return -1;
96
97 if (i > j)
98 std::swap(i, j);
99
100 //if ((i < 0) || (j > n-1))
101 // exit(111);
102
103 const unsigned int ki = mortonCodesKey[i];
104 const unsigned int kj = mortonCodesKey[j];
105
106 //Поиск номера самого старшего ненулевого бита в числе c
107 int count = 0;
108 for (unsigned int c = (ki ^ kj); c; c >>= 1, ++count);
109
110 if ((!count) && (i != j))
111 {
112 int addCount = 0;
113 //единички к номерам i и j добавлены для совместимости с Wolfram Mathematica,
114 //для кода они не важны, но дерево без них почти наверняка построится по-другому
115 for (unsigned int add = ((i + 1) ^ (j + 1)); add; add >>= 1, ++addCount);
116 return 2 * codeLength + (2 * codeLength - addCount);
117 }//if ((!count) && (i != j))
118
119 return (2 * codeLength - count);
120 }//Delta(...)
121
122
123
124
125 CpuTreeInfo::CpuTreeInfo(tree_T treeType_, object_T objectType_, scheme_T schemeType_)
126 :
127 treeType(treeType_),
128 objectType(objectType_),
129 schemeType(schemeType_)
130 {
131 /*
132 switch (objectType)
133 {
134 case object_T::point2:
135 sizeOfElement = sizeof(Point2D);
136 break;
137
138 case object_T::point4:
139 sizeOfElement = sizeof(Vortex2D);
140 break;
141
142 case object_T::panel:
143 switch (treeType)
144 {
145 case tree_T::aux:
146 sizeOfElement = sizeof(double) * 6;
147 break;
148 case tree_T::contr:
149 sizeOfElement = sizeof(Point2D);
150 break;
151 case tree_T::vortex:
152 case tree_T::source:
153 sizeOfElement = sizeof(double) * 12;
154 break;
155 }
156 break;
157 }
158 */
159 };
160
162
163
164 float CpuTreeInfo::Update(const std::vector<Vortex2D>& vtx, int cntrLev)
165 {
166 VMlib::vmTimer timer;
167 timer.reset();
168
169 timer.start();
170 int nObject = (int)vtx.size();
171
172 if (cntrLev == 0)
173 controlLevel = std::max(4, (int)(log2(nObject) - 3));
174 else
175 controlLevel = cntrLev;
176
177 indexControlCells.resize(0);
178 indexControlCells.reserve(nObject);
179
180 bool inflTree = (treeType == tree_T::vortex || treeType == tree_T::source);
181
182 object.resize(nObject);
183 //if (objectType == object_T::point4 && inflTree)
184 {
185 gamma.resize(nObject);
186 sigma.resize(nObject);
187 }
188
189 gabForLeaves.resize(nObject);
190 mortonCodesKeyUnsort.resize(nObject);
191 mortonCodesIdxUnsort.resize(nObject);
192 mortonCodesKey.resize(nObject);
193 mortonCodesIdx.resize(nObject);
194
195 levelUnsort.resize(nObject - 1);
196 levelSort.resize(nObject - 1);
197 indexUnsort.resize(nObject - 1);
198 indexSort.resize(nObject - 1);
199 indexSortT.resize(nObject - 1);
200 range.resize(2 * nObject);
201 parent.resize(2 * nObject);
202 child.resize(nObject - 1);
203 lowerupper.resize(nObject - 1);
204 center.resize(nObject - 1);
205
206 if (inflTree)
207 {
208 moms.resize((nObject - 1) * orderAlignment);
209 mass.resize(nObject - 1);
210 }
211
212
213 for (int v = 0; v < nObject; ++v)
214 {
215 Point2D r = vtx[v].r();
216 object[v] = r;
217 if (objectType == object_T::point4 /* && inflTree*/) //влияющее дерево вихрей
218 {
219 //if (inflTree)
220 {
221 gamma[v] = vtx[v].g();
222 sigma[v] = vtx[v].sigma();
223 }
224 gabForLeaves[v] = Point4D({ r[0] - sigma[v], r[1] - sigma[v], r[0] + sigma[v], r[1] + sigma[v] });
225 }
226 else
227 gabForLeaves[v] = Point4D({ r[0] - 0.0, r[1] - 0.0, r[0] + 0.0, r[1] + 0.0 });
228 }
229
230 timer.stop();
231 return (float)timer.duration();
232 }
233
234
235
236 float CpuTreeInfo::UpdatePanelGeometry(const std::vector<std::pair<Point2D, Point2D>>& panels, int cntrLev)
237 {
238 VMlib::vmTimer timer;
239 timer.reset();
240
241 timer.start();
242
243 int nObject = (int)panels.size();
244
245 if (cntrLev == 0)
246 controlLevel = std::max(4, (int)(log2(nObject) - 3));
247 else
248 controlLevel = cntrLev;
249
250 indexControlCells.resize(0);
251 indexControlCells.reserve(nObject);
252
253 object.resize(nObject);
254
256 {
257 gamma.resize(nObject);
258 sigma.resize(nObject);
259 }
260
261 gabForLeaves.resize(nObject);
262 mortonCodesKeyUnsort.resize(nObject);
263 mortonCodesIdxUnsort.resize(nObject);
264 mortonCodesKey.resize(nObject);
265 mortonCodesIdx.resize(nObject);
266
267 levelUnsort.resize(nObject - 1);
268 levelSort.resize(nObject - 1);
269 indexUnsort.resize(nObject - 1);
270 indexSort.resize(nObject - 1);
271 indexSortT.resize(nObject - 1);
272 range.resize(2 * nObject);
273 parent.resize(2 * nObject);
274 child.resize(nObject - 1);
275 lowerupper.resize(nObject - 1);
276 center.resize(nObject - 1);
277
278 mass.resize(nObject - 1);
279
280 if (treeType != tree_T::contr)
281 {
282 moms.resize((nObject - 1) * orderAlignment);
283 }
284
285 for (int v = 0; v < nObject; ++v)
286 {
287 Point2D r = 0.5 * (panels[v].first + panels[v].second);
288 object[v] = r;
289
291 {
292 // gamma[v] = vtx[v].g();
293 // sigma[v] = vtx[v].sigma();
294 }
295
296
297
298 gabForLeaves[v] = Point4D({ panels[v].first[0], panels[v].first[1], panels[v].second[0], panels[v].second[1] });
299 }
300
301 timer.stop();
302 return (float)timer.duration();
303 }
304
305
306
307
308 //Сортировка листьев
310 {
311 int nObject = (int)object.size();
312 if (nObject <= 0)
313 return;
314
315 std::memcpy(mortonCodesKey.data(), mortonCodesKeyUnsort.data(), mortonCodesKeyUnsort.size() * sizeof(unsigned));
316 std::memcpy(mortonCodesIdx.data(), mortonCodesIdxUnsort.data(), mortonCodesIdxUnsort.size() * sizeof(int));
317
318 codesSorter.sort((int*)mortonCodesKey.data(), mortonCodesIdx.data(), 0, mortonCodesKeyUnsort.size());
319
320 /*
321 std::vector<std::pair<unsigned, int>> pr(mortonCodesKeyUnsort.size());
322 for (size_t q = 0; q < mortonCodesKeyUnsort.size(); ++q)
323 pr[q] = { mortonCodesKeyUnsort[q], mortonCodesIdxUnsort[q] };
324 std::sort(pr.begin(), pr.end(), [](auto ka, auto kb) {return ka.first < kb.first;});
325
326 for (size_t q = 0; q < mortonCodesKeyUnsort.size(); ++q)
327 {
328 mortonCodesKey[q] = pr[q].first;
329 mortonCodesIdx[q] = pr[q].second;
330 }
331 */
332
333 }//RadixSortMortonCodes()
334
336 {
337 int nObject = (int)object.size();
338 int n = nObject - 1;
339
340 if (n == 0)
341 return;
342
343
344 std::memcpy(levelSort.data(), levelUnsort.data(), levelUnsort.size() * sizeof(unsigned));
345 std::memcpy(indexSort.data(), indexUnsort.data(), levelUnsort.size() * sizeof(int));
346
347 levelSorter.sort((int*)levelSort.data(), indexSort.data(), 0, levelUnsort.size());
348
349 /*
350 std::vector<std::pair<unsigned, int>> pr(levelUnsort.size());
351 for (size_t q = 0; q < levelUnsort.size(); ++q)
352 pr[q] = { levelUnsort[q], indexUnsort[q] };
353 std::sort(pr.begin(), pr.end(), [](auto ka, auto kb) {return ka.first < kb.first;});
354
355 for (size_t q = 0; q < levelUnsort.size(); ++q)
356 {
357 levelSort[q] = pr[q].first;
358 indexSort[q] = pr[q].second;
359 }
360 */
361
362 for (int k = 0; k < n; ++k)
363 indexSortT[indexSort[k]] = k;
364
365 }//RadixSortInternalCells()
366
367
368
369
371 {
372 using double4 = Point4D;
373 using double2 = Point2D;
374 using int2 = std::pair<int, int>;
375
376#pragma omp parallel
377 {
378 int i, j, ch, flag;
379
380 double4 lu[2];
381
382 //int cm;
383 int m[2];
384
385
386 const int nnodes = 2 * (int)object.size() - 1;
387 const int nbodies = (int)object.size();
388#pragma omp for
389 for (int k = nbodies; k < nnodes; ++k)
390 {
391 //MortonTree:
392 // 0 1 2 ... (nb-2) x (nb+0) (nb+1) (nb+2) ... (nb+(nb-1))
393 // ---------------- -----------------------------------
394 // cells bodies
395
396 //Martin's tree:
397 // 0 1 2 ... (nb-1) x x x x (nn-(nb-1)) ... (nn-2) (nn-1)
398 // ---------------- ----------------------------
399 // bodies sorted and reversed cells
400
401 flag = 0;
402 j = 0;
403 // iterate over all cells assigned to thread
404 while (flag == 0)
405 {
406 j = 2;
407 const int kch = ((nnodes - 1) - k);
408 const int srt = indexSort[kch];
409 int2 chdPair = child[srt];
410
411 //cm = 0;
412
413 //int chdSorted[2];
414
415 for (i = 0; i < 2; i++)
416 {
417 int chd = i * chdPair.second + (1 - i) * chdPair.first; // i==0 => .x; i==1 => .y
418 ch = (chd >= nbodies) ? (chd - nbodies) : ((nnodes - 1) - indexSortT[chd]);
419 if ((chd >= nbodies) || (mass[nnodes - 1 - ch] >= 0))
420 j--;
421 }
422
423 if (j == 0)
424 {
425 for (i = 0; i < 2; i++)
426 {
427 const int chd = i * chdPair.second + (1 - i) * chdPair.first;
428 if (chd >= nbodies)
429 {
430 ch = chd - nbodies;
431 const int sortedBody = mortonCodesIdx[ch];
432
433 double4 xyAB = gabForLeaves[sortedBody];
434 lu[i] = double4{
435 ::fmin(xyAB[0], xyAB[2]), ::fmin(xyAB[1], xyAB[3]),
436 ::fmax(xyAB[0], xyAB[2]), ::fmax(xyAB[1], xyAB[3])
437 };
438 m[i] = 1;
439 }
440 else
441 {
442 const int srtT = indexSortT[chd];
443 lu[i] = lowerupper[chd];
444 m[i] = mass[srtT];
445 }
446 }
447
448 const double4 loup = double4{
449 ::fmin(lu[0][0], lu[1][0]),
450 ::fmin(lu[0][1], lu[1][1]),
451 ::fmax(lu[0][2], lu[1][2]),
452 ::fmax(lu[0][3], lu[1][3]) };
453
454 lowerupper[srt] = loup;
455
456 // Центр текущего узла = центр его AABB
457 center[srt] = double2{
458 0.5 * (loup[0] + loup[2]),
459 0.5 * (loup[1] + loup[3])
460 };
461 flag = 1;
462 }//if j==0
463
464#pragma omp flush
465
466 if (flag != 0)
467 mass[nnodes - 1 - k] = m[0] + m[1];
468
469 }//while flag
470 }//for k
471 }
472 }//CalcAABB(...)
473
474
475
477 {
478 VMlib::vmTimer timer;
479 timer.start();
480
481 int nObject = (int)object.size();
482 if (nObject > 0)
483 {
484 //treeBoundingBox;
485 //to_parallel
486 auto minmaxX = std::minmax_element(object.begin(), object.end(), Point2D::cmp<'x'>);
487 auto minmaxY = std::minmax_element(object.begin(), object.end(), Point2D::cmp<'y'>);
488 minr = Point2D({ (*minmaxX.first)[0], (*minmaxY.first)[1] });
489 maxr = Point2D({ (*minmaxX.second)[0], (*minmaxY.second)[1] });
490
491 //treeMortonCodes;
492 double lmax, quadSideFactor;
493 lmax = std::max(maxr[0] - minr[0], maxr[1] - minr[1]);
494 Point2D rcen = 0.5 * (maxr + minr); //координаты центра
495 quadSideFactor = rbound / lmax; //1;
496
497#pragma omp parallel for
498 for (int bdy = 0; bdy < nObject; ++bdy)
499 {
500 Point2D rScaled = twoPowCodeLength * ((object[bdy] - rcen) * quadSideFactor + 0.5 * Point2D{ rbound, rbound });
501
502 unsigned int xx = ExpandBits((unsigned int)rScaled[0]);
503 unsigned int yy = ExpandBits((unsigned int)rScaled[1]);
504 mortonCodesKeyUnsort[bdy] = yy | (xx << 1);
505 mortonCodesIdxUnsort[bdy] = bdy;
506 //printf("{%d, %d},\n", bdy, mortonCodesKeyUnsort[bdy]);
507 }
508
509
510
512 //for (int i = 0; i < nObject; ++i)
513 //printf("{%d, %d},\n", mortonCodesKey[i], mortonCodesIdx[i]);
514
515 //treeMortonInternalNodes
516 //if (treeType != tree_T::contr) для CPU нужны внутренние ячейки для обоих деревьев
517 {
518#pragma omp parallel for
519 for (int i = 0; i < nObject - 1; ++i)
520 {
521 int codei = mortonCodesKey[i];
522
523 int Deltap1 = Delta(i, i + 1);
524 int Deltam1 = Delta(i, i - 1);
525
526 int d = (Deltap1 - Deltam1 > 0) ? 1 : -1;
527
528 int delta_min = (d > 0) ? Deltam1 : Deltap1;
529
530 int Lmax = 2;
531 int pos = i + Lmax * d;
532
533 while (Delta(i, pos) > delta_min)
534 {
535 Lmax *= 2;
536 pos = i + Lmax * d;
537 }
538
539 int L = 0;
540 for (int t = (Lmax >> 1); t >= 1; t >>= 1)
541 {
542 pos = i + (L + t) * d;
543
544 if (Delta(i, pos) > delta_min)
545 L += t;
546 }
547
548 int j = i + L * d;
549 pos = j;
550
551 int delta_node = Delta(i, j);
552
553 levelUnsort[i] = delta_node;
554 indexUnsort[i] = i;
555 //if (i == 2)
556 // printf("!!! %d, %d\n", mortonCodesKey[i], mortonCodesKey[j]);
557
558 int s = 0;
559 for (int p = 1, t = ceilhalf(L); L > (1 << (p - 1)); ++p, t = ceilpow2(L, p))
560 {
561 pos = i + (s + t) * d;
562
563 int dl = Delta(i, pos);
564
565 if (dl > delta_node)
566 s += t;
567 }//for p
568
569 int gammaPos = i + s * d + d * (d < 0); // = std::min(d, 0);
570
571 int Mmin = std::min(i, j);
572 int Mmax = std::max(i, j);
573
574 int left = gammaPos;
575 int right = gammaPos + 1;
576
577 // -
578 int childLeft = (Mmin == gammaPos) * nObject + left;
579 range[childLeft] = { Mmin, gammaPos };
580 parent[childLeft] = i;
581
582 // -
583 int childRight = (Mmax == gammaPos + 1) * nObject + right;
584 range[childRight] = { gammaPos + 1, Mmax };
585 parent[childRight] = i;
586
587 child[i] = { childLeft, childRight };
588 //printf("i:%d, %d, {%d, %d}\n", indexUnsort[i], levelUnsort[i], childLeft, childRight);
589 }
590
591 //if (treeType == tree_T::contr)
592 {
593 for (int i = 0; i < nObject - 1; ++i)
594 {
596 indexControlCells.push_back(i);
597 }
598 for (int i = 0; i < nObject; ++i)
599 {
600 if (levelUnsort[parent[nObject + i]] < controlLevel)
601 indexControlCells.push_back(nObject + i);
602 }
603 }
605 }// if contr
606 }
607 timer.stop();
608
609 return (float)timer.duration();
610 }//Build()
611
612
614 {
615 VMlib::vmTimer timer;
616 timer.start();
617
618 if (object.size() > 0)
619 {
620 mass.assign(object.size() - 1, -1);
621
623 {
624#include <SummChoice.h>
625 }
626 else
627 CalcAABB();
628 }
629 timer.stop();
630
631 return (float)timer.duration();
632 }//UpwardTraversal(...)
633
634
635
636 inline double mindist2(const Point4D& lowerUpper, const Point2D& rhs) noexcept
637 {
638 const double dx = ::fmin(lowerUpper[2], ::fmax(lowerUpper[0], rhs[0])) - rhs[0];
639 const double dy = ::fmin(lowerUpper[3], ::fmax(lowerUpper[1], rhs[1])) - rhs[1];
640 return dx * dx + dy * dy;
641 }
642
643 inline double minmaxdist2(const Point4D& lowerUpper, const Point2D& rhs) noexcept
644 {
645 std::pair<double, double> rm_sq = std::make_pair<double, double>((lowerUpper[0] - rhs[0]) * (lowerUpper[0] - rhs[0]),
646 (lowerUpper[1] - rhs[1]) * (lowerUpper[1] - rhs[1]));
647 std::pair<double, double> rM_sq = std::make_pair<double, double>((lowerUpper[2] - rhs[0]) * (lowerUpper[2] - rhs[0]),
648 (lowerUpper[3] - rhs[1]) * (lowerUpper[3] - rhs[1]));
649
650 if ((lowerUpper[2] + lowerUpper[0]) * 0.5 < rhs[0])
651 std::swap(rm_sq.first, rM_sq.first);
652
653 if ((lowerUpper[3] + lowerUpper[1]) * 0.5 < rhs[1])
654 std::swap(rm_sq.second, rM_sq.second);
655
656 const double dx = rm_sq.first + rM_sq.second;
657 const double dy = rM_sq.first + rm_sq.second;
658
659 return fmin(dx, dy);
660 }
661
662
663 inline std::pair<double, int> distance_calculator_point2segment(const Point2D& point, const Point4D& object)
664 {
665 int location;
666
667 double a = object[3] - object[1];
668 double b = object[2] - object[0];
669
670 double distanceSegment;
671
672 std::pair<double, double> dr = std::make_pair<double, double>(point[0] - object[0], point[1] - object[1]);
673
674 double r_numerator = dr.first * b + dr.second * a;
675 double r_denomenator = b * b + a * a;
676 double r = r_numerator / r_denomenator;
677
678 double s = (dr.first * a - dr.second * b);
679
680 if ((r >= 0) && (r <= 1))
681 {
682 distanceSegment = s * s / r_denomenator;
683 location = 0;
684 }
685 else
686 {
687 double dist1 = dr.first * dr.first + dr.second * dr.second;
688 double dist2 = sqr(point[0] - object[2]) + sqr(point[1] - object[3]);
689 if (dist1 < dist2)
690 {
691 distanceSegment = dist1;
692 location = -1;
693 }
694 else
695 {
696 distanceSegment = dist2;
697 location = 1;
698 }
699 }
700
701 return std::make_pair(distanceSegment, location);
702 }
703
704 inline double MinDist2AABB(const Point4D& gabI, const Point4D& gabJ)
705 {
706 double dx = 0.0, dy = 0.0;
707
708 if (gabI[0] > gabJ[2])
709 dx = gabI[0] - gabJ[2];
710 else if (gabJ[0] > gabI[2])
711 dx = gabJ[0] - gabI[2];
712
713 if (gabI[1] > gabJ[3])
714 dy = gabI[1] - gabJ[3];
715 else if (gabJ[1] > gabI[3])
716 dy = gabJ[1] - gabI[3];
717
718 return dx * dx + dy * dy;
719 }
720
721
722 const double onePlusMachineEps = 1.0f + 2.0e-7f;
723
724 float CpuTreeInfo::DownwardTraversalClosestPanelToPoints(CpuTreeInfo& cntrTree, std::vector<std::pair<int, double>>& indexPnlDist, bool findOnlyInside, double* pseudonormals)
725 {
726 using double4 = Point4D;
727 const int nbodies = (int)object.size(); //количество вихрей
728 const int nnodes = 2 * nbodies - 1; //количество узлов дерева вихрей (листья-вихри + внутренние узлы)
729 const int npoints = (int)cntrTree.object.size(); //количество точек наблюдения
730
731 int indexOfPoint;
732 const int maxDepth = 32;
733
734 VMlib::vmTimer timer;
735 timer.start();
736
737#pragma omp parallel for schedule(dynamic, 1)
738 for (int indexK = 0; indexK < (int)cntrTree.indexControlCells.size(); ++indexK)
739 {
740 Point2D query;
741
742 const int k = cntrTree.indexControlCells[indexK];
743
744 Point4D gabCntrl;
745 bool isCntrLeaf = (k >= cntrTree.object.size());
746
747 if (isCntrLeaf)
748 {
749 const int leafIndex = cntrTree.mortonCodesIdx[k - npoints];
750 indexOfPoint = leafIndex;
751 query = cntrTree.object[leafIndex];
752 Point4D pnlCntrGab = cntrTree.gabForLeaves[cntrTree.mortonCodesIdx[leafIndex]];
753 gabCntrl = {
754 fmin(pnlCntrGab[0], pnlCntrGab[2]), fmin(pnlCntrGab[1], pnlCntrGab[3]),
755 fmax(pnlCntrGab[0], pnlCntrGab[2]), fmax(pnlCntrGab[1], pnlCntrGab[3])
756 };
757 }
758 else
759 {
760 query = cntrTree.center[k];
761 gabCntrl = cntrTree.lowerupper[k];
762 }
763
764 std::pair<int, double> stack[maxDepth];
765 int depth;
766
767 double md2 = mindist2(lowerupper[0], query);
768 std::pair<int, int> nearestLocation{ -1, 0 };
769
770 if ((findOnlyInside) && (md2 != 0))
771 {
772 indexPnlDist[indexK] = { -1, -1.0 };
773 continue;
774 }
775
776 depth = 0;
777 stack[0] = { 0, md2 };
778
779 double dist_to_nearest_object = 1e+100;
780
781 while (depth >= 0)
782 {
783 std::pair<int, double> nd = stack[depth];
784 --depth;
785
786 if (nd.second > dist_to_nearest_object)
787 continue;
788
789 std::pair<int, int> chBoth = child[nd.first];
790
791 bool isLeftLeaf = (chBoth.first >= object.size());
792 bool isRightLeaf = (chBoth.second >= object.size());
793
794 double4 L_box, R_box;
795 double4 pnlLeftGab, pnlRightGab;
796
797 if (!isLeftLeaf)
798 L_box = lowerupper[chBoth.first];
799 else
800 {
801 int n = chBoth.first - (int)object.size();
802 pnlLeftGab = gabForLeaves[mortonCodesIdx[n]];
803 L_box = {
804 fmin(pnlLeftGab[0], pnlLeftGab[2]), fmin(pnlLeftGab[1], pnlLeftGab[3]),
805 fmax(pnlLeftGab[0], pnlLeftGab[2]), fmax(pnlLeftGab[1], pnlLeftGab[3])
806 };
807 }
808
809 if (!isRightLeaf)
810 R_box = lowerupper[chBoth.second];
811 else
812 {
813 int n = chBoth.second - (int)object.size();
814 pnlRightGab = gabForLeaves[mortonCodesIdx[n]];
815 R_box = {
816 fmin(pnlRightGab[0], pnlRightGab[2]), fmin(pnlRightGab[1], pnlRightGab[3]),
817 fmax(pnlRightGab[0], pnlRightGab[2]), fmax(pnlRightGab[1], pnlRightGab[3])
818 };
819 }
820
821 double L_mindist2 = mindist2(L_box, query);
822 double R_mindist2 = mindist2(R_box, query);
823
824 double L_minmaxdist2 = minmaxdist2(L_box, query);
825 double R_minmaxdist2 = minmaxdist2(R_box, query);
826
827 bool pushLeft = false;
828 bool pushRight = false;
829
830 if (L_mindist2 <= R_minmaxdist2 * onePlusMachineEps) // L is worth considering
831 {
832 if (isLeftLeaf) // leaf node
833 {
834 int n = chBoth.first - (int)object.size();
835
836 std::pair<double, int> dist_code = distance_calculator_point2segment(query, pnlLeftGab);
837 if (dist_code.first <= dist_to_nearest_object)
838 {
839 dist_to_nearest_object = dist_code.first;
840 nearestLocation = { mortonCodesIdx[n], dist_code.second };
841 }
842 }
843 else
844 pushLeft = true;
845 }
846
847 if (R_mindist2 <= L_minmaxdist2 * onePlusMachineEps) // R is worth considering
848 {
849 if (isRightLeaf) // leaf node
850 {
851 int n = chBoth.second - (int)object.size();
852
853 std::pair<double, int> dist_code = distance_calculator_point2segment(query, pnlRightGab);
854 if (dist_code.first <= dist_to_nearest_object)
855 {
856 dist_to_nearest_object = dist_code.first;
857 nearestLocation = { mortonCodesIdx[n], (int)dist_code.second };
858 }
859 }
860 else
861 pushRight = true;
862 }
863
864 if (pushLeft && !pushRight)
865 {
866 ++depth;
867 stack[depth] = { chBoth.first, L_mindist2 };
868 }
869 else if (!pushLeft && pushRight)
870 {
871 ++depth;
872 stack[depth] = { chBoth.second, R_mindist2 };
873 }
874 else if (pushLeft && pushRight)
875 {
876 if (L_mindist2 < R_mindist2)
877 {
878 // Сначала добавляем правый (дальний) вниз стека
879 ++depth;
880 stack[depth] = { chBoth.second, R_mindist2 };
881 // Потом левый (ближний) наверх - будет обработан первым
882 ++depth;
883 stack[depth] = { chBoth.first, L_mindist2 };
884 }
885 else
886 {
887 // Сначала добавляем левый (дальний) вниз стека
888 ++depth;
889 stack[depth] = { chBoth.first, L_mindist2 };
890 // Потом правый (ближний) наверх - будет обработан первым
891 ++depth;
892 stack[depth] = { chBoth.second, R_mindist2 };
893 }
894 }
895 }//while depth
896
897
898 if (isCntrLeaf)
899 {
900 indexPnlDist[cntrTree.mortonCodesIdx[k - npoints]] = { nearestLocation.first, sqrt(dist_to_nearest_object) };
901 continue;
902 }
903
904 double radius = sqrt(dist_to_nearest_object) + 0.5 * sqrt(sqr(gabCntrl[2] - gabCntrl[0]) + sqr(gabCntrl[3] - gabCntrl[1]));
905
906
907 int stackCandidates[maxDepth];
908 depth = 0;
909 stackCandidates[0] = 0;
910
911 std::vector<int> candidates;
912 candidates.reserve(100);
913
914 while (depth >= 0)
915 {
916 int nd = stackCandidates[depth];
917 --depth;
918
919 std::pair<int, int> chBoth = child[nd];
920
921 bool isLeftLeaf = (chBoth.first >= object.size());
922 bool isRightLeaf = (chBoth.second >= object.size());
923
924 Point4D gabL, gabR;
925
926 if (isLeftLeaf)
927 {
928 int n = chBoth.first - (int)object.size();
929 Point4D pnlLeftGab = gabForLeaves[mortonCodesIdx[n]];
930 gabL = {
931 fmin(pnlLeftGab[0], pnlLeftGab[2]), fmin(pnlLeftGab[1], pnlLeftGab[3]),
932 fmax(pnlLeftGab[0], pnlLeftGab[2]), fmax(pnlLeftGab[1], pnlLeftGab[3])
933 };
934 }
935 else
936 gabL = lowerupper[chBoth.first];
937
938
939 if (isRightLeaf)
940 {
941 int n = chBoth.second - (int)object.size();
942 Point4D pnlRightGab = gabForLeaves[mortonCodesIdx[n]];
943 gabR = {
944 fmin(pnlRightGab[0], pnlRightGab[2]), fmin(pnlRightGab[1], pnlRightGab[3]),
945 fmax(pnlRightGab[0], pnlRightGab[2]), fmax(pnlRightGab[1], pnlRightGab[3])
946 };
947 }
948 else
949 gabR = lowerupper[chBoth.second];
950
951
952 double dist2L = MinDist2AABB(gabCntrl, gabL);
953 double dist2R = MinDist2AABB(gabCntrl, gabR);
954
955 if (dist2L < sqr(radius))
956 {
957 if (isLeftLeaf)
958 candidates.push_back(mortonCodesIdx[chBoth.first - object.size()]);
959 else
960 stackCandidates[++depth] = chBoth.first;
961 }
962
963 if (dist2R < sqr(radius))
964 {
965 if (isRightLeaf)
966 candidates.push_back(mortonCodesIdx[chBoth.second - object.size()]);
967 else
968 stackCandidates[++depth] = chBoth.second;
969 }
970
971 }//while depth
972
973 for (int particle = cntrTree.range[k].first; particle <= cntrTree.range[k].second; ++particle)
974 {
975 Point2D observPoint = cntrTree.object[cntrTree.mortonCodesIdx[particle]];
976
977 double currentMinDist2 = 1e+100;
978 int indexMinDist = -1;
979 for (int i = 0; i < candidates.size(); ++i)
980 {
981 Point4D pnl = gabForLeaves[candidates[i]];
982 double dst2 = distance_calculator_point2segment(observPoint, gabForLeaves[candidates[i]]).first;
983 if (dst2 < currentMinDist2)
984 {
985 currentMinDist2 = dst2;
986 indexMinDist = candidates[i];
987 }
988 }
989 indexPnlDist[cntrTree.mortonCodesIdx[particle]] = { indexMinDist, sqrt(currentMinDist2) };
990 }
991 }//for k
992
993 //*/
994
995 timer.stop();
996 return (float)timer.duration();
997 }
998
999
1000 float CpuTreeInfo::DownwardTraversalVorticesToPoints(CpuTreeInfo& cntrTree, std::vector<Point2D>& vel, std::vector<double>& epsast, double theta, int order, bool calcRadius)
1001 {
1002 float t1 = (float)omp_get_wtime();
1003
1004 using double4 = Point4D;
1005 using double2 = Point2D;
1006 using int2 = std::pair<int, int>;
1007
1008 if (object.size() > 0)
1009 {
1010#pragma omp parallel
1011 {
1012 double itolsq = 1.0 / (theta * theta);
1013
1014 const int nbodies = (int)object.size(); //количество вихрей
1015 const int nnodes = 2 * nbodies - 1; //количество узлов дерева вихрей (листья-вихри + внутренние узлы)
1016 const int npoints = (int)cntrTree.object.size(); //количество точек наблюдения
1017
1018 int nd; // индекс родительской ячейки дерева, которую обходим, и которая находится на верхушке стека;
1019 int2 chBoth; //индексы обоих потомков узла nd
1020 int pd; // 0 или 1 --- какого потомка ячейки nd обходим (левого или правого)
1021
1022 // для вычисления квадратов расстояний до трех ближайших вихрей
1023 double d_1, d_2, d_3, dst23, dst12;
1024 int indexOfPoint; //истинный индекс точки наблюдения
1025 int srtT; //истинный индекс внутреннего узла дерева
1026
1027 double2 p; //координаты точки наблюдения
1028 double2 v{ 0.0,0.0 }; //результат расчета скорости в точке наблюдения
1029
1030 bool isVortex; //признак того, что обрабатываемая вершина - лист
1031 double2 ps; //координаты влияющего вихря, если обрабатываемая вершина --- лист, или центра влияющей ячейки, если обрабатывается внутренняя ячейка дерева
1032 double gm; //циркуляция вихря, если обрабатываемая вершина --- лист, или 0-й мультипольный момент (суммарная циркуляция вихрей) если обрабатывается внутренняя ячейка дерева
1033 double sgm2;
1034 double sgm2Contr;
1035 double sumSide2; //габариты обрабатываемой ячейки
1036 double2 dr; //радиус-вектор из центра влияющей ячейки в точку наблюдения
1037 double r2; //квадрат модуля предыдущего
1038
1039 const int maxDepth = 32;
1040
1041 int posStack[maxDepth];
1042 int nodeStack[maxDepth];
1043 int depth;
1044 const Point2D* mom = nullptr;
1045
1046#pragma omp for schedule(dynamic,1)
1047 for (int indexK = 0; indexK < (int)cntrTree.indexControlCells.size(); ++indexK)
1048 {
1049 const int k = cntrTree.indexControlCells[indexK];
1050 std::vector<Point2D> vParticles;
1051 std::vector<numvector<double, 3>> epsastParticles;
1052
1053 v.toZero();
1054 d_1 = d_2 = d_3 = 1e+5;
1055 std::vector<Point2D> Ek(order, { 0.0, 0.0 });
1056
1057 bool isCntrLeaf = (k >= npoints);
1058
1059 if (isCntrLeaf)
1060 {
1061 const int leafIndex = cntrTree.mortonCodesIdx[k - npoints];
1062 indexOfPoint = leafIndex;
1063 p = cntrTree.object[leafIndex];
1064 sgm2Contr = sqr(sigma[leafIndex]);
1065 }
1066 else
1067 {
1068 p = cntrTree.center[k];
1069 const Point4D& gab = cntrTree.lowerupper[k];
1070 sgm2Contr = sqr((gab[2] - gab[0] + gab[3] - gab[1]));
1071 vParticles.resize(cntrTree.range[k].second - cntrTree.range[k].first + 1, { 0.0, 0.0 });
1072 epsastParticles.resize(cntrTree.range[k].second - cntrTree.range[k].first + 1, { 1e+5, 1e+5, 1e+5 });
1073 }
1074
1075 depth = 0;
1076 posStack[0] = 0;
1077 nodeStack[0] = nnodes - 1;
1078
1079 while (depth >= 0)
1080 {
1081 pd = posStack[depth];
1082 nd = nodeStack[depth];
1083
1084 chBoth = child[indexSort[(nnodes - 1) - nd]];
1085
1086 while (pd < 2)
1087 {
1088 const int chd = (pd == 0) ? chBoth.first : chBoth.second;
1089
1090 ++pd;
1091 posStack[depth] = pd;
1092
1093 isVortex = (chd >= nbodies);
1094
1095 int n;
1096 if (isVortex)
1097 {
1098 n = chd - nbodies;
1099 const int vortexIndex = mortonCodesIdx[n]; // истинный индекс вихря
1100
1101 ps = object[vortexIndex];
1102 gm = gamma[vortexIndex];
1103 sgm2 = sqr(sigma[vortexIndex]);
1104 sumSide2 = 0.0; // ???
1105 }
1106 else
1107 {
1108 srtT = indexSortT[chd];
1109 n = (nnodes - 1) - srtT;
1110
1111 ps = center[chd];
1112
1113 const Point4D& gab = lowerupper[chd];
1114 sumSide2 = sqr((gab[2] - gab[0] + gab[3] - gab[1]));
1115 }
1116
1117 dr = p - ps;
1118 r2 = dr[0] * dr[0] + dr[1] * dr[1];
1119 if ((isVortex && isCntrLeaf) || ((sumSide2 + sgm2Contr) * itolsq < r2)) // если выполнен критерий дальности (может быть как влияние кластера, так и одного дальнего вихря)
1120 {
1121 // Если это ячейка, берём её mm
1122 if (!isVortex)
1123 {
1124 mom = moms.data() + srtT * orderAlignment;
1125 gm = mom[0][0]; // нулевой момент = суммарная циркуляция
1126 sgm2 = 0.0;
1127 }
1128
1129 if ((calcRadius) && (isCntrLeaf))
1130 {
1131 if ((r2 < d_3) && (r2 > 0.0))
1132 {
1133 dst23 = std::fmin(r2, d_2);
1134 d_3 = std::fmax(r2, d_2);
1135
1136 dst12 = std::fmin(dst23, d_1);
1137 d_2 = std::fmax(dst23, d_1);
1138
1139 d_1 = dst12;
1140 }
1141 }
1142 const double f = gm / std::fmax(r2, sgm2);
1143
1144 if (isCntrLeaf)
1145 v += f * dr;
1146
1147 if (!isCntrLeaf)
1148 Ek[0] += f * dr; // = theta[m] * m[m]
1149
1150 // Вклад высших мм если влияет ячейка
1151 if ((order > 1) && r2 > 0.0)
1152 {
1153 Point2D cftr = (1.0 / r2) * dr;
1154 Point2D th = cftr;
1155
1156 if (isVortex)
1157 {
1158 if (!isCntrLeaf)
1159 for (int s = 1; s < order; ++s)
1160 {
1161 th = s * multz(th, cftr);
1162 Ek[s] += (s % 2 ? -1.0 : 1.0) * th * gm;
1163 }
1164 }
1165 else
1166 {
1167 for (int s = 1; s < order; ++s)
1168 {
1169 th = s * multz(th, cftr);
1170
1171 if (isCntrLeaf)
1172 {
1173 Point2D add = ifac[s + 1] * multzA(th, mom[s]);
1174 v += add;
1175 }
1176 else
1177 {
1178 for (int q = 0; q <= s; ++q)
1179 {
1180 //if (s == q)
1181 // std::cout << "E[" << q << "] += " << (q % 2 ? -1.0 : 1.0) << " * " << ifac[s - q + 1] << " * " << th << " * " << mom[s - q] << "\n";
1182 Ek[q] += ((q % 2 ? -1.0 : 1.0) * ifac[s - q + 1]) * multzA(th, mom[s - q]);
1183 }
1184
1185 if (s == order - 1)
1186 Ek[0] += ifac[s + 1] * multzA(multz(th, cftr), mom[s]);
1187 }
1188 }
1189 }
1190 }
1191 }
1192 else if (!isVortex)
1193 {
1194 // Ячейка слишком близка -> спускаемся ниже по дереву
1195 if (depth + 1 < maxDepth)
1196 {
1197 if (pd == 1)//если это была обработка левого потомка, и по нему пришлось идти вниз по дереву
1198 {
1199 posStack[depth] = 1;
1200 nodeStack[depth] = nd;
1201 ++depth;
1202 }
1203
1204 nd = n;
1205 pd = 0;
1206
1207 chBoth = child[indexSort[(nnodes - 1) - nd]];
1208 }
1209 }
1210 else if (!isCntrLeaf)
1211 {
1212 for (int pnt = cntrTree.range[k].first; pnt <= cntrTree.range[k].second; ++pnt)
1213 {
1214 Point2D dz = cntrTree.object[cntrTree.mortonCodesIdx[pnt]] - ps;
1215 double dz2 = dz.length2();
1216
1217 vParticles[pnt - cntrTree.range[k].first] += (gm / std::fmax(dz2, sgm2)) * dz;
1218
1219 if (calcRadius)
1220 {
1221 double& m_3 = epsastParticles[pnt - cntrTree.range[k].first][2];
1222 double& m_2 = epsastParticles[pnt - cntrTree.range[k].first][1];
1223 double& m_1 = epsastParticles[pnt - cntrTree.range[k].first][0];
1224
1225 if ((dz2 < m_3) && (dz2 > 0.0))
1226 {
1227 dst23 = std::fmin(dz2, m_2);
1228 m_3 = std::fmax(dz2, m_2);
1229
1230 dst12 = std::fmin(dst23, m_1);
1231 m_2 = std::fmax(dst23, m_1);
1232
1233 m_1 = dst12;
1234 }
1235 }
1236 }
1237 } // БС
1238 }//while pd
1239
1240 // Оба потомка обработаны -> снимаем узел со стека
1241 --depth;
1242 }//while depth
1243
1244
1245 if (isCntrLeaf)
1246 {
1247 vel[indexOfPoint] = IDPI * v.kcross();
1248 if (calcRadius)
1249 epsast[indexOfPoint] = 1.0 * sqrt((d_1 + d_2 + d_3) / 3.0);
1250 }
1251 else
1252 {
1253 for (int pnt = cntrTree.range[k].first; pnt <= cntrTree.range[k].second; ++pnt)
1254 {
1255 Point2D dz = cntrTree.object[cntrTree.mortonCodesIdx[pnt]] - p;
1256 Point2D dzPow = dz;
1257 v = Ek[0] + vParticles[pnt - cntrTree.range[k].first];
1258 for (int q = 1; q < order; ++q)
1259 {
1260 v += ifac[q + 1] * multzA(Ek[q], dzPow);
1261 dzPow = multz(dzPow, dz);
1262 }
1263 vel[cntrTree.mortonCodesIdx[pnt]] = IDPI * v.kcross();
1264
1265 if (calcRadius)
1266 {
1267 const auto& es = epsastParticles[pnt - cntrTree.range[k].first];
1268 epsast[cntrTree.mortonCodesIdx[pnt]] = 1.0 * sqrt((es[0] + es[1] + es[2]) / 3.0);
1269 }
1270 }
1271 }
1272 }//for k
1273 }
1274 }//pragma omp parallel
1275
1276 float t2 = (float)omp_get_wtime();
1277 return (t2 - t1);
1278 }//DownwardTraversalVorticesToPoints(...)
1279
1280
1281 float CpuTreeInfo::DownwardTraversalVorticesToPanels(CpuTreeInfo& cntrTree, std::vector<double>& rhs, std::vector<double>& rhsLin, double theta, int order)
1282 {
1283 float t1 = (float)omp_get_wtime();
1284
1285 using double4 = Point4D;
1286 using double2 = Point2D;
1287 using int2 = std::pair<int, int>;
1288
1289 const int nbodies = (int)object.size(); //количество вихрей
1290 const int npoints = (int)cntrTree.object.size(); //количество панелей
1291
1292 rhs.assign(npoints, 0.0);
1293 bool scheme = false;
1294 if (cntrTree.schemeType == scheme_T::linScheme)
1295 scheme = true;
1296
1297 if(scheme)
1298 rhsLin.assign(npoints, 0.0);
1299 else
1300 rhsLin.clear();
1301
1302 if (nbodies <= 0 || npoints <= 0)
1303 return 0.0f;
1304
1305//#pragma omp parallel
1306 {
1307 double itolsq = 1.0 / (theta * theta);
1308 const int nnodes = 2 * nbodies - 1; //количество узлов дерева вихрей (листья-вихри + внутренние узлы)
1309
1310 const int maxDepth = 32;
1311
1312#pragma omp parallel for schedule(dynamic, 1)//Временно: обход по панелям как на GPU!!!
1313 for (int k = 0; k < npoints; ++k)
1314 {
1315 const int indexOfPoint = cntrTree.mortonCodesIdx[k]; //истинный индекс точки наблюдения
1316 const double2 p = cntrTree.object[indexOfPoint]; //координаты центра панели
1317 const double4& pnl = cntrTree.gabForLeaves[indexOfPoint]; //начало и конец панели наблюдения
1318 const double2 beg{ pnl[0], pnl[1] }; //отдельно начало
1319 const double2 end{ pnl[2], pnl[3] }; //отдельно конец
1320 const double2 rPan = end - beg; //направляющий вектор панели
1321 const double dlen2 = rPan.length2(); //длина панели в квадрате
1322 const double idlen = 1.0 / sqrt(dlen2); //обратная длина
1323 const double2 tau = idlen * rPan; //вектор касательной к панели, направленный от начала к концу
1324 double val = 0.0; //обнуление результата (константная составляющая скорости)
1325 double vallin = 0.0; //обнуление результата (линейная составляющая скорости)
1326 int posStack[maxDepth];
1327 int nodeStack[maxDepth];
1328 std::array<double2, orderAlignment> Eloc; //коэффициенты локальных разложений
1329
1330 int depth = 0;
1331 posStack[0] = 0;
1332 nodeStack[0] = nnodes - 1;
1333 while (depth >= 0)
1334 {
1335 int pd = posStack[depth]; //0 или 1 --- какого потомка ячейки nd обходим (левого или правого)
1336 int nd = nodeStack[depth]; //индекс родительской ячейки дерева, которую обходим, и которая находится на верхушке стека;
1337
1338 int2 chBoth = child[indexSort[(nnodes - 1) - nd]]; //индексы обоих потомков узла nd
1339 while (pd < 2)
1340 {
1341 const int chd = (pd == 0) ? chBoth.first : chBoth.second;
1342 ++pd;
1343 posStack[depth] = pd;
1344
1345 const bool isVortex = (chd >= nbodies); //признак того, что обрабатывамая ячейка --- лист (в правой половине дерева в порядке karrasOrder)
1346
1347 int n; //для хранения индекса вихря или индекса ячейки
1348 int srtT = -1; //истинный индекс внутреннего узла дерева
1349
1350 double2 ps; //координаты влияющего вихря, если обрабатываемая вершина --- лист, или центра влияющей ячейки, если обрабатывается внутренняя ячейка дерева
1351 double gm = 0.0; //циркуляция вихря, если обрабатываемая вершина --- лист, или 0-й мультипольный момент (суммарная циркуляция вихрей) если обрабатывается внутренняя ячейка дерева
1352
1353 double sumSide2 = 0.0; //габариты обрабатываемой ячейки
1354
1355 const Point2D* mom = nullptr; //указатель на набор мультипольных моментов влияющей ячейки
1356
1357 if (isVortex) //если лист
1358 {
1359 n = chd - nbodies; //номер вихря в Мортоновском порядке
1360 const int vortexIndex = mortonCodesIdx[n]; //истинный номер вихря
1361
1362 ps = object[vortexIndex]; //координаты влияющего вихря
1363 gm = gamma[vortexIndex]; //циркуляция влияющего вихря
1364
1365 sumSide2 = 0.0; //листовая ячейка (вихрь) размера не имеет, т.к. является точкой
1366 }//if (isVortex)
1367 else
1368 {
1369 srtT = indexSortT[chd]; //номер внутреннего узла в "развернутом порядке burtscherOrder" (когда корень --- 0-й), отвечающий узлу chd
1370 n = (nnodes - 1) - srtT; //номер внутреннего узла в порядке burtscherOrder (когда корень --- последний)
1371 ps = center[chd]; //координаты центра внутреннего узла --- влияющей ячейки
1372
1373 const double4& gab = lowerupper[chd]; //габарит влияющей ячейки
1374 const double sumSide = gab[2] - gab[0] + gab[3] - gab[1]; //сумма габаритов (полупериметр)
1375 sumSide2 = sumSide * sumSide; //квадрат суммы габаритов влияющей ячейки
1376 }//if (!isVortex)
1377
1378 const double2 dr = p - ps; //радиус-вектор из центра влияющей ячейки в точку наблюдения
1379 const double r2 = dr[0] * dr[0] + dr[1] * dr[1]; //квадрат модуля предыдущего
1380
1381 // Для вихря всегда считаем точное влияние. Для ячейки проверяем MAC.
1382 if (isVortex || (sumSide2 + dlen2) * itolsq < r2)
1383 {
1384 if (isVortex) //если лист --- считаем напрямую
1385 {
1386 const double2 ss = ps - beg;
1387 const double2 pp = ps - end;
1388
1389 const double alpha = atan2(pp[0] * ss[1] - pp[1] * ss[0], pp[0] * ss[0] + pp[1] * ss[1]);
1390
1391 if (r2 > 1e-20)
1392 val -= gm * alpha;
1393
1394 if (scheme) //если схема Т1
1395 {
1396 const double txx = tau[0] * tau[0];
1397 const double txy = tau[0] * tau[1];
1398 const double tyy = tau[1] * tau[1];
1399
1400 double2 u1;
1401
1402 u1[0] = (pp[0] + ss[0]) * (txx - tyy) + 2.0 * (pp[1] + ss[1]) * txy;
1403
1404 u1[1] = (pp[1] + ss[1]) * (tyy - txx) + 2.0 * (pp[0] + ss[0]) * txy;
1405
1406 const double lambda = 0.5 * log(ss.length2() / pp.length2());
1407
1408 const double tempVelLin = gm * (alpha * (u1[0] * tau[0] + u1[1] * tau[1]) +
1409 lambda * (-u1[1] * tau[0] + u1[0] * tau[1]));
1410
1411 vallin -= 0.5 * idlen * tempVelLin;
1412 }//if(linScheme)
1413 }//if (isVortex)
1414 else //если не лист --- мультипольное приближение
1415 {
1416 mom = moms.data() + srtT * orderAlignment;
1417 //std::vector<double2> Eloc(order, double2{ 0.0, 0.0 });
1418 std::fill(Eloc.begin(), Eloc.begin() + order, double2{ 0.0, 0.0 }); //обнуляем коэффициенты локального разложения
1419
1420 double2 thetaLoc = (1.0 / r2) * (p - ps);
1421
1422 // Коэффициенты локального разложения
1423 for (int q = 0; q < order; ++q)
1424 {
1425 for (int s = q; s >= 0; --s)
1426 {
1427 const double sign = ((2 * q - s) & 1) ? -1.0 : 1.0;
1428
1429 Eloc[s] += sign * ifac[q - s + 1] * multzA(thetaLoc, mom[q - s]);
1430 }
1431
1432 thetaLoc = ((q + 1.0) / r2) * multz(thetaLoc, p - ps);
1433 }
1434
1435 // Интегрирование локального разложения по панели для T0 и T1
1436 double2 v = 0.5 * Eloc[0];
1437
1438 double2 vL{ 0.0, 0.0 };
1439
1440 double2 kp = rPan;
1441 double2 mulP = kp;
1442
1443 double2 taudL = (0.5 / dlen2) * rPan;
1444
1445 const double2 taudLc = taudL;
1446
1447 for (int kk = 1; kk < order; ++kk)
1448 {
1449 mulP = multz(mulP, kp);
1450
1451 taudL *= 0.5;
1452
1453 if (!(kk & 1)) // Константная проекционная функция
1454 v += ifac[kk + 2] * multz(Eloc[kk], multzA(taudL, mulP));
1455 else if(scheme) // Линейная проекционная функция
1456 vL += ifac[kk + 3] * multz(Eloc[kk], multzA(multz(taudL, taudLc), multz(mulP, (kk + 1.0) * rPan)));
1457 }
1458
1459 val += 2.0 * (-v[1] * rPan[0] + v[0] * rPan[1]);
1460 if(scheme)
1461 vallin += 2.0 * (-vL[1] * rPan[0] + vL[0] * rPan[1]);
1462 }
1463 }//MAC
1464 else //ячейка близкая --- спускаемся по дереву
1465 {
1466 if (depth + 1 < maxDepth)
1467 {
1468 if (pd == 1) //если это была обработка левого потомка, и по нему пришлось идти вниз по дереву
1469 {
1470 posStack[depth] = 1;
1471 nodeStack[depth] = nd;
1472
1473 ++depth;
1474 }
1475
1476 nd = n;
1477 pd = 0;
1478
1479 chBoth = child[indexSort[(nnodes - 1) - nd]];
1480 }
1481 }
1482 }//while (pd < 2)
1483 --depth; // Оба потомка обработаны -> снимаем узел со стека
1484 }//while (depth >= 0)
1485
1486 const double cf = IDPI * idlen;
1487
1488 rhs[indexOfPoint] = cf * val;
1489 if(scheme)
1490 rhsLin[indexOfPoint] = cf * vallin;
1491 }//for npoints
1492 }
1493
1494 float t2 = (float)omp_get_wtime();
1495 return (t2 - t1);
1496 }//DownwardTraversalVorticesToPanels(...)
1497
1498}//namespace VM2D
Описание констант и параметров для взаимодействия с графическим ускорителем
const double ifac[]
Definition Gpudefs.h:142
#define rbound
Definition Gpudefs.h:103
object_T
Definition Gpudefs.h:153
#define codeLength
Definition Gpudefs.h:101
#define orderAlignment
Definition Gpudefs.h:94
tree_T
Definition Gpudefs.h:146
#define twoPowCodeLength
Definition Gpudefs.h:102
scheme_T
Definition Gpudefs.h:158
Заголовочный файл с описанием класса TimesGen.
const double IDPI
Число .
Definition defs.h:79
Структура, хранящая данные и указатели на массивы на GPU для оптимизации итерационного решения СЛАУ н...
Definition cpuTreeInfo.h:96
std::vector< std::pair< int, int > > range
std::vector< Point2D > moms
std::vector< int > indexControlCells
float UpdatePanelGeometry(const std::vector< std::pair< Point2D, Point2D > > &panels, int cntrLev)
CpuTreeInfo(tree_T treeType_, object_T objectType_, scheme_T schemeType_)
std::vector< int > levelUnsort
float Update(const std::vector< Vortex2D > &vtx, int cntrLev=0)
std::vector< unsigned int > mortonCodesKeyUnsort
float DownwardTraversalVorticesToPanels(CpuTreeInfo &cntrTree, std::vector< double > &rhs, std::vector< double > &rhsLin, double theta, int order)
float DownwardTraversalClosestPanelToPoints(CpuTreeInfo &cntrTree, std::vector< std::pair< int, double > > &indexPnlDist, bool findOnlyInside, double *pseudonormals)
std::vector< int > parent
std::vector< int > indexSort
std::vector< unsigned int > mortonCodesKey
std::vector< Point2D > object
float UpwardTraversal(int order)
std::vector< int > mortonCodesIdx
std::vector< double > gamma
void RadixSortInternalCells()
std::vector< Point2D > center
OptimizedRadixSorter< int > levelSorter
std::vector< Point4D > gabForLeaves
std::vector< int > levelSort
std::vector< int > indexSortT
std::vector< Point4D > lowerupper
OptimizedRadixSorter< int > codesSorter
std::vector< int > mass
std::vector< double > sigma
std::vector< int > indexUnsort
std::vector< int > mortonCodesIdxUnsort
int Delta(int i, int j) const
float DownwardTraversalVorticesToPoints(CpuTreeInfo &cntrTree, std::vector< Point2D > &vel, std::vector< double > &epsast, double theta, int order, bool calcRadius)
std::vector< std::pair< int, int > > child
void sort(int *keys, ValueType *values, size_t n)
auto length2() const -> typename std::remove_const< typename std::remove_reference< decltype(this->data[0])>::type >::type
Вычисление квадрата нормы (длины) вектора
Definition numvector.h:386
Класс засекания времени
Definition TimesGen.h:59
double duration() const
Definition TimesGen.h:143
const vmTimer & stop() const
Останов работающего счетчика времени
Definition TimesGen.h:125
const vmTimer & start() const
Запуск (первый или повторный) счетчика времени
Definition TimesGen.h:109
const vmTimer & reset() const
Сброс счетчика времени
Definition TimesGen.h:101
Заголовок класса дерева для реализации быстрых алгоритмов на CPU.
double minmaxdist2(const Point4D &lowerUpper, const Point2D &rhs) noexcept
int ceilhalf(int x)
std::pair< double, int > distance_calculator_point2segment(const Point2D &point, const Point4D &object)
unsigned int ExpandBits(unsigned int v)
const double onePlusMachineEps
int ceilpow2(int x, int p)
double MinDist2AABB(const Point4D &gabI, const Point4D &gabJ)
double mindist2(const Point4D &lowerUpper, const Point2D &rhs) noexcept