1041{
1042 using namespace std::literals;
1043
1044 tbb::enumerable_thread_specific<Vector3d> tmp_force_ets, dr_ets;
1045 tbb::enumerable_thread_specific<Vector3d> matrix_ets;
1046 tbb::enumerable_thread_specific<std::vector<std::complex<double>>> eim_ets(std::vector<std::complex<double>>(
N + 1));
1047 tbb::enumerable_thread_specific<treevector<double>> Pnm_ets(treevector<double>(
N + 1));
1048 tbb::enumerable_thread_specific<std::vector<Vector3d>> buf_forces_ets((std::vector<Vector3d>(
num_particles)));
1049 tbb::enumerable_thread_specific<std::vector<double>> buf_potentials_ets((std::vector<double>(
num_particles)));
1050
1051 const auto& leaves =
tree->levels.back();
1053 const auto& particles =
tree->particles;
1054
1055#ifdef FMM_MPI
1056 auto [Begin, End] = LocalPart(0,
tree->level_sizes.back());
1057#else
1058 size_t Begin = 0, End =
tree->level_sizes.back();
1059#endif
1060
1061 decltype(&TreeCell_t::source_range) range_ptr;
1062 tree->targets_num == 0 ? range_ptr = &TreeCell_t::source_range : range_ptr = &TreeCell_t::target_range;
1063
1064 using simd_vec_t = std::conditional_t<detail::avx_vec_length == 2, __m128d, std::conditional_t<detail::avx_vec_length == 4, __m256d, __m512d>>;
1065 simd_vec_t oneVec, epsVec;
1068
1069 std::unique_ptr<size_t[]> shifts(
new size_t[
tree->level_sizes.back() + 1]);
1070 shifts[0] = 0;
1071 for (
size_t i = 0; i <
tree->level_sizes.back(); ++i)
1072 {
1073 auto [a, b] = leaves[i].source_range;
1075 }
1076 size_t num_vecs = shifts[
tree->level_sizes.back()];
1077 std::unique_ptr<simd_vec_t[]> x_simd(new simd_vec_t[num_vecs]),
1078 y_simd(new simd_vec_t[num_vecs]),
1079 z_simd(new simd_vec_t[num_vecs]),
1080 q_simd(new simd_vec_t[num_vecs]);
1081
1082 double t1 = omp_get_wtime();
1083 tbb::parallel_for(
size_t(0),
tree->level_sizes.back(), [&](
size_t i)
1084 {
1085 auto [a, b] = leaves[i].source_range;
1086 auto xptr = x_simd.get() + shifts[i];
1087 auto yptr = y_simd.get() + shifts[i];
1088 auto zptr = z_simd.get() + shifts[i];
1089 auto qptr = q_simd.get() + shifts[i];
1090 for (auto idx = a, k = (decltype(a))0; idx < b; idx += detail::avx_vec_length, ++k)
1091 {
1092#if defined(AVX128)
1093 xptr[k] = _mm_set_pd(idx + 2 > b ? 0 : particles[idx + 1].center[0], particles[idx].center[0]);
1094 yptr[k] = _mm_set_pd(idx + 2 > b ? 0 : particles[idx + 1].center[1], particles[idx].center[1]);
1095 zptr[k] = _mm_set_pd(idx + 2 > b ? 0 : particles[idx + 1].center[2], particles[idx].center[2]);
1096 qptr[k] = _mm_set_pd(idx + 2 > b ? 0 : particles[idx + 1].q, particles[idx].q);
1097#elif defined(AVX256)
1098 xptr[k] = _mm256_set_pd(idx + 4 > b ? 0 : particles[idx + 3].center[0],
1099 idx + 3 > b ? 0 : particles[idx + 2].center[0],
1100 idx + 2 > b ? 0 : particles[idx + 1].center[0], particles[idx].center[0]);
1101 yptr[k] = _mm256_set_pd(idx + 4 > b ? 0 : particles[idx + 3].center[1],
1102 idx + 3 > b ? 0 : particles[idx + 2].center[1],
1103 idx + 2 > b ? 0 : particles[idx + 1].center[1], particles[idx].center[1]);
1104 zptr[k] = _mm256_set_pd(idx + 4 > b ? 0 : particles[idx + 3].center[2],
1105 idx + 3 > b ? 0 : particles[idx + 2].center[2],
1106 idx + 2 > b ? 0 : particles[idx + 1].center[2], particles[idx].center[2]);
1107 qptr[k] = _mm256_set_pd(idx + 4 > b ? 0 : particles[idx + 3].q,
1108 idx + 3 > b ? 0 : particles[idx + 2].q,
1109 idx + 2 > b ? 0 : particles[idx + 1].q, particles[idx].q);
1110#elif defined(AVX512)
1111 xptr[k] = _mm512_set_pd(idx + 8 > b ? 0 : particles[idx + 7].center[0],
1112 idx + 7 > b ? 0 : particles[idx + 6].center[0],
1113 idx + 6 > b ? 0 : particles[idx + 5].center[0],
1114 idx + 5 > b ? 0 : particles[idx + 4].center[0],
1115 idx + 4 > b ? 0 : particles[idx + 3].center[0],
1116 idx + 3 > b ? 0 : particles[idx + 2].center[0],
1117 idx + 2 > b ? 0 : particles[idx + 1].center[0], particles[idx].center[0]);
1118 yptr[k] = _mm512_set_pd(idx + 8 > b ? 0 : particles[idx + 7].center[1],
1119 idx + 7 > b ? 0 : particles[idx + 6].center[1],
1120 idx + 6 > b ? 0 : particles[idx + 5].center[1],
1121 idx + 5 > b ? 0 : particles[idx + 4].center[1],
1122 idx + 4 > b ? 0 : particles[idx + 3].center[1],
1123 idx + 3 > b ? 0 : particles[idx + 2].center[1],
1124 idx + 2 > b ? 0 : particles[idx + 1].center[1], particles[idx].center[1]);
1125 zptr[k] = _mm512_set_pd(idx + 8 > b ? 0 : particles[idx + 7].center[2],
1126 idx + 7 > b ? 0 : particles[idx + 6].center[2],
1127 idx + 6 > b ? 0 : particles[idx + 5].center[2],
1128 idx + 5 > b ? 0 : particles[idx + 4].center[2],
1129 idx + 4 > b ? 0 : particles[idx + 3].center[2],
1130 idx + 3 > b ? 0 : particles[idx + 2].center[2],
1131 idx + 2 > b ? 0 : particles[idx + 1].center[2], particles[idx].center[2]);
1132 qptr[k] = _mm512_set_pd(idx + 8 > b ? 0 : particles[idx + 7].q,
1133 idx + 7 > b ? 0 : particles[idx + 6].q,
1134 idx + 6 > b ? 0 : particles[idx + 5].q,
1135 idx + 5 > b ? 0 : particles[idx + 4].q,
1136 idx + 4 > b ? 0 : particles[idx + 3].q,
1137 idx + 3 > b ? 0 : particles[idx + 2].q,
1138 idx + 2 > b ? 0 : particles[idx + 1].q, particles[idx].q);
1139#endif
1140 }
1141 });
1142
1143 tbb::enumerable_thread_specific<std::vector<simd_vec_t>> fx_simd_ets((std::vector<simd_vec_t>(num_vecs))),
1144 fy_simd_ets((std::vector<simd_vec_t>(num_vecs))),
1145 fz_simd_ets((std::vector<simd_vec_t>(num_vecs))),
1146 phi_simd_ets((std::vector<simd_vec_t>(num_vecs)));
1147
1148 tbb::parallel_for(Begin, End, [&](size_t leaf_idx) {
1149 auto& tmp_force = tmp_force_ets.local();
1150 auto& dr = dr_ets.local();
1151 auto& eim = eim_ets.local();
1152 auto& Pnm = Pnm_ets.local();
1153 auto& m_temp = matrix_ets.local();
1154 auto& buf_forces = buf_forces_ets.local();
1155 auto& buf_potentials = buf_potentials_ets.local();
1156 auto& fx_simd = fx_simd_ets.local();
1157 auto& fy_simd = fy_simd_ets.local();
1158 auto& fz_simd = fz_simd_ets.local();
1159 auto& phi_simd = phi_simd_ets.local();
1160 double phi = 0.0;
1161
1162 const auto& cell = leaves[leaf_idx];
1163 const auto& [p1_begin, p1_end] = cell.*range_ptr;
1164
1165 simd_vec_t x_target, y_target, z_target, q_target,
1166 x_source, y_source, z_source, q_source, dx, dy, dz, fx, fy, fz, pot, invdr, invdr2, q_invdr, q_invdr3;
1167
1168 auto x_leaf = x_simd.get() + shifts[leaf_idx];
1169 auto y_leaf = y_simd.get() + shifts[leaf_idx];
1170 auto z_leaf = z_simd.get() + shifts[leaf_idx];
1171 auto q_leaf = q_simd.get() + shifts[leaf_idx];
1172
1173 auto fx_leaf = fx_simd.data() + shifts[leaf_idx];
1174 auto fy_leaf = fy_simd.data() + shifts[leaf_idx];
1175 auto fz_leaf = fz_simd.data() + shifts[leaf_idx];
1176 auto phi_leaf = phi_simd.data() + shifts[leaf_idx];
1177
1178 for (auto j = p1_begin; j < p1_end; ++j)
1179 {
1180 auto& particle = particles[j];
1181
1182 if (range_ptr == &TreeCell_t::target_range)
1183 {
1188 fx = avx_zero_vec<simd_vec_t>();
1189 fy = avx_zero_vec<simd_vec_t>();
1190 fz = avx_zero_vec<simd_vec_t>();
1191 pot = avx_zero_vec<simd_vec_t>();
1192
1193 {
1194 const auto& [p2_begin, p2_end] = cell.source_range;
1196 {
1197 x_source = x_leaf[i];
1198 y_source = y_leaf[i];
1199 z_source = z_leaf[i];
1200 q_source = q_leaf[i];
1201 avx_inv_dr(oneVec, epsVec, x_target, y_target, z_target, x_source, y_source, z_source, dx, dy, dz, invdr, invdr2);
1202 q_invdr =
avx_mul(q_source, invdr);
1203 q_invdr3 =
avx_mul(invdr2, q_invdr);
1204
1209 }
1210 }
1211
1212 for (const auto& idx : cell.closeneighbours)
1213 {
1214 const auto& neighbour_cell = leaves[idx];
1215 const auto& [p2_begin, p2_end] = neighbour_cell.source_range;
1216
1217 auto x_nbr = x_simd.get() + shifts[idx];
1218 auto y_nbr = y_simd.get() + shifts[idx];
1219 auto z_nbr = z_simd.get() + shifts[idx];
1220 auto q_nbr = q_simd.get() + shifts[idx];
1221
1223 {
1224 x_source = x_nbr[i];
1225 y_source = y_nbr[i];
1226 z_source = z_nbr[i];
1227 q_source = q_nbr[i];
1228 avx_inv_dr(oneVec, epsVec, x_target, y_target, z_target, x_source, y_source, z_source, dx, dy, dz, invdr, invdr2);
1229 q_invdr =
avx_mul(q_source, invdr);
1230 q_invdr3 =
avx_mul(invdr2, q_invdr);
1231
1236 }
1237 }
1238 }
1239 else
1240 {
1245 fx = avx_zero_vec<simd_vec_t>();
1246 fy = avx_zero_vec<simd_vec_t>();
1247 fz = avx_zero_vec<simd_vec_t>();
1248 pot = avx_zero_vec<simd_vec_t>();
1249
1251 {
1252 x_source = x_leaf[pletnum];
1253 y_source = y_leaf[pletnum];
1254 z_source = z_leaf[pletnum];
1255 q_source = q_leaf[pletnum];
1256 avx_inv_dr(oneVec, epsVec, x_target, y_target, z_target, x_source, y_source, z_source, dx, dy, dz, invdr, invdr2);
1257 q_invdr =
avx_mul(q_source, invdr);
1258 q_invdr3 =
avx_mul(invdr2, q_invdr);
1259
1264 }
1265 pletnum++;
1267 {
1268 x_source = x_leaf[i];
1269 y_source = y_leaf[i];
1270 z_source = z_leaf[i];
1271 q_source = q_leaf[i];
1272 avx_inv_dr(oneVec, epsVec, x_target, y_target, z_target, x_source, y_source, z_source, dx, dy, dz, invdr, invdr2);
1273 q_invdr =
avx_mul(q_source, invdr);
1274 q_invdr3 =
avx_mul(invdr2, q_invdr);
1275
1280
1281 q_invdr =
avx_mul(q_target, invdr);
1282 q_invdr3 =
avx_mul(invdr2, q_invdr);
1283
1287 phi_leaf[i] =
avx_add(q_invdr, phi_leaf[i]);
1288 }
1289
1290 for (const auto& idx : cell.closeneighbours)
1291 {
1292 const auto& neighbour_cell = leaves[idx];
1293 const auto& [p2_begin, p2_end] = neighbour_cell.source_range;
1294
1295 auto x_nbr = x_simd.get() + shifts[idx];
1296 auto y_nbr = y_simd.get() + shifts[idx];
1297 auto z_nbr = z_simd.get() + shifts[idx];
1298 auto q_nbr = q_simd.get() + shifts[idx];
1299
1300 auto fx_nbr = fx_simd.data() + shifts[idx];
1301 auto fy_nbr = fy_simd.data() + shifts[idx];
1302 auto fz_nbr = fz_simd.data() + shifts[idx];
1303 auto phi_nbr = phi_simd.data() + shifts[idx];
1304
1306 {
1307 x_source = x_nbr[i];
1308 y_source = y_nbr[i];
1309 z_source = z_nbr[i];
1310 q_source = q_nbr[i];
1311 avx_inv_dr(oneVec, epsVec, x_target, y_target, z_target, x_source, y_source, z_source, dx, dy, dz, invdr, invdr2);
1312 q_invdr =
avx_mul(q_source, invdr);
1313 q_invdr3 =
avx_mul(invdr2, q_invdr);
1314
1319
1320 q_invdr =
avx_mul(q_target, invdr);
1321 q_invdr3 =
avx_mul(invdr2, q_invdr);
1322
1326 phi_nbr[i] =
avx_add(q_invdr, phi_nbr[i]);
1327 }
1328 }
1329 }
1333 buf_forces[j] += tmp_force;
1334 buf_potentials[j] +=
avx_hsum(pot);
1335 }
1336 const auto& inner = leaves_inner.data() + leaf_idx *
Nx2;
1337 std::complex<double> Ynm, coef, SphDTheta, SphDPhi;
1338 double x, y, rn, s1, s2, c1, c2;
1339
1340 for (auto j = p1_begin; j < p1_end; ++j)
1341 {
1342 auto& particle = particles[j];
1343 memset(m_temp.data(), 0,
sizeof(
Vector3d));
1344 dr =
DecToSph(particle.center - cell.center);
1345
1347
1348 for (
int m = 0; m <=
N; ++m)
1349 {
1350 x = m * dr[2];
1351 eim[m] = std::complex<double>(cos(x), sin(x));
1352 }
1353 x = cos(dr[1]);
1354 y = sin(dr[1]);;
1355 rn = 1. / dr[0];
1356
1357 phi = 0.0;
1358
1359 for (
int n = 0; n <
N; ++n)
1360 {
1361 for (int m = 0; m <= n; ++m)
1362 {
1363 Ynm = Pnm(n, m) * eim[m] *
Knm(n, m);
1364 coef = inner[n * (n + 1) / 2 + m] * rn;
1365
1366 phi += (dr[0] * Ynm * coef).
real();
1367 if (m != 0)
1368 phi += (dr[0] * Ynm * coef).
real();
1369
1370 SphDTheta =
Knm(n, m) * eim[m] * ((1 - m + n) * Pnm(n + 1, m) - (n + 1) * x * Pnm(n, m)) / y;
1371 SphDPhi = double(m) * 1.0i * Ynm;
1372
1373 m_temp[0] -= (double(n) * coef * Ynm).
real();
1374 m_temp[1] -= (coef * SphDTheta).
real();
1375 m_temp[2] -= (coef * SphDPhi).
real();
1376 if (m != 0)
1377 {
1378 m_temp[0] -= (double(n) * coef * Ynm).
real();
1379 m_temp[1] -= (coef * SphDTheta).
real();
1380 m_temp[2] -= (coef * SphDPhi).
real();
1381 }
1382 }
1383 rn *= dr[0];
1384 }
1385 buf_potentials[j] += phi;
1386
1387 s1 = sin(dr[1]); c2 = cos(dr[2]); c1 = cos(dr[1]); s2 = sin(dr[2]);
1388 auto& f = buf_forces[j];
1389 f[0] += m_temp[0] * s1 * c2 + m_temp[1] * c1 * c2 - m_temp[2] * s2 / s1;
1390 f[1] += m_temp[0] * s1 * s2 + m_temp[1] * c1 * s2 + m_temp[2] * c2 / s1;
1391 f[2] += m_temp[0] * c1 - m_temp[1] * s1;
1392 }
1393 });
1394 std::cout << omp_get_wtime() - t1 << std::endl;
1395
1396 auto& buf_forces = *buf_forces_ets.begin();
1397 for (auto it = buf_forces_ets.begin() + 1; it < buf_forces_ets.end(); it++)
1398 {
1399 const auto& x = *it;
1400 tbb::parallel_for(tbb::blocked_range<size_t>(0,
num_particles),
1401 [&](tbb::blocked_range<size_t> r) {
1402 for (auto i = r.begin(); i < r.end(); ++i)
1403 buf_forces[i] += x[i];
1404 });
1405 }
1406
1407 auto& buf_potentials = *buf_potentials_ets.begin();
1408 for (auto it = buf_potentials_ets.begin() + 1; it < buf_potentials_ets.end(); it++)
1409 {
1410 const auto& x = *it;
1411 tbb::parallel_for(tbb::blocked_range<size_t>(0,
num_particles),
1412 [&](tbb::blocked_range<size_t> r) {
1413 for (auto i = r.begin(); i < r.end(); ++i)
1414 buf_potentials[i] += x[i];
1415 });
1416 }
1417
1418 auto& fx_simd = *fx_simd_ets.begin();
1419 auto& fy_simd = *fy_simd_ets.begin();
1420 auto& fz_simd = *fz_simd_ets.begin();
1421 auto& phi_simd = *phi_simd_ets.begin();
1422
1423 tbb::parallel_for(
size_t(0),
tree->level_sizes.back(), [&](
size_t i) {
1424 auto [a, b] = leaves[i].source_range;
1425 auto sz = (b - a + detail::avx_vec_length - 1) / detail::avx_vec_length;
1426
1427 auto fx = fx_simd.data() + shifts[i];
1428 auto fy = fy_simd.data() + shifts[i];
1429 auto fz = fz_simd.data() + shifts[i];
1430 auto phi = phi_simd.data() + shifts[i];
1431 double bufVec[detail::avx_vec_length];
1432 for (int j = 0; j < sz; ++j)
1433 {
1434 for (auto it = fx_simd_ets.begin() + 1; it < fx_simd_ets.end(); it++) {
1435 const auto& x = it->data() + shifts[i];
1436 fx[j] = avx_add(fx[j], x[j]);
1437 }
1438 for (auto it = fy_simd_ets.begin() + 1; it < fy_simd_ets.end(); it++) {
1439 const auto& x = it->data() + shifts[i];
1440 fy[j] = avx_add(fy[j], x[j]);
1441 }
1442 for (auto it = fz_simd_ets.begin() + 1; it < fz_simd_ets.end(); it++) {
1443 const auto& x = it->data() + shifts[i];
1444 fz[j] = avx_add(fz[j], x[j]);
1445 }
1446 for (auto it = phi_simd_ets.begin() + 1; it < phi_simd_ets.end(); it++) {
1447 const auto& x = it->data() + shifts[i];
1448 phi[j] = avx_add(phi[j], x[j]);
1449 }
1450
1451 auto k = a + detail::avx_vec_length * j;
1452 avx_store(bufVec, fx[j]);
1453 for (int s = 0; s < detail::avx_vec_length; ++s)
1454 if (k + s < b) buf_forces[k + s][0] += bufVec[s];
1455 avx_store(bufVec, fy[j]);
1456 for (int s = 0; s < detail::avx_vec_length; ++s)
1457 if (k + s < b) buf_forces[k + s][1] += bufVec[s];
1458 avx_store(bufVec, fz[j]);
1459 for (int s = 0; s < detail::avx_vec_length; ++s)
1460 if (k + s < b) buf_forces[k + s][2] += bufVec[s];
1461 avx_store(bufVec, phi[j]);
1462 for (int s = 0; s < detail::avx_vec_length; ++s)
1463 if (k + s < b) buf_potentials[k + s] += bufVec[s];
1464 }
1465 });
1466
1467#ifdef FMM_MPI
1468 AllReduce(buf_forces.data(), buf_forces.size());
1469 AllReduce(buf_potentials.data(), buf_potentials.size());
1470#endif
1471
1472 const auto& index_mapping =
tree->positions_map;
1473
1474 tbb::parallel_for(
size_t(0),
targets_num, [&](
size_t i) {
1475 forces[i] = buf_forces[index_mapping[i]];
1476 potentials[i] = buf_potentials[index_mapping[i]];
1477 });
1478}
std::vector< double > potentials
void avx_inv_dr(const __m512d &oneVec, const __m512d &epsVec, const __m512d &x_target, const __m512d &y_target, const __m512d &z_target, __m512d &temp1, __m512d &temp2, __m512d &temp3, __m512d &dx, __m512d &dy, __m512d &dz, __m512d &invdr, __m512d &invdr2)
vec_type avx_add(const vec_type &v1, const vec_type &v2)
vec_type avx_mul(const vec_type &v1, const vec_type &v2)
constexpr double FORCE_EPS2
vec_type avx_sub(const vec_type &v1, const vec_type &v2)
double avx_hsum(__m128d v)
void avx_set_constant(vec_type &vec, double val)