233 double t1 = omp_get_wtime();
235 auto& leaves = tree->levels.back();
236 auto& leaves_outer = outer_expansions.back();
238 auto [Begin, End] = LocalPart(0, tree->level_sizes[tree->tree_depth - 1]);
240 size_t Begin = 0, End = tree->level_sizes[tree->tree_depth - 1];
242 tbb::enumerable_thread_specific<std::vector<std::complex<double>>> eim_ets((std::vector<std::complex<double>>(N)));
243 tbb::enumerable_thread_specific<treevector<double>> Pnm_ets(
treevector<double>(N + 1));
244 tbb::parallel_for(Begin, End, [&](
size_t i) {
246 auto& eim = eim_ets.local();
247 auto& Pnm = Pnm_ets.local();
248 Multipole(leaves_outer.data() + Nx2 * i, cell.source_range, cell.center, Pnm.data(), eim.data());
252 std::vector<complex_value> buf(leaves_outer.size());
253 std::vector<int> sizes(NProc());
254 std::vector<int> displs(NProc());
255 for (
int i = 0; i < NProc(); ++i)
257 sizes[i] = Nx2 * LocalPart(0, tree->level_sizes[tree->tree_depth - 1], i);
259 for (
int i = 1; i < NProc(); ++i)
261 displs[i] = displs[i - 1] + sizes[i - 1];
264 MPI_Datatype MPI_COMPLEX_VALUE;
265 MPI_Type_contiguous(
sizeof(
complex_value) /
sizeof(std::complex<double>), MPI_COMPLEX16, &MPI_COMPLEX_VALUE);
266 MPI_Type_commit(&MPI_COMPLEX_VALUE);
268 MPI_Allgatherv(leaves_outer.data() + displs[MyID()], sizes[MyID()], MPI_COMPLEX_VALUE,
269 buf.data(), sizes.data(), displs.data(), MPI_COMPLEX_VALUE, MPI_COMM_WORLD);
270 std::swap(buf, leaves_outer);
275 t1 = omp_get_wtime();
277 for (
int i = (
int)tree->tree_depth - 2; i >= 0; --i)
279 auto& top_level = tree->levels[i];
280 auto& top_outer = outer_expansions[i];
281 const auto& bottom_level = tree->levels[i + 1];
282 const auto& bottom_outer = outer_expansions[i + 1];
284 auto [Begin, End] = LocalPart(0, tree->level_sizes[i]);
286 size_t Begin = 0, End = tree->level_sizes[i];
288 tbb::parallel_for(Begin, End, [&](
size_t j) {
290 auto& work1 = local_work1.local();
291 auto& work2 = local_work2.local();
292 const auto& [begin, end] = cell.source_range;
294 for (
auto k = begin; k < end; ++k)
296 const auto& children_cell = bottom_level[k];
297 M2M(bottom_outer.data() + k * Nx2, children_cell.center - cell.center, top_outer.data() + j * Nx2, work1, work2);
303 std::vector<complex_value> buf(top_outer.size());
304 for (
int j = 0; j < NProc(); ++j)
306 sizes[j] = Nx2 * LocalPart(0, tree->level_sizes[i], j);
308 for (
int j = 1; j < NProc(); ++j)
310 displs[j] = displs[j - 1] + sizes[j - 1];
312 MPI_Allgatherv(top_outer.data() + displs[MyID()], sizes[MyID()], MPI_COMPLEX_VALUE,
313 buf.data(), sizes.data(), displs.data(), MPI_COMPLEX_VALUE, MPI_COMM_WORLD);
314 std::swap(buf, top_outer);
326 std::vector<int> sizes(NProc());
327 std::vector<int> displs(NProc());
330 for (
int i = 2; i < tree->tree_depth; ++i)
332 auto& bottom_level = tree->levels[i];
333 auto& bottom_inner = inner_expansions[i];
334 const auto& bottom_outer = outer_expansions[i];
336 auto [Begin, End] = LocalPart(0, tree->level_sizes[i]);
338 size_t Begin = 0, End = tree->level_sizes[i];
340 tbb::parallel_for(Begin, End, [&](
size_t j) {
342 auto& work1 = local_work1.local();
343 auto& work2 = local_work2.local();
345 for (
const auto& idx : cell.farneighbours)
347 const auto& far_neighbour = bottom_level[idx];
348 M2L(bottom_outer.data() + idx * Nx2, far_neighbour.center - cell.center, bottom_inner.data() + Nx2 * j, work1, work2);
354 MPI_Datatype MPI_COMPLEX_VALUE;
355 MPI_Type_contiguous(
sizeof(
complex_value) /
sizeof(std::complex<double>), MPI_COMPLEX16, &MPI_COMPLEX_VALUE);
356 MPI_Type_commit(&MPI_COMPLEX_VALUE);
358 std::vector<complex_value> buf(bottom_inner.size());
359 for (
int j = 0; j < NProc(); ++j)
361 sizes[j] = Nx2 * LocalPart(0, tree->level_sizes[i], j);
363 for (
int j = 1; j < NProc(); ++j)
365 displs[j] = displs[j - 1] + sizes[j - 1];
367 MPI_Allgatherv(bottom_inner.data() + displs[MyID()], sizes[MyID()], MPI_COMPLEX_VALUE,
368 buf.data(), sizes.data(), displs.data(), MPI_COMPLEX_VALUE, MPI_COMM_WORLD);
369 std::swap(buf, bottom_inner);
374 for (
int i = 3; i < tree->tree_depth; ++i)
376 const auto& top_level = tree->levels[i - 1];
377 const auto& top_inner = inner_expansions[i - 1];
378 auto& bottom_level = tree->levels[i];
379 auto& bottom_inner = inner_expansions[i];
381 auto [Begin, End] = LocalPart(0, tree->level_sizes[i]);
383 size_t Begin = 0, End = tree->level_sizes[i];
385 tbb::parallel_for(Begin, End, [&](
size_t j) {
387 auto& work1 = local_work1.local();
388 auto& work2 = local_work2.local();
389 auto& parent_cell = top_level[cell.parent];
390 L2L(top_inner.data() + Nx2 * cell.parent, parent_cell.center - cell.center, bottom_inner.data() + Nx2 * j, work1, work2);
395 MPI_Datatype MPI_COMPLEX_VALUE;
396 MPI_Type_contiguous(
sizeof(
complex_value) /
sizeof(std::complex<double>), MPI_COMPLEX16, &MPI_COMPLEX_VALUE);
397 MPI_Type_commit(&MPI_COMPLEX_VALUE);
399 std::vector<complex_value> buf(bottom_inner.size());
400 for (
int j = 0; j < NProc(); ++j)
402 sizes[j] = Nx2 * LocalPart(0, tree->level_sizes[i], j);
404 for (
int j = 1; j < NProc(); ++j)
406 displs[j] = displs[j - 1] + sizes[j - 1];
408 MPI_Allgatherv(bottom_inner.data() + displs[MyID()], sizes[MyID()], MPI_COMPLEX_VALUE,
409 buf.data(), sizes.data(), displs.data(), MPI_COMPLEX_VALUE, MPI_COMM_WORLD);
410 std::swap(buf, bottom_inner);
473 using namespace std::literals;
475 tbb::enumerable_thread_specific<Vector3d> tmp_force_ets, dr_ets;
476 tbb::enumerable_thread_specific<Matrix3d> matrix_ets;
477 tbb::enumerable_thread_specific<std::vector<std::complex<double>>> eim_ets(std::vector<std::complex<double>>(N + 1));
478 tbb::enumerable_thread_specific<treevector<double>> Pnm_ets(
treevector<double>(N + 3));
479 tbb::enumerable_thread_specific<std::vector<Vector3d>> buf_forces_ets((std::vector<Vector3d>(num_particles)));
480 tbb::enumerable_thread_specific<std::vector<Vector3d>> buf_moments_ets((std::vector<Vector3d>(num_particles)));
482 const auto& leaves = tree->levels.back();
483 const auto& leaves_inner = inner_expansions.back();
484 const auto& particles = tree->particles;
487 auto [Begin, End] = LocalPart(0, tree->level_sizes.back());
489 size_t Begin = 0, End = tree->level_sizes.back();
492 decltype(&TreeCell_t::source_range) range_ptr;
493 tree->targets_num == 0 ? range_ptr = &TreeCell_t::source_range : range_ptr = &TreeCell_t::target_range;
495 tbb::parallel_for(Begin, End, [&](
size_t leaf_idx) {
496 auto& tmp_force = tmp_force_ets.local();
497 auto& dr = dr_ets.local();
498 auto& eim = eim_ets.local();
499 auto& Pnm = Pnm_ets.local();
500 auto& m_temp = matrix_ets.local();
501 auto& buf_forces = buf_forces_ets.local();
502 auto& buf_moments = buf_moments_ets.local();
505 const auto& cell = leaves[leaf_idx];
506 const auto& [p1_begin, p1_end] = cell.*range_ptr;
508 for (
auto j = p1_begin; j < p1_end; ++j)
512 for (
auto k = p1_begin; k < p1_end; ++k)
526 buf_moments[j] -= (
dot(particles[j].q, particles[k].q) * lambdaA /
FORCE_EPS4 +
dot(particles[k].q, dr) *
dot(particles[j].q, dr) * etaA /
FORCE_EPS6) / std::max(Ldr, 1e-10) * dr + \
527 (
dot(particles[j].q, dr) * particles[k].q +
dot(particles[k].q, dr) * particles[j].q) * eta /
FORCE_EPS5;
532 auto Ldr2 = Ldr * Ldr;
533 auto Ldr3 = Ldr2 * Ldr;
534 auto Ldr4 = Ldr2 * Ldr2;
535 auto Ldr5 = Ldr3 * Ldr2;
536 auto Ldr6 = Ldr3 * Ldr3;
537 buf_forces[j] += particles[k].q / (-Ldr3) + 3 *
dot(particles[k].q, dr) * dr / Ldr5;
538 buf_moments[j] -= (3 *
dot(particles[j].q, particles[k].q) / Ldr4 - 15 *
dot(particles[k].q, dr) *
dot(particles[j].q, dr) / Ldr6 ) / std::max(Ldr, 1e-10) * dr + \
539 3 * (
dot(particles[j].q, dr) * particles[k].q +
dot(particles[k].q, dr) * particles[j].q) / Ldr5;
549 for (
const auto& idx : cell.closeneighbours)
551 const auto& neighbour_cell = leaves[idx];
552 const auto& [p2_begin, p2_end] = neighbour_cell.source_range;
553 for (
auto k = p2_begin; k < p2_end; ++k)
567 buf_moments[j] -= ((
dot(particles[j].q, particles[k].q) * lambdaA /
FORCE_EPS4 +
dot(particles[k].q, dr) *
dot(particles[j].q, dr) * etaA /
FORCE_EPS6) / std::max(Ldr, 1e-10) * dr + \
568 (
dot(particles[j].q, dr) * particles[k].q +
dot(particles[k].q, dr) * particles[j].q) * eta /
FORCE_EPS5);
572 auto Ldr2 = Ldr * Ldr;
573 auto Ldr3 = Ldr2 * Ldr;
574 auto Ldr4 = Ldr2 * Ldr2;
575 auto Ldr5 = Ldr3 * Ldr2;
576 auto Ldr6 = Ldr3 * Ldr3;
577 buf_forces[j] += particles[k].q / (-Ldr3) + 3 *
dot(particles[k].q, dr) * dr / Ldr5;
578 buf_moments[j] -= (3 *
dot(particles[j].q, particles[k].q) / Ldr4 - 15 *
dot(particles[k].q, dr) *
dot(particles[j].q, dr) / Ldr6) / std::max(Ldr, 1e-10) * dr + \
579 3 * (
dot(particles[j].q, dr) * particles[k].q +
dot(particles[k].q, dr) * particles[j].q) / Ldr5;
593 const auto& inner = leaves_inner.data() + leaf_idx * Nx2;
613 std::complex<double> Ynm, SphDTheta, SphDPhi,
614 SphDThetaTheta, SphDThetaPhi, SphDPhiPhi,
615 SphDThetaThetaTheta, SphDThetaThetaPhi, SphDThetaPhiPhi, SphDPhiPhiPhi;
617 double x, y, rn, s1, s2, c1, c2, sd1, sd2, cd1, cd2;
635 for (
auto j = p1_begin; j < p1_end; ++j)
709 memset(m_temp.data(), 0,
sizeof(
Matrix3d));
726 for (
int m = 0; m <= N; ++m)
729 eim[m] = std::complex<double>(cos(x), sin(x));
736 memset(t_temp[0].data(), 0, 3*
sizeof(
Matrix3d));
739 memset(u_temp[0][0].data(), 0, 9 *
sizeof(
Matrix3d));
741 for (
int n = 1; n < N; ++n)
743 for (
int m = 0; m <= n; ++m)
745 Ynm = Pnm(n, m) * eim[m] *
Knm(n, m);
746 coef = inner[n * (n + 1) / 2 + m] * rn;
747 coefmulr = coef * dr[0];
749 SphDTheta =
Knm(n, m) * eim[m] * ((1 + n - m) * Pnm(n + 1, m) - (n + 1) * x * Pnm(n, m)) / y;
751 SphDPhi = double(m) * 1.0i * Ynm;
754 SphDThetaTheta =
Knm(n, m) * eim[m] *
756 (m * m + (1 + n) * (2 + n) - (3 + 2 * n) * m) * Pnm(n + 2, m)
757 - 2.0 * (n + 2) * (1 + n - m) * x * Pnm(n + 1, m)
758 + (n + 1) * (1 + (n + 1) * x * x) * Pnm(n, m)
761 SphDThetaPhi = SphDTheta * 1.0i * (double)m;
763 SphDPhiPhi = double(-m * m) * Ynm;
766 SphDThetaThetaTheta =
Knm(n, m) * eim[m] *
768 (2 + n - m) * (1 + n - m) * ((3 + n - m) * Pnm(n + 3, m) - 3 * (3 + n) * x * Pnm(n + 2, m))
769 + 0.5 * ((3 * n * n + 18 * n + 23) + (3 * n * n + 12 * n + 13) * (x * x - y * y)) * (1 + n - m) * Pnm(n + 1, m)
770 - 0.5 * x * (n + 1) * ((n * n + 8 * n + 11) + (n + 1) * (n + 1) * (x * x - y * y)) * Pnm(n, m)
773 SphDThetaThetaPhi = SphDThetaTheta * 1.0i * (double)m;
775 SphDThetaPhiPhi = double(-m * m) * SphDTheta;
777 SphDPhiPhiPhi = double(-m * m * m) * 1.0i * Ynm;
781 double times = (m ? 2.0 : 1.0);
783 for (
int s = 0; s < 3; ++s)
785 m_temp[s][0] -= times * (double(n) * coef[s] * Ynm).
real();
786 m_temp[s][1] -= times * (coefmulr[s] * SphDTheta).
real();
787 m_temp[s][2] -= times * (coefmulr[s] * SphDPhi).
real();
789 t_temp[s][0][0] -= times * (double(n * (n - 1)) * coef[s] * Ynm).
real();
790 t_temp[s][0][1] -= times * (double(n) * coef[s] * SphDTheta).
real();
791 t_temp[s][0][2] -= times * (double(n) * coef[s] * SphDPhi).
real();
792 t_temp[s][1][1] -= times * (coefmulr[s] * SphDThetaTheta).
real();
793 t_temp[s][1][2] -= times * (coefmulr[s] * SphDThetaPhi).
real();
794 t_temp[s][2][2] -= times * (coefmulr[s] * SphDPhiPhi).
real();
796 u_temp[s][0][0][0] -= times * (double(n * (n - 1) * (n - 2)) * coef[s] * Ynm).
real();
797 u_temp[s][0][0][1] -= times * (double(n * (n - 1)) * coef[s] * SphDTheta).
real();
798 u_temp[s][0][0][2] -= times * (double(n * (n - 1)) * coef[s] * SphDPhi).
real();
799 u_temp[s][0][1][1] -= times * (double(n) * coef[s] * SphDThetaTheta).
real();
800 u_temp[s][0][1][2] -= times * (double(n) * coef[s] * SphDThetaPhi).
real();
801 u_temp[s][0][2][2] -= times * (double(n) * coef[s] * SphDPhiPhi).
real();
802 u_temp[s][1][1][1] -= times * (coefmulr[s] * SphDThetaThetaTheta).
real();
803 u_temp[s][1][1][2] -= times * (coefmulr[s] * SphDThetaThetaPhi).
real();
804 u_temp[s][1][2][2] -= times * (coefmulr[s] * SphDThetaPhiPhi).
real();
805 u_temp[s][2][2][2] -= times * (coefmulr[s] * SphDPhiPhiPhi).
real();
811 for (
int s = 0; s < 3; ++s)
815 t_temp[s][0][0] /= dr[0];
817 u_temp[s][0][0][0] /= (dr[0] * dr[0]);
818 u_temp[s][0][0][1] /= dr[0];
819 u_temp[s][0][0][2] /= dr[0];
822 t_temp[s][1][0] = t_temp[s][0][1];
823 t_temp[s][2][0] = t_temp[s][0][2];
824 t_temp[s][2][1] = t_temp[s][1][2];
826 u_temp[s][0][1][0] = u_temp[s][1][0][0] = u_temp[s][0][0][1];
827 u_temp[s][0][2][0] = u_temp[s][2][0][0] = u_temp[s][0][0][2];
828 u_temp[s][1][1][0] = u_temp[s][1][0][1] = u_temp[s][0][1][1];
829 u_temp[s][1][2][0] = u_temp[s][2][0][1] = u_temp[s][2][1][0] = u_temp[s][0][2][1] = u_temp[s][1][0][2] = u_temp[s][0][1][2];
830 u_temp[s][2][2][0] = u_temp[s][2][0][2] = u_temp[s][0][2][2];
831 u_temp[s][1][2][1] = u_temp[s][2][1][1] = u_temp[s][1][1][2];
832 u_temp[s][2][1][2] = u_temp[s][2][2][1] = u_temp[s][1][2][2];
840 c1 = cos(dr[1]); s1 = sin(dr[1]); c2 = cos(dr[2]); s2 = sin(dr[2]);
841 cd1 = cos(2.0 * dr[1]); sd1 = sin(2.0 * dr[1]); cd2 = cos(2.0 * dr[2]); sd2 = sin(2.0 * dr[2]);
843 double ct1 = cos(3.0 * dr[1]), ct2 = cos(3.0 * dr[2]), cq1 = cos(4.0 * dr[1]);
844 double st1 = sin(3.0 * dr[1]), st2 = sin(3.0 * dr[2]);
846 double s1sq = s1 * s1, c1sq = c1 * c1, c2sq = c2 * c2, s2sq = s2 * s2, drsq = dr[0] * dr[0], drcu = drsq * dr[0];
848 auto& f = buf_forces[j];
849 auto& m = buf_moments[j];
853 JacT[0] = { s1 * c2, c1 * c2 / dr[0], -s2 / (s1 * dr[0]) };
854 JacT[1] = { s1 * s2, c1 * s2 / dr[0], c2 / (s1 * dr[0]) };
855 JacT[2] = { c1, -s1 / dr[0], 0.0 };
859 for (
int s = 0; s < 3; ++s)
861 mm(JacT, t_temp[s], pm[s]);
862 mmt(pm[s], JacT, t_copy[s]);
865 double izn = 1.0 / (s1sq * drsq);
868 HCoordTransform[0][0] = { (c1sq + s1sq * s2sq) / dr[0], -(c2 * s1sq * s2) / dr[0], -(c1 * c2 * s1) / dr[0] };
869 HCoordTransform[0][1] = { HCoordTransform[0][0][1], (c1sq + c2sq * s1sq) / dr[0], -(c1 * s1 * s2) / dr[0]};
870 HCoordTransform[0][2] = { HCoordTransform[0][0][2], HCoordTransform[0][1][2], s1sq / dr[0]};
872 HCoordTransform[1][0] = { c1 * (c2sq * cd1 - cd2) / (s1 * drsq), c1 * (-2.0 + cd1) * sd2 / (2.0 * s1 * drsq), -(c2 * cd1) / drsq };
873 HCoordTransform[1][1] = { HCoordTransform[1][0][1], c1 * (cd2 + cd1 * s2sq) / (s1 * drsq), -cd1 * s2 / drsq };
874 HCoordTransform[1][2] = { HCoordTransform[1][0][2], HCoordTransform[1][1][2], sd1 / drsq };
876 HCoordTransform[2][0] = { sd2 * izn, -cd2 * izn, 0.0 };
877 HCoordTransform[2][1] = { HCoordTransform[2][0][1], -HCoordTransform[2][0][0], 0.0 };
878 HCoordTransform[2][2] = { 0.0, 0.0, 0.0 };
884 double A = -3 * c2 * s1 * (c1sq + s1sq * s2sq) / drsq;
885 double P = -3 * s2 * s1 * (c1sq + s1sq * c2sq) / drsq;
886 double B = s1 * s2 * (-1 + 3 * c2sq * s1sq) / drsq;
887 double G = s1 * c2 * (-1 + 3 * s2sq * s1sq) / drsq;
888 double F = c1 * (-c1sq + 0.5 * (1 + 3 * cd2) * s1sq) / drsq;
889 double Q = c1 * (-c1sq + 0.5 * (1 - 3 * cd2) * s1sq) / drsq;
890 double J = 0.5 * (1 + 3 * cd1) * s1 * c2 / drsq;
891 double R = 0.5 * (1 + 3 * cd1) * s1 * s2 / drsq;
892 double H = 3 * c1 * c2 * s1sq * s2 / drsq;
893 double M = -3 * c1 * s1sq / drsq;
895 TCoordTransform[0][0] =
Matrix3d({ {A, B, F}, {B, G, H}, {F, H, J} });
896 TCoordTransform[0][1] =
Matrix3d({ {B, G, H}, {G, P, Q}, {H, Q, R} });
897 TCoordTransform[0][2] =
Matrix3d({ {F, H, J}, {H, Q, R}, {J, R, M} });
901 double A = 0.5 * c2 * (-4 + cq1 + (8 - 6 * cd1 + cq1) * cd2) * c1 / (s1sq * drcu);
902 double B = 0.25 * ((-2 * cd1 + cq1) * s2 + (8 - 6 * cd1 + cq1) * st2) * c1 / (s1sq * drcu);
903 double G = (-cq1 * c2sq + cd1 * cd2) / (s1 * drcu);
904 double D = (2 - 4 * cd2 + cd1 * (-2 + 3 * cd2) + cq1 * s2sq) * c2 * c1 / (s1sq * drcu);
905 double E = (2 * cd1 - cq1) * c2 * s2 / (s1 * drcu);
906 double H = 2 * ct1 * c2 / drcu;
907 double K = 0.25 * (4 * cq1 * s2sq * s2 - 8 * st2 + 6 * cd1 * (-s2 + st2)) * c1 / (s1sq * drcu);
908 double L = -(cd1 * cd2 + cq1 * s2sq) / (s1 * drcu);
909 double M = 2 * ct1 * s2 / drcu;
910 double N = -2 * st1 / drcu;
912 TCoordTransform[1][0] =
Matrix3d({ {A, B, G}, {B, D, E}, {G, E, H} });
913 TCoordTransform[1][1] =
Matrix3d({ {B, D, E}, {D, K, L}, {E, L, M} });
914 TCoordTransform[1][2] =
Matrix3d({ {G, E, H}, {E, L, M}, {H, M, N} });
918 double iz = 2 / (s1sq * s1 * drcu);
922 TCoordTransform[2][0] =
Matrix3d({ {-P, S, 0}, {S, P, 0}, {0, 0, 0} });
923 TCoordTransform[2][1] =
Matrix3d({ {S, P, 0}, {P, -S, 0}, {0, 0, 0} });
924 TCoordTransform[2][2] =
Matrix3d({ {0, 0, 0}, {0, 0, 0}, {0, 0, 0} });
929 for (
int s = 0; s < 3; ++s)
931 vt(m_temp[s], HCoordTransform, add[s]);
932 for (
int ii = 0; ii < 3; ++ii)
933 t_copy[s][ii] += add[s][ii];
936 auto S1 = [&](
int s,
int ii,
int jj,
int kk)
939 for (
int ll = 0; ll < 3; ++ll)
940 for (
int mm = 0;
mm < 3; ++
mm)
941 for (
int nn = 0; nn < 3; ++nn)
942 res += JacT[ii][ll] * JacT[jj][
mm] * JacT[kk][nn] * u_temp[s][ll][
mm][nn];
946 auto S2 = [&](
int s,
int ii,
int jj,
int kk)
949 for (
int ll = 0; ll < 3; ++ll)
950 for (
int mm = 0;
mm < 3; ++
mm)
951 res += (HCoordTransform[ll][ii][jj] * JacT[kk][
mm] + \
952 HCoordTransform[ll][ii][kk] * JacT[jj][
mm] + \
953 HCoordTransform[ll][jj][kk] * JacT[ii][
mm]) * t_temp[s][ll][
mm];
957 auto S3 = [&](
int s,
int ii,
int jj,
int kk)
960 for (
int ll = 0; ll < 3; ++ll)
961 res += TCoordTransform[ll][ii][jj][kk] * m_temp[s][ll];
966 memset(u_copy[0][0].data(), 0, 9 *
sizeof(
Matrix3d));
967 for (
int s = 0; s < 3; ++s)
969 for (
int ii = 0; ii < 3; ++ii)
970 for (
int jj = 0; jj < 3; ++jj)
971 for (
int kk = 0; kk < 3; ++kk)
973 u_copy[s][ii][jj][kk] += S1(s, ii, jj, kk) + S2(s, ii, jj, kk) + S3(s, ii, jj, kk);
982 f[0] -= t_copy[0][0][0] + t_copy[1][0][1] + t_copy[2][0][2];
983 f[1] -= t_copy[0][1][0] + t_copy[1][1][1] + t_copy[2][1][2];
984 f[2] -= t_copy[0][2][0] + t_copy[1][2][1] + t_copy[2][2][2];
988 for (
int line = 0; line < 3; ++line)
989 G3[line] = (u_copy[0][0][line] + u_copy[1][1][line] + u_copy[2][2][line]);
997 auto& buf_forces = *buf_forces_ets.begin();
998 for (
auto it = buf_forces_ets.begin() + 1; it < buf_forces_ets.end(); it++)
1000 const auto& x = *it;
1001 tbb::parallel_for(tbb::blocked_range<size_t>(0, num_particles),
1002 [&](tbb::blocked_range<size_t> r) {
1003 for (
auto i = r.begin(); i < r.end(); ++i)
1005 buf_forces[i] += x[i];
1010 auto& buf_moments = *buf_moments_ets.begin();
1011 for (
auto it = buf_moments_ets.begin() + 1; it < buf_moments_ets.end(); it++)
1013 const auto& x = *it;
1014 tbb::parallel_for(tbb::blocked_range<size_t>(0, num_particles),
1015 [&](tbb::blocked_range<size_t> r) {
1016 for (
auto i = r.begin(); i < r.end(); ++i)
1018 buf_moments[i] += x[i];
1025 AllReduce(buf_forces.data(), buf_forces.size());
1026 AllReduce(buf_moments.data(), buf_moments.size());
1029 const auto& index_mapping = tree->positions_map;
1031 tbb::parallel_for(
size_t(0), targets_num, [&](
size_t i) {
1032 forces[i] = buf_forces[index_mapping[i]];
1033 moments[i] = buf_moments[index_mapping[i]];
1042 using namespace std::literals;
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)));
1051 const auto& leaves = tree->levels.back();
1052 const auto& leaves_inner = inner_expansions.back();
1053 const auto& particles = tree->particles;
1056 auto [Begin, End] = LocalPart(0, tree->level_sizes.back());
1058 size_t Begin = 0, End = tree->level_sizes.back();
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;
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;
1069 std::unique_ptr<size_t[]> shifts(
new size_t[tree->level_sizes.back() + 1]);
1071 for (
size_t i = 0; i < tree->level_sizes.back(); ++i)
1073 auto [a, b] = leaves[i].source_range;
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]);
1082 double t1 = omp_get_wtime();
1083 tbb::parallel_for(
size_t(0), tree->level_sizes.back(), [&](
size_t i)
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)
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);
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)));
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();
1162 const auto& cell = leaves[leaf_idx];
1163 const auto& [p1_begin, p1_end] = cell.*range_ptr;
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;
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];
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];
1178 for (
auto j = p1_begin; j < p1_end; ++j)
1182 if (range_ptr == &TreeCell_t::target_range)
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>();
1194 const auto& [p2_begin, p2_end] = cell.source_range;
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);
1212 for (
const auto& idx : cell.closeneighbours)
1214 const auto& neighbour_cell = leaves[idx];
1215 const auto& [p2_begin, p2_end] = neighbour_cell.source_range;
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];
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);
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>();
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);
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);
1281 q_invdr =
avx_mul(q_target, invdr);
1282 q_invdr3 =
avx_mul(invdr2, q_invdr);
1287 phi_leaf[i] =
avx_add(q_invdr, phi_leaf[i]);
1290 for (
const auto& idx : cell.closeneighbours)
1292 const auto& neighbour_cell = leaves[idx];
1293 const auto& [p2_begin, p2_end] = neighbour_cell.source_range;
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];
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];
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);
1320 q_invdr =
avx_mul(q_target, invdr);
1321 q_invdr3 =
avx_mul(invdr2, q_invdr);
1326 phi_nbr[i] =
avx_add(q_invdr, phi_nbr[i]);
1333 buf_forces[j] += tmp_force;
1334 buf_potentials[j] +=
avx_hsum(pot);
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;
1340 for (
auto j = p1_begin; j < p1_end; ++j)
1342 auto& particle = particles[j];
1343 memset(m_temp.data(), 0,
sizeof(
Vector3d));
1344 dr =
DecToSph(particle.center - cell.center);
1348 for (
int m = 0; m <= N; ++m)
1351 eim[m] = std::complex<double>(cos(x), sin(x));
1359 for (
int n = 0; n < N; ++n)
1361 for (
int m = 0; m <= n; ++m)
1363 Ynm = Pnm(n, m) * eim[m] *
Knm(n, m);
1364 coef = inner[n * (n + 1) / 2 + m] * rn;
1366 phi += (dr[0] * Ynm * coef).
real();
1368 phi += (dr[0] * Ynm * coef).
real();
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;
1373 m_temp[0] -= (double(n) * coef * Ynm).
real();
1374 m_temp[1] -= (coef * SphDTheta).
real();
1375 m_temp[2] -= (coef * SphDPhi).
real();
1378 m_temp[0] -= (double(n) * coef * Ynm).
real();
1379 m_temp[1] -= (coef * SphDTheta).
real();
1380 m_temp[2] -= (coef * SphDPhi).
real();
1385 buf_potentials[j] += phi;
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;
1394 std::cout << omp_get_wtime() - t1 << std::endl;
1396 auto& buf_forces = *buf_forces_ets.begin();
1397 for (
auto it = buf_forces_ets.begin() + 1; it < buf_forces_ets.end(); it++)
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];
1407 auto& buf_potentials = *buf_potentials_ets.begin();
1408 for (
auto it = buf_potentials_ets.begin() + 1; it < buf_potentials_ets.end(); it++)
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];
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();
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;
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)
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]);
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]);
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]);
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]);
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];
1468 AllReduce(buf_forces.data(), buf_forces.size());
1469 AllReduce(buf_potentials.data(), buf_potentials.size());
1472 const auto& index_mapping = tree->positions_map;
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]];