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
49using std::cout;
50using std::endl;
51using 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
68namespace Nektar
69{
70namespace MultiRegions
71{
73{
74}
75
76PatchMap::PatchMap(const int nvals)
77{
82}
83
85{
86}
87
88void 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
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
625int 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
672namespace
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)
679typedef boost::adjacency_list<boost::setS, boost::vecS, boost::undirectedS>
680 BoostGraph;
681typedef boost::graph_traits<BoostGraph>::vertex_descriptor BoostVertex;
682} // namespace
683
684void 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,
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 ASSERTL0(scGraph != nullptr,
806 "Failed to allocate Scotch graph for substructuring.");
807
808 ASSERTL0(SCOTCH_graphInit(scGraph) == 0,
809 "Failed to initialise Scotch graph for substructuring.");
810
811 SCOTCH_CALL(SCOTCH_graphBuild,
812 (scGraph, 0, nNonPartition, &xadj[0], &xadj[1], nullptr,
813 nullptr, xadj[nNonPartition], &adjncy[0], nullptr));
814
815 // This horrible looking string defines the Scotch graph
816 // reordering strategy, which essentially does a nested
817 // dissection + compression. We take this almost directly from
818 // the SCOTCH_stratGraphOrderBuild function (defined in
819 // library_graph_order.c), but by specifying the string
820 // manually, we can replace the subdivision strategy to allow us
821 // to control the number of vertices used to determine whether
822 // to perform another dissection using the mdswitch
823 // parameter. The below is essentially equivalent to calling
824 // SCOTCH_stratGraphOrderBuild with the flags
825 // SCOTCH_STRATLEAFSIMPLE and SCOTCH_STRATSEPASIMPLE to make
826 // sure leaf nodes do not have any reordering applied to them.
827 std::string strat_str =
828 "c{rat=0.7,cpr=n{sep=/(<TSTS>)?m{rat=0.7,vert=100,low="
829 "h{pass=10},asc=b{width=3,bnd=f{bal=<BBAL>},"
830 "org=(|h{pass=10})f{bal=<BBAL>}}}<SEPA>;,"
831 "ole=<OLEA>,ose=<OSEP>},unc=n{sep=/(<TSTS>)?m{rat=0.7,"
832 "vert=100,low=h{pass=10},asc=b{width=3,bnd=f{bal=<BBAL>},"
833 "org=(|h{pass=10})f{bal=<BBAL>}}}<SEPA>;"
834 ",ole=<OLEA>,ose=<OSEP>}}";
835
836 // Replace flags in the string with appropriate values.
837 boost::replace_all(strat_str, "<SEPA>",
838 "|m{rat=0.7,vert=100,low=h{pass=10},"
839 "asc=b{width=3,bnd=f{bal=<BBAL>},"
840 "org=(|h{pass=10})f{bal=<BBAL>}}}");
841 boost::replace_all(strat_str, "<OSEP>", "s");
842 boost::replace_all(strat_str, "<OLEA>", "s");
843 boost::replace_all(strat_str, "<BBAL>", "0.1");
844 boost::replace_all(strat_str, "<TSTS>",
845 "vert>" + std::to_string(mdswitch));
846
847 // Set up the re-ordering strategy.
848 SCOTCH_Strat strat;
849 SCOTCH_CALL(SCOTCH_stratInit, (&strat));
850 SCOTCH_CALL(SCOTCH_stratGraphOrder, (&strat, strat_str.c_str()));
851
852 // As output, Scotch will give us the total number of 'blocks'
853 // (i.e. the separators and all of the leaves), the separator
854 // tree as a mapping of block to parent block, and the range of
855 // indices that is contained within each block. Reordering of
856 // the vertices goes from largest index (at the top level) to
857 // smallest (at the bottom level). The precise ordering is given
858 // in the Scotch user guide.
859 //
860 // Note that we pass in iperm into the 'permtab' field of
861 // graphOrder and 'perm' into the 'peritab' field; this is
862 // because our definition of the permutation is the opposite of
863 // what's defined in Scotch (this is leftover from a previous
864 // implementation that used Metis).
865 Array<OneD, int> treetab(nNonPartition);
866 Array<OneD, int> rangtab(nNonPartition + 1);
867 int cblknbr = 0;
868 SCOTCH_CALL(SCOTCH_graphOrder,
869 (scGraph, &strat, &iperm_tmp[0], &perm_tmp[0], &cblknbr,
870 &rangtab[0], &treetab[0]));
871
872 // We're now done with Scotch: clean up the created structures.
873 SCOTCH_graphExit(scGraph);
874 SCOTCH_stratExit(&strat);
875
876 //
877 // Step 3: create a MultiLevelBisectedGraph by reading the
878 // separator tree we obtained from Scotch.
879 //
880
881 // Setup root block, which lies at the end of the blocks
882 // described in treetab[].
883 std::vector<MultiLevelBisectedGraphSharedPtr> graphs(cblknbr);
884
885 // The strategy now is to traverse backwards over the blocks
886 // described in treetab to set up the levels of the top-down
887 // graph. rangtab allows us to calculate how many degrees of
888 // freedom lie in the separator.
889 for (i = cblknbr - 1; i >= 0; --i)
890 {
891 // Set up this block.
892 graphs[i] =
894 rangtab[i + 1] - rangtab[i]);
895
896 // If we're a root block (treetab[i] == -1) we don't need to
897 // do anything, just move onto the next block.
898 if (treetab[i] == -1)
899 {
900 continue;
901 }
902
903 // Now use treetab[i] to figure out the parent block. We
904 // have to be a bit careful in setting left/right daughters
905 // here. The left daughter's degrees of freedom are ordered
906 // _first_ in the iperm/perm arrays returned from Scotch,
907 // but if there is both a left and right daughter, we'll
908 // come across the right daughter first because the
909 // separators are being traversed backwards. We'll therefore
910 // insert this at the beginning of the daughter graphs
911 // vector.
912 MultiLevelBisectedGraphSharedPtr tmp = graphs[treetab[i]];
913 std::vector<MultiLevelBisectedGraphSharedPtr> &daughters =
914 tmp->GetDaughterGraphs();
915 daughters.insert(daughters.begin(), graphs[i]);
916 }
917
918 // Change permutations from Scotch to account for initial offset
919 // in case we had partition vertices.
920 for (i = 0; i < nGraphVerts; ++i)
921 {
922 if (partVerts.count(i) == 0)
923 {
924 iperm[i] = iperm_tmp[initial_perm[i]];
925 perm[iperm[i]] = i;
926 }
927 }
928
929 auto it = partVerts.begin(), it2 = partVerts.end();
930 for (i = nNonPartition; it != it2; ++it, ++i)
931 {
932 perm[i] = *it;
933 iperm[*it] = i;
934 }
935
936 for (i = 0; i < nGraphVerts; ++i)
937 {
938 ASSERTL1(perm[iperm[i]] == i, "Perm error " + std::to_string(i));
939 }
940
941 // If we were passed a graph with disconnected regions, we need
942 // to create a bisected graph with the appropriate roots.
943 std::vector<int> rootBlocks;
944 for (i = 0; i < cblknbr; ++i)
945 {
946 if (treetab[i] == -1)
947 {
948 rootBlocks.push_back(i);
949 }
950 }
951
953 if (rootBlocks.size() == 1)
954 {
955 root = graphs[rootBlocks[0]];
956 }
957 else
958 {
960
961 for (int i = 0; i < rootBlocks.size(); ++i)
962 {
963 root->GetDaughterGraphs().push_back(graphs[rootBlocks[i]]);
964 }
965 }
966
967 // Check that our degree of freedom count in the constructed
968 // graph is the same as the number of degrees of freedom that we
969 // were given in the function input.
970 ASSERTL0(root->GetTotDofs() == nNonPartition,
971 "Error in constructing Scotch graph for multi-level"
972 " static condensation.");
973
974 //
975 // Step 4: Set up the bottom-up graph from the top-down graph,
976 // and reorder the permutation from Scotch.
977 //
978 substructgraph =
980 root, nPartition, true);
981
982 // Important: we cannot simply use the ordering given by Scotch
983 // as it does not order the different blocks as we would like
984 // it. Therefore, we use following command to re-order them
985 // again in the context of the bottom-up substructuring. As a
986 // result, we will now obtain an ordering where the interior
987 // degrees of freedom of the first (=bottom) level will be
988 // ordered last (block by block ofcoarse). The interior degrees
989 // of freedom of the second level will be ordered second to
990 // last, etc ... As a result, the boundary degrees of freedom of
991 // the last level (i.e. the dofs that will have to solved
992 // non-recursively) will be ordered first (after the Dirichlet
993 // Dofs that is). (this way, we actually follow the same idea
994 // and convention in the standard (non-multi-level) static
995 // condensation approach).
996 substructgraph->UpdateBottomUpReordering(perm, iperm);
997 }
998 else
999 {
1000 // This is the very special case of a graph without any
1001 // connectivity i.e. a collection of vertices without any edges
1002 for (int i = 0; i < nGraphVerts; i++)
1003 {
1004 perm[i] = i;
1005 iperm[i] = i;
1006 }
1007 substructgraph =
1009 nGraphVerts);
1010 }
1011#endif
1012}
1013
1014void NoReordering(const BoostGraph &graph, Array<OneD, int> &perm,
1015 Array<OneD, int> &iperm)
1016{
1017 int nGraphVerts = boost::num_vertices(graph);
1018
1019 ASSERTL1(perm.size() >= nGraphVerts && iperm.size() >= nGraphVerts,
1020 "Non-matching dimensions");
1021
1022 for (int i = 0; i < nGraphVerts; i++)
1023 {
1024 perm[i] = i;
1025 iperm[i] = i;
1026 }
1027}
1028} // namespace MultiRegions
1029} // 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