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