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