VM2D 1.14
Vortex methods for 2D flows simulation
Loading...
Searching...
No Matches
fmm::FastMultipole3d< value > Class Template Reference

#include <multipole3d.h>

Collaboration diagram for fmm::FastMultipole3d< value >:

Public Types

using complex_value = std::conditional_t< std::is_same_v< value, double >, std::complex< double >, Vector3cd >
 
using particle_t = particle< Vector3d, value >
 
using MortonTree_t = MortonTree< 3, point3d, value >
 
using TreeCell_t = typename MortonTree_t::TreeCell_t
 

Public Member Functions

 FastMultipole3d (const std::vector< particle_t > &particles, double eps=1.e-8, int N=FMM_AUTO, int tree_depth=FMM_AUTO)
 
 FastMultipole3d (std::vector< particle_t > &&particles, double eps=1.e-8, int N=FMM_AUTO, int tree_depth=FMM_AUTO)
 
 FastMultipole3d (const std::vector< particle_t > &source_particles, const std::vector< particle_t > &target_particles, double eps=1.e-8, int N=FMM_AUTO, int tree_depth=FMM_AUTO)
 

Public Attributes

std::vector< Vector3dforces
 
std::vector< Vector3dmoments
 
std::vector< double > potentials
 
std::vector< Vector3ddipolevelocities
 

Private Member Functions

void Solve (std::vector< particle_t > &&particles, double eps=1.e-8, int N=FMM_AUTO, int tree_depth=FMM_AUTO)
 
void Multipole (complex_value *a, const std::pair< size_t, size_t > &particle_range, const Vector3d &z0, double *Pnm, std::complex< double > *eim)
 
void M2M (const complex_value *a, const Vector3d &p0, complex_value *b, std::vector< complex_value > &work1, std::vector< complex_value > &work2)
 
void M2L (const complex_value *a, const Vector3d &p0, complex_value *b, std::vector< complex_value > &work1, std::vector< complex_value > &work2)
 
void L2L (const complex_value *a, const Vector3d &p0, complex_value *b, std::vector< complex_value > &work1, std::vector< complex_value > &work2)
 
void RotateY (const complex_value *a, const std::vector< double > &dmatrix, complex_value *res)
 
void RotateZ (const complex_value *a, const std::vector< std::complex< double > > &rotation_exp, complex_value *b)
 
void RotateZ (complex_value *a, const std::vector< std::complex< double > > &rotation_exp)
 
void Upward ()
 
void Downward ()
 
void ComputeLeaves ()
 
void Prepare ()
 
void ComputeLeaves ()
 
void ComputeLeaves ()
 

Private Attributes

std::shared_ptr< MortonTree_ttree
 
std::vector< std::vector< complex_value > > outer_expansions
 
std::vector< std::vector< complex_value > > inner_expansions
 
size_t tree_depth
 
int N
 
int Nx2
 
size_t num_particles
 
size_t targets_num
 
tbb::enumerable_thread_specific< std::vector< complex_value > > local_work1
 
tbb::enumerable_thread_specific< std::vector< complex_value > > local_work2
 

Detailed Description

template<typename value>
class fmm::FastMultipole3d< value >

Definition at line 11 of file multipole3d.h.

Member Typedef Documentation

◆ complex_value

template<typename value >
using fmm::FastMultipole3d< value >::complex_value = std::conditional_t<std::is_same_v<value, double>, std::complex<double>, Vector3cd>

Definition at line 14 of file multipole3d.h.

◆ MortonTree_t

template<typename value >
using fmm::FastMultipole3d< value >::MortonTree_t = MortonTree<3, point3d, value>

Definition at line 16 of file multipole3d.h.

◆ particle_t

template<typename value >
using fmm::FastMultipole3d< value >::particle_t = particle<Vector3d, value>

Definition at line 15 of file multipole3d.h.

◆ TreeCell_t

template<typename value >
using fmm::FastMultipole3d< value >::TreeCell_t = typename MortonTree_t::TreeCell_t

Definition at line 17 of file multipole3d.h.

Constructor & Destructor Documentation

◆ FastMultipole3d() [1/3]

template<typename value >
fmm::FastMultipole3d< value >::FastMultipole3d ( const std::vector< particle_t > &  particles,
double  eps = 1.e-8,
int  N = FMM_AUTO,
int  tree_depth = FMM_AUTO 
)

Definition at line 1481 of file multipole3d.cpp.

1482{
1483 auto particles_copy = particles;
1484 targets_num = particles.size();
1485 Solve(std::move(particles_copy), eps, N_, tree_depth_);
1486}
void Solve(std::vector< particle_t > &&particles, double eps=1.e-8, int N=FMM_AUTO, int tree_depth=FMM_AUTO)

◆ FastMultipole3d() [2/3]

template<typename value >
fmm::FastMultipole3d< value >::FastMultipole3d ( std::vector< particle_t > &&  particles,
double  eps = 1.e-8,
int  N = FMM_AUTO,
int  tree_depth = FMM_AUTO 
)

Definition at line 1489 of file multipole3d.cpp.

1490{
1491 targets_num = particles.size();
1492 Solve(std::move(particles), eps, N_, tree_depth_);
1493}

◆ FastMultipole3d() [3/3]

template<typename value >
fmm::FastMultipole3d< value >::FastMultipole3d ( const std::vector< particle_t > &  source_particles,
const std::vector< particle_t > &  target_particles,
double  eps = 1.e-8,
int  N = FMM_AUTO,
int  tree_depth = FMM_AUTO 
)

Definition at line 1496 of file multipole3d.cpp.

1497{
1498 std::vector<particle_t> particles(source_particles.size() + target_particles.size());
1499 memcpy(particles.data(), target_particles.data(), target_particles.size() * sizeof(particle_t));
1500 memcpy(particles.data() + target_particles.size(), source_particles.data(), source_particles.size() * sizeof(particle_t));
1501 targets_num = target_particles.size();
1502 Solve(std::move(particles), eps, N_, tree_depth_);
1503}
particle< Vector3d, value > particle_t
Definition multipole3d.h:15

Member Function Documentation

◆ ComputeLeaves() [1/3]

void fmm::FastMultipole3d< Vector3d >::ComputeLeaves ( )
private

Definition at line 471 of file multipole3d.cpp.

472{
473 using namespace std::literals;
474
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)); //up to 3rd derivatives
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)));
481
482 const auto& leaves = tree->levels.back();
483 const auto& leaves_inner = inner_expansions.back();
484 const auto& particles = tree->particles;
485
486#ifdef FMM_MPI
487 auto [Begin, End] = LocalPart(0, tree->level_sizes.back());
488#else
489 size_t Begin = 0, End = tree->level_sizes.back();
490#endif
491
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;
494
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();
503
504
505 const auto& cell = leaves[leaf_idx];
506 const auto& [p1_begin, p1_end] = cell.*range_ptr;
507
508 for (auto j = p1_begin; j < p1_end; ++j)
509 {
510 auto& particle = particles[j];
511
512 for (auto k = p1_begin; k < p1_end; ++k)
513 {
514 dr = particle.center - particles[k].center;
515
516 // velDipole //
517 //*
518 auto Ldr = abs(dr);
519
520 auto argLE = (Ldr / FORCE_EPS);
521 if (argLE < 3.0)
522 {
523 auto [lambda, eta] = lambdaEta(argLE);
524 auto [lambdaA, etaA] = lambdaEtaA(argLE);
525 buf_forces[j] += particles[k].q * lambda / FORCE_EPS3 + dot(particles[k].q, dr) * dr * eta / FORCE_EPS5;
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;
528
529 }
530 else
531 {
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;
540 }
541 //*/
542 // end velDipole //
543
544 // vel BS //
545 //buf_forces[j] += cross(particles[k].q, dr) / MyPow(std::max(abs(dr), FORCE_EPS), 3);
546 // end vel BS //
547 }
548
549 for (const auto& idx : cell.closeneighbours)
550 {
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)
554 {
555 dr = particle.center - particles[k].center;
556
557 // velDipole //
558 //*
559 auto Ldr = abs(dr);
560
561 auto argLE = (Ldr / FORCE_EPS);
562 if (argLE < 3.0)
563 {
564 auto [lambda, eta] = lambdaEta(argLE);
565 auto [lambdaA, etaA] = lambdaEtaA(argLE);
566 buf_forces[j] += particles[k].q * lambda / FORCE_EPS3 + dot(particles[k].q, dr) * dr * eta / FORCE_EPS5;
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);
569 }
570 else
571 {
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;
580 }
581 //*/
582 // end velDipole //
583
584 // vel BS //
585 //buf_forces[j] += cross(particles[k].q, dr) / MyPow(std::max(abs(dr), FORCE_EPS), 3);
586 // end vel BS //
587 }
588 }
589 }
590
591
592
593 const auto& inner = leaves_inner.data() + leaf_idx * Nx2;
594
595 //if (leaf_idx == 0)
596 //{
597 // std::ofstream cftFile("coeffts.txt");
598 // cftFile.precision(16);
599 // for (int n = 1; n < N; ++n)
600 // {
601 // for (int m = 0; m <= n; ++m)
602 // {
603 // cftFile << n << " " << m << " " \
604 // << inner[n * (n + 1) / 2 + m][0].real() << " " \
605 // << inner[n * (n + 1) / 2 + m][0].imag() << "\n";
606 // }
607 // }
608 // cftFile.close();
609 // std::cout << "beg,end = " << p1_begin << ", " << p1_end << std::endl;
610 //}
611
612
613 std::complex<double> Ynm, SphDTheta, SphDPhi,
614 SphDThetaTheta, SphDThetaPhi, SphDPhiPhi,
615 SphDThetaThetaTheta, SphDThetaThetaPhi, SphDThetaPhiPhi, SphDPhiPhiPhi;
616 Vector3cd coef, coefmulr;
617 double x, y, rn, s1, s2, c1, c2, sd1, sd2, cd1, cd2;
618
619 //if (leaf_idx == 0)
620 //{
621 // std::ofstream coordFile("coord.txt");
622 // coordFile.precision(16);
623 // for (int j = p1_begin; j < p1_end; ++j)
624 // {
625 // coordFile << (particles[j].center - cell.center)[0] << " " \
626 // << (particles[j].center - cell.center)[1] << " " \
627 // << (particles[j].center - cell.center)[2] << " " \
628 // << DecToSph(particles[j].center - cell.center)[0] << " " \
629 // << DecToSph(particles[j].center - cell.center)[1] << " " \
630 // << DecToSph(particles[j].center - cell.center)[2] << "\n";
631 // }
632 // coordFile.close();
633 //}
634
635 for (auto j = p1_begin; j < p1_end; ++j)
636 {
637 /* //�������� �� ������ ��� --- ������
638 auto& particle = particles[j];
639 memset(m_temp.data(), 0, sizeof(Matrix3d));
640 dr = DecToSph(particle.center - cell.center);
641 ComputePnm(N + 1, dr[1], Pnm.data()); // +1 for computing derivative
642
643 for (int m = 0; m <= N; ++m)
644 {
645 x = m * dr[2];
646 eim[m] = std::complex<double>(cos(x), sin(x));
647 }
648 x = cos(dr[1]);
649 y = sin(dr[1]);;
650 rn = 1.;
651
652
653 for (int n = 1; n < N; ++n)
654 {
655 for (int m = 0; m <= n; ++m)
656 {
657 Ynm = Pnm(n, m) * eim[m] * Knm(n, m);
658 coef = inner[n * (n + 1) / 2 + m] * rn;
659 SphDTheta = Knm(n, m) * eim[m] * ((1 - m + n) * Pnm(n + 1, m) - (n + 1) * x * Pnm(n, m)) / y;
660 SphDPhi = double(m) * 1.0i * Ynm;
661
662 for (int s = 0; s < 3; ++s)
663 {
664 m_temp[s][0] -= (double(n) * coef[s] * Ynm).real();
665 m_temp[s][1] -= (coef[s] * SphDTheta).real() * dr[0];
666 m_temp[s][2] -= (coef[s] * SphDPhi).real() * dr[0];
667 if (m != 0)
668 {
669 m_temp[s][0] -= (double(n) * coef[s] * Ynm).real();
670 m_temp[s][1] -= (coef[s] * SphDTheta).real() * dr[0];
671 m_temp[s][2] -= (coef[s] * SphDPhi).real() * dr[0];
672 }
673 }
674 }
675 rn *= dr[0];
676 }
677
678 s1 = sin(dr[1]); c2 = cos(dr[2]); c1 = cos(dr[1]); s2 = sin(dr[2]);
679 auto& f = buf_forces[j];
680
681 Matrix3d m_copy;
682 Matrix3d JacT;
683 JacT[0] = { s1 * c2, c1 * c2 / dr[0], -s2 / (s1 * dr[0]) };
684 JacT[1] = { s1 * s2, c1 * s2 / dr[0], c2 / (s1 * dr[0]) };
685 JacT[2] = { c1, -s1 / dr[0], 0.0 };
686
687 Matrix3d Jac;
688 Jac[0] = { JacT[0][0], JacT[1][0], JacT[2][0] };
689 Jac[1] = { JacT[0][1], JacT[1][1], JacT[2][1] };
690 Jac[2] = { JacT[0][2], JacT[1][2], JacT[2][2] };
691
692 //for (int s = 0; s < 3; ++s)
693 // mv(JacT, m_temp[s], m_copy[s]);
694
695 mm(m_temp, Jac, m_copy); //�������������� ����������� ��� -> ���
696
697 f[0] += m_copy[1][2] - m_copy[2][1]; //�-� ��� --- ������
698 f[1] += m_copy[2][0] - m_copy[0][2];
699 f[2] += m_copy[0][1] - m_copy[1][0];
700
701 if (leaf_idx == 0)
702 std::cout << "gr: " << m_copy[0][0] << ", " << m_copy[0][1] << ", " << m_copy[0][2] << std::endl;
703 //*/ //�������� �� ������ ��� --- ������
704
705
706
707 //* //�������� �������������� ��������
708 auto& particle = particles[j];
709 memset(m_temp.data(), 0, sizeof(Matrix3d));
710 dr = DecToSph(particle.center - cell.center);
711
712 /* //������������� �������� � DecToSph
713 if (dr[0] < 1e-10)
714 dr[0] = 1e-10;
715 if (sin(dr[1]) < 1e-10)
716 {
717 if (cos(dr[1]) > 0)
718 dr[1] = 1e-10;
719 else
720 dr[1] = 3.141592653589793 - 1e-10;
721 }
722 */
723
724 ComputePnm(N + 3, dr[1], Pnm.data()); // +3 for computing 3rd derivative
725
726 for (int m = 0; m <= N; ++m)
727 {
728 x = m * dr[2];
729 eim[m] = std::complex<double>(cos(x), sin(x));
730 }
731 x = cos(dr[1]);
732 y = sin(dr[1]);
733 rn = 1.0;
734
735 Matrix3d t_temp[3];
736 memset(t_temp[0].data(), 0, 3*sizeof(Matrix3d));
737
738 Matrix3d u_temp[3][3];
739 memset(u_temp[0][0].data(), 0, 9 * sizeof(Matrix3d));
740
741 for (int n = 1; n < N; ++n)
742 {
743 for (int m = 0; m <= n; ++m)
744 {
745 Ynm = Pnm(n, m) * eim[m] * Knm(n, m);
746 coef = inner[n * (n + 1) / 2 + m] * rn;
747 coefmulr = coef * dr[0];
748
749 SphDTheta = Knm(n, m) * eim[m] * ((1 + n - m) * Pnm(n + 1, m) - (n + 1) * x * Pnm(n, m)) / y;
750
751 SphDPhi = double(m) * 1.0i * Ynm;
752
753
754 SphDThetaTheta = Knm(n, m) * eim[m] *
755 (
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)
759 ) / (y * y);
760
761 SphDThetaPhi = SphDTheta * 1.0i * (double)m;
762
763 SphDPhiPhi = double(-m * m) * Ynm;
764
765
766 SphDThetaThetaTheta = Knm(n, m) * eim[m] *
767 (
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)
771 ) / (y * y * y);
772
773 SphDThetaThetaPhi = SphDThetaTheta * 1.0i * (double)m;
774
775 SphDThetaPhiPhi = double(-m * m) * SphDTheta;
776
777 SphDPhiPhiPhi = double(-m * m * m) * 1.0i * Ynm;
778
779
780
781 double times = (m ? 2.0 : 1.0);
782
783 for (int s = 0; s < 3; ++s)
784 {
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();
788
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();
795
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();
806 }
807 }
808 rn *= dr[0];
809 }
810
811 for (int s = 0; s < 3; ++s)
812 {
813 if (dr[0] > 1e-10)
814 {
815 t_temp[s][0][0] /= dr[0];
816
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];
820 }
821
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];
825
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];
833 }
834
835 //if (leaf_idx == 0)
836 //{
837 // std::cout << dr[0] << " " << dr[1] << " " << dr[2] << "\n";
838 //}
839
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]);
842
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]);
845
846 double s1sq = s1 * s1, c1sq = c1 * c1, c2sq = c2 * c2, s2sq = s2 * s2, drsq = dr[0] * dr[0], drcu = drsq * dr[0];
847
848 auto& f = buf_forces[j];
849 auto& m = buf_moments[j];
850
851 //Matrix3d m_copy;
852 Matrix3d JacT;
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 };
856
857 //�������������� ����������� ��� -> ���
858 Matrix3d pm[3], t_copy[3];
859 for (int s = 0; s < 3; ++s)
860 {
861 mm(JacT, t_temp[s], pm[s]);
862 mmt(pm[s], JacT, t_copy[s]);
863 }
864
865 double izn = 1.0 / (s1sq * drsq);
866
867 Matrix3d HCoordTransform[3];
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]};
871
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 };
875
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 };
879
880 // cos(theta) -> c1, sin(theta) -> s1, cos(phi) -> c2, sin(phi) -> s2
881 // cos(2theta) -> cd1, sin(2theta) -> sd1, cos(2phi) -> cd2, sin(2phi) -> sd2
882 Matrix3d TCoordTransform[3][3];
883 {
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;
894
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} });
898 }
899
900 {
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;
911
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} });
915 }
916
917 {
918 double iz = 2 / (s1sq * s1 * drcu);
919 double P = iz * st2;
920 double S = iz * ct2;
921
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} });
925 }
926
927
928 Matrix3d add[3];
929 for (int s = 0; s < 3; ++s)
930 {
931 vt(m_temp[s], HCoordTransform, add[s]);
932 for (int ii = 0; ii < 3; ++ii)
933 t_copy[s][ii] += add[s][ii];
934 }
935
936 auto S1 = [&](int s, int ii, int jj, int kk)
937 {
938 double res = 0;
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];
943 return res;
944 };
945
946 auto S2 = [&](int s, int ii, int jj, int kk)
947 {
948 double res = 0;
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];
954 return res;
955 };
956
957 auto S3 = [&](int s, int ii, int jj, int kk)
958 {
959 double res = 0;
960 for (int ll = 0; ll < 3; ++ll)
961 res += TCoordTransform[ll][ii][jj][kk] * m_temp[s][ll];
962 return res;
963 };
964
965 Matrix3d u_copy[3][3];
966 memset(u_copy[0][0].data(), 0, 9 * sizeof(Matrix3d));
967 for (int s = 0; s < 3; ++s)
968 {
969 for (int ii = 0; ii < 3; ++ii)
970 for (int jj = 0; jj < 3; ++jj)
971 for (int kk = 0; kk < 3; ++kk)
972 {
973 u_copy[s][ii][jj][kk] += S1(s, ii, jj, kk) + S2(s, ii, jj, kk) + S3(s, ii, jj, kk);
974 }
975 }
976
977
978 //if (fabs(t_copy[0][0][0] + t_copy[1][0][1] + t_copy[2][0][2]) > 1e-10)
979 // std::cout << "f: " << t_copy[0][0][0] << " " << t_copy[1][0][1] << " " << t_copy[2][0][2] << "\n";
980
981
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];
985 //*/ //�������� �������������� ��������
986
987 Matrix3d G3;
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]);
990 Vector3d add_mom;
991 mv(G3, particle.q, add_mom);
992
993 m += add_mom;
994 }
995 });
996
997 auto& buf_forces = *buf_forces_ets.begin();
998 for (auto it = buf_forces_ets.begin() + 1; it < buf_forces_ets.end(); it++)
999 {
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)
1004 {
1005 buf_forces[i] += x[i];
1006 }
1007 });
1008 }
1009
1010 auto& buf_moments = *buf_moments_ets.begin();
1011 for (auto it = buf_moments_ets.begin() + 1; it < buf_moments_ets.end(); it++)
1012 {
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)
1017 {
1018 buf_moments[i] += x[i];
1019 }
1020 });
1021 }
1022
1023
1024#ifdef FMM_MPI
1025 AllReduce(buf_forces.data(), buf_forces.size());
1026 AllReduce(buf_moments.data(), buf_moments.size());
1027#endif
1028
1029 const auto& index_mapping = tree->positions_map;
1030
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]];
1034 });
1035}
std::vector< std::vector< complex_value > > inner_expansions
Definition multipole3d.h:47
std::vector< Vector3d > moments
Definition multipole3d.h:24
std::vector< Vector3d > forces
Definition multipole3d.h:23
std::shared_ptr< MortonTree_t > tree
Definition multipole3d.h:45
constexpr knm_wrapper< _3d_MAX_MULTIPOLE_NUM > Knm
constexpr double FORCE_EPS6
Definition defs.h:33
Vector3< std::complex< double > > Vector3cd
Definition utils.h:65
void vt(const Vector3d &x, const Matrix3d *T, Matrix3d &M)
void ComputePnm(int N, double theta, double *Pnm)
void mv(const Matrix3d &A, const Vector3d &x, Vector3d &y)
std::array< Vector3d, 3 > Matrix3d
Definition utils.h:64
constexpr Vector3d real(const Vector3cd &rhs)
Definition utils.h:98
Vector3d DecToSph(const Vector3d &vec)
constexpr double FORCE_EPS4
Definition defs.h:31
double abs(const Vector3< T > &vec)
Definition utils.h:122
void mm(const Matrix3d &A, const Matrix3d &B, Matrix3d &C)
constexpr double FORCE_EPS3
Definition defs.h:30
std::pair< T, T > lambdaEtaA(T xi)
constexpr double FORCE_EPS5
Definition defs.h:32
constexpr double dot(const Vector3d &lhs, const Vector3d &rhs)
Definition utils.h:110
std::pair< T, T > lambdaEta(T xi)
constexpr double FORCE_EPS
Definition defs.h:28
Vector3< double > Vector3d
Definition utils.h:63
void mmt(const Matrix3d &A, const Matrix3d &B, Matrix3d &C)
Here is the call graph for this function:

◆ ComputeLeaves() [2/3]

void fmm::FastMultipole3d< double >::ComputeLeaves ( )
private

Definition at line 1040 of file multipole3d.cpp.

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();
1052 const auto& leaves_inner = inner_expansions.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;
1067 avx_set_constant(oneVec, 1.0);
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;
1074 shifts[i + 1] = shifts[i] + (b - a + detail::avx_vec_length - 1) / detail::avx_vec_length;
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 {
1184 avx_set_constant(x_target, particle.center[0]);
1185 avx_set_constant(y_target, particle.center[1]);
1186 avx_set_constant(z_target, particle.center[2]);
1187 avx_set_constant(q_target, particle.q);
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;
1195 for (auto k = p2_begin, i = (decltype(p2_begin))0; k < p2_end; k += detail::avx_vec_length, ++i)
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
1205 fx = avx_add(avx_mul(dx, q_invdr3), fx);
1206 fy = avx_add(avx_mul(dy, q_invdr3), fy);
1207 fz = avx_add(avx_mul(dz, q_invdr3), fz);
1208 pot = avx_add(q_invdr, pot);
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
1222 for (auto k = p2_begin, i = (decltype(p2_begin))0; k < p2_end; k += detail::avx_vec_length, ++i)
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
1232 fx = avx_add(avx_mul(dx, q_invdr3), fx);
1233 fy = avx_add(avx_mul(dy, q_invdr3), fy);
1234 fz = avx_add(avx_mul(dz, q_invdr3), fz);
1235 pot = avx_add(q_invdr, pot);
1236 }
1237 }
1238 }
1239 else
1240 {
1241 avx_set_constant(x_target, particle.center[0]);
1242 avx_set_constant(y_target, particle.center[1]);
1243 avx_set_constant(z_target, particle.center[2]);
1244 avx_set_constant(q_target, particle.q);
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
1250 auto pletnum = (j - p1_begin) / detail::avx_vec_length;
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
1260 fx = avx_add(avx_mul(dx, q_invdr3), fx);
1261 fy = avx_add(avx_mul(dy, q_invdr3), fy);
1262 fz = avx_add(avx_mul(dz, q_invdr3), fz);
1263 pot = avx_add(q_invdr, pot);
1264 }
1265 pletnum++;
1266 for (auto k = p1_begin + pletnum * detail::avx_vec_length, i = pletnum; k < p1_end; k += detail::avx_vec_length, ++i)
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
1276 fx = avx_add(avx_mul(dx, q_invdr3), fx);
1277 fy = avx_add(avx_mul(dy, q_invdr3), fy);
1278 fz = avx_add(avx_mul(dz, q_invdr3), fz);
1279 pot = avx_add(q_invdr, pot);
1280
1281 q_invdr = avx_mul(q_target, invdr);
1282 q_invdr3 = avx_mul(invdr2, q_invdr);
1283
1284 fx_leaf[i] = avx_sub(fx_leaf[i], avx_mul(dx, q_invdr3));
1285 fy_leaf[i] = avx_sub(fy_leaf[i], avx_mul(dy, q_invdr3));
1286 fz_leaf[i] = avx_sub(fz_leaf[i], avx_mul(dz, q_invdr3));
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
1305 for (auto k = p2_begin, i = (decltype(p2_begin))0; k < p2_end; k += detail::avx_vec_length, ++i)
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
1315 fx = avx_add(avx_mul(dx, q_invdr3), fx);
1316 fy = avx_add(avx_mul(dy, q_invdr3), fy);
1317 fz = avx_add(avx_mul(dz, q_invdr3), fz);
1318 pot = avx_add(q_invdr, pot);
1319
1320 q_invdr = avx_mul(q_target, invdr);
1321 q_invdr3 = avx_mul(invdr2, q_invdr);
1322
1323 fx_nbr[i] = avx_sub(fx_nbr[i], avx_mul(dx, q_invdr3));
1324 fy_nbr[i] = avx_sub(fy_nbr[i], avx_mul(dy, q_invdr3));
1325 fz_nbr[i] = avx_sub(fz_nbr[i], avx_mul(dz, q_invdr3));
1326 phi_nbr[i] = avx_add(q_invdr, phi_nbr[i]);
1327 }
1328 }
1329 }
1330 tmp_force[0] = avx_hsum(fx);
1331 tmp_force[1] = avx_hsum(fy);
1332 tmp_force[2] = avx_hsum(fz);
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
1346 ComputePnm(N + 1, dr[1], Pnm.data()); // +1 for computing derivative
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
Definition multipole3d.h:25
const int avx_vec_length
Definition avx.h:13
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)
Definition avx.h:111
vec_type avx_add(const vec_type &v1, const vec_type &v2)
Definition avx.h:42
vec_type avx_mul(const vec_type &v1, const vec_type &v2)
Definition avx.h:64
constexpr double FORCE_EPS2
Definition defs.h:29
vec_type avx_sub(const vec_type &v1, const vec_type &v2)
Definition avx.h:53
double avx_hsum(__m128d v)
Definition avx.h:74
void avx_set_constant(vec_type &vec, double val)
Definition avx.h:101
Here is the call graph for this function:

◆ ComputeLeaves() [3/3]

template<typename value >
void fmm::FastMultipole3d< value >::ComputeLeaves ( )
private

◆ Downward()

template<typename value >
void fmm::FastMultipole3d< value >::Downward ( )
private

Definition at line 323 of file multipole3d.cpp.

324{
325#ifdef FMM_MPI
326 std::vector<int> sizes(NProc());
327 std::vector<int> displs(NProc());
328#endif
329
330 for (int i = 2; i < tree->tree_depth; ++i)
331 {
332 auto& bottom_level = tree->levels[i];
333 auto& bottom_inner = inner_expansions[i];
334 const auto& bottom_outer = outer_expansions[i];
335#ifdef FMM_MPI
336 auto [Begin, End] = LocalPart(0, tree->level_sizes[i]);
337#else
338 size_t Begin = 0, End = tree->level_sizes[i];
339#endif
340 tbb::parallel_for(Begin, End, [&](size_t j) {
341 TreeCell_t& cell = bottom_level[j];
342 auto& work1 = local_work1.local();
343 auto& work2 = local_work2.local();
344
345 for (const auto& idx : cell.farneighbours)
346 {
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);
349 }
350 });
351
352#ifdef FMM_MPI
353 {
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);
357
358 std::vector<complex_value> buf(bottom_inner.size());
359 for (int j = 0; j < NProc(); ++j)
360 {
361 sizes[j] = Nx2 * LocalPart(0, tree->level_sizes[i], j);
362 }
363 for (int j = 1; j < NProc(); ++j)
364 {
365 displs[j] = displs[j - 1] + sizes[j - 1];
366 }
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);
370 }
371#endif
372 }
373
374 for (int i = 3; i < tree->tree_depth; ++i)
375 {
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];
380#ifdef FMM_MPI
381 auto [Begin, End] = LocalPart(0, tree->level_sizes[i]);
382#else
383 size_t Begin = 0, End = tree->level_sizes[i];
384#endif
385 tbb::parallel_for(Begin, End, [&](size_t j) {
386 TreeCell_t& cell = bottom_level[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);
391 });
392
393#ifdef FMM_MPI
394 {
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);
398
399 std::vector<complex_value> buf(bottom_inner.size());
400 for (int j = 0; j < NProc(); ++j)
401 {
402 sizes[j] = Nx2 * LocalPart(0, tree->level_sizes[i], j);
403 }
404 for (int j = 1; j < NProc(); ++j)
405 {
406 displs[j] = displs[j - 1] + sizes[j - 1];
407 }
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);
411 }
412#endif
413 }
414}
std::vector< std::vector< complex_value > > outer_expansions
Definition multipole3d.h:46
tbb::enumerable_thread_specific< std::vector< complex_value > > local_work2
Definition multipole3d.h:57
void M2L(const complex_value *a, const Vector3d &p0, complex_value *b, std::vector< complex_value > &work1, std::vector< complex_value > &work2)
void L2L(const complex_value *a, const Vector3d &p0, complex_value *b, std::vector< complex_value > &work1, std::vector< complex_value > &work2)
std::conditional_t< std::is_same_v< value, double >, std::complex< double >, Vector3cd > complex_value
Definition multipole3d.h:14
typename MortonTree_t::TreeCell_t TreeCell_t
Definition multipole3d.h:17
tbb::enumerable_thread_specific< std::vector< complex_value > > local_work1
Definition multipole3d.h:56

◆ L2L()

template<typename value >
void fmm::FastMultipole3d< value >::L2L ( const complex_value a,
const Vector3d p0,
complex_value b,
std::vector< complex_value > &  work1,
std::vector< complex_value > &  work2 
)
private

Definition at line 195 of file multipole3d.cpp.

196{
197 auto rho = DecToSph(p0);
198 const auto& theta = rho[1];
199 const auto& phi = rho[2];
200
201 const auto& dm1 = dmatrix.at(doublehash(theta));
202 const auto& dm2 = dmatrix.at(doublehash(-theta));
203 const auto& exp1 = rotation_exponents.at(doublehash(phi));
204 const auto& exp2 = rotation_exponents.at(doublehash(-phi));
205
206 memset(work2.data(), 0, work2.size() * sizeof(complex_value));
207 RotateZ(a, exp1, work2.data());
208 RotateY(work2.data(), dm1, work1.data());
209 memset(work2.data(), 0, work2.size() * sizeof(complex_value));
210
211 std::vector<double> rn(N, 1);
212 for (int i = 1; i < N; ++i)
213 rn[i] = rn[i - 1] * rho[0];
214
215 for (int j = 0; j < N; ++j)
216 {
217 for (int k = 0; k <= j; ++k)
218 {
219 for (int n = j; n < N; ++n)
220 {
221 work2[j * (j + 1) / 2 + k] += Anm(n - j, 0) * Anm(j, k) * ni(n + j) * rn[n - j] / Anm(n, k) * work1[n * (n + 1) / 2 + k];
222 }
223 }
224 }
225
226 RotateY(work2.data(), dm2, work1.data());
227 RotateZ(work1.data(), exp2, b);
228}
void RotateZ(const complex_value *a, const std::vector< std::complex< double > > &rotation_exp, complex_value *b)
void RotateY(const complex_value *a, const std::vector< double > &dmatrix, complex_value *res)
constexpr anm_wrapper< _3d_MAX_MULTIPOLE_NUM > Anm
std::unordered_map< int, std::vector< double > > dmatrix
std::unordered_map< int, std::vector< std::complex< double > > > rotation_exponents
constexpr double ni(int n)
Definition simple_math.h:99
int doublehash(double val)
Definition utils.h:124
Here is the call graph for this function:

◆ M2L()

template<typename value >
void fmm::FastMultipole3d< value >::M2L ( const complex_value a,
const Vector3d p0,
complex_value b,
std::vector< complex_value > &  work1,
std::vector< complex_value > &  work2 
)
private

Definition at line 155 of file multipole3d.cpp.

156{
157 auto rho = DecToSph(p0);
158 const auto& theta = rho[1];
159 const auto& phi = rho[2];
160
161 const auto& dm1 = dmatrix.at(doublehash(theta));
162 const auto& dm2 = dmatrix.at(doublehash(-theta));
163 const auto& exp1 = rotation_exponents.at(doublehash(phi));
164 const auto& exp2 = rotation_exponents.at(doublehash(-phi));
165
166 memset(work2.data(), 0, work2.size() * sizeof(complex_value));
167 RotateZ(a, exp1, work2.data());
168 RotateY(work2.data(), dm1, work1.data());
169 memset(work2.data(), 0, work2.size() * sizeof(complex_value));
170
171 std::vector<double> rn(2 * N + 1, 1);
172 for (int i = 1; i < 2 * N + 1; ++i)
173 rn[i] = rn[i - 1] / rho[0];
174
175 int idx1, idx2, idx3;
176 for (int j = 0; j < N; ++j)
177 {
178 idx1 = (j + 2) * (j + 1) / 2;
179 for (int k = 0; k <= j; ++k)
180 {
181 idx2 = detail::_3d_MAX_MULTIPOLE_NUM * (idx1 + k + 1) + 1;
182 for (int n = k; n < N; ++n)
183 {
184 idx3 = idx2 + n;
185 work2[j * (j + 1) / 2 + k] += (m2lcoef.m2lcoef[idx3] * rn[j + n + 1]) * work1[n * (n + 1) / 2 + k];
186 }
187 }
188 }
189
190 RotateY(work2.data(), dm2, work1.data());
191 RotateZ(work1.data(), exp2, b);
192}
const int _3d_MAX_MULTIPOLE_NUM
Definition defs.h:42
constexpr m2lcoef_wrapper< _3d_MAX_MULTIPOLE_NUM > m2lcoef
Here is the call graph for this function:

◆ M2M()

template<typename value >
void fmm::FastMultipole3d< value >::M2M ( const complex_value a,
const Vector3d p0,
complex_value b,
std::vector< complex_value > &  work1,
std::vector< complex_value > &  work2 
)
private

Definition at line 118 of file multipole3d.cpp.

119{
120 auto rho = DecToSph(p0);
121 const auto& theta = rho[1];
122 const auto& phi = rho[2];
123
124 const auto& dm1 = dmatrix.at(doublehash(theta));
125 const auto& dm2 = dmatrix.at(doublehash(-theta));
126 const auto& exp1 = rotation_exponents.at(doublehash(phi));
127 const auto& exp2 = rotation_exponents.at(doublehash(-phi));
128
129 memset(work2.data(), 0, work2.size() * sizeof(complex_value));
130 RotateZ(a, exp1, work2.data());
131 RotateY(work2.data(), dm1, work1.data());
132 memset(work2.data(), 0, work2.size() * sizeof(complex_value));
133
134 std::vector<double> rn(N, 1);
135 for (int i = 1; i < N; ++i)
136 rn[i] = rn[i - 1] * rho[0];
137
138 for (int j = 0; j < N; ++j)
139 {
140 for (int k = 0; k <= j; ++k)
141 {
142 for (int n = 0; j - n >= k; ++n)
143 {
144 int j_minus_n = j - n;
145 work2[j * (j + 1) / 2 + k] += Anm(n, 0) * Anm(j_minus_n, k) * rn[n] / Anm(j, k) * work1[j_minus_n * (j_minus_n + 1) / 2 + k];
146 }
147 }
148 }
149
150 RotateY(work2.data(), dm2, work1.data());
151 RotateZ(work1.data(), exp2, b);
152}
Here is the call graph for this function:

◆ Multipole()

template<typename value >
void fmm::FastMultipole3d< value >::Multipole ( complex_value a,
const std::pair< size_t, size_t > &  particle_range,
const Vector3d z0,
double *  Pnm,
std::complex< double > *  eim 
)
private

Definition at line 23 of file multipole3d.cpp.

24{
25 auto [p_begin, p_end] = particle_range;
26 Vector3d rho;
27 double r, rn, mphi;
28
29 for (auto i = p_begin; i < p_end; ++i)
30 {
31 auto& particle = tree->particles[i];
32 rho = DecToSph(particle.center - p0);
33 rn = r = rho[0];
34 ComputePnm(N, rho[1], Pnm);
35 for (int m = 0; m < N; ++m)
36 {
37 mphi = m * rho[2];
38 eim[m] = std::complex<double>(cos(mphi), -sin(mphi));
39 }
40
41 for (int n = 1; n < N; ++n)
42 {
43 for (int m = 0; m <= n; ++m)
44 {
45 a[n * (n + 1) / 2 + m] += particle.q * rn * Knm(n, m) * Pnm[n * (n + 1)/ 2 + m]* eim[m];
46 }
47 rn *= r;
48 }
49 a[0] += particle.q;
50 }
51}
Here is the call graph for this function:

◆ Prepare()

template<typename value >
void fmm::FastMultipole3d< value >::Prepare ( )
private

Definition at line 1568 of file multipole3d.cpp.

1569{
1570 local_work1 = tbb::enumerable_thread_specific<std::vector<complex_value>>{ std::vector<complex_value>(Nx2) };
1571 local_work2 = tbb::enumerable_thread_specific<std::vector<complex_value>>{ std::vector<complex_value>(Nx2) };
1572
1573 /*m2lcoef.resize(2 * N * N * N);
1574 for (int j = 0; j < N; ++j)
1575 {
1576 for (int k = 0; k <= j; ++k)
1577 {
1578 for (int n = k; n < N; ++n)
1579 {
1580 int idx = N * (j + 2) * (j + 1) / 2 + N * (k + 1) + n + 1;
1581 m2lcoef[idx] = Anm(n, k) * Anm(j, k) * ni(j + k) / Anm(j + n, 0);
1582 }
1583 }
1584 }*/
1585}

◆ RotateY()

template<typename value >
void fmm::FastMultipole3d< value >::RotateY ( const complex_value a,
const std::vector< double > &  dmatrix,
complex_value res 
)
private

Definition at line 82 of file multipole3d.cpp.

83{
84 int idx0, idx1, idx2;
85 complex_value val;
86 for (int n = 0; n < N; ++n)
87 {
88 idx0 = (n * (5 + n * (3 + 4 * n))) / 6;
89 idx1 = 2 * n + 1;
90 for (int m = 0; m <= n; ++m)
91 {
92 idx2 = idx0 + idx1 * m;
93 if constexpr (std::is_same_v<value, double>)
94 val = 0.0;
95 else
96 val = { 0,0,0 };
97 for (int k = 1; k <= n; ++k)
98 {
99 const auto z1 = dmatrix[idx2 + k];
100 const auto z2 = dmatrix[idx2 - k];
101 const auto w = a[n * (n + 1) / 2 + k];
102 if constexpr (std::is_same_v<value, double>)
103 {
104 val.real(w.real() * (z1 + z2) + val.real());
105 val.imag(w.imag() * (z1 - z2) + val.imag());
106 }
107 else
108 for (int s = 0; s < 3; ++s)
109 val[s] += std::complex<double>(w[s].real() * (z1 + z2), w[s].imag() * (z1 - z2));
110 }
111 val += dmatrix[idx2] * a[n * (n + 1) / 2];
112 res[n * (n + 1) / 2 + m] = val;
113 }
114 }
115}
constexpr Vector3d imag(const Vector3cd &rhs)
Definition utils.h:103
Here is the call graph for this function:

◆ RotateZ() [1/2]

template<typename value >
void fmm::FastMultipole3d< value >::RotateZ ( complex_value a,
const std::vector< std::complex< double > > &  rotation_exp 
)
private

Definition at line 54 of file multipole3d.cpp.

55{
56 int idx;
57 for (int n = 0; n < N; ++n)
58 {
59 idx = n * (n + 1) / 2;
60 for (int m = 0; m <= n; ++m)
61 {
62 a[idx + m] *= rotation_exp[m];
63 }
64 }
65}

◆ RotateZ() [2/2]

template<typename value >
void fmm::FastMultipole3d< value >::RotateZ ( const complex_value a,
const std::vector< std::complex< double > > &  rotation_exp,
complex_value b 
)
private

Definition at line 68 of file multipole3d.cpp.

69{
70 int idx;
71 for (int n = 0; n < N; ++n)
72 {
73 idx = n * (n + 1) / 2;
74 for (int m = 0; m <= n; ++m)
75 {
76 b[idx + m] += a[idx + m] * rotation_exp[m];
77 }
78 }
79}

◆ Solve()

template<typename value >
void fmm::FastMultipole3d< value >::Solve ( std::vector< particle_t > &&  particles,
double  eps = 1.e-8,
int  N = FMM_AUTO,
int  tree_depth = FMM_AUTO 
)
private

Definition at line 1506 of file multipole3d.cpp.

1507{
1508 double T = omp_get_wtime();
1509 // if (IAmRoot()) std::cout << "\n***************** Start FMM **************" << std::endl;
1510 num_particles = particles.size();
1511 eps = std::max(eps, 1.e-11);
1512 if (N_ == FMM_AUTO)
1513 N = std::min(fmm::detail::_3d_MAX_MULTIPOLE_NUM, int(54.2 - 10.73 * sqrt(log(2.4 * eps) + 25.5)));
1514 else
1515 N = std::min(detail::_3d_MAX_MULTIPOLE_NUM, N_);
1516 Nx2 = N * (N + 1) / 2;
1517 // if (IAmRoot()) std::cout << "multipole num = " << N << std::endl;
1518
1519 size_t initial_depth;
1520 if (tree_depth_ == FMM_AUTO)
1521 initial_depth = (size_t)std::max(2.0, 1 + log(double(num_particles) / 100.0) / log(8.0));
1522 else
1523 initial_depth = tree_depth_;
1524 // if (IAmRoot()) std::cout << "tree depth = " << initial_depth << std::endl;
1525
1526 tree = std::make_shared<MortonTree_t>(std::move(particles), initial_depth);
1527 tree_depth = tree->tree_depth;
1528
1529 if (tree_depth < initial_depth)
1530 std::cout << "Warning: tree_depth was reduced from " << initial_depth << " to " << tree_depth << " to ensure H_CELL < EPS_FORCE" << std::endl;
1531
1532 double t = omp_get_wtime();
1535 for (int i = 0; i < tree->tree_depth; ++i)
1536 {
1537 auto& outer = outer_expansions[i];
1538 auto& inner = inner_expansions[i];
1539 outer.resize(tree->level_sizes[i] * Nx2);
1540 inner.resize(tree->level_sizes[i] * Nx2);
1541 }
1542 forces.resize(targets_num);
1543 moments.resize(targets_num);
1544 if constexpr (std::is_same_v<value, double>)
1545 potentials.resize(targets_num);
1546
1547 //if (IAmRoot()) std::cout << "allocation time: " << omp_get_wtime() - t << std::endl;
1548
1549 t = omp_get_wtime();
1550 Prepare();
1551 //if (IAmRoot()) std::cout << "prepare time: " << omp_get_wtime() - t << std::endl;
1552
1553 Upward();
1554
1555 t = omp_get_wtime();
1556 Downward();
1557 //if (IAmRoot()) std::cout << "m2l+l2l time: " << omp_get_wtime() - t << std::endl;
1558
1559 t = omp_get_wtime();
1560 ComputeLeaves();
1561 //if (IAmRoot()) std::cout << "leaf time: " << omp_get_wtime() - t << std::endl;
1562
1563 // if (IAmRoot()) std::cout << "total fmm time: " << omp_get_wtime() - T << std::endl;
1564 // if (IAmRoot()) std::cout << "***************** End FMM **************\n" << std::endl;
1565}
const int FMM_AUTO
Definition defs.h:26

◆ Upward()

template<typename value >
void fmm::FastMultipole3d< value >::Upward ( )
private

Definition at line 231 of file multipole3d.cpp.

232{
233 double t1 = omp_get_wtime();
234
235 auto& leaves = tree->levels.back();
236 auto& leaves_outer = outer_expansions.back();
237#ifdef FMM_MPI
238 auto [Begin, End] = LocalPart(0, tree->level_sizes[tree->tree_depth - 1]);
239#else
240 size_t Begin = 0, End = tree->level_sizes[tree->tree_depth - 1];
241#endif
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) {
245 TreeCell_t& cell = leaves[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());
249 });
250
251#ifdef FMM_MPI
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)
256 {
257 sizes[i] = Nx2 * LocalPart(0, tree->level_sizes[tree->tree_depth - 1], i);
258 }
259 for (int i = 1; i < NProc(); ++i)
260 {
261 displs[i] = displs[i - 1] + sizes[i - 1];
262 }
263
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);
267
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);
271#endif
272
273 //if (IAmRoot()) std::cout << "multipole time: " << omp_get_wtime() - t1 << std::endl;
274
275 t1 = omp_get_wtime();
276
277 for (int i = (int)tree->tree_depth - 2; i >= 0; --i)
278 {
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];
283#ifdef FMM_MPI
284 auto [Begin, End] = LocalPart(0, tree->level_sizes[i]);
285#else
286 size_t Begin = 0, End = tree->level_sizes[i];
287#endif
288 tbb::parallel_for(Begin, End, [&](size_t j) {
289 TreeCell_t& cell = top_level[j];
290 auto& work1 = local_work1.local();
291 auto& work2 = local_work2.local();
292 const auto& [begin, end] = cell.source_range;
293
294 for (auto k = begin; k < end; ++k)
295 {
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);
298 }
299 });
300
301#ifdef FMM_MPI
302 {
303 std::vector<complex_value> buf(top_outer.size());
304 for (int j = 0; j < NProc(); ++j)
305 {
306 sizes[j] = Nx2 * LocalPart(0, tree->level_sizes[i], j);
307 }
308 for (int j = 1; j < NProc(); ++j)
309 {
310 displs[j] = displs[j - 1] + sizes[j - 1];
311 }
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);
315 }
316#endif
317 }
318
319 //if (IAmRoot()) std::cout << "m2m time: " << omp_get_wtime() - t1 << std::endl;
320}
void Multipole(complex_value *a, const std::pair< size_t, size_t > &particle_range, const Vector3d &z0, double *Pnm, std::complex< double > *eim)
void M2M(const complex_value *a, const Vector3d &p0, complex_value *b, std::vector< complex_value > &work1, std::vector< complex_value > &work2)

Member Data Documentation

◆ dipolevelocities

template<typename value >
std::vector<Vector3d> fmm::FastMultipole3d< value >::dipolevelocities

Definition at line 26 of file multipole3d.h.

◆ forces

template<typename value >
std::vector<Vector3d> fmm::FastMultipole3d< value >::forces

Definition at line 23 of file multipole3d.h.

◆ inner_expansions

template<typename value >
std::vector<std::vector<complex_value> > fmm::FastMultipole3d< value >::inner_expansions
private

Definition at line 47 of file multipole3d.h.

◆ local_work1

template<typename value >
tbb::enumerable_thread_specific<std::vector<complex_value> > fmm::FastMultipole3d< value >::local_work1
private

Definition at line 56 of file multipole3d.h.

◆ local_work2

template<typename value >
tbb::enumerable_thread_specific<std::vector<complex_value> > fmm::FastMultipole3d< value >::local_work2
private

Definition at line 57 of file multipole3d.h.

◆ moments

template<typename value >
std::vector<Vector3d> fmm::FastMultipole3d< value >::moments

Definition at line 24 of file multipole3d.h.

◆ N

template<typename value >
int fmm::FastMultipole3d< value >::N
private

Definition at line 50 of file multipole3d.h.

◆ num_particles

template<typename value >
size_t fmm::FastMultipole3d< value >::num_particles
private

Definition at line 52 of file multipole3d.h.

◆ Nx2

template<typename value >
int fmm::FastMultipole3d< value >::Nx2
private

Definition at line 51 of file multipole3d.h.

◆ outer_expansions

template<typename value >
std::vector<std::vector<complex_value> > fmm::FastMultipole3d< value >::outer_expansions
private

Definition at line 46 of file multipole3d.h.

◆ potentials

template<typename value >
std::vector<double> fmm::FastMultipole3d< value >::potentials

Definition at line 25 of file multipole3d.h.

◆ targets_num

template<typename value >
size_t fmm::FastMultipole3d< value >::targets_num
private

Definition at line 53 of file multipole3d.h.

◆ tree

template<typename value >
std::shared_ptr<MortonTree_t> fmm::FastMultipole3d< value >::tree
private

Definition at line 45 of file multipole3d.h.

◆ tree_depth

template<typename value >
size_t fmm::FastMultipole3d< value >::tree_depth
private

Definition at line 49 of file multipole3d.h.


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