Reference documentation for deal.II version 9.3.3
\(\newcommand{\dealvcentcolon}{\mathrel{\mathop{:}}}\) \(\newcommand{\dealcoloneq}{\dealvcentcolon\mathrel{\mkern-1.2mu}=}\) \(\newcommand{\jump}[1]{\left[\!\left[ #1 \right]\!\right]}\) \(\newcommand{\average}[1]{\left\{\!\left\{ #1 \right\}\!\right\}}\)
particle_handler.cc
Go to the documentation of this file.
1// ---------------------------------------------------------------------
2//
3// Copyright (C) 2017 - 2021 by the deal.II authors
4//
5// This file is part of the deal.II library.
6//
7// The deal.II library is free software; you can use it, redistribute
8// it, and/or modify it under the terms of the GNU Lesser General
9// Public License as published by the Free Software Foundation; either
10// version 2.1 of the License, or (at your option) any later version.
11// The full text of the license can be found in the file LICENSE.md at
12// the top level directory of deal.II.
13//
14// ---------------------------------------------------------------------
15
18
20
22
23#include <memory>
24#include <utility>
25
27
28namespace Particles
29{
30 namespace
31 {
32 template <int dim, int spacedim>
33 std::vector<char>
34 pack_particles(std::vector<Particle<dim, spacedim>> &particles)
35 {
36 std::vector<char> buffer;
37
38 if (particles.size() == 0)
39 return buffer;
40
41 buffer.resize(particles.size() *
42 particles.front().serialized_size_in_bytes());
43 void *current_data = buffer.data();
44
45 for (const auto &particle : particles)
46 {
47 current_data = particle.write_particle_data_to_memory(current_data);
48 }
49
50 return buffer;
51 }
52
53
54
55 template <int dim, int spacedim>
56 std::vector<Particle<dim, spacedim>>
57 unpack_particles(
58 const boost::iterator_range<std::vector<char>::const_iterator>
59 & data_range,
60 PropertyPool<dim, spacedim> &property_pool)
61 {
62 std::vector<Particle<dim, spacedim>> particles;
63
64 if (data_range.empty())
65 return particles;
66
67 Particle<dim, spacedim> particle;
68 particle.set_property_pool(property_pool);
69 const unsigned int particle_size = particle.serialized_size_in_bytes();
70
71 particles.reserve(data_range.size() / particle_size);
72
73 const void *data = static_cast<const void *>(&(*data_range.begin()));
74
75 while (data < &(*data_range.end()))
76 {
77 particles.emplace_back(data, &property_pool);
78 }
79
80 Assert(
81 data == &(*data_range.end()),
83 "The particle data could not be deserialized successfully. "
84 "Check that when deserializing the particles you expect the same "
85 "number of properties that were serialized."));
86
87 return particles;
88 }
89 } // namespace
90
91 template <int dim, int spacedim>
94 , mapping()
95 , property_pool(std::make_unique<PropertyPool<dim, spacedim>>(0))
96 , particles()
97 , ghost_particles()
98 , global_number_of_particles(0)
99 , global_max_particles_per_cell(0)
100 , next_free_particle_index(0)
101 , size_callback()
102 , store_callback()
103 , load_callback()
105 {}
106
107
108
109 template <int dim, int spacedim>
112 const Mapping<dim, spacedim> & mapping,
113 const unsigned int n_properties)
114 : triangulation(&triangulation, typeid(*this).name())
115 , mapping(&mapping, typeid(*this).name())
116 , property_pool(std::make_unique<PropertyPool<dim, spacedim>>(n_properties))
117 , particles()
118 , ghost_particles()
119 , global_number_of_particles(0)
120 , global_max_particles_per_cell(0)
121 , next_free_particle_index(0)
122 , size_callback()
123 , store_callback()
124 , load_callback()
126 {
128 std::make_unique<GridTools::Cache<dim, spacedim>>(triangulation, mapping);
129 }
130
131
132
133 template <int dim, int spacedim>
134 void
136 const Triangulation<dim, spacedim> &new_triangulation,
137 const Mapping<dim, spacedim> & new_mapping,
138 const unsigned int n_properties)
139 {
140 triangulation = &new_triangulation;
141 mapping = &new_mapping;
142
143 // Create the memory pool that will store all particle properties
144 property_pool = std::make_unique<PropertyPool<dim, spacedim>>(n_properties);
145
146 // Create the grid cache to cache the information about the triangulation
147 // that is used to locate the particles into subdomains and cells
148 triangulation_cache =
149 std::make_unique<GridTools::Cache<dim, spacedim>>(new_triangulation,
150 new_mapping);
151 }
152
153
154
155 template <int dim, int spacedim>
156 void
158 const ParticleHandler<dim, spacedim> &particle_handler)
159 {
160 // clear and initialize this object before copying particles
161 clear();
162 const unsigned int n_properties =
163 particle_handler.property_pool->n_properties_per_slot();
164 initialize(*particle_handler.triangulation,
165 *particle_handler.mapping,
166 n_properties);
167 property_pool->reserve(particle_handler.particles.size() +
168 particle_handler.ghost_particles.size());
169
170 // copy static members
171 global_number_of_particles = particle_handler.global_number_of_particles;
172 global_max_particles_per_cell =
173 particle_handler.global_max_particles_per_cell;
174 next_free_particle_index = particle_handler.next_free_particle_index;
175 particles = particle_handler.particles;
176 ghost_particles = particle_handler.ghost_particles;
177
178 ghost_particles_cache.ghost_particles_by_domain =
179 particle_handler.ghost_particles_cache.ghost_particles_by_domain;
180 handle = particle_handler.handle;
181
182 // copy dynamic properties
183 auto from_particle = particle_handler.begin();
184 for (auto &particle : *this)
185 {
186 particle.set_property_pool(*property_pool);
187 ++from_particle;
188 }
189
190 auto from_ghost = particle_handler.begin_ghost();
191 for (auto ghost = begin_ghost(); ghost != end_ghost();
192 ++ghost, ++from_ghost)
193 {
194 ghost->set_property_pool(*property_pool);
195 }
196 }
197
198
199
200 template <int dim, int spacedim>
201 void
203 {
204 clear_particles();
205 global_number_of_particles = 0;
206 next_free_particle_index = 0;
207 global_max_particles_per_cell = 0;
208 }
209
210
211
212 template <int dim, int spacedim>
213 void
215 {
216 particles.clear();
217 ghost_particles.clear();
218
219 // the particle properties have already been deleted by their destructor,
220 // but the memory is still allocated. Return the memory as well.
221 property_pool->clear();
222 }
223
224
225
226 template <int dim, int spacedim>
227 void
229 {
230 types::particle_index locally_highest_index = 0;
231 unsigned int local_max_particles_per_cell = 0;
232 unsigned int current_particles_per_cell = 0;
234 triangulation->begin_active();
235
236 for (const auto &particle : *this)
237 {
238 locally_highest_index =
239 std::max(locally_highest_index, particle.get_id());
240
241 if (particle.get_surrounding_cell(*triangulation) != current_cell)
242 {
243 current_particles_per_cell = 0;
244 current_cell = particle.get_surrounding_cell(*triangulation);
245 }
246
247 ++current_particles_per_cell;
248 local_max_particles_per_cell =
249 std::max(local_max_particles_per_cell, current_particles_per_cell);
250 }
251
252 if (const auto parallel_triangulation =
253 dynamic_cast<const parallel::TriangulationBase<dim, spacedim> *>(
254 &*triangulation))
255 {
256 global_number_of_particles = ::Utilities::MPI::sum(
257 particles.size(), parallel_triangulation->get_communicator());
258 next_free_particle_index =
259 global_number_of_particles == 0 ?
260 0 :
262 locally_highest_index,
263 parallel_triangulation->get_communicator()) +
264 1;
265 global_max_particles_per_cell = ::Utilities::MPI::max(
266 local_max_particles_per_cell,
267 parallel_triangulation->get_communicator());
268 }
269 else
270 {
271 global_number_of_particles = particles.size();
272 next_free_particle_index =
273 global_number_of_particles == 0 ? 0 : locally_highest_index + 1;
274 global_max_particles_per_cell = local_max_particles_per_cell;
275 }
276 }
277
278
279
280 template <int dim, int spacedim>
284 const
285 {
286 const internal::LevelInd found_cell =
287 std::make_pair(cell->level(), cell->index());
288
289 if (cell->is_locally_owned())
290 return particles.count(found_cell);
291 else if (cell->is_ghost())
292 return ghost_particles.count(found_cell);
293 else
294 AssertThrow(false,
295 ExcMessage("You can't ask for the particles on an artificial "
296 "cell since we don't know what exists on these "
297 "kinds of cells."));
298
300 }
301
302
303
304 template <int dim, int spacedim>
308 const
309 {
310 return (const_cast<ParticleHandler<dim, spacedim> *>(this))
311 ->particles_in_cell(cell);
312 }
313
314
315
316 template <int dim, int spacedim>
320 {
321 const internal::LevelInd level_index =
322 std::make_pair(cell->level(), cell->index());
323
324 if (cell->is_ghost())
325 {
326 const auto particles_in_cell = ghost_particles.equal_range(level_index);
328 particle_iterator(ghost_particles, particles_in_cell.first),
329 particle_iterator(ghost_particles, particles_in_cell.second));
330 }
331 else if (cell->is_locally_owned())
332 {
333 const auto particles_in_cell = particles.equal_range(level_index);
335 particle_iterator(particles, particles_in_cell.first),
336 particle_iterator(particles, particles_in_cell.second));
337 }
338 else
339 AssertThrow(false,
340 ExcMessage("You can't ask for the particles on an artificial "
341 "cell since we don't know what exists on these "
342 "kinds of cells."));
343
344 return {};
345 }
346
347
348
349 template <int dim, int spacedim>
350 void
353 {
354 particles.erase(particle->particle);
355 }
356
357
358
359 template <int dim, int spacedim>
362 const Particle<dim, spacedim> & particle,
364 {
365 typename std::multimap<internal::LevelInd,
366 Particle<dim, spacedim>>::iterator it =
367 particles.insert(
368 std::make_pair(internal::LevelInd(cell->level(), cell->index()),
369 particle));
370
371 particle_iterator particle_it(particles, it);
372 particle_it->set_property_pool(*property_pool);
373
374 if (particle.has_properties())
375 for (unsigned int n = 0; n < particle.get_properties().size(); ++n)
376 particle_it->get_properties()[n] = particle.get_properties()[n];
377
378 return particle_it;
379 }
380
381
382
383 template <int dim, int spacedim>
384 void
386 const std::multimap<
388 Particle<dim, spacedim>> &new_particles)
389 {
390 for (const auto &particle : new_particles)
391 {
392 // Insert the particle. Store an iterator to the newly
393 // inserted particle, and then set its property_pool.
394 auto it = particles.insert(
395 particles.end(),
396 std::make_pair(internal::LevelInd(particle.first->level(),
397 particle.first->index()),
398 particle.second));
399 it->second.set_property_pool(*property_pool);
400 }
401
402 update_cached_numbers();
403 }
404
405
406
407 template <int dim, int spacedim>
408 void
410 const std::vector<Point<spacedim>> &positions)
411 {
412 update_cached_numbers();
413
414 // Determine the starting particle index of this process, which
415 // is the highest currently existing particle index plus the sum
416 // of the number of newly generated particles of all
417 // processes with a lower rank if in a parallel computation.
418 const types::particle_index local_next_particle_index =
419 get_next_free_particle_index();
420 types::particle_index local_start_index = 0;
421
422#ifdef DEAL_II_WITH_MPI
423 if (const auto parallel_triangulation =
424 dynamic_cast<const parallel::TriangulationBase<dim, spacedim> *>(
425 &*triangulation))
426 {
427 types::particle_index particles_to_add_locally = positions.size();
428 const int ierr = MPI_Scan(&particles_to_add_locally,
429 &local_start_index,
430 1,
432 MPI_SUM,
433 parallel_triangulation->get_communicator());
434 AssertThrowMPI(ierr);
435 local_start_index -= particles_to_add_locally;
436 }
437#endif
438
439 local_start_index += local_next_particle_index;
440
441 auto point_locations =
443 positions);
444
445 auto &cells = std::get<0>(point_locations);
446 auto &local_positions = std::get<1>(point_locations);
447 auto &index_map = std::get<2>(point_locations);
448 auto &missing_points = std::get<3>(point_locations);
449 // If a point was not found, throwing an error, as the old
450 // implementation of compute_point_locations would have done
451 AssertThrow(missing_points.size() == 0,
453
454 (void)missing_points;
455
456 if (cells.size() == 0)
457 return;
458
459 auto hint =
460 particles.find(std::make_pair(cells[0]->level(), cells[0]->index()));
461 for (unsigned int i = 0; i < cells.size(); ++i)
462 {
463 internal::LevelInd current_cell(cells[i]->level(), cells[i]->index());
464 for (unsigned int p = 0; p < local_positions[i].size(); ++p)
465 {
466 hint = particles.insert(
467 hint,
468 std::make_pair(current_cell,
469 Particle<dim, spacedim>(positions[index_map[i][p]],
470 local_positions[i][p],
471 local_start_index +
472 index_map[i][p])));
473
474 hint->second.set_property_pool(*property_pool);
475 }
476 }
477
478 update_cached_numbers();
479 }
480
481
482
483 template <int dim, int spacedim>
484 std::map<unsigned int, IndexSet>
486 const std::vector<Point<spacedim>> &positions,
487 const std::vector<std::vector<BoundingBox<spacedim>>>
488 & global_bounding_boxes,
489 const std::vector<std::vector<double>> & properties,
490 const std::vector<types::particle_index> &ids)
491 {
492 if (!properties.empty())
493 {
494 AssertDimension(properties.size(), positions.size());
495#ifdef DEBUG
496 for (const auto &p : properties)
497 AssertDimension(p.size(), n_properties_per_particle());
498#endif
499 }
500
501 if (!ids.empty())
502 AssertDimension(ids.size(), positions.size());
503
504 const auto comm = triangulation->get_communicator();
505
507
508 // Compute the global number of properties
509 const auto n_global_properties =
510 Utilities::MPI::sum(properties.size(), comm);
511
512 // Gather the number of points per processor
513 const auto n_particles_per_proc =
514 Utilities::MPI::all_gather(comm, positions.size());
515
516 // Calculate all starting points locally
517 std::vector<unsigned int> particle_start_indices(n_mpi_processes);
518
519 unsigned int particle_start_index = get_next_free_particle_index();
520 for (unsigned int process = 0; process < particle_start_indices.size();
521 ++process)
522 {
523 particle_start_indices[process] = particle_start_index;
524 particle_start_index += n_particles_per_proc[process];
525 }
526
527 // Get all local information
528 const auto cells_positions_and_index_maps =
530 positions,
531 global_bounding_boxes);
532
533 // Unpack the information into several vectors:
534 // All cells that contain at least one particle
535 const auto &local_cells_containing_particles =
536 std::get<0>(cells_positions_and_index_maps);
537
538 // The reference position of every particle in the local part of the
539 // triangulation.
540 const auto &local_reference_positions =
541 std::get<1>(cells_positions_and_index_maps);
542 // The original index in the positions vector for each particle in the
543 // local part of the triangulation
544 const auto &original_indices_of_local_particles =
545 std::get<2>(cells_positions_and_index_maps);
546 // The real spatial position of every particle in the local part of the
547 // triangulation.
548 const auto &local_positions = std::get<3>(cells_positions_and_index_maps);
549 // The MPI process that inserted each particle
550 const auto &calling_process_indices =
551 std::get<4>(cells_positions_and_index_maps);
552
553 // Create the map of cpu to indices, indicating who sent us what particle
554 std::map<unsigned int, std::vector<unsigned int>>
555 original_process_to_local_particle_indices_tmp;
556 for (unsigned int i_cell = 0;
557 i_cell < local_cells_containing_particles.size();
558 ++i_cell)
559 {
560 for (unsigned int i_particle = 0;
561 i_particle < local_positions[i_cell].size();
562 ++i_particle)
563 {
564 const unsigned int local_id_on_calling_process =
565 original_indices_of_local_particles[i_cell][i_particle];
566 const unsigned int calling_process =
567 calling_process_indices[i_cell][i_particle];
568
569 original_process_to_local_particle_indices_tmp[calling_process]
570 .push_back(local_id_on_calling_process);
571 }
572 }
573 std::map<unsigned int, IndexSet> original_process_to_local_particle_indices;
574 for (auto &process_and_particle_indices :
575 original_process_to_local_particle_indices_tmp)
576 {
577 const unsigned int calling_process = process_and_particle_indices.first;
578 original_process_to_local_particle_indices.insert(
579 {calling_process, IndexSet(n_particles_per_proc[calling_process])});
580 std::sort(process_and_particle_indices.second.begin(),
581 process_and_particle_indices.second.end());
582 original_process_to_local_particle_indices[calling_process].add_indices(
583 process_and_particle_indices.second.begin(),
584 process_and_particle_indices.second.end());
585 original_process_to_local_particle_indices[calling_process].compress();
586 }
587
588 // A map from mpi process to properties, ordered as in the IndexSet.
589 // Notice that this ordering may be different from the ordering in the
590 // vectors above, since no local ordering is guaranteed by the
591 // distribute_compute_point_locations() call.
592 // This is only filled if n_global_properties is > 0
593 std::map<unsigned int, std::vector<std::vector<double>>>
594 locally_owned_properties_from_other_processes;
595
596 // A map from mpi process to ids, ordered as in the IndexSet.
597 // Notice that this ordering may be different from the ordering in the
598 // vectors above, since no local ordering is guaranteed by the
599 // distribute_compute_point_locations() call.
600 // This is only filled if ids.size() is > 0
601 std::map<unsigned int, std::vector<types::particle_index>>
602 locally_owned_ids_from_other_processes;
603
604 if (n_global_properties > 0 || !ids.empty())
605 {
606 // Gather whom I sent my own particles to, to decide whom to send
607 // the particle properties or the ids
608 auto send_to_cpu = Utilities::MPI::some_to_some(
609 comm, original_process_to_local_particle_indices);
610
611 // Prepare the vector of properties to send
612 if (n_global_properties > 0)
613 {
614 std::map<unsigned int, std::vector<std::vector<double>>>
615 non_locally_owned_properties;
616
617 for (const auto &it : send_to_cpu)
618 {
619 std::vector<std::vector<double>> properties_to_send(
620 it.second.n_elements(),
621 std::vector<double>(n_properties_per_particle()));
622 unsigned int index = 0;
623 for (const auto el : it.second)
624 properties_to_send[index++] = properties[el];
625 non_locally_owned_properties.insert(
626 {it.first, properties_to_send});
627 }
628
629 // Send the non locally owned properties to each mpi process
630 // that needs them
631 locally_owned_properties_from_other_processes =
632 Utilities::MPI::some_to_some(comm, non_locally_owned_properties);
633
635 locally_owned_properties_from_other_processes.size(),
636 original_process_to_local_particle_indices.size());
637 }
638
639 if (!ids.empty())
640 {
641 std::map<unsigned int, std::vector<types::particle_index>>
642 non_locally_owned_ids;
643 for (const auto &it : send_to_cpu)
644 {
645 std::vector<types::particle_index> ids_to_send(
646 it.second.n_elements());
647 unsigned int index = 0;
648 for (const auto el : it.second)
649 ids_to_send[index++] = ids[el];
650 non_locally_owned_ids.insert({it.first, ids_to_send});
651 }
652
653 // Send the non locally owned ids to each mpi process
654 // that needs them
655 locally_owned_ids_from_other_processes =
656 Utilities::MPI::some_to_some(comm, non_locally_owned_ids);
657
658 AssertDimension(locally_owned_ids_from_other_processes.size(),
659 original_process_to_local_particle_indices.size());
660 }
661 }
662
663
664 // Create the multimap of local particles
665 std::multimap<typename Triangulation<dim, spacedim>::active_cell_iterator,
667 particles;
668
669 // Now fill up the actual particles
670 for (unsigned int i_cell = 0;
671 i_cell < local_cells_containing_particles.size();
672 ++i_cell)
673 {
674 for (unsigned int i_particle = 0;
675 i_particle < local_positions[i_cell].size();
676 ++i_particle)
677 {
678 const unsigned int local_id_on_calling_process =
679 original_indices_of_local_particles[i_cell][i_particle];
680
681 const unsigned int calling_process =
682 calling_process_indices[i_cell][i_particle];
683
684 const unsigned int index_within_set =
685 original_process_to_local_particle_indices[calling_process]
686 .index_within_set(local_id_on_calling_process);
687
688 const unsigned int particle_id =
689 ids.empty() ?
690 local_id_on_calling_process +
691 particle_start_indices[calling_process] :
692 locally_owned_ids_from_other_processes[calling_process]
693 [index_within_set];
694
696 local_positions[i_cell][i_particle],
697 local_reference_positions[i_cell][i_particle],
698 particle_id);
699
700 particle.set_property_pool(get_property_pool());
701
702 if (n_global_properties > 0)
703 {
704 const auto &this_particle_properties =
705 locally_owned_properties_from_other_processes
706 [calling_process][index_within_set];
707
708 particle.set_properties(this_particle_properties);
709 }
710
711 particles.emplace(local_cells_containing_particles[i_cell],
712 std::move(particle));
713 }
714 }
715
716 this->insert_particles(particles);
717
718 return original_process_to_local_particle_indices;
719 }
720
721
722
723 template <int dim, int spacedim>
724 std::map<unsigned int, IndexSet>
726 const std::vector<Particle<dim, spacedim>> &particles,
727 const std::vector<std::vector<BoundingBox<spacedim>>>
728 &global_bounding_boxes)
729 {
730 // Store the positions in a vector of points, the ids in a vector of ids,
731 // and the properties, if any, in a vector of vector of properties.
732 std::vector<Point<spacedim>> positions;
733 std::vector<std::vector<double>> properties;
734 std::vector<types::particle_index> ids;
735 positions.resize(particles.size());
736 ids.resize(particles.size());
737 if (n_properties_per_particle() > 0)
738 properties.resize(particles.size(),
739 std::vector<double>(n_properties_per_particle()));
740
741 unsigned int i = 0;
742 for (const auto &p : particles)
743 {
744 positions[i] = p.get_location();
745 ids[i] = p.get_id();
746 if (p.has_properties())
747 properties[i] = {p.get_properties().begin(),
748 p.get_properties().end()};
749 ++i;
750 }
751
752 return insert_global_particles(positions,
753 global_bounding_boxes,
754 properties,
755 ids);
756 }
757
758
759
760 template <int dim, int spacedim>
763 {
764 return global_number_of_particles;
765 }
766
767
768
769 template <int dim, int spacedim>
772 {
773 return global_max_particles_per_cell;
774 }
775
776
777
778 template <int dim, int spacedim>
781 {
782 return particles.size();
783 }
784
785
786
787 template <int dim, int spacedim>
788 unsigned int
790 {
791 return property_pool->n_properties_per_slot();
792 }
793
794
795
796 template <int dim, int spacedim>
799 {
800 return next_free_particle_index;
801 }
802
803
804
805 template <int dim, int spacedim>
808 {
809 IndexSet set(get_next_free_particle_index());
810 for (const auto &p : *this)
811 set.add_index(p.get_id());
812 set.compress();
813 return set;
814 }
815
816
817
818 template <int dim, int spacedim>
819 void
821 std::vector<Point<spacedim>> &positions,
822 const bool add_to_output_vector)
823 {
824 // There should be one point per particle to gather
825 AssertDimension(positions.size(), n_locally_owned_particles());
826
827 unsigned int i = 0;
828 for (auto it = begin(); it != end(); ++it, ++i)
829 {
830 if (add_to_output_vector)
831 positions[i] = positions[i] + it->get_location();
832 else
833 positions[i] = it->get_location();
834 }
835 }
836
837
838
839 template <int dim, int spacedim>
840 void
842 const std::vector<Point<spacedim>> &new_positions,
843 const bool displace_particles)
844 {
845 // There should be one point per particle to fix the new position
846 AssertDimension(new_positions.size(), n_locally_owned_particles());
847
848 unsigned int i = 0;
849 for (auto it = begin(); it != end(); ++it, ++i)
850 {
851 if (displace_particles)
852 it->set_location(it->get_location() + new_positions[i]);
853 else
854 it->set_location(new_positions[i]);
855 }
856 sort_particles_into_subdomains_and_cells();
857 }
858
859
860
861 template <int dim, int spacedim>
862 void
864 const Function<spacedim> &function,
865 const bool displace_particles)
866 {
867 // The function should have sufficient components to displace the
868 // particles
869 AssertDimension(function.n_components, spacedim);
870
871 Vector<double> new_position(spacedim);
872 for (auto &particle : *this)
873 {
874 Point<spacedim> particle_location = particle.get_location();
875 function.vector_value(particle_location, new_position);
876 if (displace_particles)
877 for (unsigned int d = 0; d < spacedim; ++d)
878 particle_location[d] += new_position[d];
879 else
880 for (unsigned int d = 0; d < spacedim; ++d)
881 particle_location[d] = new_position[d];
882 particle.set_location(particle_location);
883 }
884 sort_particles_into_subdomains_and_cells();
885 }
886
887
888
889 template <int dim, int spacedim>
892 {
893 return *property_pool;
894 }
895
896
897
898 namespace
899 {
909 template <int dim>
910 bool
911 compare_particle_association(
912 const unsigned int a,
913 const unsigned int b,
914 const Tensor<1, dim> & particle_direction,
915 const std::vector<Tensor<1, dim>> &center_directions)
916 {
917 const double scalar_product_a = center_directions[a] * particle_direction;
918 const double scalar_product_b = center_directions[b] * particle_direction;
919
920 // The function is supposed to return if a is before b. We are looking
921 // for the alignment of particle direction and center direction,
922 // therefore return if the scalar product of a is larger.
923 return (scalar_product_a > scalar_product_b);
924 }
925 } // namespace
926
927
928
929 template <int dim, int spacedim>
930 void
932 {
933 // TODO: The current algorithm only works for particles that are in
934 // the local domain or in ghost cells, because it only knows the
935 // subdomain_id of ghost cells, but not of artificial cells. This
936 // can be extended using the distributed version of compute point
937 // locations.
938 // TODO: Extend this function to allow keeping particles on other
939 // processes around (with an invalid cell).
940
941 std::vector<particle_iterator> particles_out_of_cell;
942 particles_out_of_cell.reserve(n_locally_owned_particles());
943
944 // Now update the reference locations of the moved particles
945 std::vector<Point<spacedim>> real_locations;
946 std::vector<Point<dim>> reference_locations;
947 for (auto particle = begin(); particle != end();)
948 {
949 const auto cell = particle->get_surrounding_cell(*triangulation);
950 real_locations.clear();
951
952 // Since we might also work on artificial cells when we initialize the
953 // particles on a remote processor, we cannot use the
954 // particles_in_cell method. Thus, We instead simply go through the
955 // particles and check if the next one belongs to the same cell as the
956 // current one.
957 for (auto it = particle;
958 it != end() && it->get_surrounding_cell(*triangulation) == cell;
959 ++it)
960 real_locations.push_back(it->get_location());
961
962 reference_locations.resize(real_locations.size());
963 ArrayView<Point<dim>> reference(reference_locations.data(),
964 reference_locations.size());
965 mapping->transform_points_real_to_unit_cell(cell,
966 real_locations,
967 reference);
968
969 for (const auto &p_unit : reference_locations)
970 {
971 if (p_unit[0] == std::numeric_limits<double>::infinity() ||
973 particles_out_of_cell.push_back(particle);
974 else
975 particle->set_reference_location(p_unit);
976 ++particle;
977 }
978 }
979
980 // There are three reasons why a particle is not in its old cell:
981 // It moved to another cell, to another subdomain or it left the mesh.
982 // Particles that moved to another cell are updated and stored inside the
983 // sorted_particles vector, particles that moved to another domain are
984 // collected in the moved_particles_domain vector. Particles that left
985 // the mesh completely are ignored and removed.
986 std::vector<std::pair<internal::LevelInd, Particle<dim, spacedim>>>
987 sorted_particles;
988 std::map<types::subdomain_id, std::vector<particle_iterator>>
989 moved_particles;
990 std::map<
992 std::vector<typename Triangulation<dim, spacedim>::active_cell_iterator>>
993 moved_cells;
994
995 // We do not know exactly how many particles are lost, exchanged between
996 // domains, or remain on this process. Therefore we pre-allocate
997 // approximate sizes for these vectors. If more space is needed an
998 // automatic and relatively fast (compared to other parts of this
999 // algorithm) re-allocation will happen.
1000 using vector_size = typename std::vector<particle_iterator>::size_type;
1001 sorted_particles.reserve(
1002 static_cast<vector_size>(particles_out_of_cell.size() * 1.25));
1003
1004 std::set<types::subdomain_id> ghost_owners;
1005 if (const auto parallel_triangulation =
1006 dynamic_cast<const parallel::TriangulationBase<dim, spacedim> *>(
1007 &*triangulation))
1008 ghost_owners = parallel_triangulation->ghost_owners();
1009
1010 for (const auto ghost_owner : ghost_owners)
1011 moved_particles[ghost_owner].reserve(
1012 static_cast<vector_size>(particles_out_of_cell.size() * 0.25));
1013 for (const auto ghost_owner : ghost_owners)
1014 moved_cells[ghost_owner].reserve(
1015 static_cast<vector_size>(particles_out_of_cell.size() * 0.25));
1016
1017 {
1018 // Create a map from vertices to adjacent cells using grid cache
1019 std::vector<
1020 std::set<typename Triangulation<dim, spacedim>::active_cell_iterator>>
1021 vertex_to_cells = triangulation_cache->get_vertex_to_cell_map();
1022
1023 // Create a corresponding map of vectors from vertex to cell center using
1024 // grid cache
1025 std::vector<std::vector<Tensor<1, spacedim>>> vertex_to_cell_centers =
1026 triangulation_cache->get_vertex_to_cell_centers_directions();
1027
1028 std::vector<unsigned int> neighbor_permutation;
1029
1030 // Find the cells that the particles moved to.
1031 for (auto &out_particle : particles_out_of_cell)
1032 {
1033 // The cell the particle is in
1034 Point<dim> current_reference_position;
1035 bool found_cell = false;
1036
1037 // Check if the particle is in one of the old cell's neighbors
1038 // that are adjacent to the closest vertex
1040 current_cell = out_particle->get_surrounding_cell(*triangulation);
1041
1042 const unsigned int closest_vertex =
1043 GridTools::find_closest_vertex_of_cell<dim, spacedim>(
1044 current_cell, out_particle->get_location(), *mapping);
1045 Tensor<1, spacedim> vertex_to_particle =
1046 out_particle->get_location() - current_cell->vertex(closest_vertex);
1047 vertex_to_particle /= vertex_to_particle.norm();
1048
1049 const unsigned int closest_vertex_index =
1050 current_cell->vertex_index(closest_vertex);
1051 const unsigned int n_neighbor_cells =
1052 vertex_to_cells[closest_vertex_index].size();
1053
1054 neighbor_permutation.resize(n_neighbor_cells);
1055 for (unsigned int i = 0; i < n_neighbor_cells; ++i)
1056 neighbor_permutation[i] = i;
1057
1058 const auto cell_centers =
1059 vertex_to_cell_centers[closest_vertex_index];
1060 std::sort(neighbor_permutation.begin(),
1061 neighbor_permutation.end(),
1062 [&vertex_to_particle, &cell_centers](const unsigned int a,
1063 const unsigned int b) {
1064 return compare_particle_association(a,
1065 b,
1066 vertex_to_particle,
1067 cell_centers);
1068 });
1069
1070 // make a copy of the current cell, since we will modify the variable
1071 // current_cell in the following but we need a backup in the case
1072 // the particle is not found
1073 const auto previous_cell_of_particle = current_cell;
1074
1075 // Search all of the cells adjacent to the closest vertex of the
1076 // previous cell Most likely we will find the particle in them.
1077 for (unsigned int i = 0; i < n_neighbor_cells; ++i)
1078 {
1079 try
1080 {
1081 typename std::set<typename Triangulation<dim, spacedim>::
1082 active_cell_iterator>::const_iterator
1083 cell = vertex_to_cells[closest_vertex_index].begin();
1084
1085 std::advance(cell, neighbor_permutation[i]);
1086 const Point<dim> p_unit =
1087 mapping->transform_real_to_unit_cell(
1088 *cell, out_particle->get_location());
1090 {
1091 current_cell = *cell;
1092 current_reference_position = p_unit;
1093 found_cell = true;
1094 break;
1095 }
1096 }
1097 catch (typename Mapping<dim>::ExcTransformationFailed &)
1098 {}
1099 }
1100
1101 if (!found_cell)
1102 {
1103 // The particle is not in a neighbor of the old cell.
1104 // Look for the new cell in the whole local domain.
1105 // This case is rare.
1106 const std::pair<const typename Triangulation<dim, spacedim>::
1107 active_cell_iterator,
1108 Point<dim>>
1109 current_cell_and_position =
1110 GridTools::find_active_cell_around_point<>(
1111 *triangulation_cache, out_particle->get_location());
1112 current_cell = current_cell_and_position.first;
1113 current_reference_position = current_cell_and_position.second;
1114
1115 if (current_cell.state() != IteratorState::valid)
1116 {
1117 // We can find no cell for this particle. It has left the
1118 // domain due to an integration error or an open boundary.
1119 // Signal the loss and move on.
1120 signals.particle_lost(out_particle,
1121 previous_cell_of_particle);
1122 continue;
1123 }
1124 }
1125
1126 // If we are here, we found a cell and reference position for this
1127 // particle
1128 out_particle->set_reference_location(current_reference_position);
1129
1130 // Reinsert the particle into our domain if we own its cell.
1131 // Mark it for MPI transfer otherwise
1132 if (current_cell->is_locally_owned())
1133 {
1134 sorted_particles.push_back(
1135 std::make_pair(internal::LevelInd(current_cell->level(),
1136 current_cell->index()),
1137 out_particle->particle->second));
1138 }
1139 else
1140 {
1141 moved_particles[current_cell->subdomain_id()].push_back(
1142 out_particle);
1143 moved_cells[current_cell->subdomain_id()].push_back(current_cell);
1144 }
1145 }
1146 }
1147
1148 // Sort the updated particles. This pre-sort speeds up inserting
1149 // them into particles to O(N) complexity.
1150 std::multimap<internal::LevelInd, Particle<dim, spacedim>>
1151 sorted_particles_map;
1152
1153 // Exchange particles between processors if we have more than one process
1154#ifdef DEAL_II_WITH_MPI
1155 if (const auto parallel_triangulation =
1156 dynamic_cast<const parallel::TriangulationBase<dim, spacedim> *>(
1157 &*triangulation))
1158 {
1160 parallel_triangulation->get_communicator()) > 1)
1161 send_recv_particles(moved_particles,
1162 sorted_particles_map,
1163 moved_cells);
1164 }
1165#endif
1166
1167 sorted_particles_map.insert(sorted_particles.begin(),
1168 sorted_particles.end());
1169
1170 for (unsigned int i = 0; i < particles_out_of_cell.size(); ++i)
1171 remove_particle(particles_out_of_cell[i]);
1172
1173 particles.insert(sorted_particles_map.begin(), sorted_particles_map.end());
1174 update_cached_numbers();
1175 }
1176
1177
1178
1179 template <int dim, int spacedim>
1180 void
1182 const bool enable_cache)
1183 {
1184 // Nothing to do in serial computations
1185 const auto parallel_triangulation =
1186 dynamic_cast<const parallel::TriangulationBase<dim, spacedim> *>(
1187 &*triangulation);
1188 if (parallel_triangulation != nullptr)
1189 {
1191 parallel_triangulation->get_communicator()) == 1)
1192 return;
1193 }
1194 else
1195 return;
1196
1197#ifndef DEAL_II_WITH_MPI
1198 (void)enable_cache;
1199#else
1200 // First clear the current ghost_particle information
1201 ghost_particles.clear();
1202
1203 // Clear ghost particles data structures and invalidate cache
1204 ghost_particles_cache.ghost_particles_by_domain.clear();
1205 ghost_particles_cache.valid = false;
1206
1207
1208 const std::set<types::subdomain_id> ghost_owners =
1209 parallel_triangulation->ghost_owners();
1210 for (const auto ghost_owner : ghost_owners)
1211 ghost_particles_cache.ghost_particles_by_domain[ghost_owner].reserve(
1212 static_cast<typename std::vector<particle_iterator>::size_type>(
1213 particles.size() * 0.25));
1214
1215 const std::vector<std::set<unsigned int>> vertex_to_neighbor_subdomain =
1216 triangulation_cache->get_vertex_to_neighbor_subdomain();
1217
1218 for (const auto &cell : triangulation->active_cell_iterators())
1219 {
1220 if (cell->is_locally_owned())
1221 {
1222 std::set<unsigned int> cell_to_neighbor_subdomain;
1223 for (const unsigned int v : cell->vertex_indices())
1224 {
1225 cell_to_neighbor_subdomain.insert(
1226 vertex_to_neighbor_subdomain[cell->vertex_index(v)].begin(),
1227 vertex_to_neighbor_subdomain[cell->vertex_index(v)].end());
1228 }
1229
1230 if (cell_to_neighbor_subdomain.size() > 0)
1231 {
1232 const particle_iterator_range particle_range =
1233 particles_in_cell(cell);
1234
1235 for (const auto domain : cell_to_neighbor_subdomain)
1236 {
1237 for (typename particle_iterator_range::iterator particle =
1238 particle_range.begin();
1239 particle != particle_range.end();
1240 ++particle)
1241 ghost_particles_cache.ghost_particles_by_domain[domain]
1242 .push_back(particle);
1243 }
1244 }
1245 }
1246 }
1247
1248 send_recv_particles(
1249 ghost_particles_cache.ghost_particles_by_domain,
1250 ghost_particles,
1251 std::map<
1253 std::vector<
1255 enable_cache);
1256#endif
1257 }
1258
1259 template <int dim, int spacedim>
1260 void
1262 {
1263 // Nothing to do in serial computations
1264 const auto parallel_triangulation =
1265 dynamic_cast<const parallel::TriangulationBase<dim, spacedim> *>(
1266 &*triangulation);
1267 if (parallel_triangulation == nullptr ||
1269 parallel_triangulation->get_communicator()) == 1)
1270 {
1271 return;
1272 }
1273
1274
1275#ifdef DEAL_II_WITH_MPI
1276 // First clear the current ghost_particle information
1277 // ghost_particles.clear();
1278 Assert(
1279 ghost_particles_cache.valid,
1280 ExcMessage(
1281 "Ghost particles cannot be updated if they first have not been exchanged at least once with the cache enabled"));
1282
1283
1284 send_recv_particles_properties_and_location(
1285 ghost_particles_cache.ghost_particles_by_domain, ghost_particles);
1286#endif
1287 }
1288
1289
1290
1291#ifdef DEAL_II_WITH_MPI
1292 template <int dim, int spacedim>
1293 void
1295 const std::map<types::subdomain_id, std::vector<particle_iterator>>
1296 &particles_to_send,
1298 &received_particles,
1299 const std::map<
1302 & send_cells,
1303 const bool build_cache)
1304 {
1305 ghost_particles_cache.valid = build_cache;
1306
1307 const auto parallel_triangulation =
1308 dynamic_cast<const parallel::TriangulationBase<dim, spacedim> *>(
1309 &*triangulation);
1310 Assert(
1311 parallel_triangulation,
1312 ExcMessage(
1313 "This function is only implemented for parallel::TriangulationBase objects."));
1314
1315 // Determine the communication pattern
1316 const std::set<types::subdomain_id> ghost_owners =
1317 parallel_triangulation->ghost_owners();
1318 const std::vector<types::subdomain_id> neighbors(ghost_owners.begin(),
1319 ghost_owners.end());
1320 const unsigned int n_neighbors = neighbors.size();
1321
1322 if (send_cells.size() != 0)
1323 Assert(particles_to_send.size() == send_cells.size(), ExcInternalError());
1324
1325 // If we do not know the subdomain this particle needs to be send to,
1326 // throw an error
1327 Assert(particles_to_send.find(numbers::artificial_subdomain_id) ==
1328 particles_to_send.end(),
1330
1331 // TODO: Implement the shipping of particles to processes that are not
1332 // ghost owners of the local domain
1333 for (auto send_particles = particles_to_send.begin();
1334 send_particles != particles_to_send.end();
1335 ++send_particles)
1336 Assert(ghost_owners.find(send_particles->first) != ghost_owners.end(),
1338
1339 std::size_t n_send_particles = 0;
1340 for (auto send_particles = particles_to_send.begin();
1341 send_particles != particles_to_send.end();
1342 ++send_particles)
1343 n_send_particles += send_particles->second.size();
1344
1345 const unsigned int cellid_size = sizeof(CellId::binary_type);
1346
1347 // Containers for the amount and offsets of data we will send
1348 // to other processors and the data itself.
1349 std::vector<unsigned int> n_send_data(n_neighbors, 0);
1350 std::vector<unsigned int> send_offsets(n_neighbors, 0);
1351 std::vector<char> send_data;
1352
1353 const unsigned int individual_particle_data_size =
1354 Utilities::MPI::max(n_send_particles > 0 ?
1355 ((begin()->serialized_size_in_bytes() +
1356 (size_callback ? size_callback() : 0))) :
1357 0,
1358 parallel_triangulation->get_communicator());
1359
1360 const unsigned int individual_total_particle_data_size =
1361 individual_particle_data_size + cellid_size;
1362
1363 // Only serialize things if there are particles to be send.
1364 // We can not return early even if no particles
1365 // are send, because we might receive particles from other processes
1366 if (n_send_particles > 0)
1367 {
1368 // Allocate space for sending particle data
1369 send_data.resize(n_send_particles *
1370 individual_total_particle_data_size);
1371
1372 void *data = static_cast<void *>(&send_data.front());
1373
1374 // Serialize the data sorted by receiving process
1375 for (unsigned int i = 0; i < n_neighbors; ++i)
1376 {
1377 send_offsets[i] = reinterpret_cast<std::size_t>(data) -
1378 reinterpret_cast<std::size_t>(&send_data.front());
1379
1380 const unsigned int n_particles_to_send =
1381 particles_to_send.at(neighbors[i]).size();
1382
1383 Assert(static_cast<std::size_t>(n_particles_to_send) *
1384 individual_total_particle_data_size ==
1385 static_cast<std::size_t>(
1386 n_particles_to_send *
1387 individual_total_particle_data_size),
1388 ExcMessage("Overflow when trying to send particle data"));
1389
1390 for (unsigned int j = 0; j < n_particles_to_send; ++j)
1391 {
1392 // If no target cells are given, use the iterator information
1394 cell;
1395 if (send_cells.size() == 0)
1396 cell =
1397 particles_to_send.at(neighbors[i])[j]->get_surrounding_cell(
1398 *triangulation);
1399 else
1400 cell = send_cells.at(neighbors[i])[j];
1401
1402 const CellId::binary_type cellid =
1403 cell->id().template to_binary<dim>();
1404 memcpy(data, &cellid, cellid_size);
1405 data = static_cast<char *>(data) + cellid_size;
1406
1407 data = particles_to_send.at(neighbors[i])[j]
1408 ->write_particle_data_to_memory(data);
1409 if (store_callback)
1410 data =
1411 store_callback(particles_to_send.at(neighbors[i])[j], data);
1412 }
1413 n_send_data[i] = n_particles_to_send;
1414 }
1415 }
1416
1417 // Containers for the data we will receive from other processors
1418 std::vector<unsigned int> n_recv_data(n_neighbors);
1419 std::vector<unsigned int> recv_offsets(n_neighbors);
1420
1421 {
1422 const int mpi_tag = Utilities::MPI::internal::Tags::
1424
1425 std::vector<MPI_Request> n_requests(2 * n_neighbors);
1426 for (unsigned int i = 0; i < n_neighbors; ++i)
1427 {
1428 const int ierr = MPI_Irecv(&(n_recv_data[i]),
1429 1,
1430 MPI_UNSIGNED,
1431 neighbors[i],
1432 mpi_tag,
1433 parallel_triangulation->get_communicator(),
1434 &(n_requests[2 * i]));
1435 AssertThrowMPI(ierr);
1436 }
1437 for (unsigned int i = 0; i < n_neighbors; ++i)
1438 {
1439 const int ierr = MPI_Isend(&(n_send_data[i]),
1440 1,
1441 MPI_UNSIGNED,
1442 neighbors[i],
1443 mpi_tag,
1444 parallel_triangulation->get_communicator(),
1445 &(n_requests[2 * i + 1]));
1446 AssertThrowMPI(ierr);
1447 }
1448 const int ierr =
1449 MPI_Waitall(2 * n_neighbors, n_requests.data(), MPI_STATUSES_IGNORE);
1450 AssertThrowMPI(ierr);
1451 }
1452
1453 // Determine how many particles and data we will receive
1454 unsigned int total_recv_data = 0;
1455 for (unsigned int neighbor_id = 0; neighbor_id < n_neighbors; ++neighbor_id)
1456 {
1457 recv_offsets[neighbor_id] = total_recv_data;
1458 total_recv_data +=
1459 n_recv_data[neighbor_id] * individual_total_particle_data_size;
1460 }
1461
1462 // Set up the space for the received particle data
1463 std::vector<char> recv_data(total_recv_data);
1464
1465 // Exchange the particle data between domains
1466 {
1467 std::vector<MPI_Request> requests(2 * n_neighbors);
1468 unsigned int send_ops = 0;
1469 unsigned int recv_ops = 0;
1470
1471 const int mpi_tag = Utilities::MPI::internal::Tags::
1473
1474 for (unsigned int i = 0; i < n_neighbors; ++i)
1475 if (n_recv_data[i] > 0)
1476 {
1477 const int ierr =
1478 MPI_Irecv(&(recv_data[recv_offsets[i]]),
1479 n_recv_data[i] * individual_total_particle_data_size,
1480 MPI_CHAR,
1481 neighbors[i],
1482 mpi_tag,
1483 parallel_triangulation->get_communicator(),
1484 &(requests[send_ops]));
1485 AssertThrowMPI(ierr);
1486 send_ops++;
1487 }
1488
1489 for (unsigned int i = 0; i < n_neighbors; ++i)
1490 if (n_send_data[i] > 0)
1491 {
1492 const int ierr =
1493 MPI_Isend(&(send_data[send_offsets[i]]),
1494 n_send_data[i] * individual_total_particle_data_size,
1495 MPI_CHAR,
1496 neighbors[i],
1497 mpi_tag,
1498 parallel_triangulation->get_communicator(),
1499 &(requests[send_ops + recv_ops]));
1500 AssertThrowMPI(ierr);
1501 recv_ops++;
1502 }
1503 const int ierr =
1504 MPI_Waitall(send_ops + recv_ops, requests.data(), MPI_STATUSES_IGNORE);
1505 AssertThrowMPI(ierr);
1506 }
1507
1508 // Put the received particles into the domain if they are in the
1509 // triangulation
1510 const void *recv_data_it = static_cast<const void *>(recv_data.data());
1511
1512 // Store the particle iterators in the cache
1513 auto &ghost_particles_iterators =
1514 ghost_particles_cache.ghost_particles_iterators;
1515
1516 if (build_cache)
1517 {
1518 ghost_particles_iterators.clear();
1519
1520 auto &send_pointers_particles = ghost_particles_cache.send_pointers;
1521 send_pointers_particles.assign(n_neighbors + 1, 0);
1522
1523 for (unsigned int i = 0; i < n_neighbors; ++i)
1524 send_pointers_particles[i + 1] =
1525 send_pointers_particles[i] +
1526 n_send_data[i] * individual_particle_data_size;
1527
1528 auto &recv_pointers_particles = ghost_particles_cache.recv_pointers;
1529 recv_pointers_particles.assign(n_neighbors + 1, 0);
1530
1531 for (unsigned int i = 0; i < n_neighbors; ++i)
1532 recv_pointers_particles[i + 1] =
1533 recv_pointers_particles[i] +
1534 n_recv_data[i] * individual_particle_data_size;
1535
1536 ghost_particles_cache.neighbors = neighbors;
1537
1538 ghost_particles_cache.send_data.resize(
1539 ghost_particles_cache.send_pointers.back());
1540 ghost_particles_cache.recv_data.resize(
1541 ghost_particles_cache.recv_pointers.back());
1542 }
1543
1544 while (reinterpret_cast<std::size_t>(recv_data_it) -
1545 reinterpret_cast<std::size_t>(recv_data.data()) <
1546 total_recv_data)
1547 {
1548 CellId::binary_type binary_cellid;
1549 memcpy(&binary_cellid, recv_data_it, cellid_size);
1550 const CellId id(binary_cellid);
1551 recv_data_it = static_cast<const char *>(recv_data_it) + cellid_size;
1552
1554 triangulation->create_cell_iterator(id);
1555
1556 typename std::multimap<internal::LevelInd,
1557 Particle<dim, spacedim>>::iterator
1558 recv_particle = received_particles.insert(std::make_pair(
1559 internal::LevelInd(cell->level(), cell->index()),
1560 Particle<dim, spacedim>(recv_data_it, property_pool.get())));
1561
1562 if (load_callback)
1563 recv_data_it =
1564 load_callback(particle_iterator(received_particles, recv_particle),
1565 recv_data_it);
1566
1567 if (build_cache) // TODO: is this safe?
1568 ghost_particles_iterators.push_back(recv_particle);
1569 }
1570
1571 AssertThrow(recv_data_it == recv_data.data() + recv_data.size(),
1572 ExcMessage(
1573 "The amount of data that was read into new particles "
1574 "does not match the amount of data sent around."));
1575 }
1576#endif
1577
1578
1579
1580#ifdef DEAL_II_WITH_MPI
1581 template <int dim, int spacedim>
1582 void
1584 const std::map<types::subdomain_id, std::vector<particle_iterator>>
1585 &particles_to_send,
1587 &updated_particles)
1588 {
1589 const auto &neighbors = ghost_particles_cache.neighbors;
1590 const auto &send_pointers = ghost_particles_cache.send_pointers;
1591 const auto &recv_pointers = ghost_particles_cache.recv_pointers;
1592
1593 const auto parallel_triangulation =
1594 dynamic_cast<const parallel::TriangulationBase<dim, spacedim> *>(
1595 &*triangulation);
1596 Assert(
1597 parallel_triangulation,
1598 ExcMessage(
1599 "This function is only implemented for parallel::TriangulationBase objects."));
1600
1601 std::vector<char> &send_data = ghost_particles_cache.send_data;
1602
1603 // Fill data to send
1604 if (send_pointers.back() > 0)
1605 {
1606 void *data = static_cast<void *>(&send_data.front());
1607
1608 // Serialize the data sorted by receiving process
1609 for (const auto i : neighbors)
1610 for (const auto &p : particles_to_send.at(i))
1611 {
1612 data = p->write_particle_data_to_memory(data);
1613 if (store_callback)
1614 data = store_callback(p, data);
1615 }
1616 }
1617
1618 std::vector<char> &recv_data = ghost_particles_cache.recv_data;
1619
1620 // Exchange the particle data between domains
1621 {
1622 std::vector<MPI_Request> requests(2 * neighbors.size());
1623 unsigned int send_ops = 0;
1624 unsigned int recv_ops = 0;
1625
1626 const int mpi_tag = Utilities::MPI::internal::Tags::
1628
1629 for (unsigned int i = 0; i < neighbors.size(); ++i)
1630 if ((recv_pointers[i + 1] - recv_pointers[i]) > 0)
1631 {
1632 const int ierr =
1633 MPI_Irecv(recv_data.data() + recv_pointers[i],
1634 recv_pointers[i + 1] - recv_pointers[i],
1635 MPI_CHAR,
1636 neighbors[i],
1637 mpi_tag,
1638 parallel_triangulation->get_communicator(),
1639 &(requests[send_ops]));
1640 AssertThrowMPI(ierr);
1641 send_ops++;
1642 }
1643
1644 for (unsigned int i = 0; i < neighbors.size(); ++i)
1645 if ((send_pointers[i + 1] - send_pointers[i]) > 0)
1646 {
1647 const int ierr =
1648 MPI_Isend(send_data.data() + send_pointers[i],
1649 send_pointers[i + 1] - send_pointers[i],
1650 MPI_CHAR,
1651 neighbors[i],
1652 mpi_tag,
1653 parallel_triangulation->get_communicator(),
1654 &(requests[send_ops + recv_ops]));
1655 AssertThrowMPI(ierr);
1656 recv_ops++;
1657 }
1658 const int ierr =
1659 MPI_Waitall(send_ops + recv_ops, requests.data(), MPI_STATUSES_IGNORE);
1660 AssertThrowMPI(ierr);
1661 }
1662
1663 // Put the received particles into the domain if they are in the
1664 // triangulation
1665 const void *recv_data_it = static_cast<const void *>(recv_data.data());
1666
1667 // Gather ghost particle iterators from the cache
1668 auto &ghost_particles_iterators =
1669 ghost_particles_cache.ghost_particles_iterators;
1670
1671 for (auto &recv_particle : ghost_particles_iterators)
1672 {
1673 // Update particle data using previously allocated memory space
1674 // for efficiency reasons
1675 recv_data_it =
1676 recv_particle->second.read_particle_data_from_memory(recv_data_it);
1677
1678 if (load_callback)
1679 recv_data_it =
1680 load_callback(particle_iterator(updated_particles, recv_particle),
1681 recv_data_it);
1682 }
1683
1684 AssertThrow(recv_data_it == recv_data.data() + recv_data.size(),
1685 ExcMessage(
1686 "The amount of data that was read into new particles "
1687 "does not match the amount of data sent around."));
1688 }
1689#endif
1690
1691 template <int dim, int spacedim>
1692 void
1694 const std::function<std::size_t()> & size_callb,
1695 const std::function<void *(const particle_iterator &, void *)> &store_callb,
1696 const std::function<const void *(const particle_iterator &, const void *)>
1697 &load_callb)
1698 {
1699 size_callback = size_callb;
1700 store_callback = store_callb;
1701 load_callback = load_callb;
1702 }
1703
1704
1705
1706 template <int dim, int spacedim>
1707 void
1709 {
1711 *non_const_triangulation =
1714 *>(&(*triangulation)));
1715 (void)non_const_triangulation;
1716
1717 Assert(non_const_triangulation != nullptr, ::ExcNotImplemented());
1718
1719#ifdef DEAL_II_WITH_P4EST
1720 // Only save and load particles if there are any, we might get here for
1721 // example if somebody created a ParticleHandler but generated 0
1722 // particles.
1723 update_cached_numbers();
1724
1725 if (global_max_particles_per_cell > 0)
1726 {
1727 const auto callback_function =
1729 &cell_iterator,
1731 cell_status) {
1732 return this->store_particles(cell_iterator, cell_status);
1733 };
1734
1735 handle = non_const_triangulation->register_data_attach(
1736 callback_function, /*returns_variable_size_data=*/true);
1737 }
1738#endif
1739 }
1740
1741
1742
1743 template <int dim, int spacedim>
1744 void
1746 const bool serialization)
1747 {
1748 // All particles have been stored, when we reach this point. Empty the
1749 // particle data.
1750 clear_particles();
1751
1753 *non_const_triangulation =
1756 *>(&(*triangulation)));
1757 (void)non_const_triangulation;
1758
1759 Assert(non_const_triangulation != nullptr, ::ExcNotImplemented());
1760
1761#ifdef DEAL_II_WITH_P4EST
1762 // If we are resuming from a checkpoint, we first have to register the
1763 // store function again, to set the triangulation in the same state as
1764 // before the serialization. Only by this it knows how to deserialize the
1765 // data correctly. Only do this if something was actually stored.
1766 if (serialization && (global_max_particles_per_cell > 0))
1767 {
1768 const auto callback_function =
1770 &cell_iterator,
1772 cell_status) {
1773 return this->store_particles(cell_iterator, cell_status);
1774 };
1775
1776 handle = non_const_triangulation->register_data_attach(
1777 callback_function, /*returns_variable_size_data=*/true);
1778 }
1779
1780 // Check if something was stored and load it
1781 if (handle != numbers::invalid_unsigned_int)
1782 {
1783 const auto callback_function =
1784 [this](
1786 &cell_iterator,
1787 const typename Triangulation<dim, spacedim>::CellStatus cell_status,
1788 const boost::iterator_range<std::vector<char>::const_iterator>
1789 &range_iterator) {
1790 this->load_particles(cell_iterator, cell_status, range_iterator);
1791 };
1792
1793 non_const_triangulation->notify_ready_to_unpack(handle,
1794 callback_function);
1795
1796 // Reset handle and update global number of particles. The number
1797 // can change because of discarded or newly generated particles
1799 update_cached_numbers();
1800 }
1801#else
1802 (void)serialization;
1803#endif
1804 }
1805
1806
1807
1808 template <int dim, int spacedim>
1809 std::vector<char>
1812 const typename Triangulation<dim, spacedim>::CellStatus status) const
1813 {
1814 std::vector<Particle<dim, spacedim>> stored_particles_on_cell;
1815
1816 switch (status)
1817 {
1820 // If the cell persist or is refined store all particles of the
1821 // current cell.
1822 {
1823 unsigned int n_particles = 0;
1824
1825 const internal::LevelInd level_index = {cell->level(),
1826 cell->index()};
1827 const auto particles_in_cell =
1828 (cell->is_ghost() ? ghost_particles.equal_range(level_index) :
1829 particles.equal_range(level_index));
1830
1831 n_particles = n_particles_in_cell(cell);
1832 stored_particles_on_cell.reserve(n_particles);
1833
1834 std::for_each(
1835 particles_in_cell.first,
1836 particles_in_cell.second,
1837 [&stored_particles_on_cell](
1839 &particle) {
1840 stored_particles_on_cell.push_back(particle.second);
1841 });
1842
1843 AssertDimension(n_particles, stored_particles_on_cell.size());
1844 }
1845 break;
1846
1848 // If this cell is the parent of children that will be coarsened,
1849 // collect the particles of all children.
1850 {
1851 unsigned int n_particles = 0;
1852
1853 for (const auto &child : cell->child_iterators())
1854 {
1855 n_particles += n_particles_in_cell(child);
1856 }
1857
1858 stored_particles_on_cell.reserve(n_particles);
1859
1860 for (const auto &child : cell->child_iterators())
1861 {
1862 const internal::LevelInd level_index = {child->level(),
1863 child->index()};
1864 const auto particles_in_cell =
1865 (child->is_ghost() ?
1866 ghost_particles.equal_range(level_index) :
1867 particles.equal_range(level_index));
1868
1869 std::for_each(
1870 particles_in_cell.first,
1871 particles_in_cell.second,
1872 [&stored_particles_on_cell](
1874 &particle) {
1875 stored_particles_on_cell.push_back(particle.second);
1876 });
1877 }
1878
1879 AssertDimension(n_particles, stored_particles_on_cell.size());
1880 }
1881 break;
1882
1883 default:
1884 Assert(false, ExcInternalError());
1885 break;
1886 }
1887
1888 return pack_particles(stored_particles_on_cell);
1889 }
1890
1891
1892
1893 template <int dim, int spacedim>
1894 void
1896 const typename Triangulation<dim, spacedim>::cell_iterator & cell,
1897 const typename Triangulation<dim, spacedim>::CellStatus status,
1898 const boost::iterator_range<std::vector<char>::const_iterator> &data_range)
1899 {
1900 // We leave this container non-const to be able to `std::move`
1901 // its contents directly into the particles multimap later.
1902 std::vector<Particle<dim, spacedim>> loaded_particles_on_cell =
1903 unpack_particles<dim, spacedim>(data_range, *property_pool);
1904
1905 // Update the reference to the current property pool for all particles.
1906 // This was not stored, because they might be transported across process
1907 // domains.
1908 for (auto &particle : loaded_particles_on_cell)
1909 particle.set_property_pool(*property_pool);
1910
1911 switch (status)
1912 {
1914 {
1915 auto position_hint = particles.end();
1916 for (const auto &particle : loaded_particles_on_cell)
1917 {
1918 // Use std::multimap::emplace_hint to speed up insertion of
1919 // particles.
1920 position_hint =
1921 particles.emplace_hint(position_hint,
1922 std::make_pair(cell->level(),
1923 cell->index()),
1924 std::move(particle));
1925 // Move the hint position forward by one, i.e., for the next
1926 // particle. The 'hint' position will thus be right after the
1927 // one just inserted.
1928 ++position_hint;
1929 }
1930 }
1931 break;
1932
1934 {
1935 typename std::multimap<internal::LevelInd,
1936 Particle<dim, spacedim>>::iterator
1937 position_hint = particles.end();
1938 for (auto &particle : loaded_particles_on_cell)
1939 {
1940 const Point<dim> p_unit =
1941 mapping->transform_real_to_unit_cell(cell,
1942 particle.get_location());
1943 particle.set_reference_location(p_unit);
1944 // Use std::multimap::emplace_hint to speed up insertion of
1945 // particles.
1946 position_hint =
1947 particles.emplace_hint(position_hint,
1948 std::make_pair(cell->level(),
1949 cell->index()),
1950 std::move(particle));
1951 // Move the hint position forward by one, i.e., for the next
1952 // particle. The 'hint' position will thus be right after the
1953 // one just inserted.
1954 ++position_hint;
1955 }
1956 }
1957 break;
1958
1960 {
1961 std::vector<
1962 typename std::multimap<internal::LevelInd,
1963 Particle<dim, spacedim>>::iterator>
1965 for (unsigned int child_index = 0;
1966 child_index < GeometryInfo<dim>::max_children_per_cell;
1967 ++child_index)
1968 {
1970 child = cell->child(child_index);
1971 position_hints[child_index] = particles.upper_bound(
1972 std::make_pair(child->level(), child->index()));
1973 }
1974
1975 for (auto &particle : loaded_particles_on_cell)
1976 {
1977 for (unsigned int child_index = 0;
1978 child_index < GeometryInfo<dim>::max_children_per_cell;
1979 ++child_index)
1980 {
1982 child = cell->child(child_index);
1983
1984 try
1985 {
1986 const Point<dim> p_unit =
1987 mapping->transform_real_to_unit_cell(
1988 child, particle.get_location());
1990 {
1991 particle.set_reference_location(p_unit);
1992 // Use std::multimap::emplace_hint to speed up
1993 // insertion of particles.
1994 position_hints[child_index] =
1995 particles.emplace_hint(
1996 position_hints[child_index],
1997 std::make_pair(child->level(), child->index()),
1998 std::move(particle));
1999 // Move the hint position forward by one, i.e.,
2000 // for the next particle. The 'hint' position will
2001 // thus be right after the one just inserted.
2002 ++position_hints[child_index];
2003 break;
2004 }
2005 }
2006 catch (typename Mapping<dim>::ExcTransformationFailed &)
2007 {}
2008 }
2009 }
2010 }
2011 break;
2012
2013 default:
2014 Assert(false, ExcInternalError());
2015 break;
2016 }
2017 }
2018} // namespace Particles
2019
2020#include "particle_handler.inst"
2021
Definition: cell_id.h:71
std::array< unsigned int, 4 > binary_type
Definition: cell_id.h:80
const unsigned int n_components
Definition: function.h:164
virtual void vector_value(const Point< dim > &p, Vector< RangeNumberType > &values) const
void register_additional_store_load_functions(const std::function< std::size_t()> &size_callback, const std::function< void *(const particle_iterator &, void *)> &store_callback, const std::function< const void *(const particle_iterator &, const void *)> &load_callback)
void exchange_ghost_particles(const bool enable_ghost_cache=false)
types::particle_index n_global_particles() const
std::multimap< internal::LevelInd, Particle< dim, spacedim > > particles
unsigned int global_max_particles_per_cell
internal::GhostParticlePartitioner< dim, spacedim > ghost_particles_cache
unsigned int n_properties_per_particle() const
types::particle_index global_number_of_particles
void get_particle_positions(VectorType &output_vector, const bool add_to_output_vector=false)
boost::iterator_range< particle_iterator > particle_iterator_range
std::multimap< internal::LevelInd, Particle< dim, spacedim > > ghost_particles
particle_iterator begin_ghost() const
PropertyPool< dim, spacedim > & get_property_pool() const
void initialize(const Triangulation< dim, spacedim > &tria, const Mapping< dim, spacedim > &mapping, const unsigned int n_properties=0)
std::unique_ptr< PropertyPool< dim, spacedim > > property_pool
particle_iterator begin() const
void send_recv_particles_properties_and_location(const std::map< types::subdomain_id, std::vector< particle_iterator > > &particles_to_send, std::multimap< internal::LevelInd, Particle< dim, spacedim > > &received_particles)
std::map< unsigned int, IndexSet > insert_global_particles(const std::vector< Point< spacedim > > &positions, const std::vector< std::vector< BoundingBox< spacedim > > > &global_bounding_boxes, const std::vector< std::vector< double > > &properties={}, const std::vector< types::particle_index > &ids={})
void register_load_callback_function(const bool serialization)
std::vector< char > store_particles(const typename Triangulation< dim, spacedim >::cell_iterator &cell, const typename Triangulation< dim, spacedim >::CellStatus status) const
void insert_particles(const std::multimap< typename Triangulation< dim, spacedim >::active_cell_iterator, Particle< dim, spacedim > > &particles)
types::particle_index next_free_particle_index
void remove_particle(const particle_iterator &particle)
types::particle_index n_locally_owned_particles() const
types::particle_index n_particles_in_cell(const typename Triangulation< dim, spacedim >::active_cell_iterator &cell) const
particle_iterator_range particles_in_cell(const typename Triangulation< dim, spacedim >::active_cell_iterator &cell)
void load_particles(const typename Triangulation< dim, spacedim >::cell_iterator &cell, const typename Triangulation< dim, spacedim >::CellStatus status, const boost::iterator_range< std::vector< char >::const_iterator > &data_range)
SmartPointer< const Triangulation< dim, spacedim >, ParticleHandler< dim, spacedim > > triangulation
particle_iterator insert_particle(const Particle< dim, spacedim > &particle, const typename Triangulation< dim, spacedim >::active_cell_iterator &cell)
types::particle_index n_global_max_particles_per_cell() const
void copy_from(const ParticleHandler< dim, spacedim > &particle_handler)
SmartPointer< const Mapping< dim, spacedim >, ParticleHandler< dim, spacedim > > mapping
std::unique_ptr< GridTools::Cache< dim, spacedim > > triangulation_cache
std::enable_if< std::is_convertible< VectorType *, Function< spacedim > * >::value==false >::type set_particle_positions(const VectorType &input_vector, const bool displace_particles=true)
types::particle_index get_next_free_particle_index() const
void send_recv_particles(const std::map< types::subdomain_id, std::vector< particle_iterator > > &particles_to_send, std::multimap< internal::LevelInd, Particle< dim, spacedim > > &received_particles, const std::map< types::subdomain_id, std::vector< typename Triangulation< dim, spacedim >::active_cell_iterator > > &new_cells_for_particles=std::map< types::subdomain_id, std::vector< typename Triangulation< dim, spacedim >::active_cell_iterator > >(), const bool enable_cache=false)
IndexSet locally_owned_particle_ids() const
void set_property_pool(PropertyPool< dim, spacedim > &property_pool)
Definition: particle.h:564
bool has_properties() const
Definition: particle.h:626
const ArrayView< double > get_properties()
Definition: particle.cc:330
void set_properties(const ArrayView< const double > &new_properties)
Definition: particle.cc:305
numbers::NumberTraits< Number >::real_type norm() const
cell_iterator begin(const unsigned int level=0) const
cell_iterator end() const
Definition: vector.h:110
void notify_ready_to_unpack(const unsigned int handle, const std::function< void(const cell_iterator &, const CellStatus, const boost::iterator_range< std::vector< char >::const_iterator > &)> &unpack_callback)
Definition: tria_base.cc:792
unsigned int register_data_attach(const std::function< std::vector< char >(const cell_iterator &, const CellStatus)> &pack_callback, const bool returns_variable_size_data)
Definition: tria_base.cc:763
#define DEAL_II_NAMESPACE_OPEN
Definition: config.h:402
#define DEAL_II_NAMESPACE_CLOSE
Definition: config.h:403
unsigned int level
Definition: grid_out.cc:4590
__global__ void set(Number *val, const Number s, const size_type N)
static ::ExceptionBase & ExcNotImplemented()
#define Assert(cond, exc)
Definition: exceptions.h:1465
#define AssertDimension(dim1, dim2)
Definition: exceptions.h:1622
#define AssertThrowMPI(error_code)
Definition: exceptions.h:1746
static ::ExceptionBase & ExcInternalError()
static ::ExceptionBase & ExcPointNotAvailableHere()
static ::ExceptionBase & ExcMessage(std::string arg1)
#define AssertThrow(cond, exc)
Definition: exceptions.h:1575
IteratorRange< BaseIterator > make_iterator_range(const BaseIterator &begin, const typename identity< BaseIterator >::type &end)
return_type distributed_compute_point_locations(const GridTools::Cache< dim, spacedim > &cache, const std::vector< Point< spacedim > > &local_points, const std::vector< std::vector< BoundingBox< spacedim > > > &global_bboxes, const double tolerance=1e-10)
Definition: grid_tools.cc:5622
return_type compute_point_locations_try_all(const Cache< dim, spacedim > &cache, const std::vector< Point< spacedim > > &points, const typename Triangulation< dim, spacedim >::active_cell_iterator &cell_hint=typename Triangulation< dim, spacedim >::active_cell_iterator())
Definition: grid_tools.cc:5454
@ valid
Iterator points to a valid object.
types::global_dof_index size_type
Definition: cuda_kernels.h:45
std::pair< int, int > LevelInd
Definition: particle.h:42
SymmetricTensor< 2, dim, Number > b(const Tensor< 2, dim, Number > &F)
SymmetricTensor< 2, dim, Number > d(const Tensor< 2, dim, Number > &F, const Tensor< 2, dim, Number > &dF_dt)
VectorType::value_type * end(VectorType &V)
VectorType::value_type * begin(VectorType &V)
@ particle_handler_send_recv_particles_send
ParticleHandler<dim, spacedim>::send_recv_particles.
Definition: mpi_tags.h:114
@ particle_handler_send_recv_particles_setup
ParticleHandler<dim, spacedim>::send_recv_particles.
Definition: mpi_tags.h:110
std::vector< T > all_gather(const MPI_Comm &comm, const T &object_to_send)
T sum(const T &t, const MPI_Comm &mpi_communicator)
unsigned int n_mpi_processes(const MPI_Comm &mpi_communicator)
Definition: mpi.cc:117
T max(const T &t, const MPI_Comm &mpi_communicator)
std::map< unsigned int, T > some_to_some(const MPI_Comm &comm, const std::map< unsigned int, T > &objects_to_send)
const types::subdomain_id artificial_subdomain_id
Definition: types.h:293
static const unsigned int invalid_unsigned_int
Definition: types.h:196
STL namespace.
::VectorizedArray< Number, width > max(const ::VectorizedArray< Number, width > &, const ::VectorizedArray< Number, width > &)
unsigned int subdomain_id
Definition: types.h:43
const ::parallel::distributed::Triangulation< dim, spacedim > * triangulation
*braid_SplitCommworld & comm
#define DEAL_II_PARTICLE_INDEX_MPI_TYPE
Definition: property_pool.h:72
void advance(std::tuple< I1, I2 > &t, const unsigned int n)