25void FastMultipole::Multipole(std::complex<double>* a,
const std::pair<size_t, size_t>& particle_range,
const std::complex<double>& z0)
27 auto p_begin =
tree->particles.begin() + particle_range.first;
28 auto p_end =
tree->particles.begin() + particle_range.second;
29 for (
int i = 1; i <
N; ++i)
31 auto temp = std::accumulate(p_begin, p_end, 0.0i, [&z0, i](
const complex<double>& x,
const particle2d& y)
35 a[i] = -temp / double(i);
38 a[0] = std::accumulate(p_begin, p_end, 0.0i, [](
const complex<double>& x,
const particle2d& y) {
return x + y.
q; });
104 double t1 = omp_get_wtime();
106 auto& leaves =
tree->levels.back();
109 auto [Begin, End] = LocalPart(0,
tree->level_sizes[
tree->tree_depth - 1]);
111 size_t Begin = 0, End =
tree->level_sizes[
tree->tree_depth - 1];
113 tbb::parallel_for(Begin, End, [&](
size_t i) {
115 Multipole(leaves_outer.data() +
N * i, cell.source_range, cell.center);
118 std::vector<std::complex<double>> buf(leaves_outer.size());
119 std::vector<int> sizes(NProc());
120 std::vector<int> displs(NProc());
121 for (
int i = 0; i < NProc(); ++i)
123 sizes[i] =
N * LocalPart(0,
tree->level_sizes[
tree->tree_depth - 1], i);
125 for (
int i = 1; i < NProc(); ++i)
127 displs[i] = displs[i - 1] + sizes[i - 1];
129 MPI_Allgatherv(leaves_outer.data() + displs[MyID()], sizes[MyID()], MPI_COMPLEX16,
130 buf.data(), sizes.data(), displs.data(), MPI_COMPLEX16, MPI_COMM_WORLD);
131 std::swap(buf, leaves_outer);
135 t1 = omp_get_wtime();
137 for (
int i = (
int)(
tree->tree_depth - 2); i >= 0; --i)
139 auto& top_level =
tree->levels[i];
141 const auto& bottom_level =
tree->levels[i + 1];
144 auto [Begin, End] = LocalPart(0,
tree->level_sizes[i]);
146 size_t Begin = 0, End =
tree->level_sizes[i];
148 tbb::parallel_for(Begin, End, [&](
size_t j)
152 const auto& [begin, end] = cell.source_range;
154 for (
auto k = begin; k < end; ++k)
156 const auto& children_cell = bottom_level[k];
157 M2M(bottom_outer.data() + k *
N, children_cell.center - cell.center, top_outer.data() + j *
N, work);
163 std::vector<std::complex<double>> buf(top_outer.size());
164 for (
int j = 0; j < NProc(); ++j)
166 sizes[j] =
N * LocalPart(0,
tree->level_sizes[i], j);
168 for (
int j = 1; j < NProc(); ++j)
170 displs[j] = displs[j - 1] + sizes[j - 1];
172 MPI_Allgatherv(top_outer.data() + displs[MyID()], sizes[MyID()], MPI_COMPLEX16,
173 buf.data(), sizes.data(), displs.data(), MPI_COMPLEX16, MPI_COMM_WORLD);
174 std::swap(buf, top_outer);
185 std::vector<int> sizes(NProc());
186 std::vector<int> displs(NProc());
189 for (
int i = 2; i <
tree->tree_depth; ++i)
191 const auto& top_level =
tree->levels[i - 1];
193 auto& bottom_level =
tree->levels[i];
198 auto [Begin, End] = LocalPart(0,
tree->level_sizes[i]);
200 size_t Begin = 0, End =
tree->level_sizes[i];
203 tbb::parallel_for(Begin, End, [&](
size_t j)
207 auto& parent_cell = top_level[cell.parent];
208 L2L(top_inner.data() +
N * cell.parent, parent_cell.center - cell.center, bottom_inner.data() +
N * j);
210 for (
const auto& idx : cell.farneighbours)
212 const auto& far_neighbour = bottom_level[idx];
213 M2L(bottom_outer.data() + idx *
N, far_neighbour.center - cell.center, bottom_inner.data() +
N * j, work);
219 std::vector<std::complex<double>> buf(bottom_inner.size());
220 for (
int j = 0; j < NProc(); ++j)
222 sizes[j] =
N * LocalPart(0,
tree->level_sizes[i], j);
224 for (
int j = 1; j < NProc(); ++j)
226 displs[j] = displs[j - 1] + sizes[j - 1];
228 MPI_Allgatherv(bottom_inner.data() + displs[MyID()], sizes[MyID()], MPI_COMPLEX16,
229 buf.data(), sizes.data(), displs.data(), MPI_COMPLEX16, MPI_COMM_WORLD);
230 std::swap(buf, bottom_inner);
238 tbb::enumerable_thread_specific<std::complex<double>> dz_local;
239 tbb::enumerable_thread_specific<double> phi_local;
240 tbb::enumerable_thread_specific<std::vector<double>> buf_potentials_local((std::vector<double>(
num_particles)));
242 const auto& leaves =
tree->levels.back();
244 auto& particles =
tree->particles;
247 auto [Begin, End] = LocalPart(0,
tree->level_sizes.back());
249 size_t Begin = 0, End =
tree->level_sizes.back();
252 decltype(&TreeCell2d::source_range) range_ptr;
253 if (
tree->targets_num == 0)
254 range_ptr = &TreeCell2d::source_range;
256 range_ptr = &TreeCell2d::target_range;
257 const auto& index_mapping =
tree->positions_map;
259 tbb::parallel_for(Begin, End, [&](
size_t i) {
260 auto& dz = dz_local.local();
261 auto& phi = phi_local.local();
262 const auto& cell = leaves[i];
263 const auto& inner = leaves_inner.data() + i *
N;
264 auto& buf_potentials = buf_potentials_local.local();
266 const auto& [p1_begin, p1_end] = cell.*range_ptr;
268 for (
auto j = p1_begin; j < p1_end; ++j)
272 if (range_ptr == &TreeCell2d::target_range &&
particle.
q == 0)
276 const auto& [p2_begin, p2_end] = cell.source_range;
277 for (auto k = p2_begin; k < p2_end; ++k)
278 phi += Potential2d(particle, particles[k]);
280 for (const auto& idx : cell.closeneighbours)
282 const auto& neighbour_cell = leaves[idx];
283 const auto& [p2_begin, p2_end] = neighbour_cell.source_range;
285 for (auto k = p2_begin; k < p2_end; ++k)
286 phi += Potential2d(particle, particles[k]);
293 for (
auto k = j + 1; k < p1_end; ++k)
298 for (
const auto& idx : cell.closeneighbours)
300 const auto& neighbour_cell = leaves[idx];
301 const auto& [p2_begin, p2_end] = neighbour_cell.source_range;
303 for (
auto k = p2_begin; k < p2_end; ++k)
307 buf_potentials[j] += phi;
310 for (
auto j = p1_begin; j < p1_end; ++j)
315 complex<double> dzpow{ 1.0 };
316 for (
int k = 0; k <
N; ++k)
318 phi += inner[k].real() * dzpow.real() - inner[k].imag() * dzpow.imag();
321 buf_potentials[j] += phi;
325 auto& buf_potentials = *buf_potentials_local.begin();
326 for (
auto it = buf_potentials_local.begin() + 1; it < buf_potentials_local.end(); it++)
329 tbb::parallel_for(tbb::blocked_range<size_t>(0, num_particles),
330 [&](tbb::blocked_range<size_t> r) {
331 for (
auto i = r.begin(); i < r.end(); ++i)
332 buf_potentials[i] += x[i];
337 AllReduce(buf_potentials.data(), buf_potentials.size());
340 tbb::parallel_for(
size_t(0), targets_num, [&](
size_t i){
341 potentials[i] = buf_potentials[index_mapping[i]];
345void FastMultipole::ComputeForces()
347 tbb::enumerable_thread_specific<std::complex<double>> dz_local;
348 tbb::enumerable_thread_specific<std::complex<double>> force_local;
349 tbb::enumerable_thread_specific<std::vector<std::complex<double>>> buf_forces_local((std::vector<std::complex<double>>(num_particles)));
351 const auto& leaves = tree->levels.back();
352 const auto& leaves_inner = inner_expansions.back();
353 auto& particles = tree->particles;
356 auto [Begin, End] = LocalPart(0, tree->level_sizes.back());
358 size_t Begin = 0, End = tree->level_sizes.back();
361 decltype(&TreeCell2d::source_range) range_ptr;
362 if (tree->targets_num == 0)
363 range_ptr = &TreeCell2d::source_range;
365 range_ptr = &TreeCell2d::target_range;
366 const auto& index_mapping = tree->positions_map;
368 tbb::parallel_for(Begin, End, [&](
size_t i) {
369 auto& dz = dz_local.local();
370 auto& force = force_local.local();
371 const auto& cell = leaves[i];
372 const auto& inner = leaves_inner.data() + i * N;
373 auto& buf_forces = buf_forces_local.local();
375 const auto& [p1_begin, p1_end] = cell.*range_ptr;
377 for (
auto j = p1_begin; j < p1_end; ++j)
381 if (range_ptr == &TreeCell2d::target_range &&
particle.
q == 0)
385 const auto& [p2_begin, p2_end] = cell.source_range;
386 for (auto k = p2_begin; k < p2_end; ++k)
387 force += Force2d(particle, particles[k]);
389 for (const auto& idx : cell.closeneighbours)
391 const auto& neighbour_cell = leaves[idx];
392 const auto& [p2_begin, p2_end] = neighbour_cell.source_range;
394 for (auto k = p2_begin; k < p2_end; ++k)
395 force += Force2d(particle, particles[k]);
402 for (
auto k = j + 1; k < p1_end; ++k)
407 for (
const auto& idx : cell.closeneighbours)
409 const auto& neighbour_cell = leaves[idx];
410 const auto& [p2_begin, p2_end] = neighbour_cell.source_range;
412 for (
auto k = p2_begin; k < p2_end; ++k)
416 buf_forces[j] += force;
419 for (
auto j = p1_begin; j < p1_end; ++j)
424 complex<double> dzpow{ 1.0 };
425 for (
int k = 1; k < N; ++k)
427 force += double(k) * inner[k] * dzpow;
430 buf_forces[j] += force;
434 auto& buf_forces = *buf_forces_local.begin();
435 for (
auto it = buf_forces_local.begin() + 1; it < buf_forces_local.end(); it++)
438 tbb::parallel_for(tbb::blocked_range<size_t>(0, num_particles),
439 [&](tbb::blocked_range<size_t> r) {
440 for (
auto i = r.begin(); i < r.end(); ++i)
441 buf_forces[i] += x[i];
446 AllReduce(buf_forces.data(), buf_forces.size());
449 tbb::parallel_for(
size_t(0), targets_num, [&](
size_t i){
450 forces[i] = buf_forces[index_mapping[i]];
467FastMultipole::FastMultipole(
const std::vector<particle2d>& source_particles,
const std::vector<particle2d>& target_particles,
double eps,
int N_,
int tree_depth_)
469 std::vector<particle2d> particles(source_particles.size() + target_particles.size());
470 memcpy(particles.data(), target_particles.data(), target_particles.size() *
sizeof(
particle2d));
471 memcpy(particles.data() + target_particles.size(), source_particles.data(), source_particles.size() *
sizeof(
particle2d));
472 targets_num = target_particles.size();
473 Solve(std::move(particles), eps, N_, tree_depth_);
476void FastMultipole::Solve(std::vector<particle2d>&& particles,
double eps,
int N_,
int tree_depth_)
478 double T = omp_get_wtime();
480 num_particles = particles.size();
487 size_t initial_depth;
489 initial_depth = std::max((
size_t)4,
size_t(log(
double(num_particles)) / log(4.0) + 0.5)) - 1;
492 initial_depth = tree_depth_;
495 tree = std::make_shared<MortonTree2d>(std::move(particles), initial_depth);
496 tree_depth = tree->tree_depth;
502 double t = omp_get_wtime();
503 outer_expansions.resize(tree_depth);
504 inner_expansions.resize(tree_depth);
505 for (
int i = 0; i < tree->tree_depth; ++i)
507 auto& outer = outer_expansions[i];
508 auto& inner = inner_expansions[i];
509 outer.resize(tree->level_sizes[i] * N);
510 inner.resize(tree->level_sizes[i] * N);
512 forces.resize(targets_num);
513 potentials.resize(targets_num);
515 local_work = tbb::enumerable_thread_specific<vector<complex<double>>>{ vector<complex<double>>(N) };
532 if (
IAmRoot()) std::cout <<
"FMM time ConvVelo Wake: " << (omp_get_wtime() - T) * 1000.0 << std::endl;