DataTransferKit - Multiphysics Solution Transfer Services  2.0
DTK_ParallelSearch.cpp
1 //---------------------------------------------------------------------------//
2 /*
3  Copyright (c) 2012, Stuart R. Slattery
4  All rights reserved.
5 
6  Redistribution and use in source and binary forms, with or without
7  modification, are permitted provided that the following conditions are
8  met:
9 
10  *: Redistributions of source code must retain the above copyright
11  notice, this list of conditions and the following disclaimer.
12 
13  *: Redistributions in binary form must reproduce the above copyright
14  notice, this list of conditions and the following disclaimer in the
15  documentation and/or other materials provided with the distribution.
16 
17  *: Neither the name of the University of Wisconsin - Madison nor the
18  names of its contributors may be used to endorse or promote products
19  derived from this software without specific prior written permission.
20 
21  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33 //---------------------------------------------------------------------------//
39 //---------------------------------------------------------------------------//
40 
41 #include "DTK_ParallelSearch.hpp"
42 #include "DTK_DBC.hpp"
43 
44 #include <Tpetra_Distributor.hpp>
45 
46 namespace DataTransferKit
47 {
48 //---------------------------------------------------------------------------//
49 // Constructor.
51  const Teuchos::RCP<const Teuchos::Comm<int>> &comm,
52  const int physical_dimension, const EntityIterator &domain_iterator,
53  const Teuchos::RCP<EntityLocalMap> &domain_local_map,
54  const Teuchos::ParameterList &parameters )
55  : d_comm( comm )
56  , d_physical_dim( physical_dimension )
57  , d_track_missed_range_entities( false )
58  , d_missed_range_entity_ids( 0 )
59 {
60  // Set the parameters with the local map.
61  domain_local_map->setParameters( parameters );
62 
63  // Determine if we are tracking missed range entities.
64  if ( parameters.isParameter( "Track Missed Range Entities" ) )
65  {
66  d_track_missed_range_entities =
67  parameters.get<bool>( "Track Missed Range Entities" );
68  }
69 
70  // Build a coarse global search as this object must be collective across
71  // the communicator.
72  d_coarse_global_search = Teuchos::rcp( new CoarseGlobalSearch(
73  d_comm, physical_dimension, domain_iterator, parameters ) );
74 
75  // Only do the local search if there are local domain entities.
76  d_empty_domain = ( 0 == domain_iterator.size() );
77  if ( !d_empty_domain )
78  {
79  d_coarse_local_search = Teuchos::rcp( new CoarseLocalSearch(
80  domain_iterator, domain_local_map, parameters ) );
81  d_fine_local_search =
82  Teuchos::rcp( new FineLocalSearch( domain_local_map ) );
83  }
84 }
85 
86 //---------------------------------------------------------------------------//
87 // Search the domain with the range entity centroids and construct the
88 // graph. This will update the state of the object.
89 void ParallelSearch::search(
90  const EntityIterator &range_iterator,
91  const Teuchos::RCP<EntityLocalMap> &range_local_map,
92  const Teuchos::ParameterList &parameters )
93 {
94  // Set the parameters with the local map.
95  range_local_map->setParameters( parameters );
96 
97  // Empty range flag.
98  d_empty_range = ( 0 == range_iterator.size() );
99 
100  // Reset the state of the object.
101  d_range_owner_ranks.clear();
102  d_domain_to_range_map.clear();
103  d_range_to_domain_map.clear();
104  d_parametric_coords.clear();
105 
106  // Perform a coarse global search to redistribute the range entities.
107  Teuchos::Array<EntityId> range_entity_ids;
108  Teuchos::Array<int> range_owner_ranks;
109  Teuchos::Array<double> range_centroids;
110  d_coarse_global_search->search( range_iterator, range_local_map, parameters,
111  range_entity_ids, range_owner_ranks,
112  range_centroids );
113 
114  // If needed, extract the range entities that were missed during the
115  // coarse global search.
116  Teuchos::Array<EntityId> found_range_entity_ids;
117  Teuchos::Array<int> found_range_ranks;
118  Teuchos::Array<EntityId> missed_range_entity_ids;
119  Teuchos::Array<int> missed_range_ranks;
120  if ( d_track_missed_range_entities )
121  {
122  missed_range_entity_ids = Teuchos::Array<EntityId>(
123  d_coarse_global_search->getMissedRangeEntityIds() );
124  missed_range_ranks.assign( missed_range_entity_ids.size(),
125  d_comm->getRank() );
126  }
127 
128  // Only do the local search if there are local domain entities.
129  Teuchos::Array<int> export_range_ranks;
130  Teuchos::Array<EntityId> export_data;
131  if ( !d_empty_domain )
132  {
133  // For each range centroid, perform a local search.
134  int num_range = range_entity_ids.size();
135  Teuchos::Array<Entity> domain_neighbors;
136  Teuchos::Array<Entity> domain_parents;
137  Teuchos::Array<double> reference_coordinates;
138  Teuchos::Array<double> local_coords( d_physical_dim );
139  int num_parents = 0;
140  for ( int n = 0; n < num_range; ++n )
141  {
142  // Perform a coarse local search to get the nearest domain
143  // entities to the point.
144  d_coarse_local_search->search(
145  range_centroids( d_physical_dim * n, d_physical_dim ),
146  parameters, domain_neighbors );
147 
148  // Perform a fine local search to get the entities the point maps
149  // to.
150  d_fine_local_search->search(
151  domain_neighbors,
152  range_centroids( d_physical_dim * n, d_physical_dim ),
153  parameters, domain_parents, reference_coordinates );
154 
155  // Store the potentially multiple parametric realizations of the
156  // point.
157  std::unordered_map<EntityId, Teuchos::Array<double>> ref_map;
158  num_parents = domain_parents.size();
159  for ( int p = 0; p < num_parents; ++p )
160  {
161  // Store the range data in the domain parallel decomposition.
162  local_coords().assign( reference_coordinates(
163  d_physical_dim * p, d_physical_dim ) );
164  d_range_owner_ranks.emplace( range_entity_ids[n],
165  range_owner_ranks[n] );
166  d_domain_to_range_map.emplace( domain_parents[p].id(),
167  range_entity_ids[n] );
168  ref_map.emplace( domain_parents[p].id(), local_coords );
169 
170  // Extract the data to communicate back to the range parallel
171  // decomposition.
172  export_range_ranks.push_back( range_owner_ranks[n] );
173  export_data.push_back( range_entity_ids[n] );
174  export_data.push_back( domain_parents[p].id() );
175  export_data.push_back(
176  Teuchos::as<EntityId>( d_comm->getRank() ) );
177  }
178 
179  // If we found parents for the point, store them.
180  if ( num_parents > 0 )
181  {
182  d_parametric_coords.emplace( range_entity_ids[n], ref_map );
183 
184  // If we are tracking missed entities, also track those that
185  // we found so we can determine if an entity was found after
186  // being sent to multiple destinations.
187  if ( d_track_missed_range_entities )
188  {
189  found_range_entity_ids.push_back( range_entity_ids[n] );
190  found_range_ranks.push_back( range_owner_ranks[n] );
191  }
192  }
193 
194  // Otherwise, if we are tracking missed entities report this.
195  else if ( d_track_missed_range_entities )
196  {
197  missed_range_entity_ids.push_back( range_entity_ids[n] );
198  missed_range_ranks.push_back( range_owner_ranks[n] );
199  }
200  }
201  }
202 
203  // Back-communicate the domain entities in which we found each range
204  // entity to complete the mapping.
205  Tpetra::Distributor domain_to_range_dist( d_comm );
206  int num_import =
207  domain_to_range_dist.createFromSends( export_range_ranks() );
208  Teuchos::Array<EntityId> domain_data( 3 * num_import );
209  Teuchos::ArrayView<const EntityId> export_data_view = export_data();
210  domain_to_range_dist.doPostsAndWaits( export_data_view, 3, domain_data() );
211 
212  // Store the domain data in the range parallel decomposition.
213  for ( int i = 0; i < num_import; ++i )
214  {
215  d_domain_owner_ranks.emplace( domain_data[3 * i + 1],
216  domain_data[3 * i + 2] );
217  d_range_to_domain_map.emplace( domain_data[3 * i],
218  domain_data[3 * i + 1] );
219  }
220 
221  // If we are tracking missed entities, back-communicate the missing entities
222  // and found entities to determine which entities are actually missing.
223  if ( d_track_missed_range_entities )
224  {
225  // Back-communicate the missing entities.
226  Tpetra::Distributor missed_range_dist( d_comm );
227  int num_import_missed =
228  missed_range_dist.createFromSends( missed_range_ranks() );
229  Teuchos::Array<EntityId> import_missed( num_import_missed );
230  Teuchos::ArrayView<const EntityId> missed_view =
231  missed_range_entity_ids();
232  missed_range_dist.doPostsAndWaits( missed_view, 1, import_missed() );
233 
234  // Back-communicate the found entities.
235  Tpetra::Distributor found_range_dist( d_comm );
236  int num_import_found =
237  found_range_dist.createFromSends( found_range_ranks() );
238  Teuchos::Array<EntityId> import_found( num_import_found );
239  Teuchos::ArrayView<const EntityId> found_view =
240  found_range_entity_ids();
241  found_range_dist.doPostsAndWaits( found_view, 1, import_found() );
242 
243  // Create a unique list of missed entities.
244  std::sort( import_missed.begin(), import_missed.end() );
245  auto import_missed_unique_end =
246  std::unique( import_missed.begin(), import_missed.end() );
247  import_missed.resize(
248  std::distance( import_missed.begin(), import_missed_unique_end ) );
249 
250  // Create a unique list of found entities.
251  std::sort( import_found.begin(), import_found.end() );
252  auto import_found_unique_end =
253  std::unique( import_found.begin(), import_found.end() );
254  import_found.resize(
255  std::distance( import_found.begin(), import_found_unique_end ) );
256 
257  // Intersect the found and missed entities to determine if there are any
258  // that were found on one process but missed on another.
259  Teuchos::Array<EntityId> false_positive_missed( import_missed.size() );
260  auto false_positive_end = std::set_intersection(
261  import_missed.begin(), import_missed.end(), import_found.begin(),
262  import_found.end(), false_positive_missed.begin() );
263 
264  // Create a list of missed entities without the false positives.
265  d_missed_range_entity_ids.resize( num_import_missed );
266  auto missed_range_end = std::set_difference(
267  import_missed.begin(), import_missed.end(),
268  false_positive_missed.begin(), false_positive_end,
269  d_missed_range_entity_ids.begin() );
270  d_missed_range_entity_ids.resize( std::distance(
271  d_missed_range_entity_ids.begin(), missed_range_end ) );
272 
273 #if HAVE_DTK_DBC
274  unsigned long long int n_entities =
275  d_missed_range_entity_ids.size() + import_found.size();
276  DTK_REQUIRE( n_entities == range_iterator.size() );
277 #endif
278  }
279 }
280 
281 //---------------------------------------------------------------------------//
282 // Given a domain entity id, get the ids of the range entities that mapped to
283 // it.
285  const EntityId domain_id, Teuchos::Array<EntityId> &range_ids ) const
286 {
287  DTK_REQUIRE( !d_empty_domain );
288  auto domain_pair = d_domain_to_range_map.equal_range( domain_id );
289  range_ids.resize( std::distance( domain_pair.first, domain_pair.second ) );
290  auto range_it = range_ids.begin();
291  for ( auto domain_it = domain_pair.first; domain_it != domain_pair.second;
292  ++domain_it, ++range_it )
293  {
294  *range_it = domain_it->second;
295  }
296 }
297 
298 //---------------------------------------------------------------------------//
299 // Given a range entity id, get the ids of the domain entities that it mapped
300 // to.
302  const EntityId range_id, Teuchos::Array<EntityId> &domain_ids ) const
303 {
304  DTK_REQUIRE( !d_empty_range );
305  auto range_pair = d_range_to_domain_map.equal_range( range_id );
306  domain_ids.resize( std::distance( range_pair.first, range_pair.second ) );
307  auto domain_it = domain_ids.begin();
308  for ( auto range_it = range_pair.first; range_it != range_pair.second;
309  ++range_it, ++domain_it )
310  {
311  *domain_it = range_it->second;
312  }
313 }
314 
315 //---------------------------------------------------------------------------//
316 // Get the owner rank of a given range entity.
318 {
319  DTK_REQUIRE( !d_empty_domain );
320  DTK_REQUIRE( d_range_owner_ranks.count( range_id ) );
321  return d_range_owner_ranks.find( range_id )->second;
322 }
323 
324 //---------------------------------------------------------------------------//
325 // Get the owner rank of a given domain entity.
327 {
328  DTK_REQUIRE( !d_empty_range );
329  DTK_REQUIRE( d_domain_owner_ranks.count( domain_id ) );
330  return d_domain_owner_ranks.find( domain_id )->second;
331 }
332 
333 //---------------------------------------------------------------------------//
334 // Get the parametric coordinates of the range entities in the domain entities.
336  const EntityId domain_id, const EntityId range_id,
337  Teuchos::ArrayView<const double> &parametric_coords ) const
338 {
339  DTK_REQUIRE( !d_empty_domain );
340  DTK_REQUIRE( d_parametric_coords.count( range_id ) );
341  DTK_REQUIRE(
342  d_parametric_coords.find( range_id )->second.count( domain_id ) );
343  parametric_coords =
344  d_parametric_coords.find( range_id )->second.find( domain_id )->second;
345 }
346 
347 //---------------------------------------------------------------------------//
348 // Return the ids of the range entities that were not mapped during the last
349 // setup phase (i.e. those that are guaranteed to not receive data from the
350 // transfer).
351 Teuchos::ArrayView<const EntityId>
353 {
354  return d_missed_range_entity_ids();
355 }
356 
357 //---------------------------------------------------------------------------//
358 
359 } // end namespace DataTransferKit
360 
361 //---------------------------------------------------------------------------//
362 // end DTK_ParallelSearch.cpp
363 //---------------------------------------------------------------------------//
A CoarseLocalSearch data structure for local entity coarse search.
void getRangeEntitiesFromDomain(const EntityId domain_id, Teuchos::Array< EntityId > &range_ids) const
Given a domain entity id on a domain process, get the ids of the range entities that mapped to it...
int rangeEntityOwnerRank(const EntityId range_id) const
Get the owner rank of a given range entity on a domain process.
Entity iterator interface.
int domainEntityOwnerRank(const EntityId domain_id) const
Get the owner rank of a given domain entity on a rank process.
void rangeParametricCoordinatesInDomain(const EntityId domain_id, const EntityId range_id, Teuchos::ArrayView< const double > &parametric_coords) const
Get the parametric coordinates of the range entities in the domain entities on a domain process...
A FineLocalSearch data structure for local entity fine search.
Assertions and Design-by-Contract for error handling.
A CoarseGlobalSearch data structure for global entity coarse search.
unsigned long int EntityId
Entity id type.
Definition: DTK_Types.hpp:50
void getDomainEntitiesFromRange(const EntityId range_id, Teuchos::Array< EntityId > &domain_ids) const
Given a range entity id on a range process, get the ids of the domain entities that it mapped to...
ParallelSearch(const Teuchos::RCP< const Teuchos::Comm< int >> &comm, const int physical_dimension, const EntityIterator &domain_iterator, const Teuchos::RCP< EntityLocalMap > &domain_local_map, const Teuchos::ParameterList &parameters)
Constructor.
DTK_BasicEntitySet.cpp.
Teuchos::ArrayView< const EntityId > getMissedRangeEntityIds() const
Return the ids of the range entities that were not during the last search (i.e. those that are guaran...