deal.II version GIT relicensing-6834-g5b78e6bcdf 2026-10-01 11:20:01+00:00
\(\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\}}\)
Loading...
Searching...
No Matches
particle_handler.cc
Go to the documentation of this file.
1// -----------------------------------------------------------------------------
2//
3// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception OR LGPL-2.1-or-later
4// Copyright (C) 2017 - 2025 by the deal.II authors
5//
6// This file is part of the deal.II library.
7//
8// Detailed license information governing the source code and contributions
9// can be found in LICENSE.md and CONTRIBUTING.md at the top level directory.
10//
11// -----------------------------------------------------------------------------
12
15
18
20
22
23#include <limits>
24#include <memory>
25#include <utility>
26
28
29namespace Particles
30{
31 namespace
32 {
33 template <int dim, int spacedim>
34 std::vector<char>
35 pack_particles(std::vector<ParticleIterator<dim, spacedim>> &particles)
36 {
37 std::vector<char> buffer;
38
39 if (particles.empty())
40 return buffer;
41
42 buffer.resize(particles.size() *
43 particles.front()->serialized_size_in_bytes());
44 void *current_data = buffer.data();
45
46 for (const auto &particle : particles)
47 {
48 current_data = particle->write_particle_data_to_memory(current_data);
49 }
50
51 return buffer;
52 }
53 } // namespace
54
55
56
57 template <int dim, int spacedim>
59 : triangulation()
60 , mapping()
61 , property_pool(std::make_unique<PropertyPool<dim, spacedim>>(0))
62 , global_number_of_particles(0)
63 , number_of_locally_owned_particles(0)
64 , global_max_particles_per_cell(0)
65 , next_free_particle_index(0)
66 , size_callback()
67 , store_callback()
68 , load_callback()
69 , tria_attached_data_index(numbers::invalid_unsigned_int)
70 , tria_listeners()
71 {
73 }
74
75
76
77 template <int dim, int spacedim>
79 const Triangulation<dim, spacedim> &triangulation,
80 const Mapping<dim, spacedim> &mapping,
81 const unsigned int n_properties)
82 : triangulation(&triangulation, typeid(*this).name())
83 , mapping(&mapping, typeid(*this).name())
84 , property_pool(std::make_unique<PropertyPool<dim, spacedim>>(n_properties))
85 , cells_to_particle_cache(triangulation.n_active_cells(), particles.end())
86 , global_number_of_particles(0)
87 , number_of_locally_owned_particles(0)
88 , global_max_particles_per_cell(0)
89 , next_free_particle_index(0)
90 , size_callback()
91 , store_callback()
92 , load_callback()
93 , tria_attached_data_index(numbers::invalid_unsigned_int)
94 , triangulation_cache(
95 std::make_unique<GridTools::Cache<dim, spacedim>>(triangulation,
96 mapping))
97 , tria_listeners()
98 {
101 }
102
103
104
105 template <int dim, int spacedim>
107 {
108 clear_particles();
109
110 for (const auto &connection : tria_listeners)
111 connection.disconnect();
112 }
113
114
115
116 template <int dim, int spacedim>
117 void
119 const Triangulation<dim, spacedim> &new_triangulation,
120 const Mapping<dim, spacedim> &new_mapping,
121 const unsigned int n_properties)
122 {
123 clear();
124
125 triangulation = &new_triangulation;
126 mapping = &new_mapping;
127
128 reset_particle_container(particles);
129
130 // Create the memory pool that will store all particle properties
131 property_pool = std::make_unique<PropertyPool<dim, spacedim>>(n_properties);
132
133 // Create the grid cache to cache the information about the triangulation
134 // that is used to locate the particles into subdomains and cells
135 triangulation_cache =
136 std::make_unique<GridTools::Cache<dim, spacedim>>(new_triangulation,
137 new_mapping);
138
139 cells_to_particle_cache.resize(triangulation->n_active_cells(),
140 particles.end());
141
142 connect_to_triangulation_signals();
143 }
144
145
146
147 template <int dim, int spacedim>
148 void
150 const ParticleHandler<dim, spacedim> &particle_handler)
151 {
152 const unsigned int n_properties =
153 particle_handler.property_pool->n_properties_per_slot();
154 initialize(*particle_handler.triangulation,
155 *particle_handler.mapping,
156 n_properties);
157
158 property_pool = std::make_unique<PropertyPool<dim, spacedim>>(
159 *(particle_handler.property_pool));
160
161 // copy static members
162 global_number_of_particles = particle_handler.global_number_of_particles;
163 number_of_locally_owned_particles =
164 particle_handler.number_of_locally_owned_particles;
165
166 global_max_particles_per_cell =
167 particle_handler.global_max_particles_per_cell;
168 next_free_particle_index = particle_handler.next_free_particle_index;
169
170 // Manually copy over the particles because we do not want to touch the
171 // anchor iterators set by initialize()
172 particles.insert(particle_container_owned_end(),
173 particle_handler.particle_container_owned_begin(),
174 particle_handler.particle_container_owned_end());
175 particles.insert(particle_container_ghost_end(),
176 particle_handler.particle_container_ghost_begin(),
177 particle_handler.particle_container_ghost_end());
178
179 for (auto it = particles.begin(); it != particles.end(); ++it)
180 if (!it->particles.empty())
181 cells_to_particle_cache[it->cell->active_cell_index()] = it;
182
183 ghost_particles_cache.ghost_particles_by_domain =
184 particle_handler.ghost_particles_cache.ghost_particles_by_domain;
185 tria_attached_data_index = particle_handler.tria_attached_data_index;
186 }
187
188
189
190 template <int dim, int spacedim>
191 void
193 {
194 clear_particles();
195 global_number_of_particles = 0;
196 number_of_locally_owned_particles = 0;
197 next_free_particle_index = 0;
198 global_max_particles_per_cell = 0;
199 }
200
201
202
203 template <int dim, int spacedim>
204 void
206 {
207 for (auto &particles_in_cell : particles)
208 for (auto &particle : particles_in_cell.particles)
210 property_pool->deregister_particle(particle);
211
212 cells_to_particle_cache.clear();
213 reset_particle_container(particles);
214 if (triangulation != nullptr)
215 cells_to_particle_cache.resize(triangulation->n_active_cells(),
216 particles.end());
217
218 // the particle properties have already been deleted by their destructor,
219 // but the memory is still allocated. Return the memory as well.
220 property_pool->clear();
221 }
222
223
224
225 template <int dim, int spacedim>
226 void
227 ParticleHandler<dim, spacedim>::reserve(const std::size_t n_particles)
228 {
229 property_pool->reserve(n_particles);
230 }
231
232
233
234 template <int dim, int spacedim>
235 void
237 particle_container &given_particles)
238 {
239 // Make sure to set a valid past-the-end iterator also in case we have no
240 // triangulation
242 past_the_end_iterator =
243 triangulation != nullptr ?
244 triangulation->end() :
245 typename Triangulation<dim, spacedim>::cell_iterator(nullptr, -1, -1);
246
247 given_particles.clear();
248 for (unsigned int i = 0; i < 3; ++i)
249 given_particles.emplace_back(
250 std::vector<typename PropertyPool<dim, spacedim>::Handle>(),
251 past_the_end_iterator);
252
253 // Set the end of owned particles to the middle of the three elements
254 const_cast<typename particle_container::iterator &>(owned_particles_end) =
255 ++given_particles.begin();
256 }
257
258
259
260 template <int dim, int spacedim>
261 void
263 {
264 // first sort the owned particles by the active cell index
265 bool sort_is_necessary = false;
266 {
267 auto previous = particle_container_owned_begin();
268 for (auto next = previous; next != particle_container_owned_end(); ++next)
269 {
270 if (previous->cell.state() == IteratorState::valid &&
271 next->cell.state() == IteratorState::valid &&
272 previous->cell > next->cell)
273 {
274 sort_is_necessary = true;
275 break;
276 }
277 previous = next;
278 }
279 }
280 if (sort_is_necessary)
281 {
282 // we could have tried to call std::list::sort with a custom
283 // comparator to get things sorted, but things are complicated by the
284 // three anchor entries that we do not want to move, and we would
285 // hence pay an O(N log(N)) algorithm with a large constant in front
286 // of it (on the order of 20+ instructions with many
287 // difficult-to-predict branches). Therefore, we simply copy the list
288 // into a new one (keeping alive the possibly large vectors with
289 // particles on cells) into a new container.
290 particle_container sorted_particles;
291
292 // note that this call updates owned_particles_end, so that
293 // particle_container_owned_end() below already points to the
294 // new container
295 reset_particle_container(sorted_particles);
296
297 // iterate over cells and insert the entries in the new order
298 for (const auto &cell : triangulation->active_cell_iterators())
299 if (!cell->is_artificial())
300 if (cells_to_particle_cache[cell->active_cell_index()] !=
301 particles.end())
302 {
303 // before we move the sorted_particles into particles
304 // particle_container_ghost_end() still points to the
305 // old particles container. Therefore this condition looks
306 // quirky.
307 typename particle_container::iterator insert_position =
308 cell->is_locally_owned() ? particle_container_owned_end() :
309 --sorted_particles.end();
310 typename particle_container::iterator new_entry =
311 sorted_particles.insert(
312 insert_position, typename particle_container::value_type());
313 new_entry->cell = cell;
314 new_entry->particles =
315 std::move(cells_to_particle_cache[cell->active_cell_index()]
316 ->particles);
317 }
318 particles = std::move(sorted_particles);
319
320 // refresh cells_to_particle_cache
321 cells_to_particle_cache.clear();
322 cells_to_particle_cache.resize(triangulation->n_active_cells(),
323 particles.end());
324 for (auto it = particles.begin(); it != particles.end(); ++it)
325 if (!it->particles.empty())
326 cells_to_particle_cache[it->cell->active_cell_index()] = it;
327 }
328
329 // Ensure that we did not accidentally modify the anchor entries with
330 // special purpose.
331 Assert(particles.front().cell.state() == IteratorState::past_the_end &&
332 particles.front().particles.empty() &&
333 particles.back().cell.state() == IteratorState::past_the_end &&
334 particles.back().particles.empty() &&
335 owned_particles_end->cell.state() == IteratorState::past_the_end &&
336 owned_particles_end->particles.empty(),
338
339 if constexpr (running_in_debug_mode())
340 {
341 // check that no cache element hits the three anchor states in the list
342 // of particles
343 for (const auto &it : cells_to_particle_cache)
344 Assert(it != particles.begin() && it != owned_particles_end &&
345 it != --(particles.end()),
347
348 // check that we only have locally owned particles in the first region
349 // of cells; note that we skip the very first anchor element
350 for (auto it = particle_container_owned_begin();
351 it != particle_container_owned_end();
352 ++it)
353 Assert(it->cell->is_locally_owned(), ExcInternalError());
354
355 // check that the cache is consistent with the iterators
356 std::vector<typename particle_container::iterator> verify_cache(
357 triangulation->n_active_cells(), particles.end());
358 for (auto it = particles.begin(); it != particles.end(); ++it)
359 if (!it->particles.empty())
360 verify_cache[it->cell->active_cell_index()] = it;
361
362 for (unsigned int i = 0; i < verify_cache.size(); ++i)
363 Assert(verify_cache[i] == cells_to_particle_cache[i],
365 }
366
367 // now compute local result with the function above and then compute the
368 // collective results
369 number_of_locally_owned_particles = 0;
370
371 types::particle_index result[2] = {};
372 for (const auto &particles_in_cell : particles)
373 {
374 const types::particle_index n_particles_in_cell =
375 particles_in_cell.particles.size();
376
377 // local_max_particles_per_cell
378 result[0] = std::max(result[0], n_particles_in_cell);
379
380 // number of locally owned particles
381 if (n_particles_in_cell > 0 &&
382 particles_in_cell.cell->is_locally_owned())
383 number_of_locally_owned_particles += n_particles_in_cell;
384
385 // local_max_particle_index
386 for (const auto &particle : particles_in_cell.particles)
387 result[1] = std::max(result[1], property_pool->get_id(particle));
388 }
389
390 global_number_of_particles =
391 ::Utilities::MPI::sum(number_of_locally_owned_particles,
392 triangulation->get_mpi_communicator());
393
394 if (global_number_of_particles == 0)
395 {
396 next_free_particle_index = 0;
397 global_max_particles_per_cell = 0;
398 }
399 else
400 {
401 Utilities::MPI::max(result,
402 triangulation->get_mpi_communicator(),
403 result);
404
405 next_free_particle_index = result[1] + 1;
406 global_max_particles_per_cell = result[0];
407 }
408 }
409
410
411
412 template <int dim, int spacedim>
416 const
417 {
418 if (cells_to_particle_cache.empty())
419 return 0;
420
421 if (cell->is_artificial() == false)
422 {
423 return cells_to_particle_cache[cell->active_cell_index()] !=
424 particles.end() ?
425 cells_to_particle_cache[cell->active_cell_index()]
426 ->particles.size() :
427 0;
428 }
429 else
430 AssertThrow(false,
431 ExcMessage("You can't ask for the particles on an artificial "
432 "cell since we don't know what exists on these "
433 "kinds of cells."));
434
436 }
437
438
439
440 template <int dim, int spacedim>
444 const
445 {
446 return (const_cast<ParticleHandler<dim, spacedim> *>(this))
447 ->particles_in_cell(cell);
448 }
449
450
451
452 template <int dim, int spacedim>
456 {
457 const unsigned int active_cell_index = cell->active_cell_index();
458
459 if (cell->is_artificial() == false)
460 {
461 if (cells_to_particle_cache[active_cell_index] == particles.end())
462 {
463 return boost::make_iterator_range(
464 particle_iterator(particles.begin(), *property_pool, 0),
465 particle_iterator(particles.begin(), *property_pool, 0));
466 }
467 else
468 {
469 const typename particle_container::iterator
470 particles_in_current_cell =
471 cells_to_particle_cache[active_cell_index];
472 typename particle_container::iterator particles_in_next_cell =
473 particles_in_current_cell;
474 ++particles_in_next_cell;
475 return boost::make_iterator_range(
476 particle_iterator(particles_in_current_cell, *property_pool, 0),
477 particle_iterator(particles_in_next_cell, *property_pool, 0));
478 }
479 }
480 else
481 AssertThrow(false,
482 ExcMessage("You can't ask for the particles on an artificial "
483 "cell since we don't know what exists on these "
484 "kinds of cells."));
485
486 return {};
487 }
488
489
490
491 template <int dim, int spacedim>
492 void
495 {
496 auto &particles_on_cell = particle->particles_in_cell->particles;
497
498 // if the particle has an invalid handle (e.g. because it has
499 // been duplicated before calling this function) do not try
500 // to deallocate its memory again
501 auto handle = particle->get_handle();
503 property_pool->deregister_particle(handle);
504
505 // need to reduce the cached number before deleting, because the iterator
506 // may be invalid after removing the particle even if only
507 // accessing the cell
508 const auto cell = particle->get_surrounding_cell();
509 const bool owned_cell = cell->is_locally_owned();
510 if (owned_cell)
511 --number_of_locally_owned_particles;
512
513 if (particles_on_cell.size() > 1)
514 {
515 particles_on_cell[particle->particle_index_within_cell] =
516 std::move(particles_on_cell.back());
517 particles_on_cell.resize(particles_on_cell.size() - 1);
518 }
519 else
520 {
521 particles.erase(particle->particles_in_cell);
522 cells_to_particle_cache[cell->active_cell_index()] = particles.end();
523 }
524 }
525
526
527
528 template <int dim, int spacedim>
529 void
532 &particles_to_remove)
533 {
534 // We need to remove particles backwards on each cell to keep the particle
535 // iterators alive as we keep removing particles on the same cell. To
536 // ensure that this is safe, we either check if we already have sorted
537 // iterators or if we need to manually sort
538 const auto check_greater = [](const particle_iterator &a,
539 const particle_iterator &b) {
540 return a->particles_in_cell->cell > b->particles_in_cell->cell ||
541 (a->particles_in_cell->cell == b->particles_in_cell->cell &&
542 a->particle_index_within_cell > b->particle_index_within_cell);
543 };
544
545 bool particles_are_sorted = true;
546 auto previous = particles_to_remove.begin();
547 for (auto next = previous; next != particles_to_remove.end(); ++next)
548 {
549 if (check_greater(*previous, *next))
550 {
551 particles_are_sorted = false;
552 break;
553 }
554 previous = next;
555 }
556 if (particles_are_sorted)
557 {
558 // pass along backwards in array
559 for (auto it = particles_to_remove.rbegin();
560 it != particles_to_remove.rend();
561 ++it)
562 remove_particle(*it);
563 }
564 else
565 {
566 std::vector<ParticleHandler<dim, spacedim>::particle_iterator>
567 sorted_particles(particles_to_remove);
568 std::sort(sorted_particles.begin(),
569 sorted_particles.end(),
570 check_greater);
571
572 for (const auto &particle : sorted_particles)
573 remove_particle(particle);
574 }
575
576 update_cached_numbers();
577 }
578
579
580
581 template <int dim, int spacedim>
584 const Particle<dim, spacedim> &particle,
586 {
587 return insert_particle(particle.get_location(),
588 particle.get_reference_location(),
589 particle.get_id(),
590 cell,
591 particle.get_properties());
592 }
593
594
595
596 template <int dim, int spacedim>
599 const typename PropertyPool<dim, spacedim>::Handle handle,
601 {
602 const unsigned int active_cell_index = cell->active_cell_index();
603 typename particle_container::iterator &cache =
604 cells_to_particle_cache[active_cell_index];
605 if (cache == particles.end())
606 {
607 const typename particle_container::iterator insert_position =
608 cell->is_locally_owned() ? particle_container_owned_end() :
609 particle_container_ghost_end();
610 cache = particles.emplace(
611 insert_position,
612 std::vector<typename PropertyPool<dim, spacedim>::Handle>{handle},
613 cell);
614 }
615 else
616 {
617 cache->particles.push_back(handle);
618 Assert(cache->cell == cell, ExcInternalError());
619 }
620 return particle_iterator(cache,
621 *property_pool,
622 cache->particles.size() - 1);
623 }
624
625
626
627 template <int dim, int spacedim>
630 const void *&data,
632 {
633 Assert(triangulation != nullptr, ExcInternalError());
634 Assert(cells_to_particle_cache.size() == triangulation->n_active_cells(),
636 Assert(cell->is_locally_owned(),
637 ExcMessage("You tried to insert particles into a cell that is not "
638 "locally owned. This is not supported."));
639
640 particle_iterator particle_it =
641 insert_particle(property_pool->register_particle(), cell);
642
643 data = particle_it->read_particle_data_from_memory(data);
644
645 ++number_of_locally_owned_particles;
646
647 return particle_it;
648 }
649
650
651
652 template <int dim, int spacedim>
655 const Point<spacedim> &position,
656 const Point<dim> &reference_position,
657 const types::particle_index particle_index,
659 const ArrayView<const double> &properties)
660 {
661 Assert(triangulation != nullptr, ExcInternalError());
662 Assert(cells_to_particle_cache.size() == triangulation->n_active_cells(),
665 Assert(cell->is_locally_owned(),
666 ExcMessage("You tried to insert particles into a cell that is not "
667 "locally owned. This is not supported."));
668
669 particle_iterator particle_it =
670 insert_particle(property_pool->register_particle(), cell);
671
672 particle_it->set_location(position);
673 particle_it->set_reference_location(reference_position);
674 particle_it->set_id(particle_index);
675
676 if (properties.size() != 0)
677 particle_it->set_properties(properties);
678
679 ++number_of_locally_owned_particles;
680
681 return particle_it;
682 }
683
684
685
686 template <int dim, int spacedim>
687 void
689 const std::multimap<
691 Particle<dim, spacedim>> &new_particles)
692 {
693 reserve(n_locally_owned_particles() + new_particles.size());
694 for (const auto &cell_and_particle : new_particles)
695 insert_particle(cell_and_particle.second, cell_and_particle.first);
696
697 update_cached_numbers();
698 }
699
700
701
702 template <int dim, int spacedim>
703 void
705 const std::vector<Point<spacedim>> &positions)
706 {
707 Assert(triangulation != nullptr, ExcInternalError());
708
709 update_cached_numbers();
710 reserve(n_locally_owned_particles() + positions.size());
711
712 // Determine the starting particle index of this process, which
713 // is the highest currently existing particle index plus the sum
714 // of the number of newly generated particles of all
715 // processes with a lower rank if in a parallel computation.
716 const types::particle_index local_next_particle_index =
717 get_next_free_particle_index();
718 types::particle_index local_start_index = 0;
719
720#ifdef DEAL_II_WITH_MPI
721 if (const auto parallel_triangulation =
722 dynamic_cast<const parallel::TriangulationBase<dim, spacedim> *>(
723 &*triangulation))
724 {
725 types::particle_index particles_to_add_locally = positions.size();
726 const int ierr =
727 MPI_Scan(&particles_to_add_locally,
728 &local_start_index,
729 1,
730 Utilities::MPI::mpi_type_id_for_type<types::particle_index>,
731 MPI_SUM,
732 parallel_triangulation->get_mpi_communicator());
733 AssertThrowMPI(ierr);
734 local_start_index -= particles_to_add_locally;
735 }
736#endif
737
738 local_start_index += local_next_particle_index;
739
740 auto point_locations =
742 positions);
743
744 auto &cells = std::get<0>(point_locations);
745 auto &local_positions = std::get<1>(point_locations);
746 auto &index_map = std::get<2>(point_locations);
747 auto &missing_points = std::get<3>(point_locations);
748 // If a point was not found, throwing an error, as the old
749 // implementation of compute_point_locations would have done
750 AssertThrow(missing_points.empty(),
752
753 (void)missing_points;
754
755 for (unsigned int i = 0; i < cells.size(); ++i)
756 for (unsigned int p = 0; p < local_positions[i].size(); ++p)
757 insert_particle(positions[index_map[i][p]],
758 local_positions[i][p],
759 local_start_index + index_map[i][p],
760 cells[i]);
761
762 update_cached_numbers();
763 }
764
765
766
767 template <int dim, int spacedim>
768 std::map<unsigned int, IndexSet>
770 const std::vector<Point<spacedim>> &positions,
771 const std::vector<std::vector<BoundingBox<spacedim>>>
772 &global_bounding_boxes,
773 const std::vector<std::vector<double>> &properties,
774 const std::vector<types::particle_index> &ids)
775 {
776 if (!properties.empty())
777 {
778 AssertDimension(properties.size(), positions.size());
779 if constexpr (running_in_debug_mode())
780 {
781 for (const auto &p : properties)
782 AssertDimension(p.size(), n_properties_per_particle());
783 }
784 }
785
786 if (!ids.empty())
787 AssertDimension(ids.size(), positions.size());
788
789 const auto comm = triangulation->get_mpi_communicator();
790
791 const auto n_mpi_processes = Utilities::MPI::n_mpi_processes(comm);
792
793 // Compute the global number of properties
794 const auto n_global_properties =
795 Utilities::MPI::sum(properties.size(), comm);
796
797 // Gather the number of points per processor
798 const auto n_particles_per_proc =
799 Utilities::MPI::all_gather(comm, positions.size());
800
801 // Calculate all starting points locally
802 std::vector<unsigned int> particle_start_indices(n_mpi_processes);
803
804 unsigned int particle_start_index = get_next_free_particle_index();
805 for (unsigned int process = 0; process < particle_start_indices.size();
806 ++process)
807 {
808 particle_start_indices[process] = particle_start_index;
809 particle_start_index += n_particles_per_proc[process];
810 }
811
812 // Get all local information
813 const auto cells_positions_and_index_maps =
815 positions,
816 global_bounding_boxes);
817
818 // Unpack the information into several vectors:
819 // All cells that contain at least one particle
820 const auto &local_cells_containing_particles =
821 std::get<0>(cells_positions_and_index_maps);
822
823 // The reference position of every particle in the local part of the
824 // triangulation.
825 const auto &local_reference_positions =
826 std::get<1>(cells_positions_and_index_maps);
827 // The original index in the positions vector for each particle in the
828 // local part of the triangulation
829 const auto &original_indices_of_local_particles =
830 std::get<2>(cells_positions_and_index_maps);
831 // The real spatial position of every particle in the local part of the
832 // triangulation.
833 const auto &local_positions = std::get<3>(cells_positions_and_index_maps);
834 // The MPI process that inserted each particle
835 const auto &calling_process_indices =
836 std::get<4>(cells_positions_and_index_maps);
837
838 // Create the map of cpu to indices, indicating who sent us what particle
839 std::map<unsigned int, std::vector<unsigned int>>
840 original_process_to_local_particle_indices_tmp;
841 for (unsigned int i_cell = 0;
842 i_cell < local_cells_containing_particles.size();
843 ++i_cell)
844 {
845 for (unsigned int i_particle = 0;
846 i_particle < local_positions[i_cell].size();
847 ++i_particle)
848 {
849 const unsigned int local_id_on_calling_process =
850 original_indices_of_local_particles[i_cell][i_particle];
851 const unsigned int calling_process =
852 calling_process_indices[i_cell][i_particle];
853
854 original_process_to_local_particle_indices_tmp[calling_process]
855 .push_back(local_id_on_calling_process);
856 }
857 }
858 std::map<unsigned int, IndexSet> original_process_to_local_particle_indices;
859 for (auto &process_and_particle_indices :
860 original_process_to_local_particle_indices_tmp)
861 {
862 const unsigned int calling_process = process_and_particle_indices.first;
863 original_process_to_local_particle_indices.insert(
864 {calling_process, IndexSet(n_particles_per_proc[calling_process])});
865 std::sort(process_and_particle_indices.second.begin(),
866 process_and_particle_indices.second.end());
867 original_process_to_local_particle_indices[calling_process].add_indices(
868 process_and_particle_indices.second.begin(),
869 process_and_particle_indices.second.end());
870 original_process_to_local_particle_indices[calling_process].compress();
871 }
872
873 // A map from mpi process to properties, ordered as in the IndexSet.
874 // Notice that this ordering may be different from the ordering in the
875 // vectors above, since no local ordering is guaranteed by the
876 // distribute_compute_point_locations() call.
877 // This is only filled if n_global_properties is > 0
878 std::map<unsigned int, std::vector<std::vector<double>>>
879 locally_owned_properties_from_other_processes;
880
881 // A map from mpi process to ids, ordered as in the IndexSet.
882 // Notice that this ordering may be different from the ordering in the
883 // vectors above, since no local ordering is guaranteed by the
884 // distribute_compute_point_locations() call.
885 // This is only filled if ids.size() is > 0
886 std::map<unsigned int, std::vector<types::particle_index>>
887 locally_owned_ids_from_other_processes;
888
889 if (n_global_properties > 0 || !ids.empty())
890 {
891 // Gather whom I sent my own particles to, to decide whom to send
892 // the particle properties or the ids
893 auto send_to_cpu = Utilities::MPI::some_to_some(
894 comm, original_process_to_local_particle_indices);
895
896 // Prepare the vector of properties to send
897 if (n_global_properties > 0)
898 {
899 std::map<unsigned int, std::vector<std::vector<double>>>
900 non_locally_owned_properties;
901
902 for (const auto &it : send_to_cpu)
903 {
904 std::vector<std::vector<double>> properties_to_send(
905 it.second.n_elements(),
906 std::vector<double>(n_properties_per_particle()));
907 unsigned int index = 0;
908 for (const auto el : it.second)
909 properties_to_send[index++] = properties[el];
910 non_locally_owned_properties.insert(
911 {it.first, properties_to_send});
912 }
913
914 // Send the non locally owned properties to each mpi process
915 // that needs them
916 locally_owned_properties_from_other_processes =
917 Utilities::MPI::some_to_some(comm, non_locally_owned_properties);
918
920 locally_owned_properties_from_other_processes.size(),
921 original_process_to_local_particle_indices.size());
922 }
923
924 if (!ids.empty())
925 {
926 std::map<unsigned int, std::vector<types::particle_index>>
927 non_locally_owned_ids;
928 for (const auto &it : send_to_cpu)
929 {
930 std::vector<types::particle_index> ids_to_send(
931 it.second.n_elements());
932 unsigned int index = 0;
933 for (const auto el : it.second)
934 ids_to_send[index++] = ids[el];
935 non_locally_owned_ids.insert({it.first, ids_to_send});
936 }
937
938 // Send the non locally owned ids to each mpi process
939 // that needs them
940 locally_owned_ids_from_other_processes =
941 Utilities::MPI::some_to_some(comm, non_locally_owned_ids);
942
943 AssertDimension(locally_owned_ids_from_other_processes.size(),
944 original_process_to_local_particle_indices.size());
945 }
946 }
947
948 // Now fill up the actual particles
949 for (unsigned int i_cell = 0;
950 i_cell < local_cells_containing_particles.size();
951 ++i_cell)
952 {
953 for (unsigned int i_particle = 0;
954 i_particle < local_positions[i_cell].size();
955 ++i_particle)
956 {
957 const unsigned int local_id_on_calling_process =
958 original_indices_of_local_particles[i_cell][i_particle];
959
960 const unsigned int calling_process =
961 calling_process_indices[i_cell][i_particle];
962
963 const unsigned int index_within_set =
964 original_process_to_local_particle_indices[calling_process]
965 .index_within_set(local_id_on_calling_process);
966
967 const unsigned int particle_id =
968 ids.empty() ?
969 local_id_on_calling_process +
970 particle_start_indices[calling_process] :
971 locally_owned_ids_from_other_processes[calling_process]
972 [index_within_set];
973
974 auto particle_it =
975 insert_particle(local_positions[i_cell][i_particle],
976 local_reference_positions[i_cell][i_particle],
977 particle_id,
978 local_cells_containing_particles[i_cell]);
979
980 if (n_global_properties > 0)
981 {
982 particle_it->set_properties(
983 locally_owned_properties_from_other_processes
984 [calling_process][index_within_set]);
985 }
986 }
987 }
988
989 update_cached_numbers();
990
991 return original_process_to_local_particle_indices;
992 }
993
994
995
996 template <int dim, int spacedim>
997 std::map<unsigned int, IndexSet>
999 const std::vector<Particle<dim, spacedim>> &particles,
1000 const std::vector<std::vector<BoundingBox<spacedim>>>
1001 &global_bounding_boxes)
1002 {
1003 // Store the positions in a vector of points, the ids in a vector of ids,
1004 // and the properties, if any, in a vector of vector of properties.
1005 std::vector<Point<spacedim>> positions;
1006 std::vector<std::vector<double>> properties;
1007 std::vector<types::particle_index> ids;
1008 positions.resize(particles.size());
1009 ids.resize(particles.size());
1010 if (n_properties_per_particle() > 0)
1011 properties.resize(particles.size(),
1012 std::vector<double>(n_properties_per_particle()));
1013
1014 unsigned int i = 0;
1015 for (const auto &p : particles)
1016 {
1017 positions[i] = p.get_location();
1018 ids[i] = p.get_id();
1019 if (p.has_properties())
1020 properties[i] = {p.get_properties().begin(),
1021 p.get_properties().end()};
1022 ++i;
1023 }
1024
1025 return insert_global_particles(positions,
1026 global_bounding_boxes,
1027 properties,
1028 ids);
1029 }
1030
1031
1032
1033 template <int dim, int spacedim>
1036 {
1037 return global_number_of_particles;
1038 }
1039
1040
1041
1042 template <int dim, int spacedim>
1045 {
1046 return global_max_particles_per_cell;
1047 }
1048
1049
1050
1051 template <int dim, int spacedim>
1054 {
1055 return number_of_locally_owned_particles;
1056 }
1057
1058
1059
1060 template <int dim, int spacedim>
1061 unsigned int
1063 {
1064 return property_pool->n_properties_per_slot();
1065 }
1066
1067
1068
1069 template <int dim, int spacedim>
1072 {
1073 return next_free_particle_index;
1074 }
1075
1076
1077
1078 template <int dim, int spacedim>
1079 IndexSet
1081 {
1082 IndexSet set(get_next_free_particle_index());
1083 std::vector<types::particle_index> indices;
1084 indices.reserve(n_locally_owned_particles());
1085 for (const auto &p : *this)
1086 indices.push_back(p.get_id());
1087 set.add_indices(indices.begin(), indices.end());
1088 set.compress();
1089 return set;
1090 }
1091
1092
1093
1094 template <int dim, int spacedim>
1097 {
1098 return property_pool->n_slots();
1099 }
1100
1101
1102
1103 template <int dim, int spacedim>
1104 void
1106 std::vector<Point<spacedim>> &positions,
1107 const bool add_to_output_vector) const
1108 {
1109 // There should be one point per particle to gather
1110 AssertDimension(positions.size(), n_locally_owned_particles());
1111
1112 unsigned int i = 0;
1113 for (auto it = begin(); it != end(); ++it, ++i)
1114 {
1115 if (add_to_output_vector)
1116 positions[i] = positions[i] + it->get_location();
1117 else
1118 positions[i] = it->get_location();
1119 }
1120 }
1121
1122
1123
1124 template <int dim, int spacedim>
1125 void
1127 const std::vector<Point<spacedim>> &new_positions,
1128 const bool displace_particles)
1129 {
1130 // There should be one point per particle to fix the new position
1131 AssertDimension(new_positions.size(), n_locally_owned_particles());
1132
1133 unsigned int i = 0;
1134 for (auto it = begin(); it != end(); ++it, ++i)
1135 {
1136 Point<spacedim> &location = it->get_location();
1137 if (displace_particles)
1138 location += new_positions[i];
1139 else
1140 location = new_positions[i];
1141 }
1142 sort_particles_into_subdomains_and_cells();
1143 }
1144
1145
1146
1147 template <int dim, int spacedim>
1148 void
1150 const Function<spacedim> &function,
1151 const bool displace_particles)
1152 {
1153 // The function should have sufficient components to displace the
1154 // particles
1155 AssertDimension(function.n_components, spacedim);
1156
1157 Vector<double> new_position(spacedim);
1158 for (auto &particle : *this)
1159 {
1160 Point<spacedim> &particle_location = particle.get_location();
1161 function.vector_value(particle_location, new_position);
1162 if (displace_particles)
1163 for (unsigned int d = 0; d < spacedim; ++d)
1164 particle_location[d] += new_position[d];
1165 else
1166 for (unsigned int d = 0; d < spacedim; ++d)
1167 particle_location[d] = new_position[d];
1168 }
1169 sort_particles_into_subdomains_and_cells();
1170 }
1171
1172
1173
1174 template <int dim, int spacedim>
1177 {
1178 return *property_pool;
1179 }
1180
1181
1182
1183 namespace
1184 {
1194 template <int dim>
1195 bool
1196 compare_particle_association(
1197 const unsigned int a,
1198 const unsigned int b,
1199 const Tensor<1, dim> &particle_direction,
1200 const std::vector<Tensor<1, dim>> &center_directions)
1201 {
1202 const double scalar_product_a = center_directions[a] * particle_direction;
1203 const double scalar_product_b = center_directions[b] * particle_direction;
1204
1205 // The function is supposed to return if a is before b. We are looking
1206 // for the alignment of particle direction and center direction,
1207 // therefore return if the scalar product of a is larger.
1208 return (scalar_product_a > scalar_product_b);
1209 }
1210 } // namespace
1211
1212
1213
1214 template <int dim, int spacedim>
1215 void
1217 {
1218 Assert(triangulation != nullptr, ExcInternalError());
1219 Assert(cells_to_particle_cache.size() == triangulation->n_active_cells(),
1221
1222 // TODO: The current algorithm only works for particles that are in
1223 // the local domain or in ghost cells, because it only knows the
1224 // subdomain_id of ghost cells, but not of artificial cells. This
1225 // can be extended using the distributed version of compute point
1226 // locations.
1227 // TODO: Extend this function to allow keeping particles on other
1228 // processes around (with an invalid cell).
1229
1230 std::vector<particle_iterator> particles_out_of_cell;
1231
1232 // Reserve some space for particles that need sorting to avoid frequent
1233 // re-allocation. Guess 25% of particles need sorting. Balance memory
1234 // overhead and performance.
1235 particles_out_of_cell.reserve(n_locally_owned_particles() / 4);
1236
1237 // We find which particles have left their cell in stage one.
1238 // This is done in a thread-parallel worker thread.
1239 // If particles have left their cell, we must add them to the
1240 // particles_out_of_cell vector, which can only happen in
1241 // a serial copier thread.
1242 struct StageOne_CopyData
1243 {
1244 std::vector<particle_iterator> local_particles_out_of_cell;
1245
1246 StageOne_CopyData(const unsigned int size)
1247 {
1248 local_particles_out_of_cell.reserve(size);
1249 }
1250 };
1251
1252 // A structure that contains scratch data, which can be reused by the worker
1253 // threads. Using this structure avoids reallocating these variables for
1254 // every cell.
1255 struct StageOne_ScratchData
1256 {
1257 std::vector<Point<spacedim>> real_locations;
1258 std::vector<Point<dim>> reference_locations;
1259 StageOne_ScratchData(const unsigned int size)
1260 {
1261 real_locations.reserve(size);
1262 reference_locations.reserve(size);
1263 }
1264 };
1265
1266 const auto stage_one_worker =
1267 [&](
1269 StageOne_ScratchData &scratch,
1270 StageOne_CopyData &copy) {
1271 // Particles can be inserted into arbitrary cells, e.g. if their cell is
1272 // not known. However, for artificial cells we can not evaluate
1273 // the reference position of particles. Do not sort particles that are
1274 // not locally owned, because they will be sorted by the process that
1275 // owns them.
1276 copy.local_particles_out_of_cell.clear();
1277
1278 if (cell->is_locally_owned() == false)
1279 {
1280 return;
1281 }
1282
1283 scratch.real_locations.clear();
1284
1285 const unsigned int n_pic = n_particles_in_cell(cell);
1286 auto pic = particles_in_cell(cell);
1287
1288 for (const auto &particle : pic)
1289 scratch.real_locations.push_back(particle.get_location());
1290
1291 scratch.reference_locations.resize(n_pic);
1292 mapping->transform_points_real_to_unit_cell(
1293 cell, scratch.real_locations, scratch.reference_locations);
1294
1295 auto particle = pic.begin();
1296 for (const auto &p_unit : scratch.reference_locations)
1297 {
1298 if (numbers::is_finite(p_unit[0]) &&
1299 cell->reference_cell().contains_point(p_unit,
1300 tolerance_inside_cell))
1301 particle->set_reference_location(p_unit);
1302 else
1303 copy.local_particles_out_of_cell.push_back(particle);
1304
1305 ++particle;
1306 }
1307 };
1308
1309 const auto stage_one_copier = [&](const StageOne_CopyData &copy) {
1310 particles_out_of_cell.insert(particles_out_of_cell.end(),
1311 copy.local_particles_out_of_cell.begin(),
1312 copy.local_particles_out_of_cell.end());
1313 };
1314 WorkStream::run(triangulation->begin_active(),
1315 triangulation->end(),
1316 stage_one_worker,
1317 stage_one_copier,
1318 StageOne_ScratchData(global_max_particles_per_cell),
1319 StageOne_CopyData(global_max_particles_per_cell));
1320
1321 // There are three reasons why a particle is not in its old cell:
1322 // It moved to another cell, to another subdomain or it left the mesh.
1323 // Particles that moved to another cell are updated and moved inside the
1324 // particles vector, particles that moved to another domain are
1325 // collected in the moved_particles_domain vector. Particles that left
1326 // the mesh completely are ignored and removed.
1327 std::map<types::subdomain_id, std::vector<particle_iterator>>
1328 moved_particles;
1329 std::map<
1331 std::vector<typename Triangulation<dim, spacedim>::active_cell_iterator>>
1332 moved_cells;
1333
1334 // We do not know exactly how many particles are lost, exchanged between
1335 // domains, or remain on this process. Therefore we pre-allocate
1336 // approximate sizes for these vectors. If more space is needed an
1337 // automatic and relatively fast (compared to other parts of this
1338 // algorithm) re-allocation will happen.
1339 std::set<types::subdomain_id> ghost_owners;
1340 if (const auto parallel_triangulation =
1341 dynamic_cast<const parallel::TriangulationBase<dim, spacedim> *>(
1342 &*triangulation))
1343 ghost_owners = parallel_triangulation->ghost_owners();
1344
1345 // Reserve some space for particles that need communication to avoid
1346 // frequent re-allocation. Guess 25% of particles out of their old cell need
1347 // communication. Balance memory overhead and performance.
1348 for (const auto &ghost_owner : ghost_owners)
1349 moved_particles[ghost_owner].reserve(particles_out_of_cell.size() / 4);
1350 for (const auto &ghost_owner : ghost_owners)
1351 moved_cells[ghost_owner].reserve(particles_out_of_cell.size() / 4);
1352
1353 {
1354 // Create a map from vertices to adjacent cells using grid cache
1355 const std::vector<
1356 std::set<typename Triangulation<dim, spacedim>::active_cell_iterator>>
1357 &vertex_to_cells = triangulation_cache->get_vertex_to_cell_map();
1358
1359 // Create a corresponding map of vectors from vertex to cell center
1360 // using grid cache
1361 const std::vector<std::vector<Tensor<1, spacedim>>>
1362 &vertex_to_cell_centers =
1363 triangulation_cache->get_vertex_to_cell_centers_directions();
1364
1365 // In stage two we search for the new cells of particles that have left
1366 // their old cell. The particles may be located in a new cell or be lost
1367 // entirely if they left the triangulation. We try to find the new cells
1368 // of multiple particles in parallel worker threads, however we cannot
1369 // reinsert the particles until we have finished looking at all of them or
1370 // we risk invalidating the data the worker is looking at.
1371
1372 struct StageTwo_CopyData
1373 {
1374 typename Triangulation<dim, spacedim>::cell_iterator current_cell;
1375 particle_iterator out_particle;
1376 Point<dim> reference_location;
1377 bool found_cell;
1378 };
1379
1380 struct StageTwo_ScratchData
1381 {
1382 std::vector<Point<spacedim>> real_locations;
1383 std::vector<Point<dim>> reference_locations;
1384 std::vector<unsigned int> search_order;
1385
1386 StageTwo_ScratchData()
1387 {
1388 reference_locations.resize(1, numbers::signaling_nan<Point<dim>>());
1389 real_locations.resize(1, numbers::signaling_nan<Point<spacedim>>());
1390 }
1391 };
1392
1393 struct StageTwo_QueuedData
1394 {
1395 typename Triangulation<dim, spacedim>::cell_iterator current_cell;
1396 particle_iterator out_particle;
1397 };
1398
1399 std::vector<StageTwo_QueuedData> data_queue;
1400
1401 data_queue.reserve(n_locally_owned_particles() / 4);
1402 // Find the cells that the particles moved to.
1403 const auto stage_two_worker =
1404 [&](
1405 const typename std::vector<particle_iterator>::iterator out_particle,
1406 StageTwo_ScratchData &scratch,
1407 StageTwo_CopyData &copy) {
1408 // make a copy of the current cell, since we will modify the
1409 // variable current_cell below, but we need the original in
1410 // the case the particle is not found
1411 copy.current_cell = (*out_particle)->get_surrounding_cell();
1412 copy.found_cell = false;
1413
1414 scratch.real_locations[0] = (*out_particle)->get_location();
1415
1416 // Check if the particle is in one of the old cell's neighbors
1417 // that are adjacent to the closest vertex
1418 const unsigned int closest_vertex =
1419 GridTools::find_closest_vertex_of_cell<dim, spacedim>(
1420 copy.current_cell, (*out_particle)->get_location(), *mapping);
1421 const unsigned int closest_vertex_index =
1422 copy.current_cell->vertex_index(closest_vertex);
1423
1424 const auto &candidate_cells = vertex_to_cells[closest_vertex_index];
1425 const unsigned int n_candidate_cells = candidate_cells.size();
1426
1427 // The order of searching through the candidate cells matters for
1428 // performance reasons. Start with a simple order.
1429 scratch.search_order.resize(n_candidate_cells);
1430 for (unsigned int i = 0; i < n_candidate_cells; ++i)
1431 scratch.search_order[i] = i;
1432
1433 // If the particle is not on a vertex, we can do better by
1434 // sorting the candidate cells by alignment with
1435 // the vertex_to_particle direction.
1436 Tensor<1, spacedim> vertex_to_particle =
1437 (*out_particle)->get_location() -
1438 copy.current_cell->vertex(closest_vertex);
1439
1440 // Only do this if the particle is not on a vertex, otherwise we
1441 // cannot normalize
1442 if (vertex_to_particle.norm_square() >
1443 1e4 * std::numeric_limits<double>::epsilon() *
1444 std::numeric_limits<double>::epsilon() *
1445 vertex_to_cell_centers[closest_vertex_index][0].norm_square())
1446 {
1447 vertex_to_particle /= vertex_to_particle.norm();
1448 const auto &vertex_to_cells_center =
1449 vertex_to_cell_centers[closest_vertex_index];
1450
1451 std::sort(scratch.search_order.begin(),
1452 scratch.search_order.end(),
1453 [&vertex_to_particle,
1454 &vertex_to_cells_center](const unsigned int a,
1455 const unsigned int b) {
1456 return compare_particle_association<spacedim>(
1457 a, b, vertex_to_particle, vertex_to_cells_center);
1458 });
1459 }
1460
1461 // Search all of the candidate cells according to the determined
1462 // order. Most likely we will find the particle in them.
1463 for (unsigned int i = 0; i < n_candidate_cells; ++i)
1464 {
1465 typename std::set<
1467 const_iterator candidate_cell = candidate_cells.begin();
1468
1469 std::advance(candidate_cell, scratch.search_order[i]);
1470
1471 // We can not use artificial cells as a target since we
1472 // can only send particles to owned or ghost cells.
1473 // Skip them here so that the particle
1474 // is reported as lost instead of being silently dropped later.
1475 if ((*candidate_cell)->is_artificial())
1476 continue;
1477
1478 mapping->transform_points_real_to_unit_cell(
1479 *candidate_cell,
1480 scratch.real_locations,
1481 scratch.reference_locations);
1482
1484 scratch.reference_locations[0], tolerance_inside_cell))
1485 {
1486 copy.current_cell = *candidate_cell;
1487 copy.found_cell = true;
1488 break;
1489 }
1490 }
1491
1492 // If we did not find a cell the particle is not in a neighbor of
1493 // its old cell. Look for the new cell in the whole local domain.
1494 // This case should be rare.
1495 if (!copy.found_cell)
1496 {
1497 // For some clang-based compilers and boost versions the call to
1498 // RTree::query doesn't compile. We use a slower implementation as
1499 // workaround.
1500 // This is fixed in boost in
1501 // https://github.com/boostorg/numeric_conversion/commit/50a1eae942effb0a9b90724323ef8f2a67e7984a
1502#if defined(DEAL_II_WITH_BOOST_BUNDLED) || \
1503 !(defined(__clang_major__) && __clang_major__ >= 16) || \
1504 BOOST_VERSION >= 108100
1505 std::vector<std::pair<Point<spacedim>, unsigned int>>
1506 closest_vertex_in_domain;
1507 triangulation_cache->get_used_vertices_rtree().query(
1508 boost::geometry::index::nearest((*out_particle)->get_location(),
1509 1),
1510 std::back_inserter(closest_vertex_in_domain));
1511
1512 // We should have one and only one result
1513 AssertDimension(closest_vertex_in_domain.size(), 1);
1514 const unsigned int closest_vertex_index_in_domain =
1515 closest_vertex_in_domain[0].second;
1516#else
1517 const unsigned int closest_vertex_index_in_domain =
1519 *triangulation,
1520 (*out_particle)->get_location());
1521#endif
1522
1523 // Search all of the cells adjacent to the closest vertex of the
1524 // domain. Most likely we will find the particle in them.
1525 for (const auto &cell :
1526 vertex_to_cells[closest_vertex_index_in_domain])
1527 {
1528 // See the comment above: artificial cells are not valid
1529 // targets for a particle. We skip them.
1530 if (cell->is_artificial())
1531 continue;
1532
1533 mapping->transform_points_real_to_unit_cell(
1534 cell, scratch.real_locations, scratch.reference_locations);
1535
1537 scratch.reference_locations[0], tolerance_inside_cell))
1538 {
1539 copy.current_cell = cell;
1540 copy.found_cell = true;
1541 break;
1542 }
1543 }
1544 }
1545 copy.out_particle = (*out_particle);
1546 copy.reference_location = scratch.reference_locations[0];
1547 };
1548
1549 const auto stage_two_copier = [&](const StageTwo_CopyData &copy) {
1550 auto local_out_particle = copy.out_particle;
1551
1552 if (!copy.found_cell)
1553 {
1554 // We can find no cell for this particle. It has left the
1555 // domain due to an integration error or an open boundary.
1556 // Signal the loss and move on.
1557 signals.particle_lost(copy.out_particle,
1558 copy.out_particle->get_surrounding_cell());
1559 return;
1560 }
1561 // If we are here, we found a cell and reference position for this
1562 // particle
1563 local_out_particle->set_reference_location(copy.reference_location);
1564
1565 // Re-insertion into the domain while workers are running may cause
1566 // problems, we queue this for later.
1567 StageTwo_QueuedData data_entry;
1568 data_entry.current_cell = copy.current_cell;
1569 data_entry.out_particle = copy.out_particle;
1570 data_queue.push_back(data_entry);
1571 };
1572
1573 WorkStream::run(particles_out_of_cell.begin(),
1574 particles_out_of_cell.end(),
1575 stage_two_worker,
1576 stage_two_copier,
1577 StageTwo_ScratchData(),
1578 StageTwo_CopyData(),
1580 64);
1581
1582 // Now we reinsert all queued particles.
1583 for (const auto &data_entry : data_queue)
1584 {
1585 // Reinsert the particle into our domain if we own its cell.
1586 // Mark it for MPI transfer otherwise
1587 if (data_entry.current_cell->is_locally_owned())
1588 {
1590 data_entry.out_particle->particles_in_cell->particles
1591 [data_entry.out_particle->particle_index_within_cell];
1592
1593 // Avoid deallocating the memory of this particle
1594 const auto old_value = old;
1596
1597 // Allocate particle with the old handle
1598 insert_particle(old_value, data_entry.current_cell);
1599 }
1600 else
1601 {
1602 moved_particles[data_entry.current_cell->subdomain_id()]
1603 .push_back(data_entry.out_particle);
1604 moved_cells[data_entry.current_cell->subdomain_id()].push_back(
1605 data_entry.current_cell);
1606 }
1607 }
1608 }
1609
1610 // Exchange particles between processors if we have more than one process
1611#ifdef DEAL_II_WITH_MPI
1612 if (const auto parallel_triangulation =
1613 dynamic_cast<const parallel::TriangulationBase<dim, spacedim> *>(
1614 &*triangulation))
1615 {
1617 parallel_triangulation->get_mpi_communicator()) > 1)
1618 send_recv_particles(moved_particles, moved_cells);
1619 }
1620#endif
1621
1622 // remove_particles also calls update_cached_numbers()
1623 remove_particles(particles_out_of_cell);
1624
1625 // now make sure particle data is sorted in order of iteration
1626 std::vector<typename PropertyPool<dim, spacedim>::Handle> unsorted_handles;
1627 unsorted_handles.reserve(property_pool->n_registered_slots());
1628
1629 typename PropertyPool<dim, spacedim>::Handle sorted_handle = 0;
1630 for (auto &particles_in_cell : particles)
1631 for (auto &particle : particles_in_cell.particles)
1632 {
1633 unsorted_handles.push_back(particle);
1634 particle = sorted_handle++;
1635 }
1636
1637 property_pool->sort_memory_slots(unsorted_handles);
1638
1639 } // namespace Particles
1640
1641
1642
1643 template <int dim, int spacedim>
1644 void
1646 const bool enable_cache)
1647 {
1648 // Nothing to do in serial computations
1649 const auto parallel_triangulation =
1650 dynamic_cast<const parallel::TriangulationBase<dim, spacedim> *>(
1651 &*triangulation);
1652 if (parallel_triangulation != nullptr)
1653 {
1655 parallel_triangulation->get_mpi_communicator()) == 1)
1656 return;
1657 }
1658 else
1659 return;
1660
1661#ifndef DEAL_II_WITH_MPI
1662 (void)enable_cache;
1663#else
1664 // Clear ghost particles and their properties
1665 for (const auto &cell : triangulation->active_cell_iterators())
1666 if (cell->is_ghost() &&
1667 cells_to_particle_cache[cell->active_cell_index()] != particles.end())
1668 {
1669 Assert(cells_to_particle_cache[cell->active_cell_index()]->cell ==
1670 cell,
1672 // Clear particle properties
1673 for (auto &ghost_particle :
1674 cells_to_particle_cache[cell->active_cell_index()]->particles)
1675 property_pool->deregister_particle(ghost_particle);
1676
1677 // Clear particles themselves
1678 particles.erase(cells_to_particle_cache[cell->active_cell_index()]);
1679 cells_to_particle_cache[cell->active_cell_index()] = particles.end();
1680 }
1681
1682 // Clear ghost particles cache and invalidate it
1683 ghost_particles_cache.ghost_particles_by_domain.clear();
1684 ghost_particles_cache.valid = false;
1685
1686 // In the case of a parallel simulation with periodic boundary conditions
1687 // the vertices associated with periodic boundaries are not directly
1688 // connected to the ghost cells but they are connected to the ghost cells
1689 // through their coinciding vertices. We gather this information using the
1690 // vertices_with_ghost_neighbors map
1691 const std::map<unsigned int, std::set<types::subdomain_id>>
1692 &vertices_with_ghost_neighbors =
1693 triangulation_cache->get_vertices_with_ghost_neighbors();
1694
1695 const std::set<types::subdomain_id> ghost_owners =
1696 parallel_triangulation->ghost_owners();
1697 for (const auto ghost_owner : ghost_owners)
1698 ghost_particles_cache.ghost_particles_by_domain[ghost_owner].reserve(
1699 n_locally_owned_particles() / 4);
1700
1701 const std::vector<std::set<unsigned int>> vertex_to_neighbor_subdomain =
1702 triangulation_cache->get_vertex_to_neighbor_subdomain();
1703
1704 for (const auto &cell : triangulation->active_cell_iterators())
1705 {
1706 if (cell->is_locally_owned())
1707 {
1708 std::set<unsigned int> cell_to_neighbor_subdomain;
1709 for (const unsigned int v : cell->vertex_indices())
1710 {
1711 const auto vertex_ghost_neighbors =
1712 vertices_with_ghost_neighbors.find(cell->vertex_index(v));
1713 if (vertex_ghost_neighbors !=
1714 vertices_with_ghost_neighbors.end())
1715 {
1716 cell_to_neighbor_subdomain.insert(
1717 vertex_ghost_neighbors->second.begin(),
1718 vertex_ghost_neighbors->second.end());
1719 }
1720 }
1721
1722 if (cell_to_neighbor_subdomain.size() > 0)
1723 {
1724 const particle_iterator_range particle_range =
1725 particles_in_cell(cell);
1726
1727 for (const auto domain : cell_to_neighbor_subdomain)
1728 {
1729 for (typename particle_iterator_range::iterator particle =
1730 particle_range.begin();
1731 particle != particle_range.end();
1732 ++particle)
1733 ghost_particles_cache.ghost_particles_by_domain[domain]
1734 .push_back(particle);
1735 }
1736 }
1737 }
1738 }
1739
1740 send_recv_particles(
1741 ghost_particles_cache.ghost_particles_by_domain,
1742 std::map<
1744 std::vector<
1746 enable_cache);
1747#endif
1748 }
1749
1750
1751
1752 template <int dim, int spacedim>
1753 void
1755 {
1756 // Nothing to do in serial computations
1757 const auto parallel_triangulation =
1758 dynamic_cast<const parallel::TriangulationBase<dim, spacedim> *>(
1759 &*triangulation);
1760 if (parallel_triangulation == nullptr ||
1762 parallel_triangulation->get_mpi_communicator()) == 1)
1763 {
1764 return;
1765 }
1766
1767
1768#ifdef DEAL_II_WITH_MPI
1769 // First clear the current ghost_particle information
1770 // ghost_particles.clear();
1771 Assert(ghost_particles_cache.valid,
1772 ExcMessage(
1773 "Ghost particles cannot be updated if they first have not been "
1774 "exchanged at least once with the cache enabled"));
1775
1776
1777 send_recv_particles_properties_and_location(
1778 ghost_particles_cache.ghost_particles_by_domain);
1779#endif
1780 }
1781
1782
1783
1784#ifdef DEAL_II_WITH_MPI
1785 template <int dim, int spacedim>
1786 void
1788 const std::map<types::subdomain_id, std::vector<particle_iterator>>
1789 &particles_to_send,
1790 const std::map<
1793 &send_cells,
1794 const bool build_cache)
1795 {
1796 Assert(triangulation != nullptr, ExcInternalError());
1797 Assert(cells_to_particle_cache.size() == triangulation->n_active_cells(),
1799
1800 ghost_particles_cache.valid = build_cache;
1801
1802 const auto parallel_triangulation =
1803 dynamic_cast<const parallel::TriangulationBase<dim, spacedim> *>(
1804 &*triangulation);
1805 Assert(parallel_triangulation,
1806 ExcMessage("This function is only implemented for "
1807 "parallel::TriangulationBase objects."));
1808
1809 // Determine the communication pattern
1810 const std::set<types::subdomain_id> ghost_owners =
1811 parallel_triangulation->ghost_owners();
1812 const std::vector<types::subdomain_id> neighbors(ghost_owners.begin(),
1813 ghost_owners.end());
1814 const unsigned int n_neighbors = neighbors.size();
1815
1816 if (send_cells.size() != 0)
1817 Assert(particles_to_send.size() == send_cells.size(), ExcInternalError());
1818
1819 // If we do not know the subdomain this particle needs to be send to,
1820 // throw an error
1821 Assert(particles_to_send.find(numbers::artificial_subdomain_id) ==
1822 particles_to_send.end(),
1824
1825 // TODO: Implement the shipping of particles to processes that are not
1826 // ghost owners of the local domain
1827 for (auto send_particles = particles_to_send.begin();
1828 send_particles != particles_to_send.end();
1829 ++send_particles)
1830 Assert(ghost_owners.find(send_particles->first) != ghost_owners.end(),
1832
1833 std::size_t n_send_particles = 0;
1834 for (auto send_particles = particles_to_send.begin();
1835 send_particles != particles_to_send.end();
1836 ++send_particles)
1837 n_send_particles += send_particles->second.size();
1838
1839 const unsigned int cellid_size = sizeof(CellId::binary_type);
1840
1841 // Containers for the amount and offsets of data we will send
1842 // to other processors and the data itself.
1843 std::vector<unsigned int> n_send_data(n_neighbors, 0);
1844 std::vector<unsigned int> send_offsets(n_neighbors, 0);
1845 std::vector<char> send_data;
1846
1847 Particle<dim, spacedim> test_particle;
1848 test_particle.set_property_pool(*property_pool);
1849
1850 const unsigned int individual_particle_data_size =
1851 test_particle.serialized_size_in_bytes() +
1852 (size_callback ? size_callback() : 0);
1853
1854 const unsigned int individual_total_particle_data_size =
1855 individual_particle_data_size + cellid_size;
1856
1857 // Only serialize things if there are particles to be send.
1858 // We can not return early even if no particles
1859 // are send, because we might receive particles from other processes
1860 if (n_send_particles > 0)
1861 {
1862 // Allocate space for sending particle data
1863 send_data.resize(n_send_particles *
1864 individual_total_particle_data_size);
1865
1866 void *data = static_cast<void *>(&send_data.front());
1867
1868 // Serialize the data sorted by receiving process
1869 for (unsigned int i = 0; i < n_neighbors; ++i)
1870 {
1871 send_offsets[i] = reinterpret_cast<std::size_t>(data) -
1872 reinterpret_cast<std::size_t>(&send_data.front());
1873
1874 const unsigned int n_particles_to_send =
1875 particles_to_send.at(neighbors[i]).size();
1876
1877 Assert(static_cast<std::size_t>(n_particles_to_send) *
1878 individual_total_particle_data_size ==
1879 static_cast<std::size_t>(
1880 n_particles_to_send *
1881 individual_total_particle_data_size),
1882 ExcMessage("Overflow when trying to send particle "
1883 "data"));
1884
1885 for (unsigned int j = 0; j < n_particles_to_send; ++j)
1886 {
1887 // If no target cells are given, use the iterator
1888 // information
1890 cell;
1891 if (send_cells.empty())
1892 cell = particles_to_send.at(neighbors[i])[j]
1893 ->get_surrounding_cell();
1894 else
1895 cell = send_cells.at(neighbors[i])[j];
1896
1897 const CellId::binary_type cellid =
1898 cell->id().template to_binary<dim>();
1899 memcpy(data, &cellid, cellid_size);
1900 data = static_cast<char *>(data) + cellid_size;
1901
1902 data = particles_to_send.at(neighbors[i])[j]
1903 ->write_particle_data_to_memory(data);
1904 if (store_callback)
1905 data =
1906 store_callback(particles_to_send.at(neighbors[i])[j], data);
1907 }
1908 n_send_data[i] = n_particles_to_send;
1909 }
1910 }
1911
1912 // Containers for the data we will receive from other processors
1913 std::vector<unsigned int> n_recv_data(n_neighbors);
1914 std::vector<unsigned int> recv_offsets(n_neighbors);
1915
1916 {
1917 const int mpi_tag = Utilities::MPI::internal::Tags::
1919
1920 std::vector<MPI_Request> n_requests(2 * n_neighbors);
1921 for (unsigned int i = 0; i < n_neighbors; ++i)
1922 {
1923 const int ierr =
1924 MPI_Irecv(&(n_recv_data[i]),
1925 1,
1926 MPI_UNSIGNED,
1927 neighbors[i],
1928 mpi_tag,
1929 parallel_triangulation->get_mpi_communicator(),
1930 &(n_requests[2 * i]));
1931 AssertThrowMPI(ierr);
1932 }
1933 for (unsigned int i = 0; i < n_neighbors; ++i)
1934 {
1935 const int ierr =
1936 MPI_Isend(&(n_send_data[i]),
1937 1,
1938 MPI_UNSIGNED,
1939 neighbors[i],
1940 mpi_tag,
1941 parallel_triangulation->get_mpi_communicator(),
1942 &(n_requests[2 * i + 1]));
1943 AssertThrowMPI(ierr);
1944 }
1945 const int ierr =
1946 MPI_Waitall(2 * n_neighbors, n_requests.data(), MPI_STATUSES_IGNORE);
1947 AssertThrowMPI(ierr);
1948 }
1949
1950 // Determine how many particles and data we will receive
1951 unsigned int total_recv_data = 0;
1952 for (unsigned int neighbor_id = 0; neighbor_id < n_neighbors; ++neighbor_id)
1953 {
1954 recv_offsets[neighbor_id] = total_recv_data;
1955 total_recv_data +=
1956 n_recv_data[neighbor_id] * individual_total_particle_data_size;
1957 }
1958
1959 // Set up the space for the received particle data
1960 std::vector<char> recv_data(total_recv_data);
1961
1962 // Exchange the particle data between domains
1963 {
1964 std::vector<MPI_Request> requests(2 * n_neighbors);
1965 unsigned int send_ops = 0;
1966 unsigned int recv_ops = 0;
1967
1968 const int mpi_tag = Utilities::MPI::internal::Tags::
1970
1971 for (unsigned int i = 0; i < n_neighbors; ++i)
1972 if (n_recv_data[i] > 0)
1973 {
1974 const int ierr =
1975 MPI_Irecv(&(recv_data[recv_offsets[i]]),
1976 n_recv_data[i] * individual_total_particle_data_size,
1977 MPI_CHAR,
1978 neighbors[i],
1979 mpi_tag,
1980 parallel_triangulation->get_mpi_communicator(),
1981 &(requests[send_ops]));
1982 AssertThrowMPI(ierr);
1983 ++send_ops;
1984 }
1985
1986 for (unsigned int i = 0; i < n_neighbors; ++i)
1987 if (n_send_data[i] > 0)
1988 {
1989 const int ierr =
1990 MPI_Isend(&(send_data[send_offsets[i]]),
1991 n_send_data[i] * individual_total_particle_data_size,
1992 MPI_CHAR,
1993 neighbors[i],
1994 mpi_tag,
1995 parallel_triangulation->get_mpi_communicator(),
1996 &(requests[send_ops + recv_ops]));
1997 AssertThrowMPI(ierr);
1998 ++recv_ops;
1999 }
2000 const int ierr =
2001 MPI_Waitall(send_ops + recv_ops, requests.data(), MPI_STATUSES_IGNORE);
2002 AssertThrowMPI(ierr);
2003 }
2004
2005 // Put the received particles into the domain if they are in the
2006 // triangulation
2007 const void *recv_data_it = static_cast<const void *>(recv_data.data());
2008
2009 // Store the particle iterators in the cache
2010 auto &ghost_particles_iterators =
2011 ghost_particles_cache.ghost_particles_iterators;
2012
2013 if (build_cache)
2014 {
2015 ghost_particles_iterators.clear();
2016
2017 auto &send_pointers_particles = ghost_particles_cache.send_pointers;
2018 send_pointers_particles.assign(n_neighbors + 1, 0);
2019
2020 for (unsigned int i = 0; i < n_neighbors; ++i)
2021 send_pointers_particles[i + 1] =
2022 send_pointers_particles[i] +
2023 n_send_data[i] * individual_particle_data_size;
2024
2025 auto &recv_pointers_particles = ghost_particles_cache.recv_pointers;
2026 recv_pointers_particles.assign(n_neighbors + 1, 0);
2027
2028 for (unsigned int i = 0; i < n_neighbors; ++i)
2029 recv_pointers_particles[i + 1] =
2030 recv_pointers_particles[i] +
2031 n_recv_data[i] * individual_particle_data_size;
2032
2033 ghost_particles_cache.neighbors = neighbors;
2034
2035 ghost_particles_cache.send_data.resize(
2036 ghost_particles_cache.send_pointers.back());
2037 ghost_particles_cache.recv_data.resize(
2038 ghost_particles_cache.recv_pointers.back());
2039 }
2040
2041 while (reinterpret_cast<std::size_t>(recv_data_it) -
2042 reinterpret_cast<std::size_t>(recv_data.data()) <
2043 total_recv_data)
2044 {
2045 CellId::binary_type binary_cellid;
2046 memcpy(&binary_cellid, recv_data_it, cellid_size);
2047 const CellId id(binary_cellid);
2048 recv_data_it = static_cast<const char *>(recv_data_it) + cellid_size;
2049
2051 triangulation->create_cell_iterator(id);
2052
2053 insert_particle(property_pool->register_particle(), cell);
2054 const typename particle_container::iterator &cache =
2055 cells_to_particle_cache[cell->active_cell_index()];
2056 Assert(cache->cell == cell, ExcInternalError());
2057
2058 particle_iterator particle_it(cache,
2059 *property_pool,
2060 cache->particles.size() - 1);
2061
2062 recv_data_it =
2063 particle_it->read_particle_data_from_memory(recv_data_it);
2064
2065 if (load_callback)
2066 recv_data_it = load_callback(particle_it, recv_data_it);
2067
2068 if (build_cache) // TODO: is this safe?
2069 ghost_particles_iterators.push_back(particle_it);
2070 }
2071
2072 AssertThrow(recv_data_it == recv_data.data() + recv_data.size(),
2073 ExcMessage(
2074 "The amount of data that was read into new particles "
2075 "does not match the amount of data sent around."));
2076 }
2077#endif
2078
2079
2080
2081#ifdef DEAL_II_WITH_MPI
2082 template <int dim, int spacedim>
2083 void
2085 const std::map<types::subdomain_id, std::vector<particle_iterator>>
2086 &particles_to_send)
2087 {
2088 const auto parallel_triangulation =
2089 dynamic_cast<const parallel::TriangulationBase<dim, spacedim> *>(
2090 &*triangulation);
2091 Assert(
2092 parallel_triangulation,
2093 ExcMessage(
2094 "This function is only implemented for parallel::TriangulationBase "
2095 "objects."));
2096
2097 const auto &neighbors = ghost_particles_cache.neighbors;
2098 const auto &send_pointers = ghost_particles_cache.send_pointers;
2099 const auto &recv_pointers = ghost_particles_cache.recv_pointers;
2100
2101 std::vector<char> &send_data = ghost_particles_cache.send_data;
2102
2103 // Fill data to send
2104 if (send_pointers.back() > 0)
2105 {
2106 void *data = static_cast<void *>(&send_data.front());
2107
2108 // Serialize the data sorted by receiving process
2109 for (const auto i : neighbors)
2110 for (const auto &p : particles_to_send.at(i))
2111 {
2112 data = p->write_particle_data_to_memory(data);
2113 if (store_callback)
2114 data = store_callback(p, data);
2115 }
2116 }
2117
2118 std::vector<char> &recv_data = ghost_particles_cache.recv_data;
2119
2120 // Exchange the particle data between domains
2121 {
2122 std::vector<MPI_Request> requests(2 * neighbors.size());
2123 unsigned int send_ops = 0;
2124 unsigned int recv_ops = 0;
2125
2126 const int mpi_tag = Utilities::MPI::internal::Tags::
2128
2129 for (unsigned int i = 0; i < neighbors.size(); ++i)
2130 if ((recv_pointers[i + 1] - recv_pointers[i]) > 0)
2131 {
2132 const int ierr =
2133 MPI_Irecv(recv_data.data() + recv_pointers[i],
2134 recv_pointers[i + 1] - recv_pointers[i],
2135 MPI_CHAR,
2136 neighbors[i],
2137 mpi_tag,
2138 parallel_triangulation->get_mpi_communicator(),
2139 &(requests[send_ops]));
2140 AssertThrowMPI(ierr);
2141 ++send_ops;
2142 }
2143
2144 for (unsigned int i = 0; i < neighbors.size(); ++i)
2145 if ((send_pointers[i + 1] - send_pointers[i]) > 0)
2146 {
2147 const int ierr =
2148 MPI_Isend(send_data.data() + send_pointers[i],
2149 send_pointers[i + 1] - send_pointers[i],
2150 MPI_CHAR,
2151 neighbors[i],
2152 mpi_tag,
2153 parallel_triangulation->get_mpi_communicator(),
2154 &(requests[send_ops + recv_ops]));
2155 AssertThrowMPI(ierr);
2156 ++recv_ops;
2157 }
2158 const int ierr =
2159 MPI_Waitall(send_ops + recv_ops, requests.data(), MPI_STATUSES_IGNORE);
2160 AssertThrowMPI(ierr);
2161 }
2162
2163 // Put the received particles into the domain if they are in the
2164 // triangulation
2165 const void *recv_data_it = static_cast<const void *>(recv_data.data());
2166
2167 // Gather ghost particle iterators from the cache
2168 auto &ghost_particles_iterators =
2169 ghost_particles_cache.ghost_particles_iterators;
2170
2171 for (auto &recv_particle : ghost_particles_iterators)
2172 {
2173 // Update particle data using previously allocated memory space
2174 // for efficiency reasons
2175 recv_data_it =
2176 recv_particle->read_particle_data_from_memory(recv_data_it);
2177
2178 Assert(recv_particle->particles_in_cell->cell->is_ghost(),
2180
2181 if (load_callback)
2182 recv_data_it = load_callback(
2183 particle_iterator(recv_particle->particles_in_cell,
2184 *property_pool,
2185 recv_particle->particle_index_within_cell),
2186 recv_data_it);
2187 }
2188
2189 AssertThrow(recv_data_it == recv_data.data() + recv_data.size(),
2190 ExcMessage(
2191 "The amount of data that was read into new particles "
2192 "does not match the amount of data sent around."));
2193 }
2194#endif
2195
2196 template <int dim, int spacedim>
2197 void
2199 const std::function<std::size_t()> &size_callb,
2200 const std::function<void *(const particle_iterator &, void *)> &store_callb,
2201 const std::function<const void *(const particle_iterator &, const void *)>
2202 &load_callb)
2203 {
2204 size_callback = size_callb;
2205 store_callback = store_callb;
2206 load_callback = load_callb;
2207 }
2208
2209
2210 template <int dim, int spacedim>
2211 void
2213 {
2214 // First disconnect existing connections
2215 for (const auto &connection : tria_listeners)
2216 connection.disconnect();
2217
2218 tria_listeners.clear();
2219
2220 tria_listeners.push_back(triangulation->signals.create.connect([&]() {
2221 this->initialize(*(this->triangulation),
2222 *(this->mapping),
2223 this->property_pool->n_properties_per_slot());
2224 }));
2225
2226 this->tria_listeners.push_back(
2227 this->triangulation->signals.clear.connect([&]() { this->clear(); }));
2228
2229 // for distributed triangulations, connect to distributed signals
2231 *>(&(*triangulation)) != nullptr)
2232 {
2233 tria_listeners.push_back(
2234 triangulation->signals.post_distributed_refinement.connect(
2235 [&]() { this->post_mesh_change_action(); }));
2236 tria_listeners.push_back(
2237 triangulation->signals.post_distributed_repartition.connect(
2238 [&]() { this->post_mesh_change_action(); }));
2239 tria_listeners.push_back(
2240 triangulation->signals.post_distributed_load.connect(
2241 [&]() { this->post_mesh_change_action(); }));
2242 }
2243 else
2244 {
2245 tria_listeners.push_back(triangulation->signals.post_refinement.connect(
2246 [&]() { this->post_mesh_change_action(); }));
2247 }
2248 }
2249
2250
2251
2252 template <int dim, int spacedim>
2253 void
2255 {
2256 Assert(triangulation != nullptr, ExcInternalError());
2257
2258 const bool distributed_triangulation =
2259 dynamic_cast<
2261 &(*triangulation)) != nullptr;
2262 (void)distributed_triangulation;
2263
2264 Assert(
2265 distributed_triangulation || number_of_locally_owned_particles == 0,
2266 ExcMessage(
2267 "Mesh refinement in a non-distributed triangulation is not supported "
2268 "by the ParticleHandler class. Either insert particles after mesh "
2269 "creation, or use a distributed triangulation."));
2270
2271 // Resize the container if it is possible without
2272 // transferring particles
2273 if (number_of_locally_owned_particles == 0)
2274 cells_to_particle_cache.resize(triangulation->n_active_cells(),
2275 particles.end());
2276 }
2277
2278
2279
2280 template <int dim, int spacedim>
2281 void
2286
2287
2288
2289 template <int dim, int spacedim>
2290 void
2292 {
2293 register_data_attach();
2294 }
2295
2296
2297
2298 template <int dim, int spacedim>
2299 void
2301 {
2302 const auto callback_function =
2304 &cell_iterator,
2305 const CellStatus cell_status) {
2306 return this->pack_callback(cell_iterator, cell_status);
2307 };
2308
2309 tria_attached_data_index =
2310 const_cast<Triangulation<dim, spacedim> *>(&*triangulation)
2311 ->register_data_attach(callback_function,
2312 /*returns_variable_size_data=*/true);
2313 }
2314
2315
2316
2317 template <int dim, int spacedim>
2318 void
2320 {
2321 const bool serialization = false;
2322 notify_ready_to_unpack(serialization);
2323 }
2324
2325
2326
2327 template <int dim, int spacedim>
2328 void
2330 {
2331 const bool serialization = true;
2332 notify_ready_to_unpack(serialization);
2333 }
2334
2335
2336 template <int dim, int spacedim>
2337 void
2339 const bool serialization)
2340 {
2341 // First prepare container for insertion
2342 clear();
2343
2344 // If we are resuming from a checkpoint, we first have to register the
2345 // store function again, to set the triangulation to the same state as
2346 // before the serialization. Only afterwards we know how to deserialize the
2347 // data correctly.
2348 if (serialization)
2349 register_data_attach();
2350
2351 // Check if something was stored and load it
2352 if (tria_attached_data_index != numbers::invalid_unsigned_int)
2353 {
2354 const auto callback_function =
2356 &cell_iterator,
2357 const CellStatus cell_status,
2358 const boost::iterator_range<std::vector<char>::const_iterator>
2359 &range_iterator) {
2360 this->unpack_callback(cell_iterator, cell_status, range_iterator);
2361 };
2362
2363 const_cast<Triangulation<dim, spacedim> *>(&*triangulation)
2364 ->notify_ready_to_unpack(tria_attached_data_index, callback_function);
2365
2366 // Reset handle and update global numbers.
2367 tria_attached_data_index = numbers::invalid_unsigned_int;
2368 update_cached_numbers();
2369 }
2370 }
2371
2372
2373
2374 template <int dim, int spacedim>
2375 std::vector<char>
2378 const CellStatus status) const
2379 {
2380 std::vector<particle_iterator> stored_particles_on_cell;
2381
2382 switch (status)
2383 {
2386 // If the cell persist or is refined store all particles of the
2387 // current cell.
2388 {
2389 const unsigned int n_particles = n_particles_in_cell(cell);
2390 stored_particles_on_cell.reserve(n_particles);
2391
2392 for (unsigned int i = 0; i < n_particles; ++i)
2393 stored_particles_on_cell.push_back(particle_iterator(
2394 cells_to_particle_cache[cell->active_cell_index()],
2395 *property_pool,
2396 i));
2397 }
2398 break;
2399
2401 // If this cell is the parent of children that will be coarsened,
2402 // collect the particles of all children.
2403 {
2404 for (const auto &child : cell->child_iterators())
2405 {
2406 const unsigned int n_particles = n_particles_in_cell(child);
2407
2408 stored_particles_on_cell.reserve(
2409 stored_particles_on_cell.size() + n_particles);
2410
2411 const typename particle_container::iterator &cache =
2412 cells_to_particle_cache[child->active_cell_index()];
2413 for (unsigned int i = 0; i < n_particles; ++i)
2414 stored_particles_on_cell.push_back(
2415 particle_iterator(cache, *property_pool, i));
2416 }
2417 }
2418 break;
2419
2420 default:
2422 break;
2423 }
2424
2425 return pack_particles(stored_particles_on_cell);
2426 }
2427
2428
2429
2430 template <int dim, int spacedim>
2431 void
2434 const CellStatus status,
2435 const boost::iterator_range<std::vector<char>::const_iterator> &data_range)
2436 {
2437 if (data_range.begin() == data_range.end())
2438 return;
2439
2440 const auto cell_to_store_particles =
2441 (status != CellStatus::cell_will_be_refined) ? cell : cell->child(0);
2442
2443 // deserialize particles and insert into local storage
2444 if (data_range.begin() != data_range.end())
2445 {
2446 const void *data = static_cast<const void *>(&(*data_range.begin()));
2447 const void *end = static_cast<const void *>(
2448 &(*data_range.begin()) + (data_range.end() - data_range.begin()));
2449
2450 while (data < end)
2451 {
2452 const void *old_data = data;
2453 const auto x = insert_particle(data, cell_to_store_particles);
2454
2455 // Ensure that the particle read exactly as much data as
2456 // it promised it needs to store its data
2457 const void *new_data = data;
2458 (void)old_data;
2459 (void)new_data;
2460 (void)x;
2461 AssertDimension((const char *)new_data - (const char *)old_data,
2462 x->serialized_size_in_bytes());
2463 }
2464
2465 Assert(data == end,
2466 ExcMessage(
2467 "The particle data could not be deserialized successfully. "
2468 "Check that when deserializing the particles you expect "
2469 "the same number of properties that were serialized."));
2470 }
2471
2472 auto loaded_particles_on_cell = particles_in_cell(cell_to_store_particles);
2473
2474 // now update particle storage location and properties if necessary
2475 switch (status)
2476 {
2478 {
2479 // all particles are correctly inserted
2480 }
2481 break;
2482
2484 {
2485 // all particles are in correct cell, but their reference location
2486 // has changed
2487 for (auto &particle : loaded_particles_on_cell)
2488 {
2489 const Point<dim> p_unit =
2490 mapping->transform_real_to_unit_cell(cell_to_store_particles,
2491 particle.get_location());
2492 particle.set_reference_location(p_unit);
2493 }
2494 }
2495 break;
2496
2498 {
2499 // we need to find the correct child to store the particles and
2500 // their reference location has changed
2501 typename particle_container::iterator &cache =
2502 cells_to_particle_cache[cell_to_store_particles
2503 ->active_cell_index()];
2504
2505 // make sure that the call above has inserted an entry
2506 Assert(cache != particles.end(), ExcInternalError());
2507
2508 // Cannot use range-based loop, because number of particles in cell
2509 // is going to change
2510 auto particle = loaded_particles_on_cell.begin();
2511 for (unsigned int i = 0; i < cache->particles.size();)
2512 {
2513 bool found_new_cell = false;
2514
2515 for (const auto &child : cell->child_iterators())
2516 {
2517 Assert(child->is_locally_owned(), ExcInternalError());
2518
2519 try
2520 {
2521 const Point<dim> p_unit =
2522 mapping->transform_real_to_unit_cell(
2523 child, particle->get_location());
2524 if (cell->reference_cell().contains_point(
2525 p_unit, tolerance_inside_cell))
2526 {
2527 found_new_cell = true;
2528 particle->set_reference_location(p_unit);
2529
2530 // if the particle is not in the cell we stored it
2531 // in above, its handle is in the wrong place
2532 if (child != cell_to_store_particles)
2533 {
2534 // move handle into correct cell
2535 insert_particle(cache->particles[i], child);
2536 // remove handle by replacing it with last one
2537 cache->particles[i] = cache->particles.back();
2538 cache->particles.pop_back();
2539 // no loop increment, we need to process
2540 // the new i-th particle.
2541 }
2542 else
2543 {
2544 // move on to next particle
2545 ++i;
2546 ++particle;
2547 }
2548 break;
2549 }
2550 }
2551 catch (typename Mapping<dim>::ExcTransformationFailed &)
2552 {}
2553 }
2554
2555 if (found_new_cell == false)
2556 {
2557 // If we get here, we did not find the particle in any
2558 // child. This case may happen for particles that are at the
2559 // boundary for strongly curved cells. We apply a tolerance
2560 // in the call to ReferenceCell::contains_point() to
2561 // account for this, but if that is not enough, we still
2562 // need to prevent an endless loop here. Delete the particle
2563 // and move on.
2564 signals.particle_lost(particle,
2565 particle->get_surrounding_cell());
2566 if (cache->particles[i] !=
2568 property_pool->deregister_particle(cache->particles[i]);
2569 cache->particles[i] = cache->particles.back();
2570 cache->particles.pop_back();
2571 }
2572 }
2573 // clean up in case child 0 has no particle left
2574 if (cache->particles.empty())
2575 {
2576 particles.erase(cache);
2577 cache = particles.end();
2578 }
2579 }
2580 break;
2581
2582 default:
2584 break;
2585 }
2586 }
2587} // namespace Particles
2588
2589#include "particles/particle_handler.inst"
2590
*  iterator end()
*  *  iterator begin()
*  x_component_mask set(0, true)
CellStatus
Definition cell_status.h:29
@ cell_will_be_refined
@ children_will_be_coarsened
std::size_t size() const
Definition array_view.h:737
std::array< std::uint64_t, 3 > binary_type
Definition cell_id.h:71
const unsigned int n_components
Definition function.h:162
virtual void vector_value(const Point< dim > &p, Vector< RangeNumberType > &values) const
Abstract base class for mapping classes.
Definition mapping.h:318
static unsigned int n_threads()
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
unsigned int global_max_particles_per_cell
internal::GhostParticlePartitioner< dim, spacedim > ghost_particles_cache
particle_container::iterator particle_container_ghost_begin() const
unsigned int n_properties_per_particle() const
types::particle_index global_number_of_particles
particle_container::iterator particle_container_owned_end() const
particle_container::iterator particle_container_ghost_end() const
boost::iterator_range< particle_iterator > particle_iterator_range
void send_recv_particles(const std::map< types::subdomain_id, std::vector< particle_iterator > > &particles_to_send, 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)
ObserverPointer< const Mapping< dim, spacedim >, ParticleHandler< dim, spacedim > > mapping
void send_recv_particles_properties_and_location(const std::map< types::subdomain_id, std::vector< particle_iterator > > &particles_to_send)
void get_particle_positions(VectorType &output_vector, const bool add_to_output_vector=false) const
types::particle_index number_of_locally_owned_particles
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
types::particle_index get_max_local_particle_index() const
void reserve(const std::size_t n_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 notify_ready_to_unpack(const bool serialization)
std::enable_if_t< std::is_convertible_v< VectorType *, Function< spacedim > * >==false > set_particle_positions(const VectorType &input_vector, const bool displace_particles=true)
std::vector< char > pack_callback(const typename Triangulation< dim, spacedim >::cell_iterator &cell, const 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
void reset_particle_container(particle_container &particles)
types::particle_index n_particles_in_cell(const typename Triangulation< dim, spacedim >::active_cell_iterator &cell) const
typename ParticleAccessor< dim, spacedim >::particle_container particle_container
ObserverPointer< const Triangulation< dim, spacedim >, ParticleHandler< dim, spacedim > > triangulation
particle_iterator_range particles_in_cell(const typename Triangulation< dim, spacedim >::active_cell_iterator &cell)
particle_iterator insert_particle(const Particle< dim, spacedim > &particle, const typename Triangulation< dim, spacedim >::active_cell_iterator &cell)
void remove_particles(const std::vector< particle_iterator > &particles)
types::particle_index n_global_max_particles_per_cell() const
particle_container::iterator particle_container_owned_begin() const
void unpack_callback(const typename Triangulation< dim, spacedim >::cell_iterator &cell, const CellStatus status, const boost::iterator_range< std::vector< char >::const_iterator > &data_range)
void copy_from(const ParticleHandler< dim, spacedim > &particle_handler)
types::particle_index get_next_free_particle_index() const
IndexSet locally_owned_particle_ids() const
void set_property_pool(PropertyPool< dim, spacedim > &property_pool)
Definition particle.h:608
const Point< dim > & get_reference_location() const
Definition particle.h:581
const Point< spacedim > & get_location() const
Definition particle.h:554
std::size_t serialized_size_in_bytes() const
Definition particle.cc:284
types::particle_index get_id() const
Definition particle.h:590
ArrayView< double > get_properties()
Definition particle.cc:328
Definition point.h:111
numbers::NumberTraits< Number >::real_type norm() const
constexpr numbers::NumberTraits< Number >::real_type norm_square() const
IteratorState::IteratorStates state() const
virtual void clear()
cell_iterator begin(const unsigned int level=0) const
#define DEAL_II_NAMESPACE_OPEN
Definition config.h:38
constexpr bool running_in_debug_mode()
Definition config.h:76
#define DEAL_II_NAMESPACE_CLOSE
Definition config.h:39
#define DEAL_II_ASSERT_UNREACHABLE()
static ::ExceptionBase & ExcNotImplemented()
#define Assert(cond, exc)
#define AssertDimension(dim1, dim2)
#define AssertThrowMPI(error_code)
static ::ExceptionBase & ExcInternalError()
static ::ExceptionBase & ExcPointNotAvailableHere()
static ::ExceptionBase & ExcMessage(std::string arg1)
#define AssertThrow(cond, exc)
TriaIterator< CellAccessor< dim, spacedim > > cell_iterator
Definition tria.h:1621
std::vector< index_type > data
Definition mpi.cc:734
std::size_t size
Definition mpi.cc:733
const MPI_Comm comm
Definition mpi.cc:912
unsigned int find_closest_vertex(const std::map< unsigned int, Point< spacedim > > &vertices, const Point< spacedim > &p)
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())
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, const std::vector< bool > &marked_vertices={}, const bool enforce_unique_mapping=true)
@ past_the_end
Iterator reached end of container.
@ valid
Iterator points to a valid object.
@ particle_handler_send_recv_particles_send
ParticleHandler<dim, spacedim>::send_recv_particles.
Definition mpi_tags.h:112
@ particle_handler_send_recv_particles_setup
ParticleHandler<dim, spacedim>::send_recv_particles.
Definition mpi_tags.h:108
T sum(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)
unsigned int n_mpi_processes(const MPI_Comm mpi_communicator)
Definition mpi.cc:103
T max(const T &t, const MPI_Comm mpi_communicator)
std::vector< T > all_gather(const MPI_Comm comm, const T &object_to_send)
void run(const std::vector< std::vector< Iterator > > &colored_iterators, Worker worker, Copier copier, const ScratchData &sample_scratch_data, const CopyData &sample_copy_data, const unsigned int queue_length=2 *MultithreadInfo::n_threads(), const unsigned int chunk_size=8)
constexpr unsigned int invalid_unsigned_int
Definition types.h:228
T signaling_nan()
constexpr types::subdomain_id artificial_subdomain_id
Definition types.h:406
bool is_finite(const double x)
Definition numbers.h:508
STL namespace.
::VectorizedArray< Number, width > max(const ::VectorizedArray< Number, width > &, const ::VectorizedArray< Number, width > &)
unsigned int subdomain_id
Definition types.h:50