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 // Panzer: A partial differential equation assembly
4 // engine for strongly coupled complex multiphysics systems
5 //
6 // Copyright 2011 NTESS and the Panzer contributors.
7 // SPDX-License-Identifier: BSD-3-Clause
8 // *****************************************************************************
9 // @HEADER
10 
12 
13 #include "Teuchos_RCP.hpp"
14 #include "Teuchos_Comm.hpp"
15 #include "Teuchos_Assert.hpp"
16 
17 #include "Tpetra_CrsMatrix.hpp"
18 #include "Tpetra_RowMatrixTransposer.hpp"
19 
20 #include "Panzer_FaceToElement.hpp"
21 #include "Panzer_ConnManager.hpp"
22 #include "Panzer_NodeType.hpp"
23 #include "Panzer_FieldPattern.hpp"
28 
29 #include "Panzer_Workset_Builder.hpp"
31 
32 #include "Phalanx_KokkosDeviceTypes.hpp"
33 
34 #include <set>
35 #include <unordered_set>
36 #include <unordered_map>
37 
38 namespace panzer
39 {
40 
41 namespace
42 {
43 
47 void
48 buildCellGlobalIDs(panzer::ConnManager & conn,
49  PHX::View<panzer::GlobalOrdinal*> & globals)
50 {
51  // extract topologies, and build global connectivity...currently assuming only one topology
52  std::vector<shards::CellTopology> elementBlockTopologies;
53  conn.getElementBlockTopologies(elementBlockTopologies);
54  const shards::CellTopology & topology = elementBlockTopologies[0];
55 
56  // FIXME: We assume that all element blocks have the same topology.
57  for(const auto & other_topology : elementBlockTopologies){
58  TEUCHOS_ASSERT(other_topology.getKey() == topology.getKey());
59  }
60 
62  if(topology.getDimension() == 1){
63  cell_pattern = Teuchos::rcp(new panzer::EdgeFieldPattern(topology));
64  } else if(topology.getDimension() == 2){
65  cell_pattern = Teuchos::rcp(new panzer::FaceFieldPattern(topology));
66  } else if(topology.getDimension() == 3){
67  cell_pattern = Teuchos::rcp(new panzer::ElemFieldPattern(topology));
68  }
69 
70 // panzer::EdgeFieldPattern cell_pattern(elementBlockTopologies[0]);
71  conn.buildConnectivity(*cell_pattern);
72 
73  // calculate total number of local cells
74  std::vector<std::string> block_ids;
75  conn.getElementBlockIds(block_ids);
76 
77  std::size_t totalSize = 0;
78  for (std::size_t which_blk=0;which_blk<block_ids.size();which_blk++) {
79  // get the elem to face mapping
80  const std::vector<int> & localIDs = conn.getElementBlock(block_ids[which_blk]);
81  totalSize += localIDs.size();
82  }
83  globals = PHX::View<panzer::GlobalOrdinal*>("global_cells",totalSize);
84  auto globals_h = Kokkos::create_mirror_view(globals);
85 
86  for (std::size_t id=0;id<totalSize; ++id) {
87  // sanity check
88  int n_conn = conn.getConnectivitySize(id);
89  TEUCHOS_ASSERT(n_conn==1);
90 
91  const panzer::GlobalOrdinal * connectivity = conn.getConnectivity(id);
92  globals_h(id) = connectivity[0];
93  }
94  Kokkos::deep_copy(globals, globals_h);
95 
96 // print_view_1D("buildCellGlobalIDs : globals",globals);
97 }
98 
102 void
103 buildCellToNodes(panzer::ConnManager & conn, PHX::View<panzer::GlobalOrdinal**> & globals)
104 {
105  // extract topologies, and build global connectivity...currently assuming only one topology
106  std::vector<shards::CellTopology> elementBlockTopologies;
107  conn.getElementBlockTopologies(elementBlockTopologies);
108  const shards::CellTopology & topology = elementBlockTopologies[0];
109 
110  // FIXME: We assume that all element blocks have the same topology.
111  for(const auto & other_topology : elementBlockTopologies){
112  TEUCHOS_ASSERT(other_topology.getKey() == topology.getKey());
113  }
114 
115  panzer::NodalFieldPattern pattern(topology);
116  conn.buildConnectivity(pattern);
117 
118  // calculate total number of local cells
119  std::vector<std::string> block_ids;
120  conn.getElementBlockIds(block_ids);
121 
122  // compute total cells and maximum nodes
123  std::size_t totalCells=0, maxNodes=0;
124  for (std::size_t which_blk=0;which_blk<block_ids.size();which_blk++) {
125  // get the elem to face mapping
126  const std::vector<int> & localIDs = conn.getElementBlock(block_ids[which_blk]);
127  if ( localIDs.size() == 0 )
128  continue;
129  int thisSize = conn.getConnectivitySize(localIDs[0]);
130 
131  totalCells += localIDs.size();
132  maxNodes = maxNodes<Teuchos::as<std::size_t>(thisSize) ? Teuchos::as<std::size_t>(thisSize) : maxNodes;
133  }
134  globals = PHX::View<panzer::GlobalOrdinal**>("cell_to_node",totalCells,maxNodes);
135  auto globals_h = Kokkos::create_mirror_view(globals);
136 
137  // build connectivity array
138  for (std::size_t id=0;id<totalCells; ++id) {
139  const panzer::GlobalOrdinal * connectivity = conn.getConnectivity(id);
140  int nodeCnt = conn.getConnectivitySize(id);
141 
142  for(int n=0;n<nodeCnt;n++)
143  globals_h(id,n) = connectivity[n];
144  }
145  Kokkos::deep_copy(globals, globals_h);
146 
147 // print_view("buildCellToNodes : globals",globals);
148 }
149 
151 buildNodeMap(const Teuchos::RCP<const Teuchos::Comm<int> > & comm,
152  PHX::View<const panzer::GlobalOrdinal**> cells_to_nodes)
153 {
154  using Teuchos::RCP;
155  using Teuchos::rcp;
156 
157  /*
158 
159  This function converts
160 
161  cells_to_nodes(local cell, local node) = global node index
162 
163  to a map describing which global nodes are found on this process
164 
165  Note that we have to ensure that that the global nodes found on this process are unique.
166 
167  */
168 
169  typedef Tpetra::Map<panzer::LocalOrdinal,panzer::GlobalOrdinal,panzer::TpetraNodeType> map_type;
170 
171  // get locally unique global ids
172  auto cells_to_nodes_h = Kokkos::create_mirror_view(cells_to_nodes);
173  Kokkos::deep_copy(cells_to_nodes_h, cells_to_nodes);
174  std::set<panzer::GlobalOrdinal> global_nodes;
175  for(unsigned int i=0;i<cells_to_nodes.extent(0);i++)
176  for(unsigned int j=0;j<cells_to_nodes.extent(1);j++)
177  global_nodes.insert(cells_to_nodes_h(i,j));
178 
179  // build local vector contribution
180  PHX::View<panzer::GlobalOrdinal*> node_ids("global_nodes",global_nodes.size());
181  auto node_ids_h = Kokkos::create_mirror_view(node_ids);
182  int i = 0;
183  for(auto itr=global_nodes.begin();itr!=global_nodes.end();++itr,++i)
184  node_ids_h(i) = *itr;
185  Kokkos::deep_copy(node_ids, node_ids_h);
186 
187 // print_view("buildNodeMap : cells_to_nodes",cells_to_nodes);
188 // print_view_1D("buildNodeMap : node_ids",node_ids);
189 
190  return rcp(new map_type(Teuchos::OrdinalTraits<Tpetra::global_size_t>::invalid(),node_ids,0,comm));
191 }
192 
197 buildNodeToCellMatrix(const Teuchos::RCP<const Teuchos::Comm<int> > & comm,
198  PHX::View<const panzer::GlobalOrdinal*> owned_cells,
199  PHX::View<const panzer::GlobalOrdinal**> owned_cells_to_nodes)
200 {
201  using Teuchos::RCP;
202  using Teuchos::rcp;
203 
204  typedef Tpetra::Map<panzer::LocalOrdinal,panzer::GlobalOrdinal,panzer::TpetraNodeType> map_type;
205  typedef Tpetra::CrsMatrix<panzer::LocalOrdinal,panzer::LocalOrdinal,panzer::GlobalOrdinal,panzer::TpetraNodeType> crs_type;
206  typedef Tpetra::Import<panzer::LocalOrdinal,panzer::GlobalOrdinal,panzer::TpetraNodeType> import_type;
207 
208 
209  PANZER_FUNC_TIME_MONITOR_DIFF("panzer_stk::buildNodeToCellMatrix",BNTCM);
210 
211  TEUCHOS_ASSERT(owned_cells.extent(0)==owned_cells_to_nodes.extent(0));
212 
213  // build a unque node map to use with fill complete
214 
215  // This map identifies all nodes linked to cells on this process
216  auto node_map = buildNodeMap(comm,owned_cells_to_nodes);
217 
218  // This map identifies nodes owned by this process
219  auto unique_node_map = Tpetra::createOneToOne(node_map);
220 
221  // This map identifies the cells owned by this process
222  RCP<map_type> cell_map = rcp(new map_type(Teuchos::OrdinalTraits<Tpetra::global_size_t>::invalid(),owned_cells,0,comm));
223 
224  // Create a CRS matrix that stores a pointless value for every global node that belongs to a global cell
225  // This is essentially another way to store cells_to_nodes
226  RCP<crs_type> cell_to_node;
227  {
228  PANZER_FUNC_TIME_MONITOR_DIFF("Build matrix",BuildMatrix);
229 
230  // fill in the cell to node matrix
231  const unsigned int num_local_cells = owned_cells_to_nodes.extent(0);
232  const unsigned int num_nodes_per_cell = owned_cells_to_nodes.extent(1);
233 
234  // The matrix is indexed by (global cell, global node) = local node
235  cell_to_node = rcp(new crs_type(cell_map,num_nodes_per_cell));
236 
237  std::vector<panzer::LocalOrdinal> local_node_indexes(num_nodes_per_cell);
238  std::vector<panzer::GlobalOrdinal> global_node_indexes(num_nodes_per_cell);
239  auto owned_cells_h = Kokkos::create_mirror_view(owned_cells);
240  auto owned_cells_to_nodes_h = Kokkos::create_mirror_view(owned_cells_to_nodes);
241  Kokkos::deep_copy(owned_cells_h, owned_cells);
242  Kokkos::deep_copy(owned_cells_to_nodes_h, owned_cells_to_nodes);
243  for(unsigned int i=0;i<num_local_cells;i++) {
244  const panzer::GlobalOrdinal global_cell_index = owned_cells_h(i);
245  for(unsigned int j=0;j<num_nodes_per_cell;j++) {
246  local_node_indexes[j] = Teuchos::as<panzer::LocalOrdinal>(j);
247  global_node_indexes[j] = owned_cells_to_nodes_h(i,j);
248  }
249 
250  // cell_to_node_mat->insertGlobalValues(cells(i),cols,vals);
251  cell_to_node->insertGlobalValues(global_cell_index,global_node_indexes,local_node_indexes);
252  }
253  cell_to_node->fillComplete(unique_node_map,cell_map);
254 
255  }
256 
257  // Transpose the cell_to_node array to create the node_to_cell array
258  RCP<crs_type> node_to_cell;
259  {
260  PANZER_FUNC_TIME_MONITOR_DIFF("Tranpose matrix",TransposeMatrix);
261  // Create an object designed to transpose the (global cell, global node) matrix to give
262  // a (global node, global cell) matrix
263  Tpetra::RowMatrixTransposer<panzer::LocalOrdinal,panzer::LocalOrdinal,panzer::GlobalOrdinal,panzer::TpetraNodeType> transposer(cell_to_node);
264 
265  // Create the transpose crs matrix
266  auto trans = transposer.createTranspose();
267 
268  // We want to import the portion of the transposed matrix relating to all nodes on this process
269  // The importer must import nodes required by this process (node_map) from the unique nodes (nodes living on a process)
270  RCP<import_type> import = rcp(new import_type(unique_node_map,node_map));
271 
272  // Create the crs matrix to store (ghost node, global cell) array
273  // This CRS matrix will have overlapping rows for shared global nodes
274  node_to_cell = rcp(new crs_type(node_map,0));
275 
276  // Transfer data from the transpose array (associated with unique_node_map) to node_to_cell (associated with node_map)
277  node_to_cell->doImport(*trans,*import,Tpetra::INSERT);
278 
279  // Set the fill - basicially locks down the structured of the CRS matrix - required before doing some operations
280  //node_to_cell->fillComplete();
281  node_to_cell->fillComplete(cell_map,unique_node_map);
282  }
283 
284  // Return the node to cell array
285  return node_to_cell;
286 }
287 
290 PHX::View<panzer::GlobalOrdinal*>
291 buildGhostedCellOneRing(const Teuchos::RCP<const Teuchos::Comm<int> > & comm,
292  PHX::View<const panzer::GlobalOrdinal*> cells,
293  PHX::View<const panzer::GlobalOrdinal**> cells_to_nodes)
294 {
295 
296  PANZER_FUNC_TIME_MONITOR_DIFF("panzer_stk::buildGhostedCellOneRing",BGCOR);
297  typedef Tpetra::CrsMatrix<int,int,panzer::GlobalOrdinal,panzer::TpetraNodeType> crs_type;
298 
299  // cells : (local cell index) -> global cell index
300  // cells_to_nodes : (local cell index, local node_index) -> global node index
301 
302  /*
303 
304  This function creates a list of global indexes relating to ghost cells
305 
306  It is a little misleading how it does this, but the idea is to use the indexing of a CRS matrix to identify
307  what global cells are connected to which global nodes. The values in the CRS matrix are meaningless, however,
308  the fact that they are filled allows us to ping what index combinations exist.
309 
310  To do this we are going to use cell 'nodes' which could also be cell 'vertices'. It is unclear.
311 
312  First we construct an array that stores that global node indexes make up the connectivity for a given global cell index (order doesn't matter)
313 
314  cell_to_node : cell_to_node[global cell index][global node index] = some value (not important, just has to exist)
315 
316  We are then going to transpose that array
317 
318  node_to_cell : node_to_cell[global node index][global cell index] = some value (not important, just has to exist)
319 
320  The coloring of the node_to_cell array tells us what global cells are connected to a given global node.
321 
322 
323  */
324 
325  // the node to cell matrix: Row = Global Node Id, Cell = Global Cell Id, Value = Cell Local Node Id
326  Teuchos::RCP<crs_type> node_to_cell = buildNodeToCellMatrix(comm,cells,cells_to_nodes);
327 
328  // the set of cells already known
329  std::unordered_set<panzer::GlobalOrdinal> unique_cells;
330 
331  // mark all owned cells as already known, e.g. and not in the list of
332  // ghstd cells to be constructed
333  auto cells_h = Kokkos::create_mirror_view(cells);
334  Kokkos::deep_copy(cells_h, cells);
335  for(size_t i=0;i<cells.extent(0);i++) {
336  unique_cells.insert(cells_h(i));
337  }
338 
339  // The set of ghost cells that share a global node with an owned cell
340  std::set<panzer::GlobalOrdinal> ghstd_cells_set;
341 
342  // Get a list of global node indexes associated with the cells owned by this process
343 // auto node_map = node_to_cell->getRangeMap()->getMyGlobalIndices();
344  auto node_map = node_to_cell->getMap()->getMyGlobalIndices();
345 
346  // Iterate through the global node indexes associated with this process
347  for(size_t i=0;i<node_map.extent(0);i++) {
348  const panzer::GlobalOrdinal global_node_index = node_map(i);
349  size_t numEntries = node_to_cell->getNumEntriesInGlobalRow(node_map(i));
350  typename crs_type::nonconst_global_inds_host_view_type indices("indices", numEntries);
351  typename crs_type::nonconst_values_host_view_type values("values", numEntries);
352 
353  // Copy the row for a global node index into a local vector
354  node_to_cell->getGlobalRowCopy(global_node_index,indices,values,numEntries);
355 
356  for(size_t j=0; j<indices.extent(0); ++j) {
357  auto index = indices(j);
358  // if this is a new index (not owned, not previously found ghstd index
359  // add it to the list of ghstd cells
360  if(unique_cells.find(index)==unique_cells.end()) {
361  ghstd_cells_set.insert(index);
362  unique_cells.insert(index); // make sure you don't find it again
363  }
364  }
365  }
366 
367  // build an array containing only the ghstd cells
368  int indx = 0;
369  PHX::View<panzer::GlobalOrdinal*> ghstd_cells("ghstd_cells",ghstd_cells_set.size());
370  auto ghstd_cells_h = Kokkos::create_mirror_view(ghstd_cells);
371  for(auto global_cell_index : ghstd_cells_set) {
372  ghstd_cells_h(indx) = global_cell_index;
373  indx++;
374  }
375 
376 // print_view_1D("ghstd_cells",ghstd_cells);
377  Kokkos::deep_copy(ghstd_cells, ghstd_cells_h);
378  return ghstd_cells;
379 }
380 
381 }
382 
383 namespace partitioning_utilities
384 {
385 
386 
387 void
389  const std::vector<panzer::LocalOrdinal> & owned_parent_cells,
390  panzer::LocalMeshInfoBase & sub_info)
391 {
392  using GO = panzer::GlobalOrdinal;
393  using LO = panzer::LocalOrdinal;
394 
395  PANZER_FUNC_TIME_MONITOR_DIFF("panzer::partitioning_utilities::setupSubLocalMeshInfo",setupSLMI);
396  // The goal of this function is to fill a LocalMeshInfoBase (sub_info) with
397  // a subset of cells from a given parent LocalMeshInfoBase (parent_info)
398 
399  // Note: owned_parent_cells are the owned cells for sub_info in the parent_info's indexing scheme
400  // We need to generate sub_info's ghosts and figure out the virtual cells
401 
402  // Note: We will only handle a single ghost layer
403 
404  // Note: We assume owned_parent_cells are owned cells of the parent
405  // i.e. owned_parent_indexes cannot refer to ghost or virtual cells in parent_info
406 
407  // Note: This function works with inter-face connectivity. NOT node connectivity.
408 
409  const int num_owned_cells = owned_parent_cells.size();
410  TEUCHOS_TEST_FOR_EXCEPT_MSG(num_owned_cells == 0, "panzer::partitioning_utilities::setupSubLocalMeshInfo : Input parent subcells must exist (owned_parent_cells)");
411 
412  const int num_parent_owned_cells = parent_info.num_owned_cells;
413  TEUCHOS_TEST_FOR_EXCEPT_MSG(num_parent_owned_cells <= 0, "panzer::partitioning_utilities::setupSubLocalMeshInfo : Input parent info must contain owned cells");
414 
415  const int num_parent_ghstd_cells = parent_info.num_ghstd_cells;
416 
417  const int num_parent_total_cells = parent_info.num_owned_cells + parent_info.num_ghstd_cells + parent_info.num_virtual_cells;
418 
419  // Just as a precaution, make sure the parent_info is setup properly
420  TEUCHOS_ASSERT(static_cast<int>(parent_info.cell_to_faces.extent(0)) == num_parent_total_cells);
421  const int num_faces_per_cell = parent_info.cell_to_faces.extent(1);
422 
423  // The first thing to do is construct a vector containing the parent cell indexes of all
424  // owned, ghstd, and virtual cells
425  std::vector<LO> ghstd_parent_cells;
426  std::vector<LO> virtual_parent_cells;
427  {
428  PANZER_FUNC_TIME_MONITOR_DIFF("Construct parent cell vector",ParentCell);
429  // We grab all of the owned cells and put their global indexes into sub_info
430  // We also put all of the owned cell indexes in the parent's indexing scheme into a set to use for lookups
431  std::unordered_set<LO> owned_parent_cells_set(owned_parent_cells.begin(), owned_parent_cells.end());
432 
433  // We need to create a list of ghstd and virtual cells
434  // We do this by running through sub_cell_indexes
435  // and looking at the neighbors to find neighbors that are not owned
436 
437  // Virtual cells are defined as cells with indexes outside of the range of owned_cells and ghstd_cells
438  const int virtual_parent_cell_offset = num_parent_owned_cells + num_parent_ghstd_cells;
439 
440  std::unordered_set<LO> ghstd_parent_cells_set;
441  std::unordered_set<LO> virtual_parent_cells_set;
442  auto cell_to_faces_h = Kokkos::create_mirror_view(parent_info.cell_to_faces);
443  auto face_to_cells_h = Kokkos::create_mirror_view(parent_info.face_to_cells);
444  Kokkos::deep_copy(cell_to_faces_h, parent_info.cell_to_faces);
445  Kokkos::deep_copy(face_to_cells_h, parent_info.face_to_cells);
446  for(int i=0;i<num_owned_cells;++i){
447  const LO parent_cell_index = owned_parent_cells[i];
448  for(int local_face_index=0;local_face_index<num_faces_per_cell;++local_face_index){
449  const LO parent_face = cell_to_faces_h(parent_cell_index, local_face_index);
450 
451  // Sidesets can have owned cells that border the edge of the domain (i.e. parent_face == -1)
452  // If we are at the edge of the domain, we can ignore this face.
453  if(parent_face < 0)
454  continue;
455 
456  // Find the side index for neighbor cell with respect to the face
457  const LO neighbor_parent_side = (face_to_cells_h(parent_face,0) == parent_cell_index) ? 1 : 0;
458 
459  // Get the neighbor cell index in the parent's indexing scheme
460  const LO neighbor_parent_cell = face_to_cells_h(parent_face, neighbor_parent_side);
461 
462  // If the face exists, then the neighbor should exist
463  TEUCHOS_ASSERT(neighbor_parent_cell >= 0);
464 
465  // We can easily check if this is a virtual cell
466  if(neighbor_parent_cell >= virtual_parent_cell_offset){
467  virtual_parent_cells_set.insert(neighbor_parent_cell);
468  } else if(neighbor_parent_cell >= num_parent_owned_cells){
469  // This is a quick check for a ghost cell
470  // This branch only exists to cut down on the number of times the next branch (much slower) is called
471  ghstd_parent_cells_set.insert(neighbor_parent_cell);
472  } else {
473  // There is still potential for this to be a ghost cell with respect to 'our' cells
474  // The only way to check this is with a super slow lookup call
475  if(owned_parent_cells_set.find(neighbor_parent_cell) == owned_parent_cells_set.end()){
476  // The neighbor cell is not owned by 'us', therefore it is a ghost
477  ghstd_parent_cells_set.insert(neighbor_parent_cell);
478  }
479  }
480  }
481  }
482 
483  // We now have a list of the owned, ghstd, and virtual cells in the parent's indexing scheme.
484  // We will take the 'unordered_set's ordering for the the sub-indexing scheme
485 
486  ghstd_parent_cells.assign(ghstd_parent_cells_set.begin(), ghstd_parent_cells_set.end());
487  virtual_parent_cells.assign(virtual_parent_cells_set.begin(), virtual_parent_cells_set.end());
488 
489  }
490 
491  const int num_ghstd_cells = ghstd_parent_cells.size();
492  const int num_virtual_cells = virtual_parent_cells.size();
493  const int num_real_cells = num_owned_cells + num_ghstd_cells;
494  const int num_total_cells = num_real_cells + num_virtual_cells;
495 
496  std::vector<std::pair<LO, LO> > all_parent_cells(num_total_cells);
497  for (std::size_t i=0; i< owned_parent_cells.size(); ++i)
498  all_parent_cells[i] = std::pair<LO, LO>(owned_parent_cells[i], i);
499 
500  for (std::size_t i=0; i< ghstd_parent_cells.size(); ++i) {
501  LO insert = owned_parent_cells.size()+i;
502  all_parent_cells[insert] = std::pair<LO, LO>(ghstd_parent_cells[i], insert);
503  }
504 
505  for (std::size_t i=0; i< virtual_parent_cells.size(); ++i) {
506  LO insert = owned_parent_cells.size()+ ghstd_parent_cells.size()+i;
507  all_parent_cells[insert] = std::pair<LO, LO>(virtual_parent_cells[i], insert);
508  }
509 
510  sub_info.num_owned_cells = owned_parent_cells.size();
511  sub_info.num_ghstd_cells = ghstd_parent_cells.size();
512  sub_info.num_virtual_cells = virtual_parent_cells.size();
513 
514  // We now have the indexing order for our sub_info
515 
516  // Just as a precaution, make sure the parent_info is setup properly
517  TEUCHOS_ASSERT(static_cast<int>(parent_info.cell_nodes.extent(0)) == num_parent_total_cells);
518  TEUCHOS_ASSERT(static_cast<int>(parent_info.local_cells.extent(0)) == num_parent_total_cells);
519  TEUCHOS_ASSERT(static_cast<int>(parent_info.global_cells.extent(0)) == num_parent_total_cells);
520 
521  const int num_nodes_per_cell = parent_info.cell_nodes.extent(1);
522  const int num_dims = parent_info.cell_nodes.extent(2);
523 
524  // Fill owned, ghstd, and virtual cells: global indexes, local indexes and vertices
525  sub_info.global_cells = PHX::View<GO*>("global_cells", num_total_cells);
526  sub_info.local_cells = PHX::View<LO*>("local_cells", num_total_cells);
527  sub_info.cell_nodes = PHX::View<double***>("cell_nodes", num_total_cells, num_nodes_per_cell, num_dims);
528  auto global_cells_h = Kokkos::create_mirror_view(sub_info.global_cells);
529  auto local_cells_h = Kokkos::create_mirror_view(sub_info.local_cells);
530  auto cell_nodes_h = Kokkos::create_mirror_view(sub_info.cell_nodes);
531  auto p_global_cells_h = Kokkos::create_mirror_view(parent_info.global_cells);
532  auto p_local_cells_h = Kokkos::create_mirror_view(parent_info.local_cells);
533  auto p_cell_nodes_h = Kokkos::create_mirror_view(parent_info.cell_nodes);
534  Kokkos::deep_copy(p_global_cells_h,parent_info.global_cells);
535  Kokkos::deep_copy(p_local_cells_h,parent_info.local_cells);
536  Kokkos::deep_copy(p_cell_nodes_h,parent_info.cell_nodes);
537 
538  for (int cell=0; cell<num_total_cells; ++cell) {
539  const LO parent_cell = all_parent_cells[cell].first;
540  global_cells_h(cell) = p_global_cells_h(parent_cell);
541  local_cells_h(cell) = p_local_cells_h(parent_cell);
542  for(int node=0;node<num_nodes_per_cell;++node){
543  for(int dim=0;dim<num_dims;++dim){
544  cell_nodes_h(cell,node,dim) = p_cell_nodes_h(parent_cell,node,dim);
545  }
546  }
547  }
548  Kokkos::deep_copy(sub_info.global_cells, global_cells_h);
549  Kokkos::deep_copy(sub_info.local_cells, local_cells_h);
550  Kokkos::deep_copy(sub_info.cell_nodes, cell_nodes_h);
551  // Now for the difficult part
552 
553  // We need to create a new face indexing scheme from the old face indexing scheme
554 
555  // Create an auxiliary list with all cells - note this preserves indexing
556 
557  struct face_t{
558  face_t(LO c0, LO c1, LO sc0, LO sc1)
559  {
560  cell_0=c0;
561  cell_1=c1;
562  subcell_index_0=sc0;
563  subcell_index_1=sc1;
564  }
565  LO cell_0;
566  LO cell_1;
567  LO subcell_index_0;
568  LO subcell_index_1;
569  };
570 
571 
572  // First create the faces
573  std::vector<face_t> faces;
574  {
575  PANZER_FUNC_TIME_MONITOR_DIFF("Create faces",CreateFaces);
576  // faces_set: cell_0, subcell_index_0, cell_1, subcell_index_1
577  std::unordered_map<LO,std::unordered_map<LO, std::pair<LO,LO> > > faces_set;
578 
579  std::sort(all_parent_cells.begin(), all_parent_cells.end());
580 
581  auto cell_to_faces_h = Kokkos::create_mirror_view(parent_info.cell_to_faces);
582  auto face_to_cells_h = Kokkos::create_mirror_view(parent_info.face_to_cells);
583  auto face_to_lidx_h = Kokkos::create_mirror_view(parent_info.face_to_lidx);
584  Kokkos::deep_copy(cell_to_faces_h, parent_info.cell_to_faces);
585  Kokkos::deep_copy(face_to_cells_h, parent_info.face_to_cells);
586  Kokkos::deep_copy(face_to_lidx_h, parent_info.face_to_lidx);
587  for(int owned_cell=0;owned_cell<num_owned_cells;++owned_cell){
588  const LO owned_parent_cell = owned_parent_cells[owned_cell];
589  for(int local_face=0;local_face<num_faces_per_cell;++local_face){
590  const LO parent_face = cell_to_faces_h(owned_parent_cell,local_face);
591 
592  // Skip faces at the edge of the domain
593  if(parent_face<0)
594  continue;
595 
596  // Get the cell on the other side of the face
597  const LO neighbor_side = (face_to_cells_h(parent_face,0) == owned_parent_cell) ? 1 : 0;
598 
599  const LO neighbor_parent_cell = face_to_cells_h(parent_face, neighbor_side);
600  const LO neighbor_subcell_index = face_to_lidx_h(parent_face, neighbor_side);
601 
602  // Convert parent cell index into sub cell index
603  std::pair<LO, LO> search_point(neighbor_parent_cell, 0);
604  auto itr = std::lower_bound(all_parent_cells.begin(), all_parent_cells.end(), search_point);
605 
606  TEUCHOS_TEST_FOR_EXCEPT_MSG(itr == all_parent_cells.end(), "panzer_stk::setupSubLocalMeshInfo : Neighbor cell was not found in owned, ghosted, or virtual cells");
607 
608  const LO neighbor_cell = itr->second;
609 
610  LO cell_0, cell_1, subcell_index_0, subcell_index_1;
611  if(owned_cell < neighbor_cell){
612  cell_0 = owned_cell;
613  subcell_index_0 = local_face;
614  cell_1 = neighbor_cell;
615  subcell_index_1 = neighbor_subcell_index;
616  } else {
617  cell_1 = owned_cell;
618  subcell_index_1 = local_face;
619  cell_0 = neighbor_cell;
620  subcell_index_0 = neighbor_subcell_index;
621  }
622 
623  // Add this interface to the set of faces - smaller cell index is 'left' (or '0') side of face
624  faces_set[cell_0][subcell_index_0] = std::pair<LO,LO>(cell_1, subcell_index_1);
625  }
626  }
627 
628  for(const auto & cell_pair : faces_set){
629  const LO cell_0 = cell_pair.first;
630  for(const auto & subcell_pair : cell_pair.second){
631  const LO subcell_index_0 = subcell_pair.first;
632  const LO cell_1 = subcell_pair.second.first;
633  const LO subcell_index_1 = subcell_pair.second.second;
634  faces.push_back(face_t(cell_0,cell_1,subcell_index_0,subcell_index_1));
635  }
636  }
637  }
638 
639  const int num_faces = faces.size();
640 
641  sub_info.face_to_cells = PHX::View<LO*[2]>("face_to_cells", num_faces);
642  sub_info.face_to_lidx = PHX::View<LO*[2]>("face_to_lidx", num_faces);
643  sub_info.cell_to_faces = PHX::View<LO**>("cell_to_faces", num_total_cells, num_faces_per_cell);
644  auto cell_to_faces_h = Kokkos::create_mirror_view(sub_info.cell_to_faces);
645  auto face_to_cells_h = Kokkos::create_mirror_view(sub_info.face_to_cells);
646  auto face_to_lidx_h = Kokkos::create_mirror_view(sub_info.face_to_lidx);
647 
648 
649  // Default the system with invalid cell index
650  Kokkos::deep_copy(cell_to_faces_h, -1);
651 
652  for(int face_index=0;face_index<num_faces;++face_index){
653  const face_t & face = faces[face_index];
654 
655  face_to_cells_h(face_index,0) = face.cell_0;
656  face_to_cells_h(face_index,1) = face.cell_1;
657 
658  cell_to_faces_h(face.cell_0,face.subcell_index_0) = face_index;
659  cell_to_faces_h(face.cell_1,face.subcell_index_1) = face_index;
660 
661  face_to_lidx_h(face_index,0) = face.subcell_index_0;
662  face_to_lidx_h(face_index,1) = face.subcell_index_1;
663 
664  }
665  Kokkos::deep_copy(sub_info.cell_to_faces, cell_to_faces_h);
666  Kokkos::deep_copy(sub_info.face_to_cells, face_to_cells_h);
667  Kokkos::deep_copy(sub_info.face_to_lidx, face_to_lidx_h);
668  // Complete.
669 
670 }
671 
672 void
674  const int splitting_size,
675  std::vector<panzer::LocalMeshPartition> & partitions)
676 {
677 
678  using LO = panzer::LocalOrdinal;
679 
680  // Make sure the splitting size makes sense
681  TEUCHOS_ASSERT((splitting_size > 0) or (splitting_size == WorksetSizeType::ALL_ELEMENTS));
682 
683  // Default partition size
684  const LO base_partition_size = std::min(mesh_info.num_owned_cells, (splitting_size > 0) ? splitting_size : mesh_info.num_owned_cells);
685 
686  // Cells to partition
687  std::vector<LO> partition_cells;
688  partition_cells.resize(base_partition_size);
689 
690  // Create the partitions
691  LO cell_count = 0;
692  while(cell_count < mesh_info.num_owned_cells){
693 
694  LO partition_size = base_partition_size;
695  if(cell_count + partition_size > mesh_info.num_owned_cells)
696  partition_size = mesh_info.num_owned_cells - cell_count;
697 
698  // Error check for a null partition - this should never happen by design
699  TEUCHOS_ASSERT(partition_size != 0);
700 
701  // In the final partition, we need to reduce the size of partition_cells
702  if(partition_size != base_partition_size)
703  partition_cells.resize(partition_size);
704 
705  // Set the partition indexes - not really a partition, just a chunk of cells
706  for(LO i=0; i<partition_size; ++i)
707  partition_cells[i] = cell_count+i;
708 
709  // Create an empty partition
710  partitions.push_back(panzer::LocalMeshPartition());
711 
712  // Fill the empty partition
713  partitioning_utilities::setupSubLocalMeshInfo(mesh_info,partition_cells,partitions.back());
714 
715  // Update the cell count
716  cell_count += partition_size;
717  }
718 
719 }
720 
721 }
722 
723 void
725  const panzer::WorksetDescriptor & description,
726  std::vector<panzer::LocalMeshPartition> & partitions)
727 {
728  // We have to make sure that the partitioning is possible
730  TEUCHOS_ASSERT(description.getWorksetSize() != 0);
731 
732  // This could just return, but it would be difficult to debug why no partitions were returned
733  TEUCHOS_ASSERT(description.requiresPartitioning());
734 
735  const std::string & element_block_name = description.getElementBlock();
736 
737  // We have two processes for in case this is a sideset or element block
738  if(description.useSideset()){
739 
740  // If the element block doesn't exist, there are no partitions to create
741  if(mesh_info.sidesets.find(element_block_name) == mesh_info.sidesets.end())
742  return;
743  const auto & sideset_map = mesh_info.sidesets.at(element_block_name);
744 
745  const std::string & sideset_name = description.getSideset();
746 
747  // If the sideset doesn't exist, there are no partitions to create
748  if(sideset_map.find(sideset_name) == sideset_map.end())
749  return;
750 
751  const panzer::LocalMeshSidesetInfo & sideset_info = sideset_map.at(sideset_name);
752 
753  // Partitioning is not important for sidesets
754  panzer::partitioning_utilities::splitMeshInfo(sideset_info, description.getWorksetSize(), partitions);
755 
756  for(auto & partition : partitions){
757  partition.sideset_name = sideset_name;
758  partition.element_block_name = element_block_name;
759  partition.cell_topology = sideset_info.cell_topology;
760  partition.has_connectivity = true;
761  }
762 
763  } else {
764 
765  // If the element block doesn't exist, there are no partitions to create
766  if(mesh_info.element_blocks.find(element_block_name) == mesh_info.element_blocks.end())
767  return;
768 
769  // Grab the element block we're interested in
770  const panzer::LocalMeshBlockInfo & block_info = mesh_info.element_blocks.at(element_block_name);
771 
773  // We only have one partition describing the entire local mesh
774  panzer::partitioning_utilities::splitMeshInfo(block_info, -1, partitions);
775  } else {
776  // We need to partition local mesh
777 
778  // FIXME: Until the above function is fixed, we will use this hack - this will lead to horrible partitions
779  panzer::partitioning_utilities::splitMeshInfo(block_info, description.getWorksetSize(), partitions);
780 
781  }
782 
783  for(auto & partition : partitions){
784  partition.element_block_name = element_block_name;
785  partition.cell_topology = block_info.cell_topology;
786  partition.has_connectivity = true;
787  }
788  }
789 
790 }
791 
792 
793 void
795  panzer::ConnManager & conn,
796  PHX::View<panzer::GlobalOrdinal*> & owned_cells,
797  PHX::View<panzer::GlobalOrdinal*> & ghost_cells,
798  PHX::View<panzer::GlobalOrdinal*> & virtual_cells)
799 {
800 
801  using Teuchos::RCP;
802 
803  // build cell to node map
804  PHX::View<panzer::GlobalOrdinal**> owned_cell_to_nodes;
805  buildCellToNodes(conn, owned_cell_to_nodes);
806 
807  // Build the local to global cell ID map
808  buildCellGlobalIDs(conn, owned_cells);
809 
810  // Get ghost cells
811  ghost_cells = buildGhostedCellOneRing(comm,owned_cells,owned_cell_to_nodes);
812 
813  // Build virtual cells
814  // Note: virtual cells are currently defined by faces (only really used for FV/DG type discretizations)
815 
816  // this class comes from Mini-PIC and Matt B
818  faceToElement->initialize(conn, comm);
819  auto elems_by_face = faceToElement->getFaceToElementsMap();
820  auto face_to_lidx = faceToElement->getFaceToCellLocalIdxMap();
821 
822  const panzer::LocalOrdinal num_owned_cells = owned_cells.extent(0);
823 
824  // We also need to consider faces that connect to cells that do not exist, but are needed for boundary conditions
825  // We dub them virtual cell since there should be no geometry associated with them, or topology really
826  // They exist only for datastorage so that they are consistent with 'real' cells from an algorithm perspective
827 
828  // Each virtual face (face linked to a '-1' cell) requires a virtual cell (i.e. turn the '-1' into a virtual cell)
829  // Virtual cells are those that do not exist but are connected to an owned cell
830  // Note - in the future, ghosted cells will also need to connect to virtual cells at boundary conditions, but for the moment we will ignore this.
831 
832  // Iterate over all faces and identify the faces connected to a potential virtual cell
833  std::vector<int> all_boundary_faces;
834  const int num_faces = elems_by_face.extent(0);
835  auto elems_by_face_h = Kokkos::create_mirror_view(elems_by_face);
836  Kokkos::deep_copy(elems_by_face_h, elems_by_face);
837  for(int face=0;face<num_faces;++face)
838  if(elems_by_face_h(face,0) < 0 or elems_by_face_h(face,1) < 0)
839  all_boundary_faces.push_back(face);
840  const panzer::LocalOrdinal num_virtual_cells = all_boundary_faces.size();
841 
842  // Create some global indexes associated with the virtual cells
843  // Note: We are assuming that virtual cells belong to ranks and are not 'shared' - this will change later on
844  virtual_cells = PHX::View<panzer::GlobalOrdinal*>("virtual_cells",num_virtual_cells);
845  auto virtual_cells_h = Kokkos::create_mirror_view(virtual_cells);
846  {
847  PANZER_FUNC_TIME_MONITOR_DIFF("Initial global index creation",InitialGlobalIndexCreation);
848 
849  const int num_ranks = comm->getSize();
850  const int rank = comm->getRank();
851 
852  std::vector<panzer::GlobalOrdinal> owned_cell_distribution(num_ranks,0);
853  {
854  std::vector<panzer::GlobalOrdinal> my_owned_cell_distribution(num_ranks,0);
855  my_owned_cell_distribution[rank] = num_owned_cells;
856 
857  Teuchos::reduceAll(*comm,Teuchos::REDUCE_SUM, num_ranks, my_owned_cell_distribution.data(),owned_cell_distribution.data());
858  }
859 
860  std::vector<panzer::GlobalOrdinal> virtual_cell_distribution(num_ranks,0);
861  {
862  std::vector<panzer::GlobalOrdinal> my_virtual_cell_distribution(num_ranks,0);
863  my_virtual_cell_distribution[rank] = num_virtual_cells;
864 
865  Teuchos::reduceAll(*comm,Teuchos::REDUCE_SUM, num_ranks, my_virtual_cell_distribution.data(),virtual_cell_distribution.data());
866  }
867 
868  panzer::GlobalOrdinal num_global_real_cells=0;
869  for(int i=0;i<num_ranks;++i){
870  num_global_real_cells+=owned_cell_distribution[i];
871  }
872 
873  panzer::GlobalOrdinal global_virtual_start_idx = num_global_real_cells;
874  for(int i=0;i<rank;++i){
875  global_virtual_start_idx += virtual_cell_distribution[i];
876  }
877 
878  for(int i=0;i<num_virtual_cells;++i){
879  virtual_cells_h(i) = global_virtual_start_idx + panzer::GlobalOrdinal(i);
880  }
881  }
882  Kokkos::deep_copy(virtual_cells, virtual_cells_h);
883 }
884 
885 }
Backwards compatibility mode that ignores the worksetSize in the WorksetDescriptor.
std::map< std::string, std::map< std::string, LocalMeshSidesetInfo > > sidesets
const std::string & getSideset(const int block=0) const
Get sideset name.
Teuchos::RCP< const shards::CellTopology > cell_topology
PHX::View< panzer::LocalOrdinal * > local_cells
void fillLocalCellIDs(const Teuchos::RCP< const Teuchos::Comm< int >> &comm, panzer::ConnManager &conn, PHX::View< panzer::GlobalOrdinal * > &owned_cells, PHX::View< panzer::GlobalOrdinal * > &ghost_cells, PHX::View< panzer::GlobalOrdinal * > &virtual_cells)
Get the owned, ghost and virtual global cell ids for this process.
const std::string & getElementBlock(const int block=0) const
Get element block name.
void generateLocalMeshPartitions(const panzer::LocalMeshInfo &mesh_info, const panzer::WorksetDescriptor &description, std::vector< panzer::LocalMeshPartition > &partitions)
virtual LocalOrdinal getConnectivitySize(LocalOrdinal localElmtId) const =0
PHX::View< panzer::LocalOrdinal *[2]> face_to_cells
bool useSideset() const
This descriptor is for a side set.
panzer::LocalOrdinal num_owned_cells
panzer::LocalOrdinal num_virtual_cells
Teuchos::RCP< const shards::CellTopology > cell_topology
TEUCHOS_DEPRECATED RCP< T > rcp(T *p, Dealloc_T dealloc, bool owns_mem)
#define TEUCHOS_TEST_FOR_EXCEPT_MSG(throw_exception_test, msg)
virtual const std::vector< LocalOrdinal > & getElementBlock(const std::string &blockID) const =0
PHX::View< panzer::LocalOrdinal *[2]> face_to_lidx
virtual void getElementBlockIds(std::vector< std::string > &elementBlockIds) const =0
Pure virtual base class for supplying mesh connectivity information to the DOF Manager.
virtual void getElementBlockTopologies(std::vector< shards::CellTopology > &elementBlockTopologies) const =0
panzer::LocalOrdinal num_ghstd_cells
std::map< std::string, LocalMeshBlockInfo > element_blocks
void splitMeshInfo(const panzer::LocalMeshInfoBase &mesh_info, const int splitting_size, std::vector< panzer::LocalMeshPartition > &partitions)
PHX::View< double *** > cell_nodes
#define TEUCHOS_ASSERT(assertion_test)
int getWorksetSize() const
Get the requested workset size (default -2 (workset size is set elsewhere), -1 (largest possible work...
void setupSubLocalMeshInfo(const panzer::LocalMeshInfoBase &parent_info, const std::vector< panzer::LocalOrdinal > &owned_parent_cells, panzer::LocalMeshInfoBase &sub_info)
bool requiresPartitioning() const
Do we need to partition the local mesh prior to generating worksets.
PHX::View< panzer::GlobalOrdinal * > global_cells
virtual void buildConnectivity(const FieldPattern &fp)=0
Workset size is set to the total number of local elements in the MPI process.
PHX::View< panzer::LocalOrdinal ** > cell_to_faces
virtual const GlobalOrdinal * getConnectivity(LocalOrdinal localElmtId) const =0