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