Panzer  Version of the Day
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
Panzer_LocalPartitioningUtilities.cpp
Go to the documentation of this file.
1 // @HEADER
2 // ***********************************************************************
3 //
4 // Panzer: A partial differential equation assembly
5 // engine for strongly coupled complex multiphysics systems
6 // Copyright (2011) Sandia Corporation
7 //
8 // Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
9 // the U.S. Government retains certain rights in this software.
10 //
11 // Redistribution and use in source and binary forms, with or without
12 // modification, are permitted provided that the following conditions are
13 // met:
14 //
15 // 1. Redistributions of source code must retain the above copyright
16 // notice, this list of conditions and the following disclaimer.
17 //
18 // 2. Redistributions in binary form must reproduce the above copyright
19 // notice, this list of conditions and the following disclaimer in the
20 // documentation and/or other materials provided with the distribution.
21 //
22 // 3. Neither the name of the Corporation nor the names of the
23 // contributors may be used to endorse or promote products derived from
24 // this software without specific prior written permission.
25 //
26 // THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "AS IS" AND ANY
27 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SANDIA CORPORATION OR THE
30 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
31 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
32 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
33 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
34 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
35 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
36 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 //
38 // Questions? Contact Roger P. Pawlowski (rppawlo@sandia.gov) and
39 // Eric C. Cyr (eccyr@sandia.gov)
40 // ***********************************************************************
41 // @HEADER
42 
44 
45 #include "Teuchos_RCP.hpp"
46 #include "Teuchos_Comm.hpp"
47 #include "Teuchos_Assert.hpp"
48 
49 #include "Panzer_Workset_Builder.hpp"
51 
52 #include "Phalanx_KokkosDeviceTypes.hpp"
53 
54 #include <unordered_set>
55 #include <unordered_map>
56 
57 namespace panzer
58 {
59 
60 namespace partitioning_utilities
61 {
62 
63 template<typename LO, typename GO>
64 void
66  const std::vector<LO> & owned_parent_cells,
68 {
69  PANZER_FUNC_TIME_MONITOR_DIFF("panzer::partitioning_utilities::setupSubLocalMeshInfo",setupSLMI);
70  // The goal of this function is to fill a LocalMeshInfoBase (sub_info) with
71  // a subset of cells from a given parent LocalMeshInfoBase (parent_info)
72 
73  // Note: owned_parent_cells are the owned cells for sub_info in the parent_info's indexing scheme
74  // We need to generate sub_info's ghosts and figure out the virtual cells
75 
76  // Note: We will only handle a single ghost layer
77 
78  // Note: We assume owned_parent_cells are owned cells of the parent
79  // i.e. owned_parent_indexes cannot refer to ghost or virtual cells in parent_info
80 
81  // Note: This function works with inter-face connectivity. NOT node connectivity.
82 
83  const int num_owned_cells = owned_parent_cells.size();
84  TEUCHOS_TEST_FOR_EXCEPT_MSG(num_owned_cells == 0, "panzer::partitioning_utilities::setupSubLocalMeshInfo : Input parent subcells must exist (owned_parent_cells)");
85 
86  const int num_parent_owned_cells = parent_info.num_owned_cells;
87  TEUCHOS_TEST_FOR_EXCEPT_MSG(num_parent_owned_cells <= 0, "panzer::partitioning_utilities::setupSubLocalMeshInfo : Input parent info must contain owned cells");
88 
89  const int num_parent_ghstd_cells = parent_info.num_ghstd_cells;
90 
91  const int num_parent_total_cells = parent_info.num_owned_cells + parent_info.num_ghstd_cells + parent_info.num_virtual_cells;
92 
93  // Just as a precaution, make sure the parent_info is setup properly
94  TEUCHOS_ASSERT(static_cast<int>(parent_info.cell_to_faces.extent(0)) == num_parent_total_cells);
95  const int num_faces_per_cell = parent_info.cell_to_faces.extent(1);
96 
97  // The first thing to do is construct a vector containing the parent cell indexes of all
98  // owned, ghstd, and virtual cells
99  std::vector<LO> ghstd_parent_cells;
100  std::vector<LO> virtual_parent_cells;
101  {
102  PANZER_FUNC_TIME_MONITOR_DIFF("Construct parent cell vector",ParentCell);
103  // We grab all of the owned cells and put their global indexes into sub_info
104  // We also put all of the owned cell indexes in the parent's indexing scheme into a set to use for lookups
105  std::unordered_set<LO> owned_parent_cells_set(owned_parent_cells.begin(), owned_parent_cells.end());
106 
107  // We need to create a list of ghstd and virtual cells
108  // We do this by running through sub_cell_indexes
109  // and looking at the neighbors to find neighbors that are not owned
110 
111  // Virtual cells are defined as cells with indexes outside of the range of owned_cells and ghstd_cells
112  const int virtual_parent_cell_offset = num_parent_owned_cells + num_parent_ghstd_cells;
113 
114  std::unordered_set<LO> ghstd_parent_cells_set;
115  std::unordered_set<LO> virtual_parent_cells_set;
116  for(int i=0;i<num_owned_cells;++i){
117  const LO parent_cell_index = owned_parent_cells[i];
118  for(int local_face_index=0;local_face_index<num_faces_per_cell;++local_face_index){
119  const LO parent_face = parent_info.cell_to_faces(parent_cell_index, local_face_index);
120 
121  // Sidesets can have owned cells that border the edge of the domain (i.e. parent_face == -1)
122  // If we are at the edge of the domain, we can ignore this face.
123  if(parent_face < 0)
124  continue;
125 
126  // Find the side index for neighbor cell with respect to the face
127  const LO neighbor_parent_side = (parent_info.face_to_cells(parent_face,0) == parent_cell_index) ? 1 : 0;
128 
129  // Get the neighbor cell index in the parent's indexing scheme
130  const LO neighbor_parent_cell = parent_info.face_to_cells(parent_face, neighbor_parent_side);
131 
132  // If the face exists, then the neighbor should exist
133  TEUCHOS_ASSERT(neighbor_parent_cell >= 0);
134 
135  // We can easily check if this is a virtual cell
136  if(neighbor_parent_cell >= virtual_parent_cell_offset){
137  virtual_parent_cells_set.insert(neighbor_parent_cell);
138  } else if(neighbor_parent_cell >= num_parent_owned_cells){
139  // This is a quick check for a ghost cell
140  // This branch only exists to cut down on the number of times the next branch (much slower) is called
141  ghstd_parent_cells_set.insert(neighbor_parent_cell);
142  } else {
143  // There is still potential for this to be a ghost cell with respect to 'our' cells
144  // The only way to check this is with a super slow lookup call
145  if(owned_parent_cells_set.find(neighbor_parent_cell) == owned_parent_cells_set.end()){
146  // The neighbor cell is not owned by 'us', therefore it is a ghost
147  ghstd_parent_cells_set.insert(neighbor_parent_cell);
148  }
149  }
150  }
151  }
152 
153  // We now have a list of the owned, ghstd, and virtual cells in the parent's indexing scheme.
154  // We will take the 'unordered_set's ordering for the the sub-indexing scheme
155 
156  ghstd_parent_cells.assign(ghstd_parent_cells_set.begin(), ghstd_parent_cells_set.end());
157  virtual_parent_cells.assign(virtual_parent_cells_set.begin(), virtual_parent_cells_set.end());
158 
159  }
160 
161  const int num_ghstd_cells = ghstd_parent_cells.size();
162  const int num_virtual_cells = virtual_parent_cells.size();
163  const int num_real_cells = num_owned_cells + num_ghstd_cells;
164  const int num_total_cells = num_real_cells + num_virtual_cells;
165 
166  std::vector<std::pair<LO, LO> > all_parent_cells(num_total_cells);
167  for (std::size_t i=0; i< owned_parent_cells.size(); ++i)
168  all_parent_cells[i] = std::pair<LO, LO>(owned_parent_cells[i], i);
169 
170  for (std::size_t i=0; i< ghstd_parent_cells.size(); ++i) {
171  LO insert = owned_parent_cells.size()+i;
172  all_parent_cells[insert] = std::pair<LO, LO>(ghstd_parent_cells[i], insert);
173  }
174 
175  for (std::size_t i=0; i< virtual_parent_cells.size(); ++i) {
176  LO insert = owned_parent_cells.size()+ ghstd_parent_cells.size()+i;
177  all_parent_cells[insert] = std::pair<LO, LO>(virtual_parent_cells[i], insert);
178  }
179 
180  sub_info.num_owned_cells = owned_parent_cells.size();
181  sub_info.num_ghstd_cells = ghstd_parent_cells.size();
182  sub_info.num_virtual_cells = virtual_parent_cells.size();
183 
184  // We now have the indexing order for our sub_info
185 
186  // Just as a precaution, make sure the parent_info is setup properly
187  TEUCHOS_ASSERT(static_cast<int>(parent_info.cell_vertices.extent(0)) == num_parent_total_cells);
188  TEUCHOS_ASSERT(static_cast<int>(parent_info.local_cells.extent(0)) == num_parent_total_cells);
189  TEUCHOS_ASSERT(static_cast<int>(parent_info.global_cells.extent(0)) == num_parent_total_cells);
190 
191  const int num_vertices_per_cell = parent_info.cell_vertices.extent(1);
192  const int num_dims = parent_info.cell_vertices.extent(2);
193 
194  // Fill owned, ghstd, and virtual cells: global indexes, local indexes and vertices
195  sub_info.global_cells = Kokkos::View<GO*>("global_cells", num_total_cells);
196  sub_info.local_cells = Kokkos::View<LO*>("local_cells", num_total_cells);
197  sub_info.cell_vertices = Kokkos::View<double***, PHX::Device>("cell_vertices", num_total_cells, num_vertices_per_cell, num_dims);
198  for(int cell=0;cell<num_total_cells;++cell){
199  const LO parent_cell = all_parent_cells[cell].first;
200  sub_info.global_cells(cell) = parent_info.global_cells(parent_cell);
201  sub_info.local_cells(cell) = parent_info.local_cells(parent_cell);
202  for(int vertex=0;vertex<num_vertices_per_cell;++vertex){
203  for(int dim=0;dim<num_dims;++dim){
204  sub_info.cell_vertices(cell,vertex,dim) = parent_info.cell_vertices(parent_cell,vertex,dim);
205  }
206  }
207  }
208 
209  // Now for the difficult part
210 
211  // We need to create a new face indexing scheme from the old face indexing scheme
212 
213  // Create an auxiliary list with all cells - note this preserves indexing
214 
215  struct face_t{
216  face_t(LO c0, LO c1, LO sc0, LO sc1)
217  {
218  cell_0=c0;
219  cell_1=c1;
220  subcell_index_0=sc0;
221  subcell_index_1=sc1;
222  }
223  LO cell_0;
224  LO cell_1;
225  LO subcell_index_0;
226  LO subcell_index_1;
227  };
228 
229 
230  // First create the faces
231  std::vector<face_t> faces;
232  {
233  PANZER_FUNC_TIME_MONITOR_DIFF("Create faces",CreateFaces);
234  // faces_set: cell_0, subcell_index_0, cell_1, subcell_index_1
235  std::unordered_map<LO,std::unordered_map<LO, std::pair<LO,LO> > > faces_set;
236 
237  std::sort(all_parent_cells.begin(), all_parent_cells.end());
238 
239  for(int owned_cell=0;owned_cell<num_owned_cells;++owned_cell){
240  const LO owned_parent_cell = owned_parent_cells[owned_cell];
241  for(int local_face=0;local_face<num_faces_per_cell;++local_face){
242  const LO parent_face = parent_info.cell_to_faces(owned_parent_cell,local_face);
243 
244  // Skip faces at the edge of the domain
245  if(parent_face<0)
246  continue;
247 
248  // Get the cell on the other side of the face
249  const LO neighbor_side = (parent_info.face_to_cells(parent_face,0) == owned_parent_cell) ? 1 : 0;
250 
251  const LO neighbor_parent_cell = parent_info.face_to_cells(parent_face, neighbor_side);
252  const LO neighbor_subcell_index = parent_info.face_to_lidx(parent_face, neighbor_side);
253 
254  // Convert parent cell index into sub cell index
255  std::pair<LO, LO> search_point(neighbor_parent_cell, 0);
256  auto itr = std::lower_bound(all_parent_cells.begin(), all_parent_cells.end(), search_point);
257 
258  TEUCHOS_TEST_FOR_EXCEPT_MSG(itr == all_parent_cells.end(), "panzer_stk::setupSubLocalMeshInfo : Neighbor cell was not found in owned, ghosted, or virtual cells");
259 
260  const LO neighbor_cell = itr->second;
261 
262  LO cell_0, cell_1, subcell_index_0, subcell_index_1;
263  if(owned_cell < neighbor_cell){
264  cell_0 = owned_cell;
265  subcell_index_0 = local_face;
266  cell_1 = neighbor_cell;
267  subcell_index_1 = neighbor_subcell_index;
268  } else {
269  cell_1 = owned_cell;
270  subcell_index_1 = local_face;
271  cell_0 = neighbor_cell;
272  subcell_index_0 = neighbor_subcell_index;
273  }
274 
275  // Add this interface to the set of faces - smaller cell index is 'left' (or '0') side of face
276  faces_set[cell_0][subcell_index_0] = std::pair<LO,LO>(cell_1, subcell_index_1);
277  }
278  }
279 
280  for(const auto & cell_pair : faces_set){
281  const LO cell_0 = cell_pair.first;
282  for(const auto & subcell_pair : cell_pair.second){
283  const LO subcell_index_0 = subcell_pair.first;
284  const LO cell_1 = subcell_pair.second.first;
285  const LO subcell_index_1 = subcell_pair.second.second;
286  faces.push_back(face_t(cell_0,cell_1,subcell_index_0,subcell_index_1));
287  }
288  }
289  }
290 
291  const int num_faces = faces.size();
292 
293  sub_info.face_to_cells = Kokkos::View<LO*[2]>("face_to_cells", num_faces);
294  sub_info.face_to_lidx = Kokkos::View<LO*[2]>("face_to_lidx", num_faces);
295  sub_info.cell_to_faces = Kokkos::View<LO**>("cell_to_faces", num_total_cells, num_faces_per_cell);
296 
297  // Default the system with invalid cell index
298  Kokkos::deep_copy(sub_info.cell_to_faces, -1);
299 
300  for(int face_index=0;face_index<num_faces;++face_index){
301  const face_t & face = faces[face_index];
302 
303  sub_info.face_to_cells(face_index,0) = face.cell_0;
304  sub_info.face_to_cells(face_index,1) = face.cell_1;
305 
306  sub_info.cell_to_faces(face.cell_0,face.subcell_index_0) = face_index;
307  sub_info.cell_to_faces(face.cell_1,face.subcell_index_1) = face_index;
308 
309  sub_info.face_to_lidx(face_index,0) = face.subcell_index_0;
310  sub_info.face_to_lidx(face_index,1) = face.subcell_index_1;
311 
312  }
313 
314  // Complete.
315 
316 }
317 
318 
319 
320 
321 
322 template<typename LO, typename GO>
323 void
325  const int splitting_size,
326  std::vector<panzer::LocalMeshPartition<LO,GO> > & partitions)
327 {
328 
329  // Make sure the splitting size makes sense
330  TEUCHOS_ASSERT(splitting_size != 0);
331 
332  // This is not a partitioning scheme.
333  // This just breaks the mesh_info into equally sized chunks and ignores connectivity
334  // This means that the cells in the partition probably won't be nearby each other - leads to an excess of ghost cells
335 
336  const LO num_owned_cells = mesh_info.num_owned_cells;
337 
338  if(splitting_size < 0){
339 
340  // Just one chunk
341  std::vector<LO> partition_cells;
342  partition_cells.reserve(mesh_info.num_owned_cells);
343  for(LO i=0;i<mesh_info.num_owned_cells;++i){
344  partition_cells.push_back(i);
345  }
346 
347  // It seems this can happen
348  if(partition_cells.size() > 0){
349  partitions.push_back(panzer::LocalMeshPartition<LO,GO>());
350  setupSubLocalMeshInfo(mesh_info,partition_cells,partitions.back());
351  }
352 
353  } else {
354 
355  std::vector<LO> partition_cells;
356  partition_cells.reserve(splitting_size);
357 
358  // There should be at least one partition
359  const LO num_partitions = mesh_info.num_owned_cells / splitting_size + ((mesh_info.num_owned_cells % splitting_size == 0) ? 0 : 1);
360 
361  LO partition_start_index = 0;
362  for(LO partition=0;partition<num_partitions;++partition){
363 
364  // Make sure end of partition maxes out at num_owned_cells
365  const LO partition_end_index = std::min(partition_start_index + splitting_size, num_owned_cells);
366 
367  partition_cells.resize(partition_end_index - partition_start_index,-1);
368  for(int i=partition_start_index;i<partition_end_index;++i){
369  partition_cells[i] = i;
370  }
371 
372  // It seems this can happen
373  if(partition_cells.size() > 0){
374  partitions.push_back(panzer::LocalMeshPartition<LO,GO>());
375  setupSubLocalMeshInfo(mesh_info,partition_cells,partitions.back());
376  }
377 
378  partition_start_index = partition_end_index;
379  }
380 
381  }
382 
383 }
384 
385 template<typename LO, typename GO>
386 void
388  const size_t /* requested_partition_size */,
389  std::vector<panzer::LocalMeshPartition<LO,GO>>& /* partitions */)
390 {
391  // Not yet sure how to do this
392  TEUCHOS_ASSERT(false);
393 }
394 
395 }
396 
397 template <typename LO, typename GO>
398 void
400  const panzer::WorksetDescriptor & description,
401  std::vector<panzer::LocalMeshPartition<LO,GO> > & partitions)
402 {
403 
404  // We have to make sure that the partitioning is possible
406  TEUCHOS_ASSERT(description.getWorksetSize() != 0);
407 
408  // This could just return, but it would be difficult to debug why no partitions were returned
409  TEUCHOS_ASSERT(description.requiresPartitioning());
410 
411  const std::string & element_block_name = description.getElementBlock();
412 
413  // We have two processes for in case this is a sideset or element block
414  if(description.useSideset()){
415 
416  // If the element block doesn't exist, there are no partitions to create
417  if(mesh_info.sidesets.find(element_block_name) == mesh_info.sidesets.end())
418  return;
419  const auto & sideset_map = mesh_info.sidesets.at(element_block_name);
420 
421  const std::string & sideset_name = description.getSideset();
422 
423  // If the sideset doesn't exist, there are no partitions to create
424  if(sideset_map.find(sideset_name) == sideset_map.end())
425  return;
426 
427  const panzer::LocalMeshSidesetInfo<LO,GO> & sideset_info = sideset_map.at(sideset_name);
428 
429  // Partitioning is not important for sidesets
430  panzer::partitioning_utilities::splitMeshInfo<LO,GO>(sideset_info, description.getWorksetSize(), partitions);
431 
432  for(auto & partition : partitions){
433  partition.sideset_name = sideset_name;
434  partition.element_block_name = element_block_name;
435  partition.cell_topology = sideset_info.cell_topology;
436  }
437 
438  } else {
439 
440  // If the element block doesn't exist, there are no partitions to create
441  if(mesh_info.element_blocks.find(element_block_name) == mesh_info.element_blocks.end())
442  return;
443 
444  // Grab the element block we're interested in
445  const panzer::LocalMeshBlockInfo<LO,GO> & block_info = mesh_info.element_blocks.at(element_block_name);
446 
448  // We only have one partition describing the entire local mesh
449  panzer::partitioning_utilities::splitMeshInfo(block_info, -1, partitions);
450  } else {
451  // We need to partition local mesh
452 
453  // TODO: This needs to be used, but first needs to be written
454  //panzer::partitioning_utilities::partitionMeshInfo<LO,GO>(block_info, description.getWorksetSize(), partitions);
455 
456  // FIXME: Until the above function is fixed, we will use this hack - this will lead to horrible partitions
457  panzer::partitioning_utilities::splitMeshInfo(block_info, description.getWorksetSize(), partitions);
458 
459  }
460 
461  for(auto & partition : partitions){
462  partition.element_block_name = element_block_name;
463  partition.cell_topology = block_info.cell_topology;
464  }
465  }
466 
467 }
468 
469 }
470 
471 
472 template
473 void
474 panzer::partitioning_utilities::setupSubLocalMeshInfo<int,int>(const panzer::LocalMeshInfoBase<int,int> & parent_info,
475  const std::vector<int> & owned_parent_cells,
477 
478 #ifndef PANZER_ORDINAL64_IS_INT
479 template
480 void
481 panzer::partitioning_utilities::setupSubLocalMeshInfo<int,panzer::Ordinal64>(const panzer::LocalMeshInfoBase<int,panzer::Ordinal64> & parent_info,
482  const std::vector<int> & owned_parent_cells,
484 #endif
485 
486 template
487 void
488 panzer::generateLocalMeshPartitions<int,int>(const panzer::LocalMeshInfo<int,int> & mesh_info,
489  const panzer::WorksetDescriptor & description,
490  std::vector<panzer::LocalMeshPartition<int,int> > & partitions);
491 
492 #ifndef PANZER_ORDINAL64_IS_INT
493 template
494 void
495 panzer::generateLocalMeshPartitions<int,panzer::Ordinal64>(const panzer::LocalMeshInfo<int,panzer::Ordinal64> & mesh_info,
496  const panzer::WorksetDescriptor & description,
497  std::vector<panzer::LocalMeshPartition<int,panzer::Ordinal64> > & partitions);
498 #endif
Backwards compatibility mode that ignores the worksetSize in the WorksetDescriptor.
const std::string & getSideset(const int block=0) const
Get sideset name.
std::map< std::string, LocalMeshBlockInfo< LO, GO > > element_blocks
Kokkos::View< GO * > global_cells
const std::string & getElementBlock(const int block=0) const
Get element block name.
Teuchos::RCP< const shards::CellTopology > cell_topology
bool useSideset() const
This descriptor is for a side set.
void setupSubLocalMeshInfo(const panzer::LocalMeshInfoBase< LO, GO > &parent_info, const std::vector< LO > &owned_parent_cells, panzer::LocalMeshInfoBase< LO, GO > &sub_info)
void splitMeshInfo(const panzer::LocalMeshInfoBase< LO, GO > &mesh_info, const int splitting_size, std::vector< panzer::LocalMeshPartition< LO, GO > > &partitions)
std::map< std::string, std::map< std::string, LocalMeshSidesetInfo< LO, GO > > > sidesets
Kokkos::View< LO * > local_cells
#define TEUCHOS_TEST_FOR_EXCEPT_MSG(throw_exception_test, msg)
Kokkos::View< LO *[2]> face_to_cells
Kokkos::View< double ***, PHX::Device > cell_vertices
Kokkos::View< LO ** > cell_to_faces
Kokkos::View< LO *[2]> face_to_lidx
void partitionMeshInfo(const panzer::LocalMeshInfoBase< LO, GO > &, const size_t, std::vector< panzer::LocalMeshPartition< LO, GO >> &)
void generateLocalMeshPartitions(const panzer::LocalMeshInfo< LO, GO > &mesh_info, const panzer::WorksetDescriptor &description, std::vector< panzer::LocalMeshPartition< LO, GO > > &partitions)
#define TEUCHOS_ASSERT(assertion_test)
int getWorksetSize() const
Get the requested workset size (default -2 (workset size is set elsewhere), -1 (largest possible work...
bool requiresPartitioning() const
Do we need to partition the local mesh prior to generating worksets.
Workset size is set to the total number of local elements in the MPI process.