3#include <oneapi/tbb/parallel_for.h>
31 std::array<double, n * n>
cft;
38 for (
int i = 1; i < n; ++i)
42 for (
int j = 1; j < i; ++j)
59 std::vector<double>
cft;
69 for (
int i = 1; i <= n; ++i)
73 for (
int j = 1; j < i; ++j)
101 return ((n & 1) == 1) ? -1.0 : 1.0;
106 const double eps = 1e-12;
108 c[0] =
abs(vec) + eps;
109 c[1] = acos(vec[2] / c[0]);
110 if (fabs(vec[0]) + fabs(vec[1]) < eps) {
113 else if (fabs(vec[0]) < eps) {
114 c[2] = vec[1] / fabs(vec[1]) * 3.14159265358979323846 * 0.5;
117 c[2] = atan2(vec[1], vec[0]);
135 return p2.q / std::max(
abs(p1.center - p2.center),
FORCE_EPS);
140 auto dr = p1.center - p2.center;
146 auto dr = p1.center - p2.center;
158namespace detail_interp {
171 constexpr double sqrtPi = 1.7724538509055159;
175 lambda[i] = 8.0 / (3.0 * sqrtPi);
176 eta[i] = 8.0 / (5.0 * sqrtPi);
180 double xi2 = xi * xi, xi3 = xi2 * xi, xi4 = xi2 * xi2;
181 double xi5 = xi3 * xi2, xi6 = xi4 * xi2;
182 double e = std::erf(xi), ex = std::exp(-xi2);
183 lambda[i] = -e/xi3 + (2/sqrtPi)*(2 + 1/xi2)*ex;
184 eta[i] = 3*e/xi5 - (2/sqrtPi/xi2)*(2 + 3/xi2)*ex;
185 lambdaA[i] = 3*e/xi4 - (8/sqrtPi)*(3/(4*xi3) + 1/(2*xi) + xi)*ex;
186 etaA[i] = -15*e/xi6 + (1/sqrtPi)*(8/xi + 20/xi3 + 30/xi5)*ex;
197inline std::pair<double,double>
lookup2(
const double* tA,
const double* tB,
double xi) {
199 int idx =
static_cast<int>(idx_f);
201 double frac = idx_f - idx;
202 double a = tA[idx] * (1.0 - frac) + tA[idx + 1] * frac;
203 double b = tB[idx] * (1.0 - frac) + tB[idx + 1] * frac;
213 T sqrtPi = sqrt((T)3.141592653589793);
214 if (fabs(xi) < 1e-10)
215 return { 8.0 / (3.0 * sqrtPi), 8.0 / (5.0 * sqrtPi) };
216 T xi2 = xi * xi, xi3 = xi2 * xi, xi5 = xi3 * xi2;
217 T e = std::erf(xi), ex = std::exp(-xi2);
219 -e/xi3 + (2/sqrtPi) *(2 + 1/xi2)*ex,
220 3 * e/xi5 - (2/sqrtPi/xi2)*(2 + 3/xi2)*ex
230 double xi3 = xi*xi*xi, xi5 = xi3*xi*xi;
231 return { -1.0/xi3, 3.0/xi5 };
240 T sqrtPi = sqrt((T)3.141592653589793);
241 if (fabs(xi) < 1e-10)
return { 0.0, 0.0 };
242 T xi2 = xi*xi, xi3 = xi2*xi, xi4 = xi2*xi2, xi5 = xi3*xi2, xi6 = xi4*xi2;
243 T e = std::erf(xi), ex = std::exp(-xi2);
245 3*e/xi4 - (8/sqrtPi)*(3/(4*xi3) + 1/(2*xi) + xi)*ex,
246 -15*e/xi6 + (1/sqrtPi)*(8/xi + 20/xi3 + 30/xi5)*ex
255 double xi4 = xi*xi*xi*xi, xi6 = xi4*xi*xi;
256 return { 3.0/xi4, -15.0/xi6 };
265 auto dr = (p1.center - p2.center);
279 auto Ldr2 = Ldr * Ldr;
280 auto Ldr3 = Ldr2 * Ldr;
281 auto Ldr5 = Ldr3 * Ldr2;
282 auto result = p2.q / (-Ldr3) + 3 *
dot(p2.q, dr) * dr / Ldr5;
292 auto dr = (p1.center - p2.center);
301 auto result = -1 * ((
dot(p1.q, p2.q) * lambdaA /
FORCE_EPS4 +
dot(p2.q, dr) *
dot(p1.q, dr) * etaA /
FORCE_EPS6) / std::max(Ldr, 1e-10) * dr + \
310 auto Ldr2 = Ldr * Ldr;
311 auto Ldr3 = Ldr2 * Ldr;
312 auto Ldr4 = Ldr2 * Ldr2;
313 auto Ldr5 = Ldr3 * Ldr2;
314 auto Ldr6 = Ldr3 * Ldr3;
315 auto result = -1 * ((
dot(p1.q, p2.q) * (3 / Ldr4) +
dot(p2.q, dr) *
dot(p1.q, dr) * (-15 / Ldr6)) / std::max(Ldr, 1e-10) * dr + \
316 (p2.q *
dot(p1.q, dr) + p1.q *
dot(p2.q, dr)) * 3 / Ldr5);
327 auto invdr = 1.0 / std::max(
abs(p1.center - p2.center),
FORCE_EPS);
328 potential1 += p2.q * invdr;
329 potential2 += p1.q * invdr;
334 auto dr = p1.center - p2.center;
336 force1 += p2.q * invdr;
337 force2 -= p1.q * invdr;
342 auto dr = p1.center - p2.center;
344 force1 +=
cross(p2.q, invdr);
345 force2 -=
cross(p1.q, invdr);
351 potential1 += p2.
q * dz;
352 potential2 += p1.
q * dz;
359 force1 += p2.
q * invdz;
360 force2 -= p1.
q * invdz;
364__device__
inline double Potential2d(
const gpu::particle2d& p1,
const gpu::particle2d& p2)
366 return p2.q * 0.5 * log(max(cuda::std::norm(p1.center - p2.center), CUDA_FORCE_EPS2));
369__device__
inline gpu::cuda_complex
Force2d(
const gpu::particle2d& p1,
const gpu::particle2d& p2)
371 auto dz = p1.center - p2.center;
372 return p2.q * cuda::std::conj(dz) / max(cuda::std::norm(dz), CUDA_FORCE_EPS2);
375__device__
inline double Potential3d(
const gpu::particle3d& p1,
const gpu::particle3d& p2)
377 double dr2 = max(
norm(p1.center - p2.center), CUDA_FORCE_EPS2);
378 return p2.q * rsqrt(dr2);
381__device__
inline Vector3d Force3d(
const gpu::particle3d& p1,
const gpu::particle3d& p2)
383 auto dr = p1.center - p2.center;
384 auto dr2 = max(
norm(dr), CUDA_FORCE_EPS2);
385 return p2.q * dr *
MyPow(rsqrt(dr2), 3);
388__device__
inline Vector3d Force3d(
const gpu::particle3d3& p1,
const gpu::particle3d3& p2)
390 auto dr = p1.center - p2.center;
391 auto dr2 = max(
norm(dr), CUDA_FORCE_EPS2);
392 return cross(p2.q, dr) *
MyPow(rsqrt(dr2), 3);
397template <
typename po
int_type,
typename interaction_type,
typename value_type>
402 size_t num_particles = target_particles.size();
404 auto [shift, end_part] = LocalPart(0, num_particles);
405 size_t local_size = end_part - shift;
407 size_t shift = 0, local_size = num_particles;
409 std::vector<interaction_type> exact(local_size);
411 auto particles_ptr = target_particles.data() + shift;
412 tbb::parallel_for(
size_t(0), local_size, [&](
size_t i)
414 const auto& p1 = particles_ptr[i];
415 for (
const auto& p2 : source_particles)
417 exact[i] += func(p1, p2);
422 std::vector<int> sizes(NProc());
423 std::vector<int> displs(NProc());
424 std::vector<interaction_type> buf(num_particles);
425 for (
int j = 0; j < NProc(); ++j)
427 sizes[j] = LocalPart(0, num_particles, j);
429 for (
int j = 1; j < NProc(); ++j)
431 displs[j] = displs[j - 1] + sizes[j - 1];
433 if constexpr (std::is_same_v<interaction_type,double>)
434 MPI_Allgatherv(exact.data(), local_size, MPI_DOUBLE,
435 buf.data(), sizes.data(), displs.data(), MPI_DOUBLE, MPI_COMM_WORLD);
436 if constexpr (std::is_same_v<interaction_type,std::complex<double>>)
437 MPI_Allgatherv(exact.data(), local_size, MPI_COMPLEX16,
438 buf.data(), sizes.data(), displs.data(), MPI_COMPLEX16, MPI_COMM_WORLD);
439 if constexpr (std::is_same_v<interaction_type, Vector3d>)
441 MPI_Datatype MPI_VECTOR3;
442 MPI_Type_contiguous(3, MPI_DOUBLE, &MPI_VECTOR3);
443 MPI_Type_commit(&MPI_VECTOR3);
444 MPI_Allgatherv(exact.data(), local_size, MPI_VECTOR3,
445 buf.data(), sizes.data(), displs.data(), MPI_VECTOR3, MPI_COMM_WORLD);
449 std::ofstream fout(filename);
452 for (
const auto& x : buf)
455 for (
const auto& x : exact)
493template <InteractionType it,
typename po
int_type,
typename value_type>
498 ComputeExact<point2d, double, value_type>(source_particles, target_particles, foo,
"potential2d_exact.txt");
502 ComputeExact<point2d, point2d, value_type>(source_particles, target_particles, foo,
"force2d_exact.txt");
507 ComputeExact<point3d, double, value_type>(source_particles, target_particles, foo,
"potential3d_exact.txt");
512 ComputeExact<point3d, Vector3d, value_type>(source_particles, target_particles, foo,
"force3d_exact.txt");
517 ComputeExact<point3d, Vector3d, value_type>(source_particles, target_particles, foo,
"velDipole3d_exact.txt");
522 ComputeExact<point3d, Vector3d, value_type>(source_particles, target_particles, foo,
"momDipole3d_exact.txt");
526template <InteractionType it,
typename po
int_type,
typename value_type>
529 ComputeExact<it, point_type, value_type>(particles, particles);
532template <InteractionType it,
typename T>
535 size_t num_particles = res.size();
536 std::vector<T> exact(num_particles);
538 std::string filename;
542 filename =
"potential2d_exact.txt";
545 filename =
"force2d_exact.txt";
548 filename =
"potential3d_exact.txt";
551 filename =
"force3d_exact.txt";
554 filename =
"velDipole3d_exact.txt";
560 std::ifstream fin(filename);
561 for (
auto& x : exact)
564 double max_error = 0.0;
565 double err1 = 0.0, err2 = 0.0;
566 double l2error = 0.0;
568 for (
int i = 0; i < num_particles; ++i)
572 err = std::abs(exact[i] - res[i]);
574 err =
abs(exact[i] - res[i]);
575 max_error = std::max(max_error, err);
581 err2 += std::abs(exact[i]);
582 l2error += (err / std::abs(exact[i])) * (err / std::abs(exact[i]));
586 err2 +=
abs(exact[i]);
587 l2error += (err /
abs(exact[i])) * (err /
abs(exact[i]));
591 l2error /= num_particles;
595 std::cout <<
"FMM relative error = " << err1 / err2 << std::endl;
602 size_t num_particles = res.size();
603 std::vector<std::complex<double>> exact(num_particles);
605 std::string filename =
"force2d_exact.txt";
607 std::ifstream fin(filename);
608 for (
auto& x : exact)
611 double max_error = 0.0;
612 double err1 = 0.0, err2 = 0.0;
613 double l2error = 0.0;
616 for (
int i = 0; i < num_particles; ++i)
620 std::array<double, 2> hyb = { res[i][1] * 6.283185307179586476925286766559, res[i][0] * 6.283185307179586476925286766559 };
622 err = sqrt((exact[i].
real() - hyb[0]) * (exact[i].
real() - hyb[0]) +
623 (exact[i].
imag() - hyb[1]) * (exact[i].
imag() - hyb[1]));
625 max_error = std::max(max_error, err);
630 err2 += std::abs(exact[i]);
631 l2error += (err / std::abs(exact[i])) * (err / std::abs(exact[i]));
635 l2error /= num_particles;
639 std::cout <<
"Hybrid relative error = " << err1 / err2 << std::endl;
640 std::cout <<
"--------------------------------" << std::endl;
std::vector< double > cft
double operator()(int p, int q) const
static constexpr int TABLE_SIZE
const KernelTables & tables()
std::pair< double, double > lookup2(const double *tA, const double *tB, double xi)
static constexpr double INV_STEP
static constexpr double XI_MAX_INTERP
constexpr BinomNewton_wrapper< 2 *_2d_MAX_MULTIPOLE_NUM > binom
constexpr double FORCE_EPS6
constexpr T MyPow(T base, unsigned int exp)
constexpr Vector3d imag(const Vector3cd &rhs)
constexpr double ni(int n)
void ReadError(const std::vector< T > &res)
Vector3d momDipole3d(const particle3d3 &p1, const particle3d3 &p2)
constexpr Vector3d real(const Vector3cd &rhs)
Vector3d DecToSph(const Vector3d &vec)
void ComputeExact(const std::vector< particle< point_type, value_type > > &source_particles, const std::vector< particle< point_type, value_type > > &target_particles, std::function< interaction_type(const particle< point_type, value_type > &, const particle< point_type, value_type > &)> func, std::string filename)
constexpr double FORCE_EPS4
Vector3d velDipole3d(const particle3d3 &p1, const particle3d3 &p2)
double norm(const Vector3< T > &vec)
void Force3dMutual(const particle3d &p1, const particle3d &p2, Vector3d &force1, Vector3d &force2)
constexpr double FORCE_EPS2
std::pair< double, double > lambdaEtaA< double >(double xi)
void ReadErrorHyb(const std::vector< std::array< double, 2 > > &res)
void Potential2dMutual(const particle2d &p1, const particle2d &p2, double &potential1, double &potential2)
double abs(const Vector3< T > &vec)
std::pair< double, double > lambdaEta< double >(double xi)
constexpr double FORCE_EPS3
std::pair< T, T > lambdaEtaA(T xi)
constexpr double FORCE_EPS5
constexpr double dot(const Vector3d &lhs, const Vector3d &rhs)
std::pair< T, T > lambdaEta(T xi)
constexpr double FORCE_EPS
Vector3< double > Vector3d
constexpr Vector3< T > cross(const Vector3< T > &lhs, const Vector3< T > &rhs)
particle< point3d, point3d > particle3d3
void Force2dMutual(const particle2d &p1, const particle2d &p2, std::complex< double > &force1, std::complex< double > &force2)
void Potential3dMutual(const particle3d &p1, const particle3d &p2, double &potential1, double &potential2)
particle< point3d, double > particle3d
constexpr BinomNewton_wrapper()
constexpr double operator()(int p, int q) const
std::array< double, n *n > cft
double lambdaA[TABLE_SIZE]
double lambda[TABLE_SIZE]