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
39#include "cpuTreeInfo.h"
40#include "Gpudefs.h"
41#include <algorithm>
42#include <omp.h>
43#include <stdio.h>
44
45namespace VM2D
46{
47 //"Разрежение" двоичного представления беззнакового целого, вставляя по одному нулику между всеми битами
48 inline unsigned int ExpandBits(unsigned int v)
49 {
50 // вставит 1 нуль
51 v = (v | (v << 8)) & 0x00FF00FF; // 00000000`00000000`abcdefgh`ijklmnop
52 // | 00000000`abcdefgh`ijklmnop`00000000
53 // = 00000000`abcdefgh`XXXXXXXX`ijklmnop
54 // & 00000000`11111111`00000000`11111111
55 // = 00000000`abcdefgh`00000000`ijklmnop
56
57 v = (v | (v << 4)) & 0x0F0F0F0F; // 00000000`abcdefgh`00000000`ijklmnop
58 // | 0000abcd`efgh0000`0000ijkl`mnop0000
59 // = 0000abcd`XXXXefgh`0000ijkl`XXXXmnop
60 // & 00001111`00001111`00001111`00001111
61 // = 0000abcd`0000efgh`0000ijkl`0000mnop
62
63 v = (v | (v << 2)) & 0x33333333; // 0000abcd`0000efgh`0000ijkl`0000mnop
64 // | 00abcd00`00efgh00`00ijkl00`00mnop00
65 // = 00abXXcd`00efXXgh`00ijXXkl`00mnXXop
66 // & 00110011`00110011`00110011`00110011
67 // = 00ab00cd`00ef00gh`00ij00kl`00mn00op
68
69 v = (v | (v << 1)) & 0x55555555; // 00ab00cd`00ef00gh`00ij00kl`00mn00op
70 // | 0ab00cd0`0ef00gh0`0ij00kl0`0mn00op0
71 // = 0aXb0cXd`0eXf0gXh`0iXj0kXl`0mXn0oXp
72 // & 01010101`01010101`01010101`01010101
73 // = 0a0b0c0d`0e0f0g0h`0i0j0k0l`0m0n0o0p
74 return v;
75 }
76
77 //Округление "в потолок" результата деления на степень двойки
78 inline int ceilpow2(int x, int p) // =ceil(x / 2^p)
79 {
80 return (x >> p) + !!(x & ((1 << p) - 1));
81 }
82
83 //Округление "в потолок" результата деления пополам
84 inline int ceilhalf(int x) // =ceil(x / 2), т.е. предыдущая функция при p=1
85 {
86 return (x >> 1) + (x & 1);
87 }
88
89 int CpuTreeInfo::Delta(int i, int j) const
90 {
91 if ((j < 0) || (j > (int)object.size() - 1))
92 return -1;
93
94 if (i > j)
95 std::swap(i, j);
96
97 //if ((i < 0) || (j > n-1))
98 // exit(111);
99
100 const unsigned int ki = mortonCodesKeyUnsort[i];
101 const unsigned int kj = mortonCodesKeyUnsort[j];
102
103 //Поиск номера самого старшего ненулевого бита в числе c
104 int count = 0;
105 for (unsigned int c = (ki ^ kj); c; c >>= 1, ++count);
106
107 if ((!count) && (i != j))
108 {
109 int addCount = 0;
110 //единички к номерам i и j добавлены для совместимости с Wolfram Mathematica,
111 //для кода они не важны, но дерево без них почти наверняка построится по-другому
112 for (unsigned int add = ((i + 1) ^ (j + 1)); add; add >>= 1, ++addCount);
113 return 2 * codeLength + (2 * codeLength - addCount);
114 }//if ((!count) && (i != j))
115
116 return (2 * codeLength - count);
117 }//Delta(...)
118
119
120
121
122 CpuTreeInfo::CpuTreeInfo(tree_T treeType_, object_T objectType_, scheme_T schemeType_)
123 :
124 treeType(treeType_),
125 objectType(objectType_),
126 schemeType(schemeType_)
127 {
128 /*
129 switch (objectType)
130 {
131 case object_T::point2:
132 sizeOfElement = sizeof(Point2D);
133 offsetOfPointInElement = 0;
134 break;
135
136 case object_T::point3:
137 sizeOfElement = sizeof(Vortex2D);
138 offsetOfPointInElement = 0;
139 break;
140
141 case object_T::panel:
142 switch (treeType)
143 {
144 case tree_T::aux:
145 sizeOfElement = sizeof(double) * 6;
146 offsetOfPointInElement = 0;
147 break;
148 case tree_T::contr:
149 sizeOfElement = sizeof(Point2D);
150 offsetOfPointInElement = 0;
151 break;
152 case tree_T::vortex:
153 case tree_T::source:
154 sizeOfElement = sizeof(double) * 12;
155 offsetOfPointInElement = 0;
156 break;
157 }
158 break;
159 }
160 */
161 };
162
164
165 float CpuTreeInfo::Update(const std::vector<Vortex2D>& vtx, double eps)
166 {
167 float t1 = (float)omp_get_wtime();
168 int nObject = (int)vtx.size();
169 //if (firstCall)
170 {
171 object.resize(nObject);
172 gamma.resize(nObject);
173 gabForLeaves.resize(nObject);
174 mortonCodesKeyUnsort.resize(nObject);
175 mortonCodesIdxUnsort.resize(nObject);
176 mortonCodesKey.resize(nObject);
177 mortonCodesIdx.resize(nObject);
178
179 if (treeType != tree_T::contr)
180 {
181 levelUnsort.resize(nObject - 1);
182 levelSort.resize(nObject - 1);
183 indexUnsort.resize(nObject - 1);
184 indexSort.resize(nObject - 1);
185 indexSortT.resize(nObject - 1);
186 range.resize(nObject - 1);
187 parent.resize(nObject - 1);
188 child.resize(nObject - 1);
189 mass.resize(nObject - 1);
190 }
191 }
192
193 for (int v = 0; v < nObject; ++v)
194 {
195 Point2D r = vtx[v].r();
196 object[v] = r;
197 gamma[v] = vtx[v].g();
198 gabForLeaves[v] = Point4D({ r[0] - eps, r[1] - eps, r[0] + eps, r[1] + eps });
199 }
200
201 float t2 = (float)omp_get_wtime();
202 return (t2 - t1);
203 }
204
205
206 //Сортировка листьев
208 {
209 int nObject = (int)object.size();
210 if (nObject <= 0)
211 return;
212
213 const unsigned int threads = (unsigned int)(omp_get_max_threads());
214 //временные массивы
215 std::vector<unsigned int> mortonCodesKeyTemp(nObject);
216 std::vector<int> mortonCodesIdxTemp(nObject);
217 std::vector<unsigned int> s(256 * threads);
218
219 std::copy(mortonCodesKeyUnsort.begin(), mortonCodesKeyUnsort.end(),
220 mortonCodesKey.begin());
221
222 std::copy(mortonCodesIdxUnsort.begin(), mortonCodesIdxUnsort.end(),
223 mortonCodesIdx.begin());
224
225#pragma omp parallel num_threads(threads)
226 {
227 unsigned int* sourceKey = mortonCodesKey.data();
228 unsigned int* destKey = mortonCodesKeyTemp.data();
229
230 int* sourceIdx = mortonCodesIdx.data();
231 int* destIdx = mortonCodesIdxTemp.data();
232
233 const unsigned int tid = (unsigned int)(omp_get_thread_num()); //текущий номер потока
234 const unsigned int nt = (unsigned int)(omp_get_num_threads()); //общее число потоков в секции
235
236 //делим данные между потоками
237 const unsigned int div = nObject / nt;
238 const unsigned int mod = nObject % nt;
239
240 const unsigned int chunk = div + (tid < mod ? 1u : 0u); //длина участка, который обрабатывает этот поток = div или div + 1
241
242 //Если поток попадает в первые mod потоков, он получает кусок длины div + 1
243 const unsigned int left_index = (tid < mod) ? tid * (div + 1u) : mod * (div + 1u) + (tid - mod) * div;
244 const unsigned int right_index = (chunk == 0u) ? left_index : (left_index + chunk - 1u);
245
246 for (unsigned int digit = 0; digit < sizeof(unsigned int); ++digit)
247 {
248 unsigned int s_sum[256] = { 0 };
249 unsigned int s0[256] = { 0 };
250
251 //Локальная гистограмма текущего байта
252 // Для каждого элемента берём текущий байт ключа: (sourceKey[i] >> (8 * digit)) & 0xFF
253 // сдвиг >> переносит нужный байт в младшие 8 бит
254 // & 0xFF оставляет только этот байт
255 // bucket находится в диапазоне [0..255]
256
257 if (chunk != 0u)
258 {
259 for (unsigned int i = left_index; i <= right_index; ++i)
260 {
261 const unsigned int bucket = (sourceKey[i] >> (8u * digit)) & 0xFFu;
262 ++s0[bucket];
263 }
264 }
265
266 // Сохраняем локальную гистограмму текущего потока в общий буфер s.
267 // Для потока tid его участок — это: s[256 * tid + 0 ... 256 * tid + 255]
268 for (unsigned int b = 0; b < 256u; ++b)
269 s[256u * tid + b] = s0[b];
270#pragma omp barrier
271
272 // Собираем суммарные размеры корзин и частичные смещения для текущего потока
273 // Ниже:
274 // s_sum[b] = общее количество элементов во всех потоках, которые попали в bucket b
275 // s0[b] = количество элементов в bucket b у потоков с номерами меньше tid + локальное количество текущего потока
276 for (unsigned int t = 0; t < nt; ++t)
277 for (unsigned int b = 0; b < 256u; ++b)
278 {
279 s_sum[b] += s[256u * t + b];
280
281 if (t < tid)
282 s0[b] += s[256u * t + b];
283 }
284
285 // Ниже:
286 // s_sum[b] = конец bucket b в глобальном выходном массиве
287 // s0[b] = конец части bucket b, принадлежащей текущему потоку
288 for (unsigned int b = 1; b < 256u; ++b)
289 {
290 s_sum[b] += s_sum[b - 1];
291 s0[b] += s_sum[b - 1];
292 }
293
294 // каждый поток раскладывает свои элементы в нужные позиции выходного массива
295 if (chunk != 0u)
296 {
297 for (int i = (int)(right_index); i >= (int)(left_index); --i)
298 {
299 const unsigned int bucket = (sourceKey[i] >> (8u * digit)) & 0xFFu;
300
301 // Берём следующую свободную позицию в части корзины текущего потока
302 const unsigned int pos = --s0[bucket];
303
304 // Переносим и ключ, и индекс.
305 // сортируем по key, idx переставляется вместе с ним.
306 destKey[pos] = sourceKey[i];
307 destIdx[pos] = sourceIdx[i];
308 }
309 }
310#pragma omp barrier
311 // Меняем местами source и dest. Cледующий проход по следующему байту будет читать уже из только что отсортированного массива.
312 // проход 0: source = mortonCodesKey, dest = temp
313 // проход 1: source = temp, dest = mortonCodesKey
314 // проход 2: source = mortonCodesKey, dest = temp
315 // проход 3: source = temp, dest = mortonCodesKey
316 std::swap(sourceKey, destKey);
317 std::swap(sourceIdx, destIdx);
318#pragma omp barrier
319 }//for digit
320 }//#pragma omp parallel
321 }//RadixSortMortonCodes()
322
324 {
325 int nObject = (int)object.size();
326 int n = nObject - 1;
327
328 if (n == 0)
329 return;
330
331 std::copy(levelUnsort.begin(), levelUnsort.end(), levelSort.begin());
332 std::copy(indexUnsort.begin(), indexUnsort.end(), indexSort.begin());
333
334 std::vector<int> levelTemp(n);
335 std::vector<int> indexTemp(n);
336
337 const unsigned int threads = (unsigned int)(omp_get_max_threads());
338 std::vector<unsigned int> s(256 * threads);
339
340#pragma omp parallel num_threads(threads)
341 {
342 int* sourceKey = levelSort.data();
343 int* destKey = levelTemp.data();
344
345 int* sourceIdx = indexSort.data();
346 int* destIdx = indexTemp.data();
347
348 const unsigned int tid = (unsigned int)(omp_get_thread_num());
349 const unsigned int nt = (unsigned int)(omp_get_num_threads());
350
351 const unsigned int div = n / nt;
352 const unsigned int mod = n % nt;
353
354 const unsigned int chunk = div + (tid < mod ? 1u : 0u);
355
356 const unsigned int left_index = (tid < mod) ? tid * (div + 1u) : mod * (div + 1u) + (tid - mod) * div;
357
358 const unsigned int right_index = (chunk == 0u) ? left_index : (left_index + chunk - 1u);
359
360 // Сортировка по байтам int
361 for (unsigned int digit = 0; digit < sizeof(int); ++digit)
362 {
363 unsigned int s_sum[256] = { 0 };
364 unsigned int s0[256] = { 0 };
365
366 if (chunk != 0u)
367 {
368 for (unsigned int i = left_index; i <= right_index; ++i)
369 {
370 const unsigned int bucket =
371 ((unsigned int)(sourceKey[i]) >> (8u * digit)) & 0xFFu;
372 ++s0[bucket];
373 }
374 }
375
376 for (unsigned int b = 0; b < 256u; ++b)
377 s[256u * tid + b] = s0[b];
378
379#pragma omp barrier
380
381 for (unsigned int t = 0; t < nt; ++t)
382 {
383 for (unsigned int b = 0; b < 256u; ++b)
384 {
385 s_sum[b] += s[256u * t + b];
386 if (t < tid)
387 s0[b] += s[256u * t + b];
388 }
389 }
390
391 for (unsigned int b = 1; b < 256u; ++b)
392 {
393 s_sum[b] += s_sum[b - 1];
394 s0[b] += s_sum[b - 1];
395 }
396
397 if (chunk != 0u)
398 {
399 for (int i = (int)(right_index);
400 i >= (int)(left_index); --i)
401 {
402 const unsigned int bucket = ((unsigned int)(sourceKey[i]) >> (8u * digit)) & 0xFFu;
403
404 const unsigned int pos = --s0[bucket];
405
406 destKey[pos] = sourceKey[i];
407 destIdx[pos] = sourceIdx[i];
408 }
409 }
410
411#pragma omp barrier
412 std::swap(sourceKey, destKey);
413 std::swap(sourceIdx, destIdx);
414#pragma omp barrier
415 }
416 }
417
418 // indexSortT[internalCell] = position in sorted array
419#pragma omp parallel for
420 for (int k = 0; k < n; ++k)
421 indexSortT[indexSort[k]] = k;
422 }//RadixSortInternalCells()
423
424
426 {
427 using double4 = Point4D;
428 using double2 = Point2D;
429 using int2 = std::pair<int, int>;
430
431 int i, j, ch, flag;
432
433 double4 lu[2]; //bounding boxes двух детей
434
435 double2 mom0;
436 double2 mom1;
437 double2 mom2;
438 double2 mom3;
439 double2 mom4;
440 double2 mom5;
441 double2 mom6;
442 double2 mom7;
443 double2 mom8;
444 double2 mom9;
445 double2 mom10;
446 double2 mom11;
447
448 double2 cen; //центр текущего родительского узла
449 double2 dr; //вектор от центра родителя к центру ребенка
450
451 int m[2]; //для листа это 1, для внутреннего узла это число листьев в нем
452 int cm; //масса текущего узла = сумма масс двух детей
453 const int nnodes = 2 * (int)object.size() - 1;
454 const int nbodies = (int)object.size();
455
456 //цикл по внутренним узлам снизу вверх
457 for (int k = nbodies; k < nnodes; ++k)
458 {
459 //MortonTree:
460 // 0 1 2 ... (nb-2) x (nb+0) (nb+1) (nb+2) ... (nb+(nb-1))
461 // ---------------- -----------------------------------
462 // cells bodies
463
464 //Martin's tree:
465 // 0 1 2 ... (nb-1) x x x x (nn-(nb-1)) ... (nn-2) (nn-1)
466 // ---------------- ----------------------------
467 // bodies sorted and reversed cells
468
469
470 j = 0;
471 flag = 0;
472 // iterate over all cells assigned to thread
473 while (flag == 0)
474 {
475 j = 2;
476 int srt = indexSort[(nnodes - 1) - k]; //проход снизу вверх - т.е. в обратном порядке
477 int2 chdPair = child[srt];
478
479 for (i = 0; i < 2; i++) {
480 int chd = i * chdPair.second + (1 - i) * chdPair.first; // i==0 => .x; i==1 => .y
481
482 ch = (chd >= nbodies) ? (chd - nbodies) : ((nnodes - 1) - indexSortT[chd]);
483
484 if ((chd >= nbodies) || (mass[nnodes - 1 - ch] >= 0))
485 j--;
486 }
487
488 if (j == 0)
489 {
490 // all children are ready
491 const int kch = ((nnodes - 1) - k) * orderAlignment; //позиция, куда будут записаны мм для текущего внутреннего узла в массиве moms
492 //moms хранится не по индексу srt, а по отсортированному порядку узлов
493//cm = 0;
494
495//Считаем bounding box текущего узла из двух детей
496 for (i = 0; i < 2; i++)
497 {
498 const int chd = i * chdPair.second + (1 - i) * chdPair.first; // индекс i-го ребенка
499 if (chd >= nbodies)//если ребенок - это лист
500 {
501 ch = chd - nbodies; //номер частицы в отсортированном массиве
502 const int sortedBody = mortonCodesIdx[ch];
503
504 double4 xyAB = gabForLeaves[sortedBody];
505 lu[i] = double4{
506 ::fmin(xyAB[0], xyAB[2]), ::fmin(xyAB[1], xyAB[3]),
507 ::fmax(xyAB[0], xyAB[2]), ::fmax(xyAB[1], xyAB[3])
508 };
509 }
510 else
511 {
512 ch = indexSortT[chd];
513 // если внутренний узел, его bounding box уже должен быть посчитан, тк идем снизу вверх
514 lu[i] = lowerupper[chd];
515 }
516 }//for i
517
518 // Объединяем bounding box двух детей:
519 lowerupper[srt] = double4{
520 ::fmin(lu[0][0], lu[1][0]), ::fmin(lu[0][1], lu[1][1]),
521 ::fmax(lu[0][2], lu[1][2]), ::fmax(lu[0][3], lu[1][3])
522 };
523 // Центр текущего узла
524 cen = center[srt] = double2{ 0.5 * (lowerupper[srt][0] + lowerupper[srt][2]),
525 0.5 * (lowerupper[srt][1] + lowerupper[srt][3]) };
526
527 const double2 zero = { 0.0, 0.0 };
528 double2 momh0 = zero;
529 double2 momh1 = zero;
530 double2 momh2 = zero;
531 double2 momh3 = zero;
532 double2 momh4 = zero;
533 double2 momh5 = zero;
534 double2 momh6 = zero;
535 double2 momh7 = zero;
536 double2 momh8 = zero;
537 double2 momh9 = zero;
538 double2 momh10 = zero;
539 double2 momh11 = zero;
540
541 //переносим мультипольные моменты в центр родительской ячейки
542 for (i = 0; i < 2; i++)
543 {
544 const int chd = i * chdPair.second + (1 - i) * chdPair.first;
545 if (chd >= nbodies) //если ребенок - это лист
546 {
547 ch = chd - nbodies;
548 const int sortedBody = mortonCodesIdx[ch];
549
551 {
552 mom0 = double2{ gamma[sortedBody], 0.0 };
553 //для вихря все остальные мм нулевые
554 mom1 = mom2 = mom3 = mom4 = mom5 = mom6 = mom7 = mom8 = mom9 = mom10 = mom11 = double2{ 0.0, 0.0 };
555 double2 pos = object[sortedBody];
556 dr = pos - cen;
557 m[i] = 1;
558 } //objectType==point3
559/*
560 if (objectType == object_T::panel)
561 {
562 double2 panBegin, panEnd;
563 panBegin = *(double2*)(&vtxd[sortedBody * 12 + 2]);
564 panEnd = *(double2*)(&vtxd[sortedBody * 12 + 4]);
565
566 double2 rcur, rd2Pow;
567 rcur = rd2Pow = multz(0.5 * (panEnd - panBegin), 0.5 * (panEnd - panBegin));
568 double gam;
569 double2 gammVort;
570 switch (fromVortexOrSource)
571 {
572 case tree_T::vortex:
573 gammVort = *(double2*)(&vtxd[sortedBody * 12 + 6]);
574 gam = gammVort.x + gammVort.y;
575 break;
576
577 case tree_T::source:
578 gam = vtxd[sortedBody * 12 + 8];
579 break;
580 };
581
582 mom1 = mom3 = mom5 = mom7 = mom9 = mom11 = zero;
583 mom0 = double2{ gam, 0.0 };
584
585 mom2 = (gam / 3) * rcur;
586 rcur = multz(rcur, rd2Pow);
587 mom4 = (gam / 5) * rcur;
588 rcur = multz(rcur, rd2Pow);
589 mom6 = (gam / 7) * rcur;
590 rcur = multz(rcur, rd2Pow);
591 mom8 = (gam / 9) * rcur;
592 rcur = multz(rcur, rd2Pow);
593 mom10 = (gam / 11) * rcur;
594
595 if (constOrLin == scheme_T::linScheme)
596 {
597 double gamLin;
598
599 switch (fromVortexOrSource)
600 {
601 case tree_T::vortex:
602 gamLin = vtxd[sortedBody * 12 + 9] + vtxd[sortedBody * 12 + 10];
603 break;
604 case tree_T::source:
605 gamLin = vtxd[sortedBody * 12 + 11];
606 break;
607 };
608
609 rcur = 0.5 * (panEnd - panBegin);
610 mom1 = gamLin * (0.5 / 3) * rcur;
611 rcur = multz(rcur, rd2Pow);
612 mom3 = gamLin * (0.5 / 5) * rcur;
613 rcur = multz(rcur, rd2Pow);
614 mom5 = gamLin * (0.5 / 7) * rcur;
615 rcur = multz(rcur, rd2Pow);
616 mom7 = gamLin * (0.5 / 9) * rcur;
617 rcur = multz(rcur, rd2Pow);
618 mom9 = gamLin * (0.5 / 11) * rcur;
619 rcur = multz(rcur, rd2Pow);
620 mom11 = gamLin * (0.5 / 13) * rcur;
621 }
622
623 double2 pos = *(double2*)(&vtxd[sortedBody * 12 + 0]);
624 dr = pos - cen;
625 m[i] = 1;
626 } //objectType == panel
627*/
628 }
629 else // если ребенок - внутренний узел
630 {
631 const int srtT = indexSortT[chd];
632 ch = (nnodes - 1) - srtT;
633
634 const int nch = srtT * orderAlignment;
635
636 mom0 = double2{ moms[nch + 0][0], (double)0 };
637 mom1 = moms[nch + 1];
638 mom2 = moms[nch + 2];
639 mom3 = moms[nch + 3];
640 mom4 = moms[nch + 4];
641 mom5 = moms[nch + 5];
642 mom6 = moms[nch + 6];
643 mom7 = moms[nch + 7];
644 mom8 = moms[nch + 8];
645 mom9 = moms[nch + 9];
646 mom10 = moms[nch + 10];
647 mom11 = moms[nch + 11];
648
649 dr = center[chd] - cen;
650 m[i] = mass[srtT];
651 }
652 // add child's contribution
653
654 momh0 += mom0;
655 momh1 += mom1;
656 momh2 += mom2;
657 momh3 += mom3;
658 momh4 += mom4;
659 momh5 += mom5;
660 momh6 += mom6;
661 momh7 += mom7;
662 momh8 += mom8;
663 momh9 += mom9;
664 momh10 += mom10;
665 momh11 += mom11;
666
667 double2 z = dr;
668
669 momh1 += multz(mom0, z);
670 momh2 += 2 * multz(mom1, z);
671 momh3 += 3 * multz(mom2, z);
672 momh4 += 4 * multz(mom3, z);
673 momh5 += 5 * multz(mom4, z);
674 momh6 += 6 * multz(mom5, z);
675 momh7 += 7 * multz(mom6, z);
676 momh8 += 8 * multz(mom7, z);
677 momh9 += 9 * multz(mom8, z);
678 momh10 += 10 * multz(mom9, z);
679 momh11 += 11 * multz(mom10, z);
680
681 z = multz(z, dr);
682
683 momh2 += multz(mom0, z);
684 momh3 += 3 * multz(mom1, z);
685 momh4 += 6.0 * multz(mom2, z);
686 momh5 += 10.0 * multz(mom3, z);
687 momh6 += 15.0 * multz(mom4, z);
688 momh7 += 21.0 * multz(mom5, z);
689 momh8 += 28.0 * multz(mom6, z);
690 momh9 += 36.0 * multz(mom7, z);
691 momh10 += 45.0 * multz(mom8, z);
692 momh11 += 55.0 * multz(mom9, z);
693
694 z = multz(z, dr);
695
696 momh3 += multz(mom0, z);
697 momh4 += 4 * multz(mom1, z);
698 momh5 += 10.0 * multz(mom2, z);
699 momh6 += 20.0 * multz(mom3, z);
700 momh7 += 35.0 * multz(mom4, z);
701 momh8 += 56.0 * multz(mom5, z);
702 momh9 += 84.0 * multz(mom6, z);
703 momh10 += 120.0 * multz(mom7, z);
704 momh11 += 165.0 * multz(mom8, z);
705
706 z = multz(z, dr);
707
708 momh4 += multz(mom0, z);
709 momh5 += 5 * multz(mom1, z);
710 momh6 += 15.0 * multz(mom2, z);
711 momh7 += 35.0 * multz(mom3, z);
712 momh8 += 70.0 * multz(mom4, z);
713 momh9 += 126.0 * multz(mom5, z);
714 momh10 += 210.0 * multz(mom6, z);
715 momh11 += 330.0 * multz(mom7, z);
716
717 z = multz(z, dr);
718
719 momh5 += multz(mom0, z);
720 momh6 += 6 * multz(mom1, z);
721 momh7 += 21.0 * multz(mom2, z);
722 momh8 += 56.0 * multz(mom3, z);
723 momh9 += 126.0 * multz(mom4, z);
724 momh10 += 252.0 * multz(mom5, z);
725 momh11 += 462.0 * multz(mom6, z);
726
727 z = multz(z, dr);
728
729 momh6 += multz(mom0, z);
730 momh7 += 7 * multz(mom1, z);
731 momh8 += 28.0 * multz(mom2, z);
732 momh9 += 84.0 * multz(mom3, z);
733 momh10 += 210.0 * multz(mom4, z);
734 momh11 += 462.0 * multz(mom5, z);
735
736 z = multz(z, dr);
737
738 momh7 += multz(mom0, z);
739 momh8 += 8 * multz(mom1, z);
740 momh9 += 36.0 * multz(mom2, z);
741 momh10 += 120.0 * multz(mom3, z);
742 momh11 += 330.0 * multz(mom4, z);
743
744 z = multz(z, dr);
745
746 momh8 += multz(mom0, z);
747 momh9 += 9 * multz(mom1, z);
748 momh10 += 45.0 * multz(mom2, z);
749 momh11 += 165.0 * multz(mom3, z);
750
751 z = multz(z, dr);
752
753 momh9 += multz(mom0, z);
754 momh10 += 10 * multz(mom1, z);
755 momh11 += 55.0 * multz(mom2, z);
756
757 z = multz(z, dr);
758
759 momh10 += multz(mom0, z);
760 momh11 += 11 * multz(mom1, z);
761
762 z = multz(z, dr);
763
764 momh11 += multz(mom0, z);
765 }
766
767 // Сохраняем итоговые моменты текущего внутреннего узла в общий массив
768 momh0[1] = 0;
769 moms[kch + 0] = momh0;
770 moms[kch + 1] = momh1;
771 moms[kch + 2] = momh2;
772 moms[kch + 3] = momh3;
773 moms[kch + 4] = momh4;
774 moms[kch + 5] = momh5;
775 moms[kch + 6] = momh6;
776 moms[kch + 7] = momh7;
777 moms[kch + 8] = momh8;
778 moms[kch + 9] = momh9;
779 moms[kch + 10] = momh10;
780 moms[kch + 11] = momh11;
781
782 cm = m[0] + m[1];
783
784 flag = 1;
785 }
786
787 //__threadfence();
788
789 if (flag != 0)
790 {
791 mass[nnodes - 1 - k] = cm;// Записываем массу текущего узла
792 //k += inc;
793 //flag = 0;
794 }
795 }//while flag==0
796 }//for k
797 }//Summ12()
798
799
800
801
803 {
804 int i, j, ch, flag;
805 using double4 = Point4D;
806 using double2 = Point2D;
807 using int2 = std::pair<int, int>;
808
809 double4 lu[2];
810
811 //int cm;
812 int m[2];
813
814
815 const int nnodes = 2 * (int)object.size() - 1;
816 const int nbodies = (int)object.size();
817
818 for (int k = nbodies; k < nnodes; ++k)
819 {
820 //MortonTree:
821 // 0 1 2 ... (nb-2) x (nb+0) (nb+1) (nb+2) ... (nb+(nb-1))
822 // ---------------- -----------------------------------
823 // cells bodies
824
825 //Martin's tree:
826 // 0 1 2 ... (nb-1) x x x x (nn-(nb-1)) ... (nn-2) (nn-1)
827 // ---------------- ----------------------------
828 // bodies sorted and reversed cells
829
830 flag = 0;
831 j = 0;
832 // iterate over all cells assigned to thread
833 while (flag == 0)
834 {
835 j = 2;
836 const int kch = ((nnodes - 1) - k);
837 const int srt = indexSort[kch];
838 int2 chdPair = child[srt];
839
840 //cm = 0;
841
842 //int chdSorted[2];
843
844 for (i = 0; i < 2; i++)
845 {
846 int chd = i * chdPair.second + (1 - i) * chdPair.first; // i==0 => .x; i==1 => .y
847 ch = (chd >= nbodies) ? (chd - nbodies) : ((nnodes - 1) - indexSortT[chd]);
848 if ((chd >= nbodies) || (mass[nnodes - 1 - ch] >= 0))
849 j--;
850 }
851
852 if (j == 0)
853 {
854
855 for (i = 0; i < 2; i++)
856 {
857 const int chd = i * chdPair.second + (1 - i) * chdPair.first;
858 if (chd >= nbodies)
859 {
860 ch = chd - nbodies;
861 const int sortedBody = mortonCodesIdx[ch];
862
863 double4 xyAB = gabForLeaves[sortedBody];
864 lu[i] = double4{
865 ::fmin(xyAB[0], xyAB[2]), ::fmin(xyAB[1], xyAB[3]),
866 ::fmax(xyAB[0], xyAB[2]), ::fmax(xyAB[1], xyAB[3])
867 };
868 m[i] = 1;
869 }
870 else
871 {
872 const int srtT = indexSortT[chd];
873 lu[i] = lowerupper[chd];
874 m[i] = mass[srtT];
875 }
876 }
877
878 //const int kchSort = indexSort[kch];
879 const double4 loup = double4{
880 ::fmin(lu[0][0], lu[1][0]),
881 ::fmin(lu[0][1], lu[1][1]),
882 ::fmax(lu[0][2], lu[1][2]),
883 ::fmax(lu[0][3], lu[1][3]) };
884
885 lowerupper[srt] = loup;
886
887 // Центр текущего узла = центр его AABB
888 center[srt] = double2{
889 0.5 * (loup[0] + loup[2]),
890 0.5 * (loup[1] + loup[3])
891 };
892
893 //cm += (m[0] + m[1]);
894 //cm = (m[0] + m[1]);
895 flag = 1;
896 }//if j==0
897
898 if (flag != 0)
899 mass[nnodes - 1 - k] = m[0] + m[1];
900 }//while flag
901 }//for k
902 }//CalcAABB(...)
903
904
905
907 {
908 float t1 = (float)omp_get_wtime();
909 int nObject = (int)object.size();
910 if (nObject > 0)
911 {
912 //treeBoundingBox;
913 //to_parallel
914 auto minmaxX = std::minmax_element(object.begin(), object.end(), Point2D::cmp<'x'>);
915 auto minmaxY = std::minmax_element(object.begin(), object.end(), Point2D::cmp<'y'>);
916 minr = Point2D({ (*minmaxX.first)[0], (*minmaxY.first)[1] });
917 maxr = Point2D({ (*minmaxX.second)[0], (*minmaxY.second)[1] });
918
919 //treeMortonCodes;
920 double lmax, quadSideFactor;
921 lmax = std::max(maxr[0] - minr[0], maxr[1] - minr[1]);
922 Point2D rcen = 0.5 * (maxr + minr); //координаты центра
923 quadSideFactor = rbound / lmax; //1;
924
925#pragma omp parallel for
926 for (int bdy = 0; bdy < nObject; ++bdy)
927 {
928 Point2D rScaled = twoPowCodeLength * ((object[bdy] - rcen) * quadSideFactor + 0.5 * Point2D{ rbound, rbound });
929
930 unsigned int xx = ExpandBits((unsigned int)rScaled[0]);
931 unsigned int yy = ExpandBits((unsigned int)rScaled[1]);
932 mortonCodesKeyUnsort[bdy] = yy | (xx << 1);
933 mortonCodesIdxUnsort[bdy] = bdy;
934 }
935
937
938
939 //treeMortonInternalNodes
940 if (treeType != tree_T::contr)
941 {
942 for (int i = 0; i < nObject - 1; ++i)
943 {
944 int codei = mortonCodesKeyUnsort[i];
945
946 int Deltap1 = Delta(i, i + 1);
947 int Deltam1 = Delta(i, i - 1);
948
949 int d = (Deltap1 - Deltam1 > 0) ? 1 : -1;
950
951 int delta_min = (d > 0) ? Deltam1 : Deltap1;
952
953 int Lmax = 2;
954 int pos = i + Lmax * d;
955
956 while (Delta(i, pos) > delta_min)
957 {
958 Lmax *= 2;
959 pos = i + Lmax * d;
960 }
961
962 int L = 0;
963 for (int t = (Lmax >> 1); t >= 1; t >>= 1)
964 {
965 pos = i + (L + t) * d;
966
967 if (Delta(i, pos) > delta_min)
968 L += t;
969 }
970
971 int j = i + L * d;
972 pos = j;
973
974 int delta_node = Delta(i, j);
975
976 levelUnsort[i] = delta_node;
977 indexUnsort[i] = i;
978
979 int s = 0;
980 for (int p = 1, t = ceilhalf(L); L > (1 << (p - 1)); ++p, t = ceilpow2(L, p))
981 {
982 pos = i + (s + t) * d;
983
984 int dl = Delta(i, pos);
985
986 if (dl > delta_node)
987 s += t;
988 }//for p
989
990 int gammaPos = i + s * d + d * (d < 0); // = std::min(d, 0);
991
992 int Mmin = std::min(i, j);
993 int Mmax = std::max(i, j);
994
995 int left = gammaPos;
996 int right = gammaPos + 1;
997
998 // -
999 int childLeft = (Mmin == gammaPos) * nObject + left;
1000 range[childLeft] = { Mmin, gammaPos };
1001 parent[childLeft] = i;
1002
1003 // -
1004 int childRight = (Mmax == gammaPos + 1) * nObject + right;
1005 range[childRight] = { gammaPos + 1, Mmax };
1006 parent[childRight] = i;
1007
1008 child[i] = { childLeft, childRight };
1009 }
1010 }
1012 }
1013
1014 float t2 = (float)omp_get_wtime();
1015 return (t2 - t1);
1016 }//Build()
1017
1018
1020 {
1021 float t1 = (float)omp_get_wtime();
1022 if (object.size() > 0)
1023 {
1024 //treeClearKernel
1025 mass.assign(object.size() - 1, -1);
1026
1028 //treeSummarization;
1029 Summ12();
1030 else if (treeType == tree_T::aux)
1031 CalcAABB();
1032 }
1033 float t2 = (float)omp_get_wtime();
1034 return (t2 - t1);
1035 }//UpwardTraversal(...)
1036
1037
1038 float CpuTreeInfo::DownwardTraversalVorticesToPoints(CpuTreeInfo& cntrTree, Point2D* velD, double* epsastD, double eps2, double theta, int order, bool calcRadius)
1039 {
1040 float t1 = (float)omp_get_wtime();
1041
1042 using double4 = Point4D;
1043 using double2 = Point2D;
1044 using int2 = std::pair<int, int>;
1045
1046 if (object.size() > 0)
1047 {
1048 double itolsq = 1.0 / (theta * theta);
1049
1050 const int nbodies = (int)object.size(); //количество вихрей
1051 const int nnodes = 2 * nbodies - 1; //количество узлов дерева вихрей (листья-вихри + внутренние узлы)
1052 const int npoints = (int)cntrTree.object.size(); //количество точек наблюдения
1053
1054 int nd; // индекс родительской ячейки дерева, которую обходим, и которая находится на верхушке стека;
1055 int2 chBoth; //индексы обоих потомков узла nd
1056 int pd; // 0 или 1 --- какого потомка ячейки nd обходим (левого или правого)
1057
1058 // для вычисления квадратов расстояний до трех ближайших вихрей
1059 double d_1, d_2, d_3, dst23, dst12;
1060 int indexOfPoint; //истинный индекс точки наблюдения
1061 int srtT; //истинный индекс внутреннего узла дерева
1062
1063 double2 p; //координаты точки наблюдения
1064 double2 v{ 0.0,0.0 }; //результат расчета скорости в точке наблюдения
1065
1066 bool isVortex; //признак того, что обрабатываемая вершина - лист
1067 double2 ps; //координаты влияющего вихря, если обрабатываемая вершина --- лист, или центра влияющей ячейки, если обрабатывается внутренняя ячейка дерева
1068 double gm; //циркуляция вихря, если обрабатываемая вершина --- лист, или 0-й мультипольный момент (суммарная циркуляция вихрей) если обрабатывается внутренняя ячейка дерева
1069 double sumSide2; //габариты обрабатываемой ячейки
1070 double2 dr; //радиус-вектор из центра влияющей ячейки в точку наблюдения
1071 double r2; //квадрат модуля предыдущего
1072
1073 const int maxDepth = 32;
1074
1075 int posStack[maxDepth];
1076 int nodeStack[maxDepth];
1077 int depth;
1078 const Point2D* mom = nullptr;
1079
1080#pragma omp parallel for
1081 for (int k = 0; k < npoints; ++k)
1082 {
1083 d_1 = d_2 = d_3 = 1e+5;
1084 indexOfPoint = cntrTree.mortonCodesIdx[k];
1085 p = cntrTree.object[indexOfPoint];
1086
1087 depth = 0;
1088 posStack[0] = 0;
1089 nodeStack[0] = nnodes - 1;
1090
1091 while (depth >= 0)
1092 {
1093 pd = posStack[depth];
1094 nd = nodeStack[depth];
1095
1096 chBoth = child[indexSort[(nnodes - 1) - nd]];
1097
1098 while (pd < 2)
1099 {
1100 const int chd = (pd == 0) ? chBoth.first : chBoth.second;
1101
1102 ++pd;
1103 posStack[depth] = pd;
1104
1105 isVortex = (chd >= nbodies);
1106
1107 int n;
1108 if (isVortex)
1109 {
1110 n = chd - nbodies;
1111 const int vortexIndex = mortonCodesIdx[n]; // истинный индекс вихря
1112
1113 ps = object[vortexIndex];
1114 gm = gamma[vortexIndex];
1115 sumSide2 = 0.0; // ???
1116 }
1117 else
1118 {
1119 srtT = indexSortT[chd];
1120 n = (nnodes - 1) - srtT;
1121
1122 ps = center[chd];
1123
1124 const Point4D& gab = lowerupper[chd];
1125 sumSide2 = (gab[2] - gab[0] + gab[3] - gab[1]);
1126 sumSide2 *= sumSide2;
1127 }
1128
1129 dr = p - ps;
1130 r2 = dr[0] * dr[0] + dr[1] * dr[1];
1131 if (isVortex || ((sumSide2 + eps2) * itolsq < r2))
1132 {
1133 // Если это ячейка, берём её mm
1134 if (!isVortex)
1135 {
1136 mom = moms.data() + srtT * orderAlignment;
1137 gm = mom[0][0]; // нулевой момент = суммарная циркуляция
1138 }
1139
1140 if (calcRadius)
1141 {
1142 if ((r2 < d_3) && (r2 > 0.0))
1143 {
1144 dst23 = std::fmin(r2, d_2);
1145 d_3 = std::fmax(r2, d_2);
1146
1147 dst12 = std::fmin(dst23, d_1);
1148 d_2 = std::fmax(dst23, d_1);
1149
1150 d_1 = dst12;
1151 }
1152 }
1153 const double f = gm / std::fmax(r2, eps2);
1154 v += f * dr;
1155
1156 // Вклад высших мм
1157 if ((!isVortex) && (order > 1) && r2 > 0.0)
1158 {
1159 Point2D cftr = (1.0 / r2) * dr;
1160 Point2D th = cftr;
1161
1162 for (int s = 1; s < order; ++s)
1163 {
1164 th = multz(th, cftr);
1165
1166 Point2D add = multzA(th, mom[s]);
1167 v += add;
1168 }
1169 }
1170 }
1171 else
1172 {
1173 // Ячейка слишком близка -> спускаемся ниже по дереву
1174 if (depth + 1 < maxDepth)
1175 {
1176 ++depth;
1177 posStack[depth] = 0;
1178 nodeStack[depth] = n;
1179
1180 // Выходим из текущего while(pd < 2),
1181 // чтобы начать обход нового узла с его левого ребёнка
1182 pd = 2;
1183 }
1184 }
1185 }//while pd
1186
1187 // Оба потомка обработаны -> снимаем узел со стека
1188 --depth;
1189 }//while depth
1190
1191 }//for k
1192
1193 }
1194 float t2 = (float)omp_get_wtime();
1195 return (t2 - t1);
1196 }//DownwardTraversalVorticesToPoints(...)
1197
1198}
Описание констант и параметров для взаимодействия с графическим ускорителем
#define rbound
Definition Gpudefs.h:99
object_T
Definition Gpudefs.h:146
#define codeLength
Definition Gpudefs.h:97
#define orderAlignment
Definition Gpudefs.h:88
tree_T
Definition Gpudefs.h:139
#define twoPowCodeLength
Definition Gpudefs.h:98
scheme_T
Definition Gpudefs.h:151
Структура, хранящая данные и указатели на массивы на GPU для оптимизации итерационного решения СЛАУ н...
Definition cpuTreeInfo.h:97
std::vector< std::pair< int, int > > range
std::vector< Point2D > moms
CpuTreeInfo(tree_T treeType_, object_T objectType_, scheme_T schemeType_)
float Update(const std::vector< Vortex2D > &vtx, double eps)
float DownwardTraversalVorticesToPoints(CpuTreeInfo &cntrTree, Point2D *velD, double *epsastD, double eps2, double theta, int order, bool calcRadius)
std::vector< int > levelUnsort
std::vector< unsigned int > mortonCodesKeyUnsort
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
std::vector< Point4D > gabForLeaves
std::vector< int > levelSort
std::vector< int > indexSortT
std::vector< Point4D > lowerupper
std::vector< int > mass
std::vector< int > indexUnsort
std::vector< int > mortonCodesIdxUnsort
int Delta(int i, int j) const
std::vector< std::pair< int, int > > child
Заголовок класса дерева для реализации быстрых алгоритмов на CPU.
int ceilhalf(int x)
unsigned int ExpandBits(unsigned int v)
int ceilpow2(int x, int p)