VM2D 1.14
Vortex methods for 2D flows simulation
Loading...
Searching...
No Matches
TreeBH.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: TreeBH.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 <algorithm>
40#include <cstring>
41
42#include "TreeBH.h"
43
44namespace BH
45{
46 extern long long op;
47
48 //Конструктор
49 MortonTree::MortonTree(const params& prm_, int maxTreeLevel_, std::vector<PointsCopy>& points)
50 : prm(prm_), pointsCopy(points), maxTreeLevel(maxTreeLevel_)
51 {
52 mortonTree.resize(2 * points.size());
53 for (auto& c : mortonTree)
54 {
55 c.mom.resize(prm.order + 1);
56 c.E.resize(prm.order);
57 }
58
59 mortonCodes.resize(points.size());
60 mortonLowCells.reserve(points.size());
61
62
63 //Вычисляем факториалы и биномиальные коэффиценты
64 iFact.resize(prm.order + 1);
65 binomCft.resize((prm.order + 1) * (prm.order + 1));
66
67 iFact[0] = 1.0;
68 for (int i = 1; i <= prm.order; ++i)
69 iFact[i] = iFact[i - 1] / i;
70 getBinom(0, 0) = 1.0;
71 for (int i = 1; i <= prm.order; ++i)
72 {
73 getBinom(i, 0) = 1.0;
74 getBinom(i, i) = 1.0;
75 for (int j = 1; j < i; ++j)
76 getBinom(i, j) = getBinom(i - 1, j - 1) + getBinom(i - 1, j);
77 }//for i
78 }//MortonTree(...)
79
80
81 //Поиск габаритного прямоугольника системы точек
82 std::pair<Point2D, Point2D> MortonTree::FindEnclosingRectangleOld(const std::vector<PointsCopy>& points)
83 {
84 double shared_minx = 1e+10, shared_maxx = -1e+10, shared_miny = 1e+10, shared_maxy = -1e+10;
85#pragma omp parallel
86 {
87 double minx = 1e+10, maxx = -1e+10, miny = 1e+10, maxy = -1e+10;
88#pragma omp for nowait
89 for (int ii = 0; ii < (int)points.size(); ++ii)
90 {
91 const Point2D& r = points[ii].r();
92 minx = std::min(r[0], minx);
93 maxx = std::max(r[0], maxx);
94 miny = std::min(r[1], miny);
95 maxy = std::max(r[1], maxy);
96 }//for ii
97#pragma omp critical
98 {
99 shared_minx = std::min(shared_minx, minx);
100 shared_maxx = std::max(shared_maxx, maxx);
101 shared_miny = std::min(shared_miny, miny);
102 shared_maxy = std::max(shared_maxy, maxy);
103 }//critical
104 }//parallel
105
106 return { {shared_minx, shared_miny}, {shared_maxx, shared_maxy} };
107 }//FindEnclosingRectangleOld(...)
108
109
110/*
111//Поиск габаритного прямоугольника системы точек
112#pragma omp declare reduction(min : struct PointsCopy : \
113 omp_out.r()[0] = omp_in.r()[0] > omp_out.r()[0] ? omp_out.r()[0] : omp_in.r()[0], \
114 omp_out.r()[1] = omp_in.r()[1] > omp_out.r()[1] ? omp_out.r()[1] : omp_in.r()[1] ) \
115 initializer( omp_priv = Vortex2D{ { 1e+10, 1e+10 } , 0.0} )
116
117#pragma omp declare reduction(max : struct PointsCopy : \
118 omp_out.r()[0] = omp_in.r()[0] < omp_out.r()[0] ? omp_out.r()[0] : omp_in.r()[0], \
119 omp_out.r()[1] = omp_in.r()[1] < omp_out.r()[1] ? omp_out.r()[1] : omp_in.r()[1] ) \
120 initializer( omp_priv = Vortex2D{ { -1e+10, -1e+10 } , 0.0} )
121
122
123 std::pair<Point2D, Point2D> MortonTree::FindEnclosingRectangle(const std::vector<PointsCopy>& points)
124 {
125 PointsCopy minp(Vortex2D{ { 1e+10, 1e+10 } , 0.0}), maxp(Vortex2D{ { -1e+10, -1e+10 } , 0.0 });
126
127#pragma omp parallel for reduction(min:minp) reduction(max:maxp)
128 for (int i = 0; i < (int)points.size(); ++i) {
129 if (points[i].r()[0] < minp.r()[0]) minp.r()[0] = points[i].r()[0];
130 if (points[i].r()[1] < minp.r()[1]) minp.r()[1] = points[i].r()[1];
131 if (points[i].r()[0] > maxp.r()[0]) maxp.r()[0] = points[i].r()[0];
132 if (points[i].r()[1] > maxp.r()[1]) maxp.r()[1] = points[i].r()[1];
133 }//for i
134 return { minp.r(), maxp.r() };
135 }//FindEnclosingRectangle(...)
136*/
137
138
139 //Построение корня дерева и задание его общих параметров
141 {
143
144 double iLeft = gabs.first[0];
145 double iBottom = gabs.first[1];
146
147 double iRight = gabs.second[0];
148 double iTop = gabs.second[1];
149
150 Point2D posCentre = { 0.5 * (iLeft + iRight), 0.5 * (iBottom + iTop) };
151
152 double quadSide = std::max(iRight - iLeft, iTop - iBottom);
153 iQuadSideVar = 1.0 / (quadSide * (1.0 + 1.0 / ((1 << codeLength) - 1)));
154 lowLeft = posCentre - 0.5*quadSide*Point2D{ 1.0, 1.0 };
155
156 //iQuadSideVar = 1.0;
157 //lowLeft = Point2D{ 0.0, 0.0 };
158
159#pragma omp parallel for schedule(dynamic, 1000)
160 for (int i = 0; i < (int)pointsCopy.size(); ++i)
161 {
162 Point2D locR = (pointsCopy[i].r() - lowLeft) * iQuadSideVar;
163 unsigned int mortonCode = Morton2D(locR);
164
165 auto& mc = mortonCodes[i];
166 mc.key = mortonCode;
167 mc.originNumber = i;
168 mc.r = pointsCopy[i].r();
169 mc.g = pointsCopy[i].g();
170 }//for i
171
172 //std::cout << "omp_threads = " << omp_get_max_threads() << std::endl;
173
174 //RSort_Node3(mortonCodes.data(), mortonCodes_temp.data(), (int)pointsCopy.size());
175
176 //Временные массивы для сортировки
177 std::vector<unsigned int> s(256 * omp_get_max_threads());
178 std::vector<TParticleCode> mortonCodes_temp(pointsCopy.size());
179 RSort_Parallel(mortonCodes.data(), mortonCodes_temp.data(), (int)pointsCopy.size(), s.data());
180
181 mortonTree[0].range = { 0, (int)pointsCopy.size() - 1 };
182 mortonTree[0].particle = false;
183 }//MakeRootMortonTree()
184
185
186 //Функция вычисления длины общей части префиксов двух(а значит - и диапазона) частиц
187 int MortonTree::Delta(int i, int j) const
188 {
189 if ((j < 0) || (j > (int)pointsCopy.size() - 1))
190 return -1;
191
192 if (i > j)
193 std::swap(i, j);
194
195 //if ((i < 0) || (j > n-1))
196 // exit(111);
197
198 const unsigned int& ki = mortonCodes[i].key;
199 const unsigned int& kj = mortonCodes[j].key;
200
201 //Поиск номера самого старшего ненулевого бита в числе c
202 int count = 0;
203 for (unsigned int c = (ki ^ kj); c; c >>= 1, ++count);
204
205 if ((!count) && (i != j))
206 {
207 int addCount = 0;
208 //единички к номерам i и j добавлены для совместимости с Wolfram Mathematica,
209 //для кода они не важны, но дерево без них почти наверняка построится по-другому
210 for (unsigned int add = ((i + 1) ^ (j + 1)); add; add >>= 1, ++addCount);
211 return 2 * codeLength + (2 * codeLength - addCount);
212 }//if ((!count) && (i != j))
213
214 return (2 * codeLength - count);
215 }//Delta(...)
216
217
218 //Функция вычисления длины общей части префиксов всех частиц в конкретной ячейке
219 int MortonTree::PrefixLength(int cell) const
220 {
221 const auto& range = mortonTree[cell].range;
222 return std::min(Delta(range[0], range[1]), 2 * codeLength);
223 }//PrefixLength(...)
224
225
226 //Функция вычисления общего префикса двух частиц
227 std::pair<unsigned int, int> MortonTree::Prefix(int cell) const
228 {
229 int length = PrefixLength(cell);
230 unsigned int el = mortonCodes[mortonTree[cell].range[0]].key;
231 return { el >> (2 * codeLength - length), length };
232 }//Prefix(...)
233
234
235 //Функция вычисления геометрических параметров внутренней ячейки дерева
237 {
238 int prLength;
239 unsigned int pr;
240 std::tie<unsigned int, int>(pr, prLength) = Prefix(cell);
241 prLength -= PrefixLength(0);
242
243 Point2D sz = { 1.0 / (double)(1 << ceilhalf(prLength)), 1.0 / (1 << (prLength / 2)) };
244
245 Point2D pos = 0.5 * sz;
246
247 for (int i = 0; i < prLength; i += 2)
248 if (pr & (1 << (prLength - i - 1)))
249 pos[0] += 1.0 / (1 << (1 + i / 2));
250
251 for (int i = 1; i < prLength; i += 2)
252 if (pr & (1 << (prLength - i - 1)))
253 pos[1] += 1.0 / (1 << (1 + i / 2));
254
255 mortonTree[cell].center = pos * (1.0 / iQuadSideVar) + lowLeft;
256 mortonTree[cell].size = sz * (1.0 / iQuadSideVar);
257 mortonTree[cell].level = prLength;
258
259 mortonTree[cell].leaf = (prLength >= maxTreeLevel);
260 }//SetCellGeometry(...)
261
262
263 //Функция построения i-й внутренней ячейки дерева
265 {
266 const int n = (int)pointsCopy.size();
267 int d = sign(Delta(i, i + 1) - Delta(i, i - 1));
268 int delta_min = Delta(i, i - d);
269
270 int Lmax = 2;
271 while (Delta(i, i + Lmax * d) > delta_min)
272 Lmax *= 2;
273
274 int L = 0;
275 for (int t = (Lmax >> 1); t >= 1; t >>= 1)
276 if (Delta(i, i + (L + t) * d) > delta_min)
277 L += t;
278
279 int j = i + L * d;
280 int delta_node = Delta(i, j);
281 int s = 0;
282 for (int p = 1, t = ceilhalf(L); L > (1 << (p - 1)); ++p, t = ceilpow2(L, p))
283 {
284 int dl = Delta(i, i + (s + t) * d);
285 if (dl > delta_node)
286 s += t;
287 }//for p
288 int gamma = i + s * d + std::min(d, 0);
289
290 auto min_max = std::minmax(i, j);
291
292 const int& left = gamma;
293 const int& right = gamma + 1;
294
295 treeCellT& treeCell = mortonTree[i];
296
297 // Левый потомок - лист или внутренний узел
298 bool ifLeftParticle = (min_max.first == gamma);
299 treeCell.child[0] = ifLeftParticle ? n + left : left;
300 mortonTree[treeCell.child[0]].range = { min_max.first, gamma };
301 mortonTree[treeCell.child[0]].particle = ifLeftParticle;
302 mortonTree[treeCell.child[0]].parent = i;
303
304 // Правый потомок - лист или внутренний узел
305 bool ifRightParticle = (min_max.second == gamma + 1);
306 treeCell.child[1] = ifRightParticle ? n + right : right;
307 mortonTree[treeCell.child[1]].range = { gamma + 1, min_max.second };
308 mortonTree[treeCell.child[1]].particle = ifRightParticle;
309 mortonTree[treeCell.child[1]].parent = i;
310 }//BuildInternalTreeCell(...)
311
312
313 //Построение внутренних ячеек дерева
315 {
316#pragma omp parallel for schedule(dynamic, 1000)
317 for (int q = 0; q < (int)pointsCopy.size() - 1; ++q)
319
320 mortonTree[pointsCopy.size() - 1].leaf = false;
321 mortonTree[pointsCopy.size() - 1].particle = false;
322
323#pragma omp parallel for schedule(dynamic, 1000)
324 for (int q = 0; q < (int)pointsCopy.size() - 1; ++q)
326 }//BuildMortonInternalTree()
327
328
329 //Построение верхушек дерева --- отдельных частиц
331 {
332#pragma omp parallel for
333 for (int q = 0; q < (int)pointsCopy.size(); ++q)
334 {
335 auto& pt = mortonCodes[q];
336
337 auto& cell = mortonTree[pointsCopy.size() + q];
338 cell.center = pt.r;
339
340 cell.leaf = true;
341 cell.size = { 0.0, 0.0 };
342 }//for q
343 }//BuildMortonParticlesTree(...)
344
345
346 //Заполнение списка нижних вершин:
347 //рекурсивный алгоритм, быстро работает в последовательном варианте
349 {
350 if (mortonTree[cell].leaf)
351 mortonLowCells.push_back(cell);
352 else
353 {
354 FillMortonLowCells(mortonTree[cell].child[0]);
355 FillMortonLowCells(mortonTree[cell].child[1]);
356 }//else
357 }//FillMortonLowCells(...)
358
359
360 //Заполнение списка нижних вершин:
361 //нерекурсивный алгоритм, хорошо распараллеливается, но неэффективен в последовательном варианте
363 {
364#pragma omp parallel
365 {
366 std::vector<int> good_matches_private;
367#pragma omp for nowait
368 for (int cell = 0; cell < (int)mortonTree.size(); ++cell)
369 if (!mortonTree[mortonTree[cell].parent].leaf && mortonTree[cell].leaf)
370 good_matches_private.push_back(cell);
371#pragma omp critical
372 mortonLowCells.insert(mortonLowCells.end(), good_matches_private.begin(), good_matches_private.end());
373 }//parallel
374 }//FillMortonLowCellsA()
375
376
378 void MortonTree::CalculateMortonTreeParams(int cell, int omplevel)
379 {
380 const int maxLevelOmp = int(log2(omp_get_max_threads()));
381
382 auto& cl = mortonTree[cell];
383 if (!cl.particle)
384 {
385#pragma omp parallel /*default(none)*/ shared(cl, omplevel) num_threads(2) if (omplevel < maxLevelOmp + 1)
386 {
387 std::vector<Point2D> h(prm.order);
388#pragma omp for
389 for (int s = 0; s < 2; ++s)
390 CalculateMortonTreeParams(cl.child[s], omplevel + 1);
391
392#pragma omp single
393 {
394 for (auto& m : cl.mom)
395 m.toZero();
396
397 for (int s = 0; s < 2; ++s)//цикл по двум потомкам
398 {
399 auto& chld = mortonTree[cl.child[s]];
400 const Point2D& g = chld.mom[0];
401 cl.mom[0] += g;
402
403
404 h[0] = (chld.center) - (cl.center);
405 for (int i = 1; i < prm.order; ++i)
406 h[i] = multz(h[i - 1], h[0]);
407
408 for (int p = 1; p <= prm.order; ++p)
409 {
410 Point2D shiftMom = chld.mom[p];
411 for (int k = 1; k <= p; ++k)
412 {
413 shiftMom += getBinom(p, k) * multz(chld.mom[p - k], h[k - 1]);
414 //ADDOP(2);
415 }//for k
416 cl.mom[p] += shiftMom;
417 }//for m
418
419 }//for s
420 }//single
421 }//parallel
422 }//if(!particle)
423 else
424 {
425 //расчет моментов ячейки нижнего уровня для вихрей или для панелей
426 //C учетом того, что дерево строится до частиц --- только монопольный момент отличен от нуля
427 cl.mom[0][0] = mortonCodes[cl.range[0]].g;
428 cl.mom[0][1] = 0.0;
429
430 for (int p = 1; p <= prm.order; ++p)
431 cl.mom[p].toZero();
432 }//else
433 }//CalculateMortonTreeParams(...)
434
435 //Расчет коэффициентов разложения в ряд Тейлора внутри ячейки нижнего уровня
436 void MortonTree::CalcLocalCoeffToLowLevel(int lowCell, std::unique_ptr<MortonTree>& treeInf, int fromWho, bool calcCloseTrees)
437 {
438 auto& lt = mortonTree[lowCell];
439 //if (calcCloseTrees)
440 // lt.closeCells.resize(0);
441
442 if (treeInf.get() != this || lowCell != fromWho)
443 {
444 auto& wh = treeInf->mortonTree[fromWho];
445
446 double h, h0;
447 h = wh.size[0] + wh.size[1];
448 h0 = lt.size[0] + lt.size[1];
449
450 Point2D& r0 = lt.center;
451 Point2D& r1 = wh.center;
452
453 Point2D rr = r0 - r1;
454
455 double crit2 = rr.length2();
456 // если выполнен критерий дальности => считаем коэффициенты
457 if ((crit2 >= sqr((h0 + h + 2.0 * prm.eps) / prm.theta)) && (wh.level <= prm.NumOfLevelsVortex))
458 {
459 if (wh.range[1] == wh.range[0])
460 lt.closeCells.push_back(fromWho);
461 else
462 {
463 Point2D rr = r0 - r1;
464 rr *= 1.0 / rr.length2();
465
466 Point2D varTheta = rr;
467 for (int diag = 0; diag < prm.order; ++diag)
468 {
469 for (int q = 0; q <= diag; ++q)
470 lt.E[q] += ((q % 2 ? -1.0 : 1.0) * iFact[diag - q]) * multzA(varTheta, wh.mom[diag - q]);
471
472 varTheta = (diag + 1) * multz(varTheta, rr);
473 }
474 lt.E[0] += iFact[prm.order] * multzA(varTheta, wh.mom[prm.order]);
475 }
476 }//if crit2
477 else // если не выполнен критерий, то рекурсия
478 {
479 if ((!wh.leaf) && (wh.level < prm.NumOfLevelsVortex))
480 {
481 CalcLocalCoeffToLowLevel(lowCell, treeInf, wh.child[0], calcCloseTrees);
482 CalcLocalCoeffToLowLevel(lowCell, treeInf, wh.child[1], calcCloseTrees);
483 }
484 else if (calcCloseTrees)
485 {
486 lt.closeCells.push_back(fromWho);
487 }
488 }//else if
489 }//if (lowTree != this)
490 else if (calcCloseTrees)
491 {
492 lt.closeCells.push_back(lowCell); //себя тоже добавляем в ближнюю зону
493 }
494 }//CalcLocalCoeffToLowLevel(...)
495
496
497
498 //Расчет влияния от ближних ячеек по формуле Био - Савара
499 void MortonTree::CalcVeloBiotSavart(int lowCell, std::unique_ptr<MortonTree>& treeInf)
500 {
501 auto& lc = mortonTree[lowCell];
502 for (int i = lc.range[0]; i <= lc.range[1]; ++i)
503 {
504 //Локальные переменные для цикла
505 Point2D velI, velLinI;
506 double dst2eps;
507 double r2;
508
509 for (size_t k = 0; k < lc.closeCells.size(); ++k)
510 {
511 velI.toZero();
512 velLinI.toZero();
513
514 const Point2D& posI = mortonCodes[i].r;
515
516 auto& rg = treeInf->mortonTree[lc.closeCells[k]].range;
517 for (int j = rg[0]; j <= rg[1]; ++j)
518 {
519 auto& pt = treeInf->mortonCodes[j];
520
521 const Point2D& posJ = pt.r;
522 const double& gamJ = pt.g;
523 r2 = (posI - posJ).length2();
524 dst2eps = std::max(r2, prm.eps2);
525 velI += (gamJ / dst2eps) * (posI - posJ).kcross();
526 }//for j
527
528 pointsCopy[mortonCodes[i].originNumber].veloCopy += velI;
529 }//for k
530
531 }//for i
532 }//CalcVeloBiotSavart(...)
533
534
535 // Расчет влияния от дальней зоны при помощи Тейлоровского разложения
537 {
538 auto& lc = mortonTree[lowCell];
539 for (int i = lc.range[0]; i <= lc.range[1]; ++i)
540 {
541 Point2D deltaPos = mortonCodes[i].r - lc.center;
542 Point2D v = lc.E[0];
543
544 Point2D hh = deltaPos;
545 for (int k = 1; k < prm.order - 1; ++k)
546 {
547 v += iFact[k] * multzA(lc.E[k], hh);
548 hh = multz(hh, deltaPos);
549 }//for k
550 v += iFact[prm.order-1] * multzA(lc.E[prm.order-1], hh);
551
552 pointsCopy[mortonCodes[i].originNumber].veloCopy += Point2D({ -v[1], v[0] });
553
554 }//for i
555 }//CalcVeloTaylorExpansion(...)
556
557
558 //"Разрежение" двоичного представления беззнакового целого, вставляя по одному нулику между всеми битами
559 unsigned int MortonTree::ExpandBits(unsigned int v)
560 {
561 // вставит 1 нуль
562 v = (v | (v << 8)) & 0x00FF00FF; // 00000000`00000000`abcdefgh`ijklmnop
563 // | 00000000`abcdefgh`ijklmnop`00000000
564 // = 00000000`abcdefgh`XXXXXXXX`ijklmnop
565 // & 00000000`11111111`00000000`11111111
566 // = 00000000`abcdefgh`00000000`ijklmnop
567
568 v = (v | (v << 4)) & 0x0F0F0F0F; // 00000000`abcdefgh`00000000`ijklmnop
569 // | 0000abcd`efgh0000`0000ijkl`mnop0000
570 // = 0000abcd`XXXXefgh`0000ijkl`XXXXmnop
571 // & 00001111`00001111`00001111`00001111
572 // = 0000abcd`0000efgh`0000ijkl`0000mnop
573
574 v = (v | (v << 2)) & 0x33333333; // 0000abcd`0000efgh`0000ijkl`0000mnop
575 // | 00abcd00`00efgh00`00ijkl00`00mnop00
576 // = 00abXXcd`00efXXgh`00ijXXkl`00mnXXop
577 // & 00110011`00110011`00110011`00110011
578 // = 00ab00cd`00ef00gh`00ij00kl`00mn00op
579
580 v = (v | (v << 1)) & 0x55555555; // 00ab00cd`00ef00gh`00ij00kl`00mn00op
581 // | 0ab00cd0`0ef00gh0`0ij00kl0`0mn00op0
582 // = 0aXb0cXd`0eXf0gXh`0iXj0kXl`0mXn0oXp
583 // & 01010101`01010101`01010101`01010101
584 // = 0a0b0c0d`0e0f0g0h`0i0j0k0l`0m0n0o0p
585 return v;
586 }
587
588
589 //Мортоновский код для пары из чисел типа double
590 //Исходное число - строго в диапазоне [0, 1 / (1.0 + 1/(2^codeLength - 1) ))
591 unsigned int MortonTree::Morton2D(const Point2D& r)
592 {
593 /*
594 if (x < 0) std::cout << "x*... < 0" << std::endl;
595 if (y < 0) std::cout << "y*... < 0" << std::endl;
596
597 if (x * twoPowCodeLength > twoPowCodeLengthMinus)
598 std::cout << "x*... > ..." << std::endl;
599
600 if (y * twoPowCodeLength > twoPowCodeLengthMinus)
601 std::cout << "y*... > ..." << std::endl;
602
603 x = std::min(std::max(x * twoPowCodeLength, 0.0), twoPowCodeLengthMinus);
604 y = std::min(std::max(y * twoPowCodeLength, 0.0), twoPowCodeLengthMinus);
605
606 unsigned int xx = expandBits((unsigned int)x);
607 unsigned int yy = expandBits((unsigned int)y);
608 */
609 const Point2D& rscale = twoPowCodeLength * r;
610 const unsigned int& xx = ExpandBits((unsigned int)(rscale[0]));
611 const unsigned int& yy = ExpandBits((unsigned int)(rscale[1]));
612 return yy | (xx << 1);
613 }
614
615
616
617 //========================================================
618 void MortonTree::RSort_step3(TParticleCode* source, TParticleCode* dest, unsigned int n, unsigned int* offset, unsigned char sortable_bit)
619 {
620 //unsigned char* b = (unsigned char*)&source[n].key + sortable_bit;
621
622 for (unsigned int i = 0; i < n; ++i)
623 {
624 TParticleCode* src = &source[i];
625 unsigned int off = (src->key >> (sortable_bit * 8)) & 0xFF;
626 dest[offset[off]++] = *src;
627 }
628 }
629 //========================================================
630 void MortonTree::RSort_Node3(TParticleCode* m, TParticleCode* m_temp, unsigned int n)
631 {
632 // Заводим массив корзин
633 unsigned int s[sizeof(m->key) * 256] = { 0 };
634 // Заполняем массив корзин для всех разрядов
635 for (unsigned int i = 0; i < n; ++i)
636 {
637 unsigned int key = m[i].key;
638 for (unsigned int digit = 0; digit < sizeof(m->key); digit++)
639 ++s[((key >> (digit << 3)) & 0xFF) + (digit << 8)];
640 }
641
642 // Пересчитываем смещения для корзин
643 for (unsigned int digit = 0; digit < sizeof(m->key); digit++)
644 {
645 unsigned int off = 0;
646 for (unsigned int i = 0; i < 256; i++)
647 {
648 auto& rs = s[i + (digit << 8)];
649 unsigned int value = rs;
650 rs = off;
651 off += value;
652 }
653 }
654
655 // Вызов сортировки по битам от младших к старшим (LSD)
656 for (unsigned int digit = 0; digit < sizeof(m->key); digit++)
657 {
658 RSort_step3(m, m_temp, n, &s[digit << 8], digit);
659 std::swap(m, m_temp);
660 //TParticleCode* temp = m;
661 //m = m_temp;
662 //m_temp = temp;
663 }
664
665 // Если ключ структуры однобайтовый, копируем отсортированное в исходный массив
666 if (sizeof(m->key) == 1)
667 {
668 std::swap(m, m_temp);
669 //TParticleCode* temp = m;
670 //m = m_temp;
671 //m_temp = temp;
672 memcpy(m, m_temp, n * sizeof(TParticleCode));
673 }
674 }
675
676
677 void MortonTree::RSort_Parallel(TParticleCode* m, TParticleCode* m_temp, unsigned int n, unsigned int* s)
678 {
679 if (n == 0)
680 return;
681 // Количество задействованных потоков
682 unsigned char threads = omp_get_max_threads();
683 //std::cout << "threads = " << (int)threads << std::endl;
684
685#pragma omp parallel num_threads(threads)
686 {
688 TParticleCode* dest = m_temp;
689 unsigned int l = omp_get_thread_num();
690 unsigned int div = n / omp_get_num_threads();
691 unsigned int mod = n % omp_get_num_threads();
692 unsigned int left_index = l < mod ? (div + (mod == 0 ? 0 : 1)) * l : n - (omp_get_num_threads() - l) * div;
693 unsigned int right_index = left_index + div - (mod > l ? 0 : 1);
694
695 for (unsigned int digit = 0; digit < sizeof(m->key); ++digit)
696 {
697 unsigned int s_sum[256] = { 0 };
698 unsigned int s0[256] = { 0 };
699 unsigned char* b1 = (unsigned char*)&source[right_index].key;
700 unsigned char* b2 = (unsigned char*)&source[left_index].key;
701 while (b1 >= b2)
702 {
703 ++s0[*(b1 + digit)];
704 b1 -= sizeof(TParticleCode);
705 }
706 for (unsigned int i = 0; i < 256; i++)
707 {
708 s[i + 256 * l] = s0[i];
709 }
710
711#pragma omp barrier
712 for (unsigned int j = 0; j < threads; j++)
713 {
714 for (unsigned int i = 0; i < 256; i++)
715 {
716 s_sum[i] += s[i + 256 * j];
717 if (j < l)
718 {
719 s0[i] += s[i + 256 * j];
720 }
721 }
722 }
723
724 for (unsigned int i = 1; i < 256; ++i)
725 {
726 s_sum[i] += s_sum[i - 1];
727 s0[i] += s_sum[i - 1];
728 }
729 unsigned char* b = (unsigned char*)&source[right_index].key + digit;
730 TParticleCode* v1 = &source[right_index];
731 TParticleCode* v2 = &source[left_index];
732 while (v1 >= v2)
733 {
734 dest[--s0[*b]] = *v1--;
735 b -= sizeof(TParticleCode);
736 }
737#pragma omp barrier
738 std::swap(source, dest);
739 }
740 }
741
742 // Если ключ структуры однобайтовый, просто копируем в исходный массив
743 if (sizeof(m->key) == 1)
744 {
745 memcpy(m, m_temp, n * sizeof(TParticleCode));
746 }
747 }
748
749 void MortonTree::getStatistics(int& numParticles, int& treshold, std::pair<int, int>& partLevel, std::pair<int, int>& leafsLevel, int& lowLevelCells) const
750 {
751 numParticles = (int)pointsCopy.size();
752 treshold = maxTreeLevel;
753
754 int leafsLevelMin = (int)1e+9;
755 int leafsLevelMax = 0;
756 for (int q = 0; q < numParticles - 1; ++q)
757 {
758 if (!mortonTree[mortonTree[q].parent].leaf)
759 {
760 if (mortonTree[q].leaf)
761 {
762 int level = mortonTree[q].level;
763
764 if (level > leafsLevelMax)
765 leafsLevelMax = level;
766
767 if (level < leafsLevelMin)
768 leafsLevelMin = level;
769
770 continue;
771 }
772
773 if ((mortonTree[mortonTree[q].child[0]].particle) || (mortonTree[mortonTree[q].child[1]].particle))
774 {
775 int level = mortonTree[q].level + 1;
776 if (level > leafsLevelMax)
777 leafsLevelMax = level;
778
779 if (level < leafsLevelMin)
780 leafsLevelMin = level;
781
782 continue;
783 }
784 }
785 }
786 leafsLevel = { leafsLevelMin, leafsLevelMax };
787
788
789 int partLevelMin = (int)1e+9;
790 int partLevelMax = 0;
791 for (int q = 0; q < numParticles - 1; ++q)
792 {
793 if ((mortonTree[mortonTree[q].child[0]].particle) || (mortonTree[mortonTree[q].child[1]].particle))
794 {
795 int level = mortonTree[q].level;
796 if (level > partLevelMax)
797 partLevelMax = level;
798
799 if (level < partLevelMin)
800 partLevelMin = level;
801 }
802 }
803 partLevel = { partLevelMin + 1, partLevelMax + 1 };
804 lowLevelCells = (int)mortonLowCells.size();
805 }
806
807
808
810 {
811 auto& cell = mortonTree[numCell];
812 if (cell.leaf)
813 {
814 MinMaxCellsCalc(cell);
815 cell.hasGabs = true;
816 }
817 else
818 {
819 std::cout << "!!!!!!!!!!!!" << std::endl;
820
821 int ch0 = cell.child[0];
822 int ch1 = cell.child[1];
825 cell.hasGabs = false;
826 }
827 }
828
830 {
831 int spoint = cell.range[0];
832 int epoint = cell.range[1];
833
834 double minx = 1e+10, maxx = -1e+10, miny = 1e+10, maxy = -1e+10;
835
836 for (int ii = spoint; ii <= epoint; ++ii)
837 {
838 const Point2D& r = mortonCodes[ii].r;
839 minx = std::min(r[0], minx);
840 maxx = std::max(r[0], maxx);
841 miny = std::min(r[1], miny);
842 maxy = std::max(r[1], maxy);
843 }
844 cell.minx_miny[0] = minx;
845 cell.minx_miny[1] = miny;
846 cell.maxx_maxy[0] = maxx;
847 cell.maxx_maxy[1] = maxy;
848
849 cell.center = (cell.minx_miny + cell.maxx_maxy) * 0.5;
850 cell.size = cell.maxx_maxy - cell.minx_miny;
851
852 }
853
854 void MortonTree::ShareGabs(int numCell/*, Point2D& minx_miny, Point2D& maxx_maxy*/)
855 {
856 auto& lcell = mortonTree[mortonTree[numCell].child[0]]; //левый ребенок
857 auto& rcell = mortonTree[mortonTree[numCell].child[1]]; //правый ребенок
858 auto& pcell = mortonTree[numCell];
859 {
860 pcell.minx_miny[0] = std::min(lcell.minx_miny[0], rcell.minx_miny[0]);
861 pcell.minx_miny[1] = std::min(lcell.minx_miny[1], rcell.minx_miny[1]);
862 pcell.maxx_maxy[0] = std::max(lcell.maxx_maxy[0], rcell.maxx_maxy[0]);
863 pcell.maxx_maxy[1] = std::max(lcell.maxx_maxy[1], rcell.maxx_maxy[1]);
864 }
865
866 pcell.center = (pcell.minx_miny + pcell.maxx_maxy) * 0.5;
867 pcell.size = pcell.maxx_maxy - pcell.minx_miny;
868 }
869
870 void MortonTree::GabsPyr(int numCell)
871 {
872 auto& cell = mortonTree[numCell];
873 if (cell.hasGabs)
874 return;
875
876 int ch0 = cell.child[0];
877 int ch1 = cell.child[1];
878 if (!mortonTree[ch0].hasGabs)
879 GabsPyr(ch0);
880 if (!mortonTree[ch1].hasGabs)
881 GabsPyr(ch1);
882
883 ShareGabs(numCell/*, cell.minx_miny, cell.maxx_maxy*/);
884 }
885
886
887}//namespace BH
888
#define codeLength
Definition Gpudefs.h:97
#define twoPowCodeLength
Definition Gpudefs.h:98
Заголовок класса Tree для метода Барнса - Хата для CPU.
void BuildMortonParticlesTree()
Построение верхушек дерева — отдельных частиц
Definition TreeBH.cpp:330
void MinMaxCellsSearch(int numCell)
Definition TreeBH.cpp:809
std::vector< treeCellT > mortonTree
Мортоновское дерево Размер дерева равен сумме числа внутренних вершин (число частиц без единицы) и чи...
Definition TreeBH.h:257
void BuildInternalTreeCell(int i)
Функция построения i-й внутренней ячейки дерева
Definition TreeBH.cpp:264
int Delta(int i, int j) const
Definition TreeBH.cpp:187
std::vector< PointsCopy > & pointsCopy
Ссылка на список обобщенных вихрей, по которым строится дерево
Definition TreeBH.h:153
void CalcLocalCoeffToLowLevel(int lowCell, std::unique_ptr< MortonTree > &treeInf, int fromWho, bool calcCloseTrees)
Definition TreeBH.cpp:436
void MinMaxCellsCalc(BH::treeCellT &cell)
Definition TreeBH.cpp:829
void RSort_step3(TParticleCode *source, TParticleCode *dest, unsigned int n, unsigned int *offset, unsigned char sortable_bit)
Вспомогательная функция к RSort_Node3.
Definition TreeBH.cpp:618
void RSort_Node3(TParticleCode *m, TParticleCode *m_temp, unsigned int n)
Поразрядная сортировка мортоновских кодов
Definition TreeBH.cpp:630
MortonTree(const params &prm_, int maxTreeLevel_, std::vector< PointsCopy > &points)
Definition TreeBH.cpp:49
void FillMortonLowCells(int cell=0)
Функция заполнения списка нижних ячеек дерева (до которых производится обход)
Definition TreeBH.cpp:348
void CalcVeloTaylorExpansion(int lowCell)
Definition TreeBH.cpp:536
void ShareGabs(int numCell)
Definition TreeBH.cpp:854
void BuildMortonInternalTree()
Построение внутренних ячеек дерева
Definition TreeBH.cpp:314
std::pair< unsigned int, int > Prefix(int cell) const
Definition TreeBH.cpp:227
static std::pair< Point2D, Point2D > FindEnclosingRectangleOld(const std::vector< PointsCopy > &points)
Поиск габаритного прямоугольника системы точек (для OpenMP 2.0)
Definition TreeBH.cpp:82
double iQuadSideVar
Definition TreeBH.h:165
void MakeRootMortonTree()
Построение корня дерева и задание его общих параметров
Definition TreeBH.cpp:140
static unsigned int Morton2D(const Point2D &r)
Definition TreeBH.cpp:591
std::vector< int > mortonLowCells
Вектор индесков ячеек нижнего уровня в дереве
Definition TreeBH.h:247
static unsigned int ExpandBits(unsigned int v)
"Разрежение" двоичного представления беззнакового целого, вставляя по одному нулику между всеми битам...
Definition TreeBH.cpp:559
void FillMortonLowCellsA()
Definition TreeBH.cpp:362
Point2D lowLeft
Положение нижнего левого угла дерева
Definition TreeBH.h:168
void RSort_Parallel(TParticleCode *m, TParticleCode *m_temp, unsigned int n, unsigned int *s)
Параллельная версия поразрядной сортировки мортоновских кодов
Definition TreeBH.cpp:677
void getStatistics(int &numParticles, int &treshold, std::pair< int, int > &partLevel, std::pair< int, int > &leafsLevel, int &lowLevelCells) const
Definition TreeBH.cpp:749
const params & prm
Ссылка на параметры, загружаемые из файла
Definition TreeBH.h:150
std::vector< TParticleCode > mortonCodes
Список мортоновских кодов, вычисляемых по положениям частиц
Definition TreeBH.h:157
double & getBinom(int n, int k)
Definition TreeBH.h:238
int PrefixLength(int cell) const
Definition TreeBH.cpp:219
void GabsPyr(int numCell)
Definition TreeBH.cpp:870
void CalcVeloBiotSavart(int lowCell, std::unique_ptr< MortonTree > &treeInf)
Definition TreeBH.cpp:499
std::vector< double > binomCft
Definition TreeBH.h:223
const int maxTreeLevel
Максимальная глубина дерева при его обходе
Definition TreeBH.h:226
std::vector< double > iFact
Спикок обратных факториалов целых чисел
Definition TreeBH.h:219
void CalculateMortonTreeParams(int cell, int omplevel)
Вычисление параметров дерева (циркуляций и высших моментов)
Definition TreeBH.cpp:378
void SetCellGeometry(int cell)
Функция вычисления геометрических параметров внутренней ячейки дерева
Definition TreeBH.cpp:236
Класс, содержащий параметры метода Баонса - Хата для CPU.
Definition ParamsBH.h:65
double eps
Радиус вихревого элемента
Definition ParamsBH.h:68
int NumOfLevelsVortex
Максимальное количество уровней дерева
Definition ParamsBH.h:81
double theta
Параметр точности
Definition ParamsBH.h:84
int order
Точность расчета скоростей:
Definition ParamsBH.h:78
double eps2
Квадрат радиуса вихревого элемента
Definition ParamsBH.h:71
numvector< T, 2 > kcross() const
Геометрический поворот двумерного вектора на 90 градусов
Definition numvector.h:513
auto length2() const -> typename std::remove_const< typename std::remove_reference< decltype(this->data[0])>::type >::type
Вычисление квадрата нормы (длины) вектора
Definition numvector.h:389
numvector< T, n > & toZero(P val=0)
Установка всех компонент вектора в константу (по умолчанию — нуль)
Definition numvector.h:530
size_t size() const
Definition numvector.h:114
int ceilhalf(unsigned int x)
Округление "в потолок" результата деления пополам, эквивалент ceil(x / 2)
Definition defsBH.h:134
int ceilpow2(unsigned int x, unsigned int p)
Округление "в потолок" результата деления на степень двойки, эквивалент ceil(x / (2^p))
Definition defsBH.h:128
T sqr(T x)
Умножение a на комплексно сопряженноe к b.
Definition defsBH.h:101
int sign(T val)
Шаблонная функция знака числа Написана оптимизированная версия, которая работает самым быстрым возмож...
Definition defsBH.h:111
long long op
Глобальная переменная - счетчик количества операций
Definition TreeBH.cpp:46
Структура, соответствующая частице и ее мортоновскому коду
Definition TreeBH.h:65
unsigned int key
Мортоновский код частицы
Definition TreeBH.h:67
Структура, соответствующая ячейке мортоновского дерева
Definition TreeBH.h:92
numvector< int, 2 > range
Диапазон индексов частиц в ячейке: 0 - первая; 1 - последняя
Definition TreeBH.h:100
Point2D minx_miny
Definition TreeBH.h:130
Point2D center
Положение центра ячейки
Definition TreeBH.h:113
Point2D maxx_maxy
Definition TreeBH.h:130
Point2D size
Размер ячейки по x и по y (для листа (0;0)
Definition TreeBH.h:110
numvector< int, 2 > child
Индексы потомков (если это не particle): 0 - левый, 1 - правый
Definition TreeBH.h:97