Nektar++
SubStructuredGraph.cpp
Go to the documentation of this file.
1 ///////////////////////////////////////////////////////////////////////////////
2 //
3 // File: SubStructuredGraph.cpp
4 //
5 // For more information, please see: http://www.nektar.info
6 //
7 // The MIT License
8 //
9 // Copyright (c) 2006 Division of Applied Mathematics, Brown University (USA),
10 // Department of Aeronautics, Imperial College London (UK), and Scientific
11 // Computing and Imaging Institute, University of Utah (USA).
12 //
13 // Permission is hereby granted, free of charge, to any person obtaining a
14 // copy of this software and associated documentation files (the "Software"),
15 // to deal in the Software without restriction, including without limitation
16 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
17 // and/or sell copies of the Software, and to permit persons to whom the
18 // Software is furnished to do so, subject to the following conditions:
19 //
20 // The above copyright notice and this permission notice shall be included
21 // in all copies or substantial portions of the Software.
22 //
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
24 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
26 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
28 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
29 // DEALINGS IN THE SOFTWARE.
30 //
31 // Description: a collection of classes that facilitates the implementation
32 // of the multi-level static condensation routines
33 //
34 ///////////////////////////////////////////////////////////////////////////////
35 
38 
39 #include <algorithm>
40 #include <boost/algorithm/string/replace.hpp>
41 #include <boost/config.hpp>
42 #include <boost/graph/adjacency_list.hpp>
43 #include <boost/graph/bandwidth.hpp>
44 #include <boost/graph/cuthill_mckee_ordering.hpp>
45 #include <boost/graph/properties.hpp>
46 #include <iomanip>
47 #include <iostream>
48 
49 using std::cout;
50 using std::endl;
51 using std::max;
52 
53 #ifdef NEKTAR_USE_SCOTCH
54 #ifdef NEKTAR_USE_MPI
55 #include <ptscotch.h>
56 #else
57 #include <scotch.h>
58 #endif
59 
60 #define SCOTCH_CALL(scotchFunc, args) \
61  { \
62  ASSERTL0(scotchFunc args == 0, \
63  std::string("Error in Scotch calling function ") + \
64  std::string(#scotchFunc)); \
65  }
66 #endif
67 
68 namespace Nektar
69 {
70 namespace MultiRegions
71 {
73 {
74 }
75 
76 PatchMap::PatchMap(const int nvals)
77 {
78  m_patchId = Array<OneD, int>(nvals);
79  m_dofId = Array<OneD, int>(nvals);
82 }
83 
85 {
86 }
87 
88 void PatchMap::SetPatchMap(const int n, const int patchId, const int dofId,
89  const unsigned int bndPatch, const NekDouble sign)
90 {
91  m_patchId[n] = patchId;
92  m_dofId[n] = dofId;
93  m_bndPatch[n] = bndPatch;
94  m_sign[n] = sign;
95 }
96 
98  Array<OneD, const unsigned int> numLocalBndCondPerPatch,
99  Array<OneD, const unsigned int> numLocalIntCondPerPatch)
100 {
101  int npatch = numLocalBndCondPerPatch.size();
102 
103  Array<OneD, int> bndoffset(npatch + 1);
104  Array<OneD, int> intoffset(npatch + 1);
105 
106  bndoffset[0] = intoffset[0] = 0;
107  for (int i = 1; i <= npatch; ++i)
108  {
109  bndoffset[i] = bndoffset[i - 1] + numLocalBndCondPerPatch[i - 1];
110  intoffset[i] = intoffset[i - 1] + numLocalIntCondPerPatch[i - 1];
111  }
112 
114 
115  for (int i = 0; i < m_dofId.size(); ++i)
116  {
117  if (m_bndPatch[i] == true)
118  {
119  m_newLevelMap[i] = bndoffset[m_patchId[i]] + m_dofId[i];
120  }
121  else
122  {
123  m_newLevelMap[i] =
124  bndoffset[npatch] + intoffset[m_patchId[i]] + m_dofId[i];
125  }
126  }
127 }
128 
130  MultiLevelBisectedGraphSharedPtr oldLevel, const int nPartition)
131 {
132  m_daughterGraphs.push_back(oldLevel);
134 }
135 
137  : m_BndDofs(MemoryManager<SubGraph>::AllocateSharedPtr(nBndDofs))
138 {
139 }
140 
142 {
143 }
144 
146 {
147  int returnval = 0;
148 
149  for (auto &g : m_daughterGraphs)
150  {
151  returnval += g->GetTotDofs();
152  }
153 
154  returnval += m_BndDofs->GetNverts();
155  return returnval;
156 }
157 
159 {
160  static int level = 0;
161  static int offset = 0;
162  level++;
163 
164  for (auto &g : m_daughterGraphs)
165  {
166  g->SetGlobalNumberingOffset();
167  }
168 
169  m_BndDofs->SetIdOffset(offset);
170  offset += m_BndDofs->GetNverts();
171 
172  level--;
173  if (level == 0)
174  {
175  offset = 0;
176  }
177 }
178 
180 {
181  static int level = 0;
182  level++;
183  cout << "LEVEL " << level << " " << m_BndDofs->GetNverts() << endl;
184 
185  for (auto &g : m_daughterGraphs)
186  {
187  g->DumpNBndDofs();
188  }
189 
190  level--;
191 }
192 
194  std::vector<SubGraphSharedPtr> &leaves) const
195 {
196  int cnt = 0;
197 
198  for (auto &g : m_daughterGraphs)
199  {
200  g->CollectLeaves(leaves);
201  cnt++;
202  }
203 
204  if (cnt == 0)
205  {
207  leaves.push_back(leave);
208  }
209 }
210 
212 {
213  int returnval;
214  static int level = 0;
215  static int nLeaves = 0;
216  level++;
217 
218  if (level == 1 && m_daughterGraphs.size() == 0)
219  {
220  level = 0;
221  nLeaves = 0;
222  return 0;
223  }
224 
225  for (auto it = m_daughterGraphs.begin(); it != m_daughterGraphs.end();)
226  {
227  auto g = *it;
228  if (g->GetNdaughterGraphs() == 0 &&
229  g->GetBndDofsGraph()->GetNverts() == 0)
230  {
231  it = m_daughterGraphs.erase(it);
232  nLeaves++;
233  }
234  else
235  {
236  g->CutEmptyLeaves();
237  ++it;
238  }
239  }
240 
241  returnval = nLeaves;
242 
243  level--;
244  if (level == 0)
245  {
246  nLeaves = 0;
247  }
248 
249  return returnval;
250 }
251 
253 {
254  int returnval;
255  static int level = 0;
256  static int nLeaves = 0;
257  level++;
258 
259  if (level == 1 && m_daughterGraphs.size() == 0)
260  {
261  level = 0;
262  nLeaves = 0;
263  return 0;
264  }
265 
266  for (auto it = m_daughterGraphs.begin(); it != m_daughterGraphs.end();)
267  {
268  auto g = *it;
269  if (g->GetNdaughterGraphs() == 0)
270  {
271  it = m_daughterGraphs.erase(it);
272  nLeaves++;
273  }
274  else
275  {
276  g->CutLeaves();
277  ++it;
278  }
279  }
280 
281  returnval = nLeaves;
282 
283  level--;
284  if (level == 0)
285  {
286  nLeaves = 0;
287  }
288 
289  return returnval;
290 }
291 
293  : m_IntBlocks(), m_daughterGraph()
294 {
295  // This constructor should only be used in the very special case
296  // when there is a graph consisting of nVerts vertices but without
297  // any connectivity whatsowever (i.e. no edges)
298 
299  SubGraphSharedPtr subgraph;
300  for (int i = 0; i < nVerts; i++)
301  {
303  m_IntBlocks.push_back(subgraph);
304  }
305 }
306 
308  MultiLevelBisectedGraphSharedPtr graph, int nPartition, bool globaloffset)
309  : m_IntBlocks(), m_daughterGraph()
310 {
311  int ncuts;
312 
313  if (nPartition > 0)
314  {
316  graph, nPartition);
317  }
318 
319  if (globaloffset)
320  {
321  graph->SetGlobalNumberingOffset();
322  }
323 
324  graph->CutEmptyLeaves();
325  graph->CollectLeaves(m_IntBlocks);
326  ncuts = graph->CutLeaves();
327 
328  if (ncuts)
329  {
332  }
333 }
334 
336 {
337 }
338 
340 {
341  static int nIntDofs = 0;
342  static int level = 0;
343  level++;
344 
345  int returnval;
346 
347  if (m_daughterGraph.get())
348  {
349  m_daughterGraph->GetTotDofs();
350  }
351 
352  for (int i = 0; i < m_IntBlocks.size(); i++)
353  {
354  nIntDofs += m_IntBlocks[i]->GetNverts();
355  }
356 
357  returnval = nIntDofs;
358 
359  level--;
360  if (level == 0)
361  {
362  nIntDofs = 0;
363  }
364 
365  return returnval;
366 }
367 
369 {
370  static int level = 0;
371  level++;
372 
373  cout << "LEVEL " << level << endl;
374  cout << "interior blocks" << endl;
375  for (int i = 0; i < m_IntBlocks.size(); i++)
376  {
377  cout << " " << i << "/" << m_IntBlocks.size() - 1 << ": "
378  << m_IntBlocks[i]->GetNverts() << ", "
379  << m_IntBlocks[i]->GetIdOffset() << endl;
380  }
381  cout << endl;
382 
383  if (m_daughterGraph.get())
384  {
385  m_daughterGraph->Dump();
386  }
387 
388  level--;
389 }
390 
392  Array<OneD, int> &perm, Array<OneD, int> &iperm) const
393 {
394  int nDofs = GetTotDofs();
395 
396  // Step 1: make a permutation array that goes from the current
397  // reordering in the bottom-up graph to an ordering where the
398  // interior dofs of the first (=bottom) level are ordered last,
399  // followed interior dofs of the second level, ...
400  Array<OneD, int> iperm1(nDofs);
401  SetBottomUpReordering(iperm1);
402 
403  // Now, based upon the input permutation array, update the
404  // permutation arrays between the original ordering of the dofs
405  // (defined somewhere outside) and the final reordering as given by
406  // iperm1
407  for (int i = 0; i < nDofs; i++)
408  {
409  iperm[i] = iperm1[iperm[i]];
410  perm[iperm[i]] = i;
411  }
412 }
413 
415  Array<OneD, int> &iperm) const
416 {
417  static int offset = 0;
418  static int level = 0;
419  level++;
420 
421  if (m_daughterGraph.get())
422  {
423  m_daughterGraph->SetBottomUpReordering(iperm);
424  }
425 
426  for (int i = 0; i < m_IntBlocks.size(); i++)
427  {
428  int GlobIdOffset = m_IntBlocks[i]->GetIdOffset();
429  m_IntBlocks[i]->SetIdOffset(offset);
430  for (int j = 0; j < m_IntBlocks[i]->GetNverts(); j++)
431  {
432  iperm[GlobIdOffset + j] = offset;
433  offset++;
434  }
435  }
436 
437  level--;
438  if (level == 0)
439  {
440  offset = 0;
441  }
442 }
443 
445  const Array<OneD, const int> &wgts)
446 {
447  static int offset = 0;
448  static int level = 0;
449  level++;
450 
451  if (m_daughterGraph.get())
452  {
453  m_daughterGraph->ExpandGraphWithVertexWeights(wgts);
454  }
455 
456  for (int i = 0; i < m_IntBlocks.size(); i++)
457  {
458  int OrigGlobIdOffset = m_IntBlocks[i]->GetIdOffset();
459  int newNverts = 0;
460 
461  m_IntBlocks[i]->SetIdOffset(offset);
462 
463  for (int j = 0; j < m_IntBlocks[i]->GetNverts(); j++)
464  {
465  newNverts += wgts[OrigGlobIdOffset + j];
466  offset += wgts[OrigGlobIdOffset + j];
467  }
468 
469  m_IntBlocks[i]->SetNverts(newNverts);
470  }
471 
472  // erase the blocks that do not have any vertices
473  m_IntBlocks.erase(
474  remove_if(m_IntBlocks.begin(), m_IntBlocks.end(), SubGraphWithoutVerts),
475  m_IntBlocks.end());
476 
477  // remove the current level if there are no interior blocks
478  if (m_IntBlocks.size() == 0 && m_daughterGraph.get())
479  {
480  m_IntBlocks = m_daughterGraph->GetInteriorBlocks();
481  m_daughterGraph = m_daughterGraph->GetDaughterGraph();
482  }
483 
484  level--;
485  if (level == 0)
486  {
487  offset = 0;
488  }
489 }
490 
492  const int leveltomask, Array<OneD, NekDouble> &maskarray) const
493 {
494  static int level = 0;
495  level++;
496 
497  if (level < leveltomask)
498  {
499  m_daughterGraph->MaskPatches(leveltomask, maskarray);
500  }
501  else if (level == leveltomask)
502  {
503  int GlobIdOffset;
504  int nVerts;
505  for (int i = 0; i < m_IntBlocks.size(); i++)
506  {
507  GlobIdOffset = m_IntBlocks[i]->GetIdOffset();
508  nVerts = m_IntBlocks[i]->GetNverts();
509  for (int j = 0; j < nVerts; j++)
510  {
511  maskarray[GlobIdOffset + j] = (NekDouble)i;
512  }
513  }
514  }
515  else
516  {
517  NEKERROR(ErrorUtil::efatal, "If statement should not arrive here");
518  }
519 
520  level--;
521 }
522 
524  const int whichlevel) const
525 {
526  int returnval = -1;
527  static int level = 0;
528  level++;
529 
530  if (level < whichlevel)
531  {
532  returnval = m_daughterGraph->GetNpatchesWithInterior(whichlevel);
533  }
534  else if (level == whichlevel)
535  {
536  returnval = m_IntBlocks.size();
537  }
538  else
539  {
540  NEKERROR(ErrorUtil::efatal, "If statement should not arrive here");
541  }
542 
543  level--;
544 
545  return returnval;
546 }
547 
549  const int whichlevel, Array<OneD, unsigned int> &outarray) const
550 {
551  static int level = 0;
552  level++;
553 
554  if (level < whichlevel)
555  {
556  m_daughterGraph->GetNintDofsPerPatch(whichlevel, outarray);
557  }
558  else if (level == whichlevel)
559  {
560  ASSERTL1(outarray.size() >= m_IntBlocks.size(),
561  "Array dimension not sufficient");
562 
563  for (int i = 0; i < m_IntBlocks.size(); i++)
564  {
565  outarray[i] = (unsigned int)m_IntBlocks[i]->GetNverts();
566  }
567  }
568  else
569  {
570  NEKERROR(ErrorUtil::efatal, "If statement should not arrive here");
571  }
572 
573  level--;
574 }
575 
577  const int patch) const
578 {
579  int retval = -1;
580  static int level = 0;
581  level++;
582 
583  if (level < whichlevel)
584  {
585  retval = m_daughterGraph->GetInteriorOffset(whichlevel, patch);
586  }
587  else if (level == whichlevel)
588  {
589  retval = m_IntBlocks[patch]->GetIdOffset();
590  }
591  else
592  {
593  NEKERROR(ErrorUtil::efatal, "If statement should not arrive here");
594  }
595 
596  level--;
597 
598  return retval;
599 }
600 
601 std::vector<SubGraphSharedPtr> BottomUpSubStructuredGraph::GetInteriorBlocks(
602  const int whichlevel) const
603 {
604  std::vector<SubGraphSharedPtr> returnval;
605  static int level = 0;
606  level++;
607 
608  if (level < whichlevel)
609  {
610  returnval = m_daughterGraph->GetInteriorBlocks(whichlevel);
611  }
612  else if (level == whichlevel)
613  {
614  returnval = m_IntBlocks;
615  }
616  else
617  {
618  NEKERROR(ErrorUtil::efatal, "If statement should not arrive here");
619  }
620 
621  level--;
622  return returnval;
623 }
624 
625 int BottomUpSubStructuredGraph::GetNumGlobalDofs(const int whichlevel) const
626 {
627  int returnval = -1;
628  static int level = 0;
629  level++;
630 
631  if (level < whichlevel)
632  {
633  returnval = m_daughterGraph->GetNumGlobalDofs(whichlevel);
634  }
635  else if (level == whichlevel)
636  {
637  returnval = m_IntBlocks[m_IntBlocks.size() - 1]->GetIdOffset() +
638  m_IntBlocks[m_IntBlocks.size() - 1]->GetNverts();
639  }
640  else
641  {
642  NEKERROR(ErrorUtil::efatal, "If statement should not arrive here");
643  }
644 
645  level--;
646 
647  return returnval;
648 }
649 
651 {
652  int returnval = 0;
653  static int level = 0;
654  level++;
655 
656  if (m_daughterGraph.get())
657  {
658  returnval = m_daughterGraph->GetNlevels();
659  }
660  returnval = max(returnval, level);
661 
662  level--;
663 
664  return returnval;
665 }
666 
668 {
669  return g->GetNverts() == 0;
670 }
671 
672 namespace
673 {
674 // the first template parameter (=OutEdgeList) is chosen to be of
675 // type std::set as in the set up of the adjacency, a similar edge
676 // might be created multiple times. And to prevent the definition
677 // of parallell edges, we use std::set (=boost::setS) rather than
678 // std::vector (=boost::vecS)
679 typedef boost::adjacency_list<boost::setS, boost::vecS, boost::undirectedS>
680  BoostGraph;
681 typedef boost::graph_traits<BoostGraph>::vertex_descriptor BoostVertex;
682 } // namespace
683 
684 void CuthillMckeeReordering(const BoostGraph &graph, Array<OneD, int> &perm,
685  Array<OneD, int> &iperm)
686 {
687  int nGraphVerts = boost::num_vertices(graph);
688 
689  ASSERTL1(perm.size() >= nGraphVerts && iperm.size() >= nGraphVerts,
690  "Non-matching dimensions");
691 
692  // Call boost::cuthill_mckee_ordering to reorder the graph-vertices
693  // using the reverse Cuthill-Mckee algorithm
694  std::vector<BoostVertex> reorderedVerts(nGraphVerts);
695  boost::cuthill_mckee_ordering(graph, reorderedVerts.rbegin());
696 
697  // copy the reordering to the Arrays perm and iperm
698  for (int i = 0; i < nGraphVerts; i++)
699  {
700  perm[i] = reorderedVerts[i];
701  iperm[reorderedVerts[i]] = i;
702  }
703 }
704 
706  const BoostGraph &graph, Array<OneD, int> &perm, Array<OneD, int> &iperm,
707  BottomUpSubStructuredGraphSharedPtr &substructgraph,
708  std::set<int> partVerts, int mdswitch)
709 {
710 #ifndef NEKTAR_USE_SCOTCH
711  boost::ignore_unused(graph, perm, iperm, substructgraph, partVerts,
712  mdswitch);
713  ASSERTL0(false, "Multi-level static condensation requires Nektar++"
714  " to be built with SCOTCH.");
715 #else
716  int nGraphVerts = boost::num_vertices(graph);
717  int nGraphEdges = boost::num_edges(graph);
718 
719  ASSERTL1(perm.size() >= nGraphVerts && iperm.size() >= nGraphVerts,
720  "Non-matching dimensions");
721 
722  // We will now use Scotch to reorder the graph. For the purpose of
723  // multi-level static condensation, we will use a Scotch routine
724  // that partitions the graph recursively using a multi-level nested
725  // bisection algorithm. The name of this routine is
726  // SCOTCH_graphOrder and it was originally designed to reorder the
727  // DOFs in a matrix in order to minimise the fill-in when applying a
728  // factorisation technique (such as Cholesky). However, this
729  // reordering of DOFs also seems to be perfectly suited in the
730  // context of multilevel substructuring. Therefore, we will use this
731  // Scotch routine instead of the more well-known graph-partitioning
732  // routines.
733  if (nGraphEdges)
734  {
735  //
736  // Step 1: Convert boost graph to a graph in adjncy-list format
737  // as required by Scotch.
738  //
739  int acnt = 0, vcnt = 0, i, cnt;
740  int nPartition = partVerts.size();
741  int nNonPartition = nGraphVerts - partVerts.size();
742 
743  Array<OneD, int> xadj(nNonPartition + 1, 0);
744  Array<OneD, int> adjncy(2 * nGraphEdges);
745  Array<OneD, int> initial_perm(nGraphVerts);
746  Array<OneD, int> iinitial_perm(nGraphVerts);
747  Array<OneD, int> perm_tmp(nNonPartition);
748  Array<OneD, int> iperm_tmp(nNonPartition);
749 
750  // Perform an initial reordering of the vertices, so that
751  // partition nodes are at the end. This allows Scotch to
752  // partition the interior nodes from values starting at zero.
753  for (i = cnt = 0; i < nGraphVerts; ++i)
754  {
755  if (partVerts.count(i) == 0)
756  {
757  initial_perm[i] = cnt;
758  iinitial_perm[cnt++] = i;
759  }
760  }
761 
762  for (i = 0; i < nGraphVerts; ++i)
763  {
764  if (partVerts.count(i) > 0)
765  {
766  initial_perm[i] = cnt;
767  iinitial_perm[cnt++] = i;
768  }
769  }
770 
771  // Apply this reordering to the graph.
772  boost::property_map<BoostGraph, boost::vertex_index_t>::type index =
773  get(boost::vertex_index, graph);
774 
775  // Now construct the adjaceny list using
776  // boost::adjacent_vertices.
777  auto verts = boost::vertices(graph);
778  for (auto vertit = verts.first; vertit != verts.second; ++vertit)
779  {
780  if (partVerts.count(index[*vertit]) > 0)
781  {
782  continue;
783  }
784 
785  auto adjverts = boost::adjacent_vertices(*vertit, graph);
786  for (auto adjvertit = adjverts.first; adjvertit != adjverts.second;
787  ++adjvertit)
788  {
789  if (partVerts.count(index[*adjvertit]) > 0)
790  {
791  continue;
792  }
793  adjncy[acnt++] = initial_perm[*adjvertit];
794  }
795  xadj[++vcnt] = acnt;
796  }
797 
798  //
799  // Step 2: pass the graph to Scotch and perform the nested
800  // dissection to obtain a separator tree.
801  //
802 
803  // Pass the adjaceny graph into Scotch.
804  SCOTCH_Graph *scGraph = SCOTCH_graphAlloc();
805  SCOTCH_CALL(SCOTCH_graphBuild,
806  (scGraph, 0, nNonPartition, &xadj[0], &xadj[1], NULL, NULL,
807  xadj[nNonPartition], &adjncy[0], NULL));
808 
809  // This horrible looking string defines the Scotch graph
810  // reordering strategy, which essentially does a nested
811  // dissection + compression. We take this almost directly from
812  // the SCOTCH_stratGraphOrderBuild function (defined in
813  // library_graph_order.c), but by specifying the string
814  // manually, we can replace the subdivision strategy to allow us
815  // to control the number of vertices used to determine whether
816  // to perform another dissection using the mdswitch
817  // parameter. The below is essentially equivalent to calling
818  // SCOTCH_stratGraphOrderBuild with the flags
819  // SCOTCH_STRATLEAFSIMPLE and SCOTCH_STRATSEPASIMPLE to make
820  // sure leaf nodes do not have any reordering applied to them.
821  std::string strat_str =
822  "c{rat=0.7,cpr=n{sep=/(<TSTS>)?m{rat=0.7,vert=100,low="
823  "h{pass=10},asc=b{width=3,bnd=f{bal=<BBAL>},"
824  "org=(|h{pass=10})f{bal=<BBAL>}}}<SEPA>;,"
825  "ole=<OLEA>,ose=<OSEP>},unc=n{sep=/(<TSTS>)?m{rat=0.7,"
826  "vert=100,low=h{pass=10},asc=b{width=3,bnd=f{bal=<BBAL>},"
827  "org=(|h{pass=10})f{bal=<BBAL>}}}<SEPA>;"
828  ",ole=<OLEA>,ose=<OSEP>}}";
829 
830  // Replace flags in the string with appropriate values.
831  boost::replace_all(strat_str, "<SEPA>",
832  "|m{rat=0.7,vert=100,low=h{pass=10},"
833  "asc=b{width=3,bnd=f{bal=<BBAL>},"
834  "org=(|h{pass=10})f{bal=<BBAL>}}}");
835  boost::replace_all(strat_str, "<OSEP>", "s");
836  boost::replace_all(strat_str, "<OLEA>", "s");
837  boost::replace_all(strat_str, "<BBAL>", "0.1");
838  boost::replace_all(strat_str, "<TSTS>",
839  "vert>" + std::to_string(mdswitch));
840 
841  // Set up the re-ordering strategy.
842  SCOTCH_Strat strat;
843  SCOTCH_CALL(SCOTCH_stratInit, (&strat));
844  SCOTCH_CALL(SCOTCH_stratGraphOrder, (&strat, strat_str.c_str()));
845 
846  // As output, Scotch will give us the total number of 'blocks'
847  // (i.e. the separators and all of the leaves), the separator
848  // tree as a mapping of block to parent block, and the range of
849  // indices that is contained within each block. Reordering of
850  // the vertices goes from largest index (at the top level) to
851  // smallest (at the bottom level). The precise ordering is given
852  // in the Scotch user guide.
853  //
854  // Note that we pass in iperm into the 'permtab' field of
855  // graphOrder and 'perm' into the 'peritab' field; this is
856  // because our definition of the permutation is the opposite of
857  // what's defined in Scotch (this is leftover from a previous
858  // implementation that used Metis).
859  Array<OneD, int> treetab(nNonPartition);
860  Array<OneD, int> rangtab(nNonPartition + 1);
861  int cblknbr = 0;
862  SCOTCH_CALL(SCOTCH_graphOrder,
863  (scGraph, &strat, &iperm_tmp[0], &perm_tmp[0], &cblknbr,
864  &rangtab[0], &treetab[0]));
865 
866  // We're now done with Scotch: clean up the created structures.
867  SCOTCH_graphExit(scGraph);
868  SCOTCH_stratExit(&strat);
869 
870  //
871  // Step 3: create a MultiLevelBisectedGraph by reading the
872  // separator tree we obtained from Scotch.
873  //
874 
875  // Setup root block, which lies at the end of the blocks
876  // described in treetab[].
877  std::vector<MultiLevelBisectedGraphSharedPtr> graphs(cblknbr);
878 
879  // The strategy now is to traverse backwards over the blocks
880  // described in treetab to set up the levels of the top-down
881  // graph. rangtab allows us to calculate how many degrees of
882  // freedom lie in the separator.
883  for (i = cblknbr - 1; i >= 0; --i)
884  {
885  // Set up this block.
886  graphs[i] =
888  rangtab[i + 1] - rangtab[i]);
889 
890  // If we're a root block (treetab[i] == -1) we don't need to
891  // do anything, just move onto the next block.
892  if (treetab[i] == -1)
893  {
894  continue;
895  }
896 
897  // Now use treetab[i] to figure out the parent block. We
898  // have to be a bit careful in setting left/right daughters
899  // here. The left daughter's degrees of freedom are ordered
900  // _first_ in the iperm/perm arrays returned from Scotch,
901  // but if there is both a left and right daughter, we'll
902  // come across the right daughter first because the
903  // separators are being traversed backwards. We'll therefore
904  // insert this at the beginning of the daughter graphs
905  // vector.
906  MultiLevelBisectedGraphSharedPtr tmp = graphs[treetab[i]];
907  std::vector<MultiLevelBisectedGraphSharedPtr> &daughters =
908  tmp->GetDaughterGraphs();
909  daughters.insert(daughters.begin(), graphs[i]);
910  }
911 
912  // Change permutations from Scotch to account for initial offset
913  // in case we had partition vertices.
914  for (i = 0; i < nGraphVerts; ++i)
915  {
916  if (partVerts.count(i) == 0)
917  {
918  iperm[i] = iperm_tmp[initial_perm[i]];
919  perm[iperm[i]] = i;
920  }
921  }
922 
923  auto it = partVerts.begin(), it2 = partVerts.end();
924  for (i = nNonPartition; it != it2; ++it, ++i)
925  {
926  perm[i] = *it;
927  iperm[*it] = i;
928  }
929 
930  for (i = 0; i < nGraphVerts; ++i)
931  {
932  ASSERTL1(perm[iperm[i]] == i, "Perm error " + std::to_string(i));
933  }
934 
935  // If we were passed a graph with disconnected regions, we need
936  // to create a bisected graph with the appropriate roots.
937  std::vector<int> rootBlocks;
938  for (i = 0; i < cblknbr; ++i)
939  {
940  if (treetab[i] == -1)
941  {
942  rootBlocks.push_back(i);
943  }
944  }
945 
947  if (rootBlocks.size() == 1)
948  {
949  root = graphs[rootBlocks[0]];
950  }
951  else
952  {
954 
955  for (int i = 0; i < rootBlocks.size(); ++i)
956  {
957  root->GetDaughterGraphs().push_back(graphs[rootBlocks[i]]);
958  }
959  }
960 
961  // Check that our degree of freedom count in the constructed
962  // graph is the same as the number of degrees of freedom that we
963  // were given in the function input.
964  ASSERTL0(root->GetTotDofs() == nNonPartition,
965  "Error in constructing Scotch graph for multi-level"
966  " static condensation.");
967 
968  //
969  // Step 4: Set up the bottom-up graph from the top-down graph,
970  // and reorder the permutation from Scotch.
971  //
972  substructgraph =
974  root, nPartition, true);
975 
976  // Important: we cannot simply use the ordering given by Scotch
977  // as it does not order the different blocks as we would like
978  // it. Therefore, we use following command to re-order them
979  // again in the context of the bottom-up substructuring. As a
980  // result, we will now obtain an ordering where the interior
981  // degrees of freedom of the first (=bottom) level will be
982  // ordered last (block by block ofcoarse). The interior degrees
983  // of freedom of the second level will be ordered second to
984  // last, etc ... As a result, the boundary degrees of freedom of
985  // the last level (i.e. the dofs that will have to solved
986  // non-recursively) will be ordered first (after the Dirichlet
987  // Dofs that is). (this way, we actually follow the same idea
988  // and convention in the standard (non-multi-level) static
989  // condensation approach).
990  substructgraph->UpdateBottomUpReordering(perm, iperm);
991  }
992  else
993  {
994  // This is the very special case of a graph without any
995  // connectivity i.e. a collection of vertices without any edges
996  for (int i = 0; i < nGraphVerts; i++)
997  {
998  perm[i] = i;
999  iperm[i] = i;
1000  }
1001  substructgraph =
1003  nGraphVerts);
1004  }
1005 #endif
1006 }
1007 
1008 void NoReordering(const BoostGraph &graph, Array<OneD, int> &perm,
1009  Array<OneD, int> &iperm)
1010 {
1011  int nGraphVerts = boost::num_vertices(graph);
1012 
1013  ASSERTL1(perm.size() >= nGraphVerts && iperm.size() >= nGraphVerts,
1014  "Non-matching dimensions");
1015 
1016  for (int i = 0; i < nGraphVerts; i++)
1017  {
1018  perm[i] = i;
1019  iperm[i] = i;
1020  }
1021 }
1022 } // namespace MultiRegions
1023 } // namespace Nektar
#define ASSERTL0(condition, msg)
Definition: ErrorUtil.hpp:215
#define NEKERROR(type, msg)
Assert Level 0 – Fundamental assert which is used whether in FULLDEBUG, DEBUG or OPT compilation mode...
Definition: ErrorUtil.hpp:209
#define ASSERTL1(condition, msg)
Assert Level 1 – Debugging which is used whether in FULLDEBUG or DEBUG compilation mode....
Definition: ErrorUtil.hpp:249
#define SCOTCH_CALL(scotchFunc, args)
#define sign(a, b)
return the sign(b)*a
Definition: Polylib.cpp:49
General purpose memory allocation routines with the ability to allocate from thread specific memory p...
static std::shared_ptr< DataType > AllocateSharedPtr(const Args &...args)
Allocate a shared pointer from the memory pool.
void GetNintDofsPerPatch(const int whichlevel, Array< OneD, unsigned int > &outarray) const
void ExpandGraphWithVertexWeights(const Array< OneD, const int > &wgts)
std::vector< SubGraphSharedPtr > GetInteriorBlocks() const
void SetBottomUpReordering(Array< OneD, int > &iperm) const
int GetInteriorOffset(const int whichlevel, const int patch=0) const
BottomUpSubStructuredGraph(MultiLevelBisectedGraphSharedPtr graph, int nPartition=0, bool globaloffset=false)
void UpdateBottomUpReordering(Array< OneD, int > &perm, Array< OneD, int > &iperm) const
BottomUpSubStructuredGraphSharedPtr m_daughterGraph
void MaskPatches(const int leveltomask, Array< OneD, NekDouble > &maskarray) const
int GetNpatchesWithInterior(const int whichlevel) const
MultiLevelBisectedGraph(MultiLevelBisectedGraphSharedPtr oldLevel, const int nPartition)
void CollectLeaves(std::vector< SubGraphSharedPtr > &leaves) const
std::vector< MultiLevelBisectedGraphSharedPtr > m_daughterGraphs
Array< OneD, unsigned int > m_bndPatch
void SetPatchMap(const int n, const int patchId, const int dofId, const unsigned int bndPatch, const NekDouble sign)
void SetNewLevelMap(Array< OneD, const unsigned int > numLocalBndCondPerPatch, Array< OneD, const unsigned int > numLocalIntCondPerPatch)
Array< OneD, NekDouble > m_sign
void NoReordering(const BoostGraph &graph, Array< OneD, int > &perm, Array< OneD, int > &iperm)
void CuthillMckeeReordering(const BoostGraph &graph, Array< OneD, int > &perm, Array< OneD, int > &iperm)
void MultiLevelBisectionReordering(const BoostGraph &graph, Array< OneD, int > &perm, Array< OneD, int > &iperm, BottomUpSubStructuredGraphSharedPtr &substructgraph, std::set< int > partVerts, int mdswitch)
std::shared_ptr< BottomUpSubStructuredGraph > BottomUpSubStructuredGraphSharedPtr
bool SubGraphWithoutVerts(const SubGraphSharedPtr g)
std::shared_ptr< MultiLevelBisectedGraph > MultiLevelBisectedGraphSharedPtr
std::shared_ptr< SubGraph > SubGraphSharedPtr
The above copyright notice and this permission notice shall be included.
Definition: CoupledSolver.h:2
double NekDouble