VM2D 1.14
Vortex methods for 2D flows simulation
Loading...
Searching...
No Matches
VM2D::CpuTreeInfo Class Reference

Структура, хранящая данные и указатели на массивы на GPU для оптимизации итерационного решения СЛАУ на GPU. More...

#include <cpuTreeInfo.h>

Collaboration diagram for VM2D::CpuTreeInfo:

Public Member Functions

 CpuTreeInfo (tree_T treeType_, object_T objectType_, scheme_T schemeType_)
 
 ~CpuTreeInfo ()
 
float Update (const std::vector< Vortex2D > &vtx, double eps)
 
void RadixSortMortonCodes ()
 
void RadixSortInternalCells ()
 
int Delta (int i, int j) const
 
void Summ12 ()
 
void CalcAABB ()
 
float Build ()
 
float UpwardTraversal (int order)
 
float DownwardTraversalVorticesToPoints (CpuTreeInfo &cntrTree, Point2D *velD, double *epsastD, double eps2, double theta, int order, bool calcRadius)
 

Public Attributes

std::vector< Point2Dobject
 
std::vector< double > gamma
 
tree_T treeType
 
object_T objectType
 
scheme_T schemeType
 
std::vector< int > mass
 
Point2D maxr
 
Point2D minr
 
std::vector< Point2Dmoms
 
std::vector< Point2DED
 
std::vector< unsigned int > mortonCodesKeyUnsort
 
std::vector< unsigned int > mortonCodesKey
 
std::vector< int > mortonCodesIdxUnsort
 
std::vector< int > mortonCodesIdx
 
std::vector< int > levelUnsort
 
std::vector< int > levelSort
 
std::vector< int > indexUnsort
 
std::vector< int > indexSort
 
std::vector< int > indexSortT
 
std::vector< Point2Dcenter
 
std::vector< Point4Dlowerupper
 
std::vector< Point4DgabForLeaves
 
std::vector< int > parent
 
std::vector< std::pair< int, int > > child
 
std::vector< std::pair< int, int > > range
 

Detailed Description

Структура, хранящая данные и указатели на массивы на GPU для оптимизации итерационного решения СЛАУ на GPU.

Author
Марчевский Илья Константинович
Сокол Ксения Сергеевна
Рятина Евгения Павловна
Колганова Александра Олеговна

\Version 1.14

Date
6 марта 2026 г. ‍/ struct infoForMatVecMul { std::vector<int> nClosePanels; ///количество листьев дерева (панелей), находящихся в ближней зоне std::vector<int> nFarCells; ///количество ячеек, находящихся в дальней зоне

std::vector<int> closePrefixSum; ///префиксная сумма для ближней зоны std::vector<int> farPrefixSum; ///префиксная сумма для дальней зоны

Point2D* i00D; ///указатель на GPU, хранящий влияние панелей из ближней зоны Point2D* i01D; ///аналог i00D, но используется для кусочно-линейной схемы Point2D* i10D; ///аналог i00D, но используется для кусочно-линейной схемы Point2D* i11D; ///аналог i00D, но используется для кусочно-линейной схемы

int* nClosePanelsD; ///количество листьев дерева (панелей), находящихся в ближней зоне int* nFarCellsD; ///количество ячеек, находящихся в дальней зоне

int* closeCellsIdxD; ///индексы ячеек ближней зоны int* farCellsIdxD; ///индексы ячеек дальней зоны

int* closePrefixSumD; ///префиксная сумма для ближней зоны int* farPrefixSumD; ///префиксная сумма для дальней зоны };

/*!
\brief Класс, определяющий структуры дерева для быстрых методов при их реализации на CPU

\author Марчевский Илья Константинович
\author Сокол Ксения Сергеевна
\author Рятина Евгения Павловна
\author Колганова Александра Олеговна

\Version 1.14
\date 6 марта 2026 г.

Definition at line 96 of file cpuTreeInfo.h.

Constructor & Destructor Documentation

◆ CpuTreeInfo()

VM2D::CpuTreeInfo::CpuTreeInfo ( tree_T  treeType_,
object_T  objectType_,
scheme_T  schemeType_ 
)

Definition at line 122 of file cpuTreeInfo.cpp.

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 };

◆ ~CpuTreeInfo()

VM2D::CpuTreeInfo::~CpuTreeInfo ( )

Definition at line 163 of file cpuTreeInfo.cpp.

163{};

Member Function Documentation

◆ Build()

float VM2D::CpuTreeInfo::Build ( )

Definition at line 906 of file cpuTreeInfo.cpp.

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()
#define rbound
Definition Gpudefs.h:99
#define twoPowCodeLength
Definition Gpudefs.h:98
std::vector< std::pair< int, int > > range
std::vector< int > levelUnsort
std::vector< unsigned int > mortonCodesKeyUnsort
std::vector< int > parent
void RadixSortInternalCells()
std::vector< int > indexUnsort
std::vector< int > mortonCodesIdxUnsort
int Delta(int i, int j) const
std::vector< std::pair< int, int > > child
int ceilhalf(int x)
unsigned int ExpandBits(unsigned int v)
int ceilpow2(int x, int p)
Here is the call graph for this function:

◆ CalcAABB()

void VM2D::CpuTreeInfo::CalcAABB ( )

Definition at line 802 of file cpuTreeInfo.cpp.

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(...)
std::vector< int > indexSort
std::vector< int > mortonCodesIdx
std::vector< Point2D > center
std::vector< Point4D > gabForLeaves
std::vector< int > indexSortT
std::vector< Point4D > lowerupper
std::vector< int > mass
Here is the caller graph for this function:

◆ Delta()

int VM2D::CpuTreeInfo::Delta ( int  i,
int  j 
) const

Definition at line 89 of file cpuTreeInfo.cpp.

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(...)
#define codeLength
Definition Gpudefs.h:97
Here is the caller graph for this function:

◆ DownwardTraversalVorticesToPoints()

float VM2D::CpuTreeInfo::DownwardTraversalVorticesToPoints ( CpuTreeInfo cntrTree,
Point2D velD,
double *  epsastD,
double  eps2,
double  theta,
int  order,
bool  calcRadius 
)

Definition at line 1038 of file cpuTreeInfo.cpp.

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(...)
#define orderAlignment
Definition Gpudefs.h:88
std::vector< Point2D > moms
std::vector< double > gamma
size_t size() const
Definition numvector.h:114
Point2D multz(const Point2D &a, const Point2D &b)
Умножение комплексных чисел
Definition Point2D.h:208
Point2D multzA(const Point2D &a, const Point2D &b)
Умножение a на комплексно сопряженноe к b.
Definition Point2D.h:217

◆ RadixSortInternalCells()

void VM2D::CpuTreeInfo::RadixSortInternalCells ( )

Definition at line 323 of file cpuTreeInfo.cpp.

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()
std::vector< int > levelSort
Here is the caller graph for this function:

◆ RadixSortMortonCodes()

void VM2D::CpuTreeInfo::RadixSortMortonCodes ( )

Definition at line 207 of file cpuTreeInfo.cpp.

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()
std::vector< unsigned int > mortonCodesKey
Here is the caller graph for this function:

◆ Summ12()

void VM2D::CpuTreeInfo::Summ12 ( )

Definition at line 425 of file cpuTreeInfo.cpp.

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()
Here is the caller graph for this function:

◆ Update()

float VM2D::CpuTreeInfo::Update ( const std::vector< Vortex2D > &  vtx,
double  eps 
)

Definition at line 165 of file cpuTreeInfo.cpp.

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 }

◆ UpwardTraversal()

float VM2D::CpuTreeInfo::UpwardTraversal ( int  order)

Definition at line 1019 of file cpuTreeInfo.cpp.

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(...)
Here is the call graph for this function:

Member Data Documentation

◆ center

std::vector<Point2D> VM2D::CpuTreeInfo::center

Definition at line 134 of file cpuTreeInfo.h.

◆ child

std::vector<std::pair<int, int> > VM2D::CpuTreeInfo::child

Definition at line 139 of file cpuTreeInfo.h.

◆ ED

std::vector<Point2D> VM2D::CpuTreeInfo::ED

Definition at line 118 of file cpuTreeInfo.h.

◆ gabForLeaves

std::vector<Point4D> VM2D::CpuTreeInfo::gabForLeaves

Definition at line 136 of file cpuTreeInfo.h.

◆ gamma

std::vector<double> VM2D::CpuTreeInfo::gamma

Definition at line 105 of file cpuTreeInfo.h.

◆ indexSort

std::vector<int> VM2D::CpuTreeInfo::indexSort

Definition at line 131 of file cpuTreeInfo.h.

◆ indexSortT

std::vector<int> VM2D::CpuTreeInfo::indexSortT

Definition at line 132 of file cpuTreeInfo.h.

◆ indexUnsort

std::vector<int> VM2D::CpuTreeInfo::indexUnsort

Definition at line 130 of file cpuTreeInfo.h.

◆ levelSort

std::vector<int> VM2D::CpuTreeInfo::levelSort

Definition at line 128 of file cpuTreeInfo.h.

◆ levelUnsort

std::vector<int> VM2D::CpuTreeInfo::levelUnsort

Definition at line 127 of file cpuTreeInfo.h.

◆ lowerupper

std::vector<Point4D> VM2D::CpuTreeInfo::lowerupper

Definition at line 135 of file cpuTreeInfo.h.

◆ mass

std::vector<int> VM2D::CpuTreeInfo::mass

Definition at line 114 of file cpuTreeInfo.h.

◆ maxr

Point2D VM2D::CpuTreeInfo::maxr

Definition at line 116 of file cpuTreeInfo.h.

◆ minr

Point2D VM2D::CpuTreeInfo::minr

Definition at line 116 of file cpuTreeInfo.h.

◆ moms

std::vector<Point2D> VM2D::CpuTreeInfo::moms

Definition at line 117 of file cpuTreeInfo.h.

◆ mortonCodesIdx

std::vector<int> VM2D::CpuTreeInfo::mortonCodesIdx

Definition at line 125 of file cpuTreeInfo.h.

◆ mortonCodesIdxUnsort

std::vector<int> VM2D::CpuTreeInfo::mortonCodesIdxUnsort

Definition at line 124 of file cpuTreeInfo.h.

◆ mortonCodesKey

std::vector<unsigned int> VM2D::CpuTreeInfo::mortonCodesKey

Definition at line 122 of file cpuTreeInfo.h.

◆ mortonCodesKeyUnsort

std::vector<unsigned int> VM2D::CpuTreeInfo::mortonCodesKeyUnsort

Definition at line 121 of file cpuTreeInfo.h.

◆ object

std::vector<Point2D> VM2D::CpuTreeInfo::object

Definition at line 104 of file cpuTreeInfo.h.

◆ objectType

object_T VM2D::CpuTreeInfo::objectType

Definition at line 108 of file cpuTreeInfo.h.

◆ parent

std::vector<int> VM2D::CpuTreeInfo::parent

Definition at line 138 of file cpuTreeInfo.h.

◆ range

std::vector<std::pair<int, int> > VM2D::CpuTreeInfo::range

Definition at line 140 of file cpuTreeInfo.h.

◆ schemeType

scheme_T VM2D::CpuTreeInfo::schemeType

Definition at line 109 of file cpuTreeInfo.h.

◆ treeType

tree_T VM2D::CpuTreeInfo::treeType

Definition at line 107 of file cpuTreeInfo.h.


The documentation for this class was generated from the following files: