288{
289 double T = omp_get_wtime();
290
291 const int dim2 =
MyPow(2, dim);
292 double t = omp_get_wtime();
293 {
296
298 {
300 }
301
302 if constexpr (dim == 2)
303 shift -= std::complex<double> {1.e-7, 1.e-7};
304 else
307 }
308
309
310
312
313
315 tbb::parallel_for(
size_t(0),
tree_depth, [&](
size_t i) {
316 levels[i].resize(std::min(num_particles, (
size_t)
MyPow(
size_t(dim2), (
unsigned int)i)));
317 });
320
321 t = omp_get_wtime();
323 x.morton_code = MortonCode<dim, point>(x.center, (int)(tree_depth - 1), shift, scale);
324 assert(x.morton_code < MyPow(dim2, tree_depth - 1));
325 });
326
327
329 t = omp_get_wtime();
330 {
331#ifdef deterministic_behaviour
332 tbb::parallel_sort(
particles.begin(),
particles.end(), [](
const auto& x,
const auto& y) {return x.morton_code < y.morton_code; });
333
334#else
336 {
337
338 tbb::concurrent_vector<std::atomic_size_t> counts(
MyPow(dim2, (
unsigned int)(
tree_depth - 1)) + 1);
339 tbb::parallel_for_each(
particles.begin(),
particles.end(), [&](
const auto& x) {counts[1 + x.morton_code]++; });
340 for (int i = 1; i < counts.size(); ++i)
341 counts[i] += counts[i - 1];
342 assert(counts.back() == num_particles);
343
344 tbb::scalable_allocator<small_particle<dim, value>> temp;
345 constexpr size_t memsize = sizeof(small_particle<dim, value>);
346 auto ptr = temp.allocate(num_particles);
347
348
349 tbb::parallel_for(size_t(0), num_particles, [&](size_t i) {
351 size_t pos = counts[x.morton_code]++;
352 assert(pos < num_particles);
354 memcpy(&ptr[pos], &x, memsize);
355 });
356
357 tbb::parallel_for(size_t(0), num_particles, [&](size_t i) {
359 });
360
361 temp.deallocate(ptr, num_particles);
362
363 assert(std::is_sorted(
particles.begin(),
particles.end(), [](
const auto& x,
const auto& y) {return x.morton_code < y.morton_code; }));
364 }
365#ifdef FMM_MPI
366 MPI_Bcast(
particles.data(), num_particles *
sizeof(
particle_t), MPI_BYTE, RootID(), MPI_COMM_WORLD);
367 MPI_Bcast(
positions_map.data(), num_particles *
sizeof(
size_t), MPI_BYTE, RootID(), MPI_COMM_WORLD);
368#endif
369
370#endif
371 }
372
373 t = omp_get_wtime();
374#ifdef deterministic_behaviour
375 tbb::parallel_for_each(
particles.begin(),
particles.end(), [&](
auto& x) {x.morton_code >>= dim * (30 - tree_depth); });
376#endif
377 {
378 auto& top_level =
levels.back();
379 auto ptr = std::unique_copy(
exec_policy,
particles.begin(),
particles.end(), top_level.begin(), [&](
const auto& x,
const auto& y) {return (x.morton_code) == (y.morton_code); });
381
382 int idx_bottom = 0, idx_top = 0;
383 for (int j = 0; j < num_particles; ++j)
384 {
385 if ((
particles[j].morton_code) != (top_level[idx_top].morton_code))
386 {
387 auto& cell = top_level[idx_top];
388 cell.source_range = { idx_bottom, j };
390 ++idx_top;
391 idx_bottom = j;
392 }
393 }
394 auto& cell = top_level[idx_top];
395 cell.source_range = { idx_bottom, num_particles };
397
398
399 }
400
401 for (
int i = (
int)(
tree_depth - 2); i >= 0; --i)
402 {
403 auto& top_level =
levels[i];
404 auto& bottom_level =
levels[i + 1];
405
406 auto ptr = std::unique_copy(
exec_policy, bottom_level.begin(), bottom_level.begin() +
level_sizes[i + 1], top_level.begin(), [&](
const auto& x,
const auto& y) {return (x.morton_code >> dim) == (y.morton_code >> dim); });
408
409
410
411 int idx_bottom = 0, idx_top = 0;
413 {
414 if ((bottom_level[j].morton_code >> dim) != (top_level[idx_top].morton_code >> dim))
415 {
416 auto& cell = top_level[idx_top];
417 cell.source_range = { idx_bottom, j };
418 cell.morton_code >>= dim;
420 ++idx_top;
421 idx_bottom = j;
422 }
423 bottom_level[j].parent = idx_top;
424 }
425 auto& cell = top_level[idx_top];
426 cell.source_range = { idx_bottom,
level_sizes[i + 1] };
427 cell.morton_code >>= dim;
429 }
430
431
432 t = omp_get_wtime();
433 {
434 std::vector<size_t> inverse_positions_map(num_particles);
435 tbb::parallel_for(size_t(0), num_particles, [&](size_t i){
437 });
438
439 value Zero;
440 if constexpr (std::is_same_v<value, double>)
441 Zero = 0;
442 if constexpr (std::is_same_v<value, Vector3d>)
443 Zero = { 0,0,0 };
444 auto& leaves =
levels.back();
445 tbb::enumerable_thread_specific<size_t> local_num(0);
446 tbb::parallel_for(
size_t(0),
level_sizes.back(), [&](
size_t k){
447 auto& leaf = leaves[k];
448 auto& [s_begin, s_end] = leaf.source_range;
449 auto& [t_begin, t_end] = leaf.target_range;
450 t_end = s_end;
451 t_begin = s_begin;
452 for (size_t i = s_begin; i < s_end; ++i)
453 {
454 if (particles[i].q != Zero)
455 {
456 std::swap(particles[t_begin], particles[i]);
457 std::swap(inverse_positions_map[t_begin++], inverse_positions_map[i]);
458 }
459 }
460 s_end = t_begin;
461 local_num.local() += t_end - t_begin;
462
463 });
464
465 tbb::parallel_for(size_t(0), num_particles, [&](size_t i){
467 });
468
469 for (const auto& x : local_num)
471
472
473 }
474
475
476 t = omp_get_wtime();
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502}
particle< point, value > particle_t
std::vector< std::vector< TreeCell_t > > levels
std::vector< particle_t > particles
std::vector< size_t > level_sizes
std::vector< size_t > positions_map
std::pair< point, double > box_extent(const std::vector< particle< point, value > > &particles)
constexpr T MyPow(T base, unsigned int exp)
point PointFromMortonCode(size_t val, int depth, point shift, double scale)
constexpr double FORCE_EPS
Vector3< double > Vector3d