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