Panzer Version of the Day
Loading...
Searching...
No Matches
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
22#include "Panzer_NodeType.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
38namespace panzer
39{
40
41namespace
42{
43
47void
48buildCellGlobalIDs(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
61 Teuchos::RCP<panzer::FieldPattern> cell_pattern;
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
102void
103buildCellToNodes(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
150Teuchos::RCP<const Tpetra::Map<panzer::LocalOrdinal,panzer::GlobalOrdinal,panzer::TpetraNodeType> >
151buildNodeMap(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
196Teuchos::RCP<Tpetra::CrsMatrix<panzer::LocalOrdinal,panzer::LocalOrdinal,panzer::GlobalOrdinal,panzer::TpetraNodeType> >
197buildNodeToCellMatrix(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
290PHX::View<panzer::GlobalOrdinal*>
291buildGhostedCellOneRing(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
383namespace partitioning_utilities
384{
385
386
387void
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
672void
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
723void
725 const panzer::WorksetDescriptor & description,
726 std::vector<panzer::LocalMeshPartition> & partitions)
727{
728 // We have to make sure that the partitioning is possible
729 TEUCHOS_ASSERT(description.getWorksetSize() != panzer::WorksetSizeType::CLASSIC_MODE);
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
793void
794fillLocalCellIDs(const Teuchos::RCP<const Teuchos::Comm<int>> & comm,
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
817 auto faceToElement = Teuchos::rcp(new panzer::FaceToElement<panzer::LocalOrdinal,panzer::GlobalOrdinal>());
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}
Pure virtual base class for supplying mesh connectivity information to the DOF Manager.
virtual const std::vector< LocalOrdinal > & getElementBlock(const std::string &blockID) const =0
virtual void buildConnectivity(const FieldPattern &fp)=0
virtual const GlobalOrdinal * getConnectivity(LocalOrdinal localElmtId) const =0
virtual void getElementBlockIds(std::vector< std::string > &elementBlockIds) const =0
virtual void getElementBlockTopologies(std::vector< shards::CellTopology > &elementBlockTopologies) const =0
virtual LocalOrdinal getConnectivitySize(LocalOrdinal localElmtId) const =0
const std::string & getElementBlock(const int block=0) const
Get element block name.
const std::string & getSideset(const int block=0) const
Get sideset name.
int getWorksetSize() const
Get the requested workset size (default -2 (workset size is set elsewhere), -1 (largest possible work...
bool useSideset() const
This descriptor is for a side set.
bool requiresPartitioning() const
Do we need to partition the local mesh prior to generating worksets.
void setupSubLocalMeshInfo(const panzer::LocalMeshInfoBase &parent_info, const std::vector< panzer::LocalOrdinal > &owned_parent_cells, panzer::LocalMeshInfoBase &sub_info)
void splitMeshInfo(const panzer::LocalMeshInfoBase &mesh_info, const int splitting_size, std::vector< panzer::LocalMeshPartition > &partitions)
@ ALL_ELEMENTS
Workset size is set to the total number of local elements in the MPI process.
@ CLASSIC_MODE
Backwards compatibility mode that ignores the worksetSize in the WorksetDescriptor.
void generateLocalMeshPartitions(const panzer::LocalMeshInfo &mesh_info, const panzer::WorksetDescriptor &description, std::vector< panzer::LocalMeshPartition > &partitions)
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.
Teuchos::RCP< const shards::CellTopology > cell_topology
panzer::LocalOrdinal num_owned_cells
PHX::View< panzer::LocalOrdinal *[2]> face_to_lidx
PHX::View< panzer::GlobalOrdinal * > global_cells
panzer::LocalOrdinal num_ghstd_cells
panzer::LocalOrdinal num_virtual_cells
PHX::View< panzer::LocalOrdinal *[2]> face_to_cells
PHX::View< panzer::LocalOrdinal ** > cell_to_faces
PHX::View< panzer::LocalOrdinal * > local_cells
PHX::View< double *** > cell_nodes
std::map< std::string, std::map< std::string, LocalMeshSidesetInfo > > sidesets
std::map< std::string, LocalMeshBlockInfo > element_blocks
Teuchos::RCP< const shards::CellTopology > cell_topology