Nektar++
Loading...
Searching...
No Matches
AssemblyMapCG.cpp
Go to the documentation of this file.
1///////////////////////////////////////////////////////////////////////////////
2//
3// File: AssemblyMapCG.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: C0-continuous Local to Global mapping routines, base class
32//
33///////////////////////////////////////////////////////////////////////////////
34
42
43#include <boost/config.hpp>
44#include <boost/graph/adjacency_list.hpp>
45#include <boost/graph/bandwidth.hpp>
46#include <boost/graph/cuthill_mckee_ordering.hpp>
47#include <boost/graph/properties.hpp>
48
49using namespace std;
50
52{
53/**
54 * @class AssemblyMapCG
55 * Mappings are created for three possible global solution types:
56 * - Direct full matrix
57 * - Direct static condensation
58 * - Direct multi-level static condensation
59 * In the latter case, mappings are created recursively for the
60 * different levels of static condensation.
61 *
62 * These mappings are used by GlobalLinSys to generate the global
63 * system.
64 */
65
66/**
67 *
68 */
71 const LibUtilities::CommSharedPtr &comm, const std::string variable)
72 : AssemblyMap(pSession, comm, variable)
73{
74 pSession->LoadParameter("MaxStaticCondLevel", m_maxStaticCondLevel, 100);
75}
76
78 const ExpList &locExp, const BndCondExp &bndCondExp,
79 const Array<OneD, const BndCond> &bndConditions,
80 const bool checkIfSystemSingular, const PeriodicMap &periodicVerts,
81 const PeriodicMap &periodicEdges, const PeriodicMap &periodicFaces,
82 DofGraph &graph, BottomUpSubStructuredGraphSharedPtr &bottomUpGraph,
83 set<int> &extraDirVerts, set<int> &extraDirEdges,
84 int &firstNonDirGraphVertId, int &nExtraDirichlet, int mdswitch)
85{
86 int graphVertId = 0;
87 int vMaxVertId = -1;
88 int i, j, k, l, cnt;
89 int meshVertId, meshEdgeId, meshFaceId;
90 int meshVertId2, meshEdgeId2;
91
93 const LocalRegions::ExpansionVector &locExpVector = *(locExp.GetExp());
94 LibUtilities::CommSharedPtr vRowComm = m_comm->GetRowComm();
95
97 m_systemSingular = checkIfSystemSingular;
98
99 for (i = 0; i < bndCondExp.size(); i++)
100 {
101 m_numLocalBndCondCoeffs += bndCondExp[i]->GetNcoeffs();
102
103 if (bndConditions[0][i]->GetBoundaryConditionType() ==
105 {
106 continue;
107 }
108
109 // Check to see if any value on boundary has Dirichlet
110 // value. note this is a vector to manage coupled
111 // solver but for scalar will just be a vector of size 11
112 cnt = 0;
113 for (k = 0; k < bndConditions.size(); ++k)
114 {
115 if (bndConditions[k][i]->GetBoundaryConditionType() ==
117 {
118 cnt++;
119 }
120 if (bndConditions[k][i]->GetBoundaryConditionType() !=
122 {
123 m_systemSingular = false;
124 }
125 }
126
127 // Find the maximum boundary vertex ID on this process. This is
128 // used later to pin a vertex if the system is singular.
129 for (j = 0; j < bndCondExp[i]->GetNumElmts(); ++j)
130 {
131 bndExp = bndCondExp[i]->GetExp(j)->as<LocalRegions::Expansion>();
132 for (k = 0; k < bndExp->GetNverts(); ++k)
133 {
134 if (vMaxVertId < bndExp->GetGeom()->GetVid(k))
135 {
136 vMaxVertId = bndExp->GetGeom()->GetVid(k);
137 }
138 }
139 }
140
141 // If all boundaries are Dirichlet fill in graph
142 if (cnt == bndConditions.size())
143 {
144 for (j = 0; j < bndCondExp[i]->GetNumElmts(); j++)
145 {
146 bndExp = bndCondExp[i]->GetExp(j);
147
148 for (k = 0; k < bndExp->GetNverts(); k++)
149 {
150 meshVertId = bndExp->GetGeom()->GetVid(k);
151 if (graph[0].count(meshVertId) == 0)
152 {
153 graph[0][meshVertId] = graphVertId++;
154 }
155 }
156
157 const int bndDim = bndExp->GetNumBases();
158 if (bndDim > 1)
159 {
160 for (k = 0; k < bndExp->GetNtraces(); k++)
161 {
162 meshEdgeId = bndExp->GetGeom()->GetEid(k);
163 if (graph[1].count(meshEdgeId) == 0)
164 {
165 graph[1][meshEdgeId] = graphVertId++;
166 }
167 }
168 }
169
170 // Possibility of a face in 3D or edge in 2D
171 meshFaceId = bndExp->GetGeom()->GetGlobalID();
172 if (graph[bndDim].count(meshFaceId) == 0)
173 {
174 graph[bndDim][meshFaceId] = graphVertId++;
175 }
176 m_numLocalDirBndCoeffs += bndExp->GetNcoeffs();
177 }
178 }
179 }
180
181 // Number of dirichlet edges and faces (not considering periodic
182 // BCs)
183 m_numDirEdges = graph[1].size();
184 m_numDirFaces = graph[2].size();
185
186 /*
187 * The purpose of this routine is to deal with those degrees of
188 * freedom that are Dirichlet, but do not have a local Dirichlet
189 * boundary condition expansion set.
190 *
191 * For example, in 2D, consider a triangulation of a square into two
192 * triangles. Now imagine one edge of the square is Dirichlet and
193 * the problem is run on two processors. On one processor, one
194 * triangle vertex is Dirichlet, but doesn't know this since the
195 * Dirichlet composite lives on the other processor.
196 *
197 * When the global linear system is solved therefore, there is an
198 * inconsistency that at best leads to an inaccurate answer or a
199 * divergence of the system.
200 *
201 * This routine identifies such cases for 2D, and also for 3D where
202 * e.g. edges may have the same problem (consider an extrusion of
203 * the case above, for example).
204 */
205
206 // Collate information on Dirichlet vertices from all processes
207 int n = vRowComm->GetSize();
208 int p = vRowComm->GetRank();
209
210 if (vRowComm->IsSerial())
211 {
212 // for FieldConvert Comm this is true and it resets
213 // parallel processing back to serial case
214 n = 1;
215 p = 0;
216 }
217 // At this point, graph only contains information from Dirichlet
218 // boundaries. Therefore make a global list of the vert and edge
219 // information on all processors.
220 Array<OneD, int> vertcounts(n, 0);
221 Array<OneD, int> vertoffsets(n, 0);
222 Array<OneD, int> edgecounts(n, 0);
223 Array<OneD, int> edgeoffsets(n, 0);
224 vertcounts[p] = graph[0].size();
225 edgecounts[p] = graph[1].size();
226 vRowComm->AllReduce(vertcounts, LibUtilities::ReduceSum);
227 vRowComm->AllReduce(edgecounts, LibUtilities::ReduceSum);
228
229 for (i = 1; i < n; ++i)
230 {
231 vertoffsets[i] = vertoffsets[i - 1] + vertcounts[i - 1];
232 edgeoffsets[i] = edgeoffsets[i - 1] + edgecounts[i - 1];
233 }
234
235 int nTotVerts = Vmath::Vsum(n, vertcounts, 1);
236 int nTotEdges = Vmath::Vsum(n, edgecounts, 1);
237
238 Array<OneD, int> vertlist(nTotVerts, 0);
239 Array<OneD, int> edgelist(nTotEdges, 0);
240
241 // construct list of global ids of global vertices
242 i = 0;
243 for (auto &it : graph[0])
244 {
245 vertlist[vertoffsets[p] + i++] = it.first;
246 }
247
248 // construct list of global ids of global edges
249 i = 0;
250 for (auto &it : graph[1])
251 {
252 edgelist[edgeoffsets[p] + i++] = it.first;
253 }
254 vRowComm->AllReduce(vertlist, LibUtilities::ReduceSum);
255 vRowComm->AllReduce(edgelist, LibUtilities::ReduceSum);
256
257 // Now we have a list of all Dirichlet vertices and edges on all
258 // processors.
259 nExtraDirichlet = 0;
260 map<int, int> extraDirVertIds, extraDirEdgeIds;
261
262 // Ensure Dirchlet vertices are consistently recorded between
263 // processes (e.g. Dirichlet region meets Neumann region across a
264 // partition boundary requires vertex on partition to be Dirichlet).
265 //
266 // To do this we look over all elements and vertices in local
267 // partition and see if they match the values stored in the vertlist
268 // from other processors and if so record the meshVertId/meshEdgeId
269 // and the processor it comes from.
270 for (i = 0; i < n; ++i)
271 {
272 if (i == p)
273 {
274 continue;
275 }
276
277 for (j = 0; j < locExpVector.size(); j++)
278 {
279 exp = locExpVector[j];
280
281 for (k = 0; k < exp->GetNverts(); k++)
282 {
283 meshVertId = exp->GetGeom()->GetVid(k);
284 if (graph[0].count(meshVertId) == 0)
285 {
286 for (l = 0; l < vertcounts[i]; ++l)
287 {
288 if (vertlist[vertoffsets[i] + l] == meshVertId)
289 {
290 extraDirVertIds[meshVertId] = i;
291 graph[0][meshVertId] = graphVertId++;
292 nExtraDirichlet++;
293 }
294 }
295 }
296 }
297
298 for (k = 0; k < exp->GetGeom()->GetNumEdges(); k++)
299 {
300 meshEdgeId = exp->GetGeom()->GetEid(k);
301 if (graph[1].count(meshEdgeId) == 0)
302 {
303 for (l = 0; l < edgecounts[i]; ++l)
304 {
305 if (edgelist[edgeoffsets[i] + l] == meshEdgeId)
306 {
307 extraDirEdgeIds[meshEdgeId] = i;
308 graph[1][meshEdgeId] = graphVertId++;
309 if (exp->GetGeom()->GetNumFaces())
310 {
311 nExtraDirichlet +=
313 ->GetEdgeNcoeffs(k) -
314 2;
315 }
316 else
317 {
318 nExtraDirichlet += exp->GetTraceNcoeffs(k) - 2;
319 }
320 }
321 }
322 }
323 }
324 }
325 }
326
327 // Low Energy preconditioner needs to know how many extra Dirichlet
328 // edges are on this process so store map in array.
329 m_extraDirEdges = Array<OneD, int>(extraDirEdgeIds.size(), -1);
330 i = 0;
331 for (auto &it : extraDirEdgeIds)
332 {
333 meshEdgeId = it.first;
334 m_extraDirEdges[i++] = meshEdgeId;
335 }
336
337 // Now we have a list of all vertices and edges that are Dirichlet
338 // and not defined on the local partition as well as which processor
339 // they are stored on.
340 //
341 // Make a full list of all such entities on all processors and which
342 // processor they belong to.
343 for (i = 0; i < n; ++i)
344 {
345 vertcounts[i] = 0;
346 vertoffsets[i] = 0;
347 edgecounts[i] = 0;
348 edgeoffsets[i] = 0;
349 }
350
351 vertcounts[p] = extraDirVertIds.size();
352 edgecounts[p] = extraDirEdgeIds.size();
353 vRowComm->AllReduce(vertcounts, LibUtilities::ReduceSum);
354 vRowComm->AllReduce(edgecounts, LibUtilities::ReduceSum);
355 nTotVerts = Vmath::Vsum(n, vertcounts, 1);
356 nTotEdges = Vmath::Vsum(n, edgecounts, 1);
357
358 vertoffsets[0] = edgeoffsets[0] = 0;
359
360 for (i = 1; i < n; ++i)
361 {
362 vertoffsets[i] = vertoffsets[i - 1] + vertcounts[i - 1];
363 edgeoffsets[i] = edgeoffsets[i - 1] + edgecounts[i - 1];
364 }
365
366 Array<OneD, int> vertids(nTotVerts, 0);
367 Array<OneD, int> edgeids(nTotEdges, 0);
368 Array<OneD, int> vertprocs(nTotVerts, 0);
369 Array<OneD, int> edgeprocs(nTotEdges, 0);
370
371 i = 0;
372 for (auto &it : extraDirVertIds)
373 {
374 vertids[vertoffsets[p] + i] = it.first;
375 vertprocs[vertoffsets[p] + i] = it.second;
376 ++i;
377 }
378
379 i = 0;
380 for (auto &it : extraDirEdgeIds)
381 {
382 edgeids[edgeoffsets[p] + i] = it.first;
383 edgeprocs[edgeoffsets[p] + i] = it.second;
384 ++i;
385 }
386
387 vRowComm->AllReduce(vertids, LibUtilities::ReduceSum);
388 vRowComm->AllReduce(vertprocs, LibUtilities::ReduceSum);
389 vRowComm->AllReduce(edgeids, LibUtilities::ReduceSum);
390 vRowComm->AllReduce(edgeprocs, LibUtilities::ReduceSum);
391
392 // Set up list of vertices that need to be shared to other
393 // partitions
394 for (i = 0; i < nTotVerts; ++i)
395 {
396 if (p == vertprocs[i]) // rank = vertproc[i]
397 {
398 extraDirVerts.insert(vertids[i]);
399 }
400 }
401
402 // Set up list of edges that need to be shared to other partitions
403 for (i = 0; i < nTotEdges; ++i)
404 {
405 if (p == edgeprocs[i]) // rank = vertproc[i]
406 {
407 extraDirEdges.insert(edgeids[i]);
408 }
409 }
410
411 // Check between processes if the whole system is singular
412 int s = m_systemSingular ? 1 : 0;
413 vRowComm->AllReduce(s, LibUtilities::ReduceMin);
414 m_systemSingular = s == 1 ? true : false;
415
416 // Find the minimum boundary vertex ID on each process
417 Array<OneD, int> bcminvertid(n, 0);
418 bcminvertid[p] = vMaxVertId;
419 vRowComm->AllReduce(bcminvertid, LibUtilities::ReduceMax);
420
421 // Find the process rank with the minimum boundary vertex ID
422 int maxIdx = Vmath::Imax(n, bcminvertid, 1);
423
424 // If the system is singular, the process with the maximum
425 // number of BCs will set a Dirichlet vertex to make
426 // system non-singular. Note: we find the process with
427 // maximum boundary regions to ensure we do not try to set
428 // a Dirichlet vertex on a partition with no intersection
429 // with the boundary.
430 meshVertId = 0;
431
432 if (m_systemSingular && checkIfSystemSingular && maxIdx == p)
433 {
434 if (m_session->DefinesParameter("SingularVertex"))
435 {
436 m_session->LoadParameter("SingularVertex", meshVertId);
437 }
438 else if (vMaxVertId == -1)
439 {
440 // All boundaries are periodic.
441 meshVertId = locExpVector[0]->GetGeom()->GetVid(0);
442 }
443 else
444 {
445 // Set pinned vertex to that with minimum vertex ID to
446 // ensure consistency in parallel.
447 meshVertId = bcminvertid[p];
448 }
449
450 if (graph[0].count(meshVertId) == 0)
451 {
452 graph[0][meshVertId] = graphVertId++;
453 }
454 }
455
456 vRowComm->AllReduce(meshVertId, LibUtilities::ReduceSum);
457
458 // When running in parallel, we need to ensure that the singular
459 // mesh vertex is communicated to any periodic vertices, otherwise
460 // the system may diverge.
461 if (m_systemSingular && checkIfSystemSingular)
462 {
463 // Firstly, we check that no other processors have this
464 // vertex. If they do, then we mark the vertex as also being
465 // Dirichlet.
466 if (maxIdx != p)
467 {
468 for (i = 0; i < locExpVector.size(); ++i)
469 {
470 for (j = 0; j < locExpVector[i]->GetNverts(); ++j)
471 {
472 if (locExpVector[i]->GetGeom()->GetVid(j) != meshVertId)
473 {
474 continue;
475 }
476
477 if (graph[0].count(meshVertId) == 0)
478 {
479 graph[0][meshVertId] = graphVertId++;
480 }
481 }
482 }
483 }
484
485 // In the case that meshVertId is periodic with other vertices,
486 // this process and all other processes need to make sure that
487 // the periodic vertices are also marked as Dirichlet.
488 int gId;
489
490 // At least one process (maxBCidx) will have already associated
491 // a graphVertId with meshVertId. Others won't even have any of
492 // the vertices. The logic below is designed to handle both
493 // cases.
494 if (graph[0].count(meshVertId) == 0)
495 {
496 gId = -1;
497 }
498 else
499 {
500 gId = graph[0][meshVertId];
501 }
502
503 for (auto &pIt : periodicVerts)
504 {
505 // Either the vertex is local to this processor (in which
506 // case it will be in the pIt.first position) or else
507 // meshVertId might be contained within another processor's
508 // vertex list. The if statement below covers both cases. If
509 // we find it, set as Dirichlet with the vertex id gId.
510 if (pIt.first == meshVertId)
511 {
512 gId = gId < 0 ? graphVertId++ : gId;
513 graph[0][meshVertId] = gId;
514
515 for (i = 0; i < pIt.second.size(); ++i)
516 {
517 if (pIt.second[i].isLocal)
518 {
519 graph[0][pIt.second[i].id] = graph[0][meshVertId];
520 }
521 }
522 }
523 else
524 {
525 bool found = false;
526 for (i = 0; i < pIt.second.size(); ++i)
527 {
528 if (pIt.second[i].id == meshVertId)
529 {
530 found = true;
531 break;
532 }
533 }
534
535 if (found)
536 {
537 gId = gId < 0 ? graphVertId++ : gId;
538 graph[0][pIt.first] = gId;
539
540 for (i = 0; i < pIt.second.size(); ++i)
541 {
542 if (pIt.second[i].isLocal)
543 {
544 graph[0][pIt.second[i].id] = graph[0][pIt.first];
545 }
546 }
547 }
548 }
549 }
550 }
551
552 // Add extra dirichlet boundary conditions to count.
553 m_numLocalDirBndCoeffs += nExtraDirichlet;
554 firstNonDirGraphVertId = graphVertId;
555
556 typedef boost::adjacency_list<boost::setS, boost::vecS, boost::undirectedS>
557 BoostGraph;
558 BoostGraph boostGraphObj;
559
560 vector<map<int, int>> tempGraph(3);
561 map<int, int> vwgts_map;
562 Array<OneD, int> localVerts;
563 Array<OneD, int> localEdges;
564 Array<OneD, int> localFaces;
565
566 int tempGraphVertId = 0;
567 int localVertOffset = 0;
568 int localEdgeOffset = 0;
569 int localFaceOffset = 0;
570 int nTotalVerts = 0;
571 int nTotalEdges = 0;
572 int nTotalFaces = 0;
573 int nVerts;
574 int nEdges;
575 int nFaces;
576 int vertCnt;
577 int edgeCnt;
578 int faceCnt;
579
586
587 map<int, int> EdgeSize;
588 map<int, int> FaceSize;
589
590 /// - Count verts, edges, face and add up edges and face sizes
591 for (i = 0; i < locExpVector.size(); ++i)
592 {
593 exp = locExpVector[i];
594 nEdges = exp->GetGeom()->GetNumEdges();
595 nFaces = exp->GetGeom()->GetNumFaces();
596
597 nTotalVerts += exp->GetNverts();
598 nTotalEdges += nEdges;
599 nTotalFaces += nFaces;
600
601 for (j = 0; j < nEdges; ++j)
602 {
603 meshEdgeId = exp->GetGeom()->GetEid(j);
604 int nEdgeInt;
605
606 if (nFaces)
607 {
608 nEdgeInt =
609 exp->as<LocalRegions::Expansion3D>()->GetEdgeNcoeffs(j) - 2;
610 }
611 else
612 {
613 nEdgeInt = exp->GetTraceNcoeffs(j) - 2;
614 }
615
616 if (EdgeSize.count(meshEdgeId) > 0)
617 {
618 EdgeSize[meshEdgeId] = min(EdgeSize[meshEdgeId], nEdgeInt);
619 }
620 else
621 {
622 EdgeSize[meshEdgeId] = nEdgeInt;
623 }
624 }
625
626 faceCnt = 0;
627 for (j = 0; j < nFaces; ++j)
628 {
629 meshFaceId = exp->GetGeom()->GetFid(j);
630 if (FaceSize.count(meshFaceId) > 0)
631 {
632 FaceSize[meshFaceId] =
633 min(FaceSize[meshFaceId], exp->GetTraceIntNcoeffs(j));
634 }
635 else
636 {
637 FaceSize[meshFaceId] = exp->GetTraceIntNcoeffs(j);
638 }
639 FaceSize[meshFaceId] = exp->GetTraceIntNcoeffs(j);
640 }
641 }
642
643 /// - Periodic vertices
644 for (auto &pIt : periodicVerts)
645 {
646 meshVertId = pIt.first;
647
648 // This periodic vertex is joined to a Dirichlet condition.
649 if (graph[0].count(pIt.first) != 0)
650 {
651 for (i = 0; i < pIt.second.size(); ++i)
652 {
653 meshVertId2 = pIt.second[i].id;
654 if (graph[0].count(meshVertId2) == 0 && pIt.second[i].isLocal)
655 {
656 graph[0][meshVertId2] = graph[0][meshVertId];
657 }
658 }
659 continue;
660 }
661
662 // One of the attached vertices is Dirichlet.
663 bool isDirichlet = false;
664 for (i = 0; i < pIt.second.size(); ++i)
665 {
666 if (!pIt.second[i].isLocal)
667 {
668 continue;
669 }
670
671 meshVertId2 = pIt.second[i].id;
672 if (graph[0].count(meshVertId2) > 0)
673 {
674 isDirichlet = true;
675 break;
676 }
677 }
678
679 if (isDirichlet)
680 {
681 graph[0][meshVertId] = graph[0][pIt.second[i].id];
682
683 for (j = 0; j < pIt.second.size(); ++j)
684 {
685 meshVertId2 = pIt.second[i].id;
686 if (j == i || !pIt.second[j].isLocal ||
687 graph[0].count(meshVertId2) > 0)
688 {
689 continue;
690 }
691
692 graph[0][meshVertId2] = graph[0][pIt.second[i].id];
693 }
694
695 continue;
696 }
697
698 // Otherwise, see if a vertex ID has already been set.
699 for (i = 0; i < pIt.second.size(); ++i)
700 {
701 if (!pIt.second[i].isLocal)
702 {
703 continue;
704 }
705
706 if (tempGraph[0].count(pIt.second[i].id) > 0)
707 {
708 break;
709 }
710 }
711
712 if (i == pIt.second.size())
713 {
714 boost::add_vertex(boostGraphObj);
715 tempGraph[0][meshVertId] = tempGraphVertId++;
717 }
718 else
719 {
720 tempGraph[0][meshVertId] = tempGraph[0][pIt.second[i].id];
721 }
722 }
723
724 // Store the temporary graph vertex id's of all element edges and
725 // vertices in these 3 arrays below
726 localVerts = Array<OneD, int>(nTotalVerts, -1);
727 localEdges = Array<OneD, int>(nTotalEdges, -1);
728 localFaces = Array<OneD, int>(nTotalFaces, -1);
729
730 // Set up vertex numbering
731 for (i = 0; i < locExpVector.size(); ++i)
732 {
733 exp = locExpVector[i];
734 vertCnt = 0;
735 nVerts = exp->GetNverts();
736 for (j = 0; j < nVerts; ++j)
737 {
738 meshVertId = exp->GetGeom()->GetVid(j);
739 if (graph[0].count(meshVertId) == 0)
740 {
741 if (tempGraph[0].count(meshVertId) == 0)
742 {
743 boost::add_vertex(boostGraphObj);
744 tempGraph[0][meshVertId] = tempGraphVertId++;
746 }
747 localVerts[localVertOffset + vertCnt++] =
748 tempGraph[0][meshVertId];
749 vwgts_map[tempGraph[0][meshVertId]] = 1;
750 }
751 }
752
753 localVertOffset += nVerts;
754 }
755
756 /// - Periodic edges
757 for (auto &pIt : periodicEdges)
758 {
759 meshEdgeId = pIt.first;
760
761 // This periodic edge is joined to a Dirichlet condition.
762 if (graph[1].count(pIt.first) != 0)
763 {
764 for (i = 0; i < pIt.second.size(); ++i)
765 {
766 meshEdgeId2 = pIt.second[i].id;
767 if (graph[1].count(meshEdgeId2) == 0 && pIt.second[i].isLocal)
768 {
769 graph[1][meshEdgeId2] = graph[1][meshEdgeId];
770 }
771 }
772 continue;
773 }
774
775 // One of the attached edges is Dirichlet.
776 bool isDirichlet = false;
777 for (i = 0; i < pIt.second.size(); ++i)
778 {
779 if (!pIt.second[i].isLocal)
780 {
781 continue;
782 }
783
784 meshEdgeId2 = pIt.second[i].id;
785 if (graph[1].count(meshEdgeId2) > 0)
786 {
787 isDirichlet = true;
788 break;
789 }
790 }
791
792 if (isDirichlet)
793 {
794 graph[1][meshEdgeId] = graph[1][pIt.second[i].id];
795
796 for (j = 0; j < pIt.second.size(); ++j)
797 {
798 meshEdgeId2 = pIt.second[i].id;
799 if (j == i || !pIt.second[j].isLocal ||
800 graph[1].count(meshEdgeId2) > 0)
801 {
802 continue;
803 }
804
805 graph[1][meshEdgeId2] = graph[1][pIt.second[i].id];
806 }
807
808 continue;
809 }
810
811 // Otherwise, see if a edge ID has already been set.
812 for (i = 0; i < pIt.second.size(); ++i)
813 {
814 if (!pIt.second[i].isLocal)
815 {
816 continue;
817 }
818
819 if (tempGraph[1].count(pIt.second[i].id) > 0)
820 {
821 break;
822 }
823 }
824
825 if (i == pIt.second.size())
826 {
827 boost::add_vertex(boostGraphObj);
828 tempGraph[1][meshEdgeId] = tempGraphVertId++;
829 m_numNonDirEdgeModes += EdgeSize[meshEdgeId];
831 }
832 else
833 {
834 tempGraph[1][meshEdgeId] = tempGraph[1][pIt.second[i].id];
835 }
836 }
837
838 int nEdgeIntCoeffs, nFaceIntCoeffs;
839
840 // Set up edge numbering
841 for (i = 0; i < locExpVector.size(); ++i)
842 {
843 exp = locExpVector[i];
844 edgeCnt = 0;
845 nEdges = exp->GetGeom()->GetNumEdges();
846
847 for (j = 0; j < nEdges; ++j)
848 {
849 meshEdgeId = exp->GetGeom()->GetEid(j);
850 nEdgeIntCoeffs = EdgeSize[meshEdgeId];
851 if (graph[1].count(meshEdgeId) == 0)
852 {
853 if (tempGraph[1].count(meshEdgeId) == 0)
854 {
855 boost::add_vertex(boostGraphObj);
856 tempGraph[1][meshEdgeId] = tempGraphVertId++;
857 m_numNonDirEdgeModes += nEdgeIntCoeffs;
858
860 }
861 localEdges[localEdgeOffset + edgeCnt++] =
862 tempGraph[1][meshEdgeId];
863 vwgts_map[tempGraph[1][meshEdgeId]] = nEdgeIntCoeffs;
864 }
865 }
866
867 localEdgeOffset += nEdges;
868 }
869
870 /// - Periodic faces
871 for (auto &pIt : periodicFaces)
872 {
873 if (!pIt.second[0].isLocal)
874 {
875 // The face mapped to is on another process.
876 meshFaceId = pIt.first;
877 ASSERTL0(graph[2].count(meshFaceId) == 0,
878 "This periodic boundary edge has been specified before");
879 boost::add_vertex(boostGraphObj);
880 tempGraph[2][meshFaceId] = tempGraphVertId++;
881 nFaceIntCoeffs = FaceSize[meshFaceId];
882 m_numNonDirFaceModes += nFaceIntCoeffs;
884 }
885 else if (pIt.first < pIt.second[0].id)
886 {
887 ASSERTL0(graph[2].count(pIt.first) == 0,
888 "This periodic boundary face has been specified before");
889 ASSERTL0(graph[2].count(pIt.second[0].id) == 0,
890 "This periodic boundary face has been specified before");
891
892 boost::add_vertex(boostGraphObj);
893 tempGraph[2][pIt.first] = tempGraphVertId;
894 tempGraph[2][pIt.second[0].id] = tempGraphVertId++;
895 nFaceIntCoeffs = FaceSize[pIt.first];
896 m_numNonDirFaceModes += nFaceIntCoeffs;
898 }
899 }
900
901 // setup face numbering
902 for (i = 0; i < locExpVector.size(); ++i)
903 {
904 exp = locExpVector[i];
905 nFaces = exp->GetGeom()->GetNumFaces();
906 faceCnt = 0;
907 for (j = 0; j < nFaces; ++j)
908 {
909 nFaceIntCoeffs = exp->GetTraceIntNcoeffs(j);
910 meshFaceId = exp->GetGeom()->GetFid(j);
911 if (graph[2].count(meshFaceId) == 0)
912 {
913 if (tempGraph[2].count(meshFaceId) == 0)
914 {
915 boost::add_vertex(boostGraphObj);
916 tempGraph[2][meshFaceId] = tempGraphVertId++;
917 m_numNonDirFaceModes += nFaceIntCoeffs;
918
920 }
921 localFaces[localFaceOffset + faceCnt++] =
922 tempGraph[2][meshFaceId];
923 vwgts_map[tempGraph[2][meshFaceId]] = nFaceIntCoeffs;
924 }
925 }
926 m_numLocalBndCoeffs += exp->NumBndryCoeffs();
927
928 localFaceOffset += nFaces;
929 }
930
931 localVertOffset = 0;
932 localEdgeOffset = 0;
933 localFaceOffset = 0;
934 for (i = 0; i < locExpVector.size(); ++i)
935 {
936 exp = locExpVector[i];
937 nVerts = exp->GetNverts();
938 nEdges = exp->GetGeom()->GetNumEdges();
939 nFaces = exp->GetGeom()->GetNumFaces();
940
941 // Now loop over all local faces, edges and vertices of this
942 // element and define that all other faces, edges and verices of
943 // this element are adjacent to them.
944
945 // Vertices
946 for (j = 0; j < nVerts; j++)
947 {
948 if (localVerts[j + localVertOffset] == -1)
949 {
950 continue;
951 }
952 // associate to other vertices
953 for (k = j + 1; k < nVerts; k++)
954 {
955 if (localVerts[k + localVertOffset] == -1)
956 {
957 continue;
958 }
959
960 boost::add_edge((size_t)localVerts[j + localVertOffset],
961 (size_t)localVerts[k + localVertOffset],
962 boostGraphObj);
963 }
964 // associate to other edges
965 for (k = 0; k < nEdges; k++)
966 {
967 if (localEdges[k + localEdgeOffset] == -1)
968 {
969 continue;
970 }
971 boost::add_edge((size_t)localVerts[j + localVertOffset],
972 (size_t)localEdges[k + localEdgeOffset],
973 boostGraphObj);
974 }
975 // associate to other faces
976 for (k = 0; k < nFaces; k++)
977 {
978 if (localFaces[k + localFaceOffset] == -1)
979 {
980 continue;
981 }
982 boost::add_edge((size_t)localVerts[j + localVertOffset],
983 (size_t)localFaces[k + localFaceOffset],
984 boostGraphObj);
985 }
986 }
987
988 // Edges
989 for (j = 0; j < nEdges; j++)
990 {
991 if (localEdges[j + localEdgeOffset] == -1)
992 {
993 continue;
994 }
995 // Associate to other edges
996 for (k = j + 1; k < nEdges; k++)
997 {
998 if (localEdges[k + localEdgeOffset] == -1)
999 {
1000 continue;
1001 }
1002 boost::add_edge((size_t)localEdges[j + localEdgeOffset],
1003 (size_t)localEdges[k + localEdgeOffset],
1004 boostGraphObj);
1005 }
1006 // Associate to faces
1007 for (k = 0; k < nFaces; k++)
1008 {
1009 if (localFaces[k + localFaceOffset] == -1)
1010 {
1011 continue;
1012 }
1013 boost::add_edge((size_t)localEdges[j + localEdgeOffset],
1014 (size_t)localFaces[k + localFaceOffset],
1015 boostGraphObj);
1016 }
1017 }
1018
1019 // Faces
1020 for (j = 0; j < nFaces; j++)
1021 {
1022 if (localFaces[j + localFaceOffset] == -1)
1023 {
1024 continue;
1025 }
1026 // Associate to other faces
1027 for (k = j + 1; k < nFaces; k++)
1028 {
1029 if (localFaces[k + localFaceOffset] == -1)
1030 {
1031 continue;
1032 }
1033 boost::add_edge((size_t)localFaces[j + localFaceOffset],
1034 (size_t)localFaces[k + localFaceOffset],
1035 boostGraphObj);
1036 }
1037 }
1038
1039 localVertOffset += nVerts;
1040 localEdgeOffset += nEdges;
1041 localFaceOffset += nFaces;
1042 }
1043
1044 // Container to store vertices of the graph which correspond to
1045 // degrees of freedom along the boundary and periodic BCs.
1046 set<int> partVerts;
1047
1050 {
1051 vector<long> procVerts, procEdges, procFaces;
1052 set<int> foundVerts, foundEdges, foundFaces;
1053
1054 // Loop over element and construct the procVerts and procEdges
1055 // vectors, which store the geometry IDs of mesh vertices and
1056 // edges respectively which are local to this process.
1057 for (i = cnt = 0; i < locExpVector.size(); ++i)
1058 {
1059 int elmtid = i;
1060 exp = locExpVector[elmtid];
1061 for (j = 0; j < exp->GetNverts(); ++j)
1062 {
1063 int vid = exp->GetGeom()->GetVid(j) + 1;
1064 if (foundVerts.count(vid) == 0)
1065 {
1066 procVerts.push_back(vid);
1067 foundVerts.insert(vid);
1068 }
1069 }
1070
1071 for (j = 0; j < exp->GetGeom()->GetNumEdges(); ++j)
1072 {
1073 int eid = exp->GetGeom()->GetEid(j) + 1;
1074
1075 if (foundEdges.count(eid) == 0)
1076 {
1077 procEdges.push_back(eid);
1078 foundEdges.insert(eid);
1079 }
1080 }
1081
1082 for (j = 0; j < exp->GetGeom()->GetNumFaces(); ++j)
1083 {
1084 int fid = exp->GetGeom()->GetFid(j) + 1;
1085
1086 if (foundFaces.count(fid) == 0)
1087 {
1088 procFaces.push_back(fid);
1089 foundFaces.insert(fid);
1090 }
1091 }
1092 }
1093
1094 int unique_verts = foundVerts.size();
1095 int unique_edges = foundEdges.size();
1096 int unique_faces = foundFaces.size();
1097
1098 bool verbose = m_session->DefinesCmdLineArgument("verbose");
1099
1100 // Now construct temporary GS objects. These will be used to
1101 // populate the arrays tmp3 and tmp4 with the multiplicity of
1102 // the vertices and edges respectively to identify those
1103 // vertices and edges which are located on partition boundary.
1104 Array<OneD, long> vertArray(unique_verts, &procVerts[0]);
1105 Gs::gs_data *tmp1 = Gs::Init(vertArray, vRowComm, verbose);
1106 Array<OneD, NekDouble> tmp4(unique_verts, 1.0);
1107 Array<OneD, NekDouble> tmp5(unique_edges, 1.0);
1108 Array<OneD, NekDouble> tmp6(unique_faces, 1.0);
1109 Gs::Gather(tmp4, Gs::gs_add, tmp1);
1110 Gs::Finalise(tmp1);
1111
1112 if (unique_edges > 0)
1113 {
1114 Array<OneD, long> edgeArray(unique_edges, &procEdges[0]);
1115 Gs::gs_data *tmp2 = Gs::Init(edgeArray, vRowComm, verbose);
1116 Gs::Gather(tmp5, Gs::gs_add, tmp2);
1117 Gs::Finalise(tmp2);
1118 }
1119
1120 if (unique_faces > 0)
1121 {
1122 Array<OneD, long> faceArray(unique_faces, &procFaces[0]);
1123 Gs::gs_data *tmp3 = Gs::Init(faceArray, vRowComm, verbose);
1124 Gs::Gather(tmp6, Gs::gs_add, tmp3);
1125 Gs::Finalise(tmp3);
1126 }
1127
1128 // Finally, fill the partVerts set with all non-Dirichlet
1129 // vertices which lie on a partition boundary.
1130 for (i = 0; i < unique_verts; ++i)
1131 {
1132 if (tmp4[i] > 1.0)
1133 {
1134 if (graph[0].count(procVerts[i] - 1) == 0)
1135 {
1136 partVerts.insert(tempGraph[0][procVerts[i] - 1]);
1137 }
1138 }
1139 }
1140
1141 for (i = 0; i < unique_edges; ++i)
1142 {
1143 if (tmp5[i] > 1.0)
1144 {
1145 if (graph[1].count(procEdges[i] - 1) == 0)
1146 {
1147 partVerts.insert(tempGraph[1][procEdges[i] - 1]);
1148 }
1149 }
1150 }
1151
1152 for (i = 0; i < unique_faces; ++i)
1153 {
1154 if (tmp6[i] > 1.0)
1155 {
1156 if (graph[2].count(procFaces[i] - 1) == 0)
1157 {
1158 partVerts.insert(tempGraph[2][procFaces[i] - 1]);
1159 }
1160 }
1161 }
1162
1163 // Now fill with all vertices on periodic BCs
1164 for (auto &pIt : periodicVerts)
1165 {
1166 if (graph[0].count(pIt.first) == 0)
1167 {
1168 partVerts.insert(tempGraph[0][pIt.first]);
1169 }
1170 }
1171 for (auto &pIt : periodicEdges)
1172 {
1173 if (graph[1].count(pIt.first) == 0)
1174 {
1175 partVerts.insert(tempGraph[1][pIt.first]);
1176 }
1177 }
1178 for (auto &pIt : periodicFaces)
1179 {
1180 if (graph[2].count(pIt.first) == 0)
1181 {
1182 partVerts.insert(tempGraph[2][pIt.first]);
1183 }
1184 }
1185 }
1186
1187 int nGraphVerts = tempGraphVertId;
1188 Array<OneD, int> perm(nGraphVerts);
1189 Array<OneD, int> iperm(nGraphVerts);
1190
1191 if (nGraphVerts)
1192 {
1193 switch (m_solnType)
1194 {
1195 case eDirectFullMatrix:
1196 case eIterativeFull:
1198 case ePETScStaticCond:
1199 case ePETScFullMatrix:
1200 case eXxtFullMatrix:
1201 case eXxtStaticCond:
1202 {
1203 NoReordering(boostGraphObj, perm, iperm);
1204 break;
1205 }
1206
1207 case eDirectStaticCond:
1208 {
1209 CuthillMckeeReordering(boostGraphObj, perm, iperm);
1210 break;
1211 }
1212
1217 {
1218 MultiLevelBisectionReordering(boostGraphObj, perm, iperm,
1219 bottomUpGraph, partVerts,
1220 mdswitch);
1221 break;
1222 }
1223 default:
1224 {
1225 ASSERTL0(false,
1226 "Unrecognised solution type: " +
1227 std::string(GlobalSysSolnTypeMap[m_solnType]));
1228 }
1229 }
1230 }
1231
1232 // For parallel multi-level static condensation determine the lowest
1233 // static condensation level amongst processors.
1238 bottomUpGraph)
1239 {
1240 m_lowestStaticCondLevel = bottomUpGraph->GetNlevels() - 1;
1242 }
1243 else
1244 {
1246 }
1247
1248 /**
1249 * STEP 4: Fill the #graph[0] and
1250 * #graph[1] with the optimal ordering from boost.
1251 */
1252 for (auto &mapIt : tempGraph[0])
1253 {
1254 graph[0][mapIt.first] = iperm[mapIt.second] + graphVertId;
1255 }
1256 for (auto &mapIt : tempGraph[1])
1257 {
1258 graph[1][mapIt.first] = iperm[mapIt.second] + graphVertId;
1259 }
1260 for (auto &mapIt : tempGraph[2])
1261 {
1262 graph[2][mapIt.first] = iperm[mapIt.second] + graphVertId;
1263 }
1264
1265 return nGraphVerts;
1266}
1267
1268/**
1269 *
1270 */
1273 const int numLocalCoeffs, const ExpList &locExp,
1274 const BndCondExp &bndCondExp, const BndCond &bndConditions,
1275 const bool checkIfSystemSingular, const std::string variable,
1276 const PeriodicMap &periodicVerts, const PeriodicMap &periodicEdges,
1277 const PeriodicMap &periodicFaces)
1278 : AssemblyMap(pSession, locExp.GetComm(), variable)
1279{
1280 int i, j, k;
1281 int p, q, numModes0, numModes1;
1282 int cnt = 0;
1283 int meshVertId, meshEdgeId, meshEdgeId2, meshFaceId, meshFaceId2;
1284 int globalId;
1285 int nEdgeInteriorCoeffs;
1286 int firstNonDirGraphVertId;
1287 LibUtilities::CommSharedPtr vRowComm = m_comm->GetRowComm();
1289 StdRegions::Orientation edgeOrient;
1290 StdRegions::Orientation faceOrient;
1291 Array<OneD, unsigned int> edgeInteriorMap;
1292 Array<OneD, int> edgeInteriorSign;
1293 Array<OneD, unsigned int> faceInteriorMap;
1294 Array<OneD, int> faceInteriorSign;
1295
1296 const LocalRegions::ExpansionVector &locExpVector = *(locExp.GetExp());
1297
1298 bool verbose = m_session->DefinesCmdLineArgument("verbose");
1299
1300 m_signChange = false;
1301
1302 // Stores vertex, edge and face reordered vertices.
1303 DofGraph graph(3);
1304 DofGraph dofs(3);
1305 vector<map<int, int>> faceModes(2);
1306 map<int, LibUtilities::ShapeType> faceType;
1307
1308 set<int> extraDirVerts, extraDirEdges;
1310
1311 // Construct list of number of degrees of freedom for each vertex,
1312 // edge and face.
1313 for (i = 0; i < locExpVector.size(); ++i)
1314 {
1315 exp = locExpVector[i];
1316
1317 for (j = 0; j < exp->GetNverts(); ++j)
1318 {
1319 dofs[0][exp->GetGeom()->GetVid(j)] = 1;
1320 }
1321
1322 for (j = 0; j < exp->GetGeom()->GetNumEdges(); ++j)
1323 {
1324 int nEdgeInt;
1325 if (exp->GetGeom()->GetNumFaces())
1326 {
1327 nEdgeInt =
1328 exp->as<LocalRegions::Expansion3D>()->GetEdgeNcoeffs(j) - 2;
1329 }
1330 else
1331 {
1332 nEdgeInt = exp->GetTraceNcoeffs(j) - 2;
1333 }
1334
1335 if (dofs[1].count(exp->GetGeom()->GetEid(j)) > 0)
1336 {
1337 if (dofs[1][exp->GetGeom()->GetEid(j)] != nEdgeInt)
1338 {
1339 ASSERTL0(
1340 (exp->GetBasisType(0) == LibUtilities::eModified_A) ||
1341 (exp->GetBasisType(1) ==
1343 (exp->GetBasisType(2) ==
1345 (exp->GetBasisType(2) ==
1347 "CG with variable order only available with "
1348 "modal expansion");
1349 }
1350 dofs[1][exp->GetGeom()->GetEid(j)] =
1351 min(dofs[1][exp->GetGeom()->GetEid(j)], nEdgeInt);
1352 }
1353 else
1354 {
1355 dofs[1][exp->GetGeom()->GetEid(j)] = nEdgeInt;
1356 }
1357 }
1358
1359 for (j = 0; j < exp->GetGeom()->GetNumFaces(); ++j)
1360 {
1361 faceOrient = exp->GetGeom()->GetForient(j);
1362 meshFaceId = exp->GetGeom()->GetFid(j);
1363 exp->GetTraceNumModes(j, numModes0, numModes1, faceOrient);
1364
1365 if (faceModes[0].count(meshFaceId) > 0)
1366 {
1367 faceModes[0][meshFaceId] =
1368 min(faceModes[0][meshFaceId], numModes0);
1369
1370 faceModes[1][meshFaceId] =
1371 min(faceModes[1][meshFaceId], numModes1);
1372 }
1373 else
1374 {
1375 faceModes[0][meshFaceId] = numModes0;
1376 faceModes[1][meshFaceId] = numModes1;
1377
1378 // Get shape of this face
1379 faceType[meshFaceId] =
1380 exp->GetGeom()->GetFace(j)->GetShapeType();
1381 }
1382 }
1383 }
1384
1385 // Add non-local periodic dofs to the map
1386 for (auto &pIt : periodicEdges)
1387 {
1388 for (i = 0; i < pIt.second.size(); ++i)
1389 {
1390 meshEdgeId2 = pIt.second[i].id;
1391 if (dofs[1].count(meshEdgeId2) == 0)
1392 {
1393 dofs[1][meshEdgeId2] = 1e6;
1394 }
1395 }
1396 }
1397 for (auto &pIt : periodicFaces)
1398 {
1399 for (i = 0; i < pIt.second.size(); ++i)
1400 {
1401 meshFaceId2 = pIt.second[i].id;
1402 if (faceModes[0].count(meshFaceId2) == 0)
1403 {
1404 faceModes[0][meshFaceId2] = 1e6;
1405 faceModes[1][meshFaceId2] = 1e6;
1406 }
1407 }
1408 }
1409
1410 // Now use information from all partitions to determine the correct
1411 // size
1412
1413 // edges
1414 Array<OneD, long> edgeId(dofs[1].size());
1415 Array<OneD, NekDouble> edgeDof(dofs[1].size());
1416 i = 0;
1417 for (auto &dofIt : dofs[1])
1418 {
1419 edgeId[i] = dofIt.first + 1;
1420 edgeDof[i++] = (NekDouble)dofIt.second;
1421 }
1422 Gs::gs_data *tmp = Gs::Init(edgeId, vRowComm, verbose);
1423 Gs::Gather(edgeDof, Gs::gs_min, tmp);
1424 Gs::Finalise(tmp);
1425 for (i = 0; i < dofs[1].size(); i++)
1426 {
1427 dofs[1][edgeId[i] - 1] = (int)(edgeDof[i] + 0.5);
1428 }
1429 // Periodic edges
1430 for (auto &pIt : periodicEdges)
1431 {
1432 meshEdgeId = pIt.first;
1433 for (i = 0; i < pIt.second.size(); ++i)
1434 {
1435 meshEdgeId2 = pIt.second[i].id;
1436 if (dofs[1][meshEdgeId2] < dofs[1][meshEdgeId])
1437 {
1438 dofs[1][meshEdgeId] = dofs[1][meshEdgeId2];
1439 }
1440 }
1441 }
1442 // faces
1443 Array<OneD, long> faceId(faceModes[0].size());
1444 Array<OneD, NekDouble> faceP(faceModes[0].size());
1445 Array<OneD, NekDouble> faceQ(faceModes[0].size());
1446
1447 i = 0;
1448 for (auto dofIt = faceModes[0].begin(), dofIt2 = faceModes[1].begin();
1449 dofIt != faceModes[0].end(); dofIt++, dofIt2++, i++)
1450 {
1451 faceId[i] = dofIt->first + 1;
1452 faceP[i] = (NekDouble)dofIt->second;
1453 faceQ[i] = (NekDouble)dofIt2->second;
1454 }
1455 Gs::gs_data *tmp2 = Gs::Init(faceId, vRowComm, verbose);
1456 Gs::Gather(faceP, Gs::gs_min, tmp2);
1457 Gs::Gather(faceQ, Gs::gs_min, tmp2);
1458 Gs::Finalise(tmp2);
1459 for (i = 0; i < faceModes[0].size(); i++)
1460 {
1461 faceModes[0][faceId[i] - 1] = (int)(faceP[i] + 0.5);
1462 faceModes[1][faceId[i] - 1] = (int)(faceQ[i] + 0.5);
1463 }
1464 // Periodic faces
1465 for (auto &pIt : periodicFaces)
1466 {
1467 meshFaceId = pIt.first;
1468 for (i = 0; i < pIt.second.size(); ++i)
1469 {
1470 meshFaceId2 = pIt.second[i].id;
1471 if (faceModes[0][meshFaceId2] < faceModes[0][meshFaceId])
1472 {
1473 faceModes[0][meshFaceId] = faceModes[0][meshFaceId2];
1474 }
1475 if (faceModes[1][meshFaceId2] < faceModes[1][meshFaceId])
1476 {
1477 faceModes[1][meshFaceId] = faceModes[1][meshFaceId2];
1478 }
1479 }
1480 }
1481 // Calculate number of dof in each face
1482 int P, Q;
1483 for (i = 0; i < faceModes[0].size(); i++)
1484 {
1485 P = faceModes[0][faceId[i] - 1];
1486 Q = faceModes[1][faceId[i] - 1];
1487 if (faceType[faceId[i] - 1] == LibUtilities::eQuadrilateral)
1488 {
1489 // Quad face
1490 dofs[2][faceId[i] - 1] =
1493 }
1494 else
1495 {
1496 // Tri face
1497 dofs[2][faceId[i] - 1] =
1500 }
1501 }
1502
1503 Array<OneD, const BndCond> bndCondVec(1, bndConditions);
1504
1505 // Note that nExtraDirichlet is not used in the logic below; it just
1506 // needs to be set so that the coupled solver in
1507 // IncNavierStokesSolver can work.
1508 int nExtraDirichlet;
1509 int mdswitch;
1510 m_session->LoadParameter("MDSwitch", mdswitch, 10);
1511
1512 int nGraphVerts = CreateGraph(
1513 locExp, bndCondExp, bndCondVec, checkIfSystemSingular, periodicVerts,
1514 periodicEdges, periodicFaces, graph, bottomUpGraph, extraDirVerts,
1515 extraDirEdges, firstNonDirGraphVertId, nExtraDirichlet, mdswitch);
1516
1517 /*
1518 * Set up an array which contains the offset information of the
1519 * different graph vertices.
1520 *
1521 * This basically means to identify to how many global degrees of
1522 * freedom the individual graph vertices correspond. Obviously,
1523 * the graph vertices corresponding to the mesh-vertices account
1524 * for a single global DOF. However, the graph vertices
1525 * corresponding to the element edges correspond to N-2 global DOF
1526 * where N is equal to the number of boundary modes on this edge.
1527 */
1528 Array<OneD, int> graphVertOffset(
1529 graph[0].size() + graph[1].size() + graph[2].size() + 1, 0);
1530
1531 graphVertOffset[0] = 0;
1532
1533 for (i = 0; i < locExpVector.size(); ++i)
1534 {
1535 exp = locExpVector[i];
1536
1537 for (j = 0; j < exp->GetNverts(); ++j)
1538 {
1539 meshVertId = exp->GetGeom()->GetVid(j);
1540 graphVertOffset[graph[0][meshVertId] + 1] = 1;
1541 }
1542
1543 for (j = 0; j < exp->GetGeom()->GetNumEdges(); ++j)
1544 {
1545 if (exp->GetGeom()->GetNumFaces()) // 3D version
1546 {
1547 nEdgeInteriorCoeffs =
1548 exp->as<LocalRegions::Expansion3D>()->GetEdgeNcoeffs(j) - 2;
1549 }
1550 else
1551 {
1552 nEdgeInteriorCoeffs = exp->GetTraceNcoeffs(j) - 2;
1553 }
1554 meshEdgeId = exp->GetGeom()->GetEid(j);
1555 graphVertOffset[graph[1][meshEdgeId] + 1] = dofs[1][meshEdgeId];
1556
1557 // Need a sign vector for modal expansions if nEdgeCoeffs
1558 // >=3 (not 4 because of variable order case)
1559 if (nEdgeInteriorCoeffs &&
1560 (exp->GetBasisType(0) == LibUtilities::eModified_A))
1561 {
1562 m_signChange = true;
1563 }
1564 }
1565
1566 for (j = 0; j < exp->GetGeom()->GetNumFaces(); ++j)
1567 {
1568 meshFaceId = exp->GetGeom()->GetFid(j);
1569 graphVertOffset[graph[2][meshFaceId] + 1] = dofs[2][meshFaceId];
1570 }
1571 }
1572
1573 for (i = 1; i < graphVertOffset.size(); i++)
1574 {
1575 graphVertOffset[i] += graphVertOffset[i - 1];
1576 }
1577
1578 // Allocate the proper amount of space for the class-data
1579 m_numLocalCoeffs = numLocalCoeffs;
1580 m_numGlobalDirBndCoeffs = graphVertOffset[firstNonDirGraphVertId];
1588
1589 // If required, set up the sign-vector
1590 if (m_signChange)
1591 {
1597 }
1598
1600 m_numPatches = locExpVector.size();
1603 for (i = 0; i < m_numPatches; ++i)
1604 {
1606 (unsigned int)locExpVector[i]->NumBndryCoeffs();
1608 (unsigned int)locExpVector[i]->GetNcoeffs() -
1609 locExpVector[i]->NumBndryCoeffs();
1610 }
1611
1612 /**
1613 * STEP 6: Now, all ingredients are ready to set up the actual
1614 * local to global mapping.
1615 *
1616 * The remainder of the map consists of the element-interior
1617 * degrees of freedom. This leads to the block-diagonal submatrix
1618 * as each element-interior mode is globally orthogonal to modes
1619 * in all other elements.
1620 */
1621 cnt = 0;
1622
1623 // Loop over all the elements in the domain
1624 int cntbdry = 0;
1625 int cntint = 0;
1626 for (i = 0; i < locExpVector.size(); ++i)
1627 {
1628 exp = locExpVector[i];
1629 cnt = locExp.GetCoeff_Offset(i);
1630
1631 int nbdry = exp->NumBndryCoeffs();
1632 int nint = exp->GetNcoeffs() - nbdry;
1633
1634 Array<OneD, unsigned int> bmap(nbdry);
1635 Array<OneD, unsigned int> imap(nint);
1636
1637 exp->GetBoundaryMap(bmap);
1638 exp->GetInteriorMap(imap);
1639
1640 for (j = 0; j < nbdry; ++j)
1641 {
1642 m_localToLocalBndMap[cntbdry++] = cnt + bmap[j];
1643 }
1644
1645 for (j = 0; j < nint; ++j)
1646 {
1647 m_localToLocalIntMap[cntint++] = cnt + imap[j];
1648 }
1649
1650 for (j = 0; j < exp->GetNverts(); ++j)
1651 {
1652 meshVertId = exp->GetGeom()->GetVid(j);
1653
1654 // Set the global DOF for vertex j of element i
1655 m_localToGlobalMap[cnt + exp->GetVertexMap(j)] =
1656 graphVertOffset[graph[0][meshVertId]];
1657 }
1658
1659 for (j = 0; j < exp->GetGeom()->GetNumEdges(); ++j)
1660 {
1661 if (exp->GetGeom()->GetNumFaces())
1662 {
1663 nEdgeInteriorCoeffs =
1664 exp->as<LocalRegions::Expansion3D>()->GetEdgeNcoeffs(j) - 2;
1665 }
1666 else
1667 {
1668 nEdgeInteriorCoeffs = exp->GetTraceNcoeffs(j) - 2;
1669 }
1670 edgeOrient = exp->GetGeom()->GetEorient(j);
1671 meshEdgeId = exp->GetGeom()->GetEid(j);
1672
1673 // obtain the vertex id attached with one of the vertices of the
1674 // edge for variable P purposes
1675 auto meshEdgeVertId = exp->GetGeom()->GetEdge(j)->GetVid(0);
1676
1677 auto pIt = periodicEdges.find(meshEdgeId);
1678
1679 // See if this edge is periodic. If it is, then we map all
1680 // edges to the one with lowest ID, and align all
1681 // coefficients to this edge orientation.
1682 if (pIt != periodicEdges.end())
1683 {
1684 pair<int, StdRegions::Orientation> idOrient =
1685 DeterminePeriodicEdgeOrientId(meshEdgeId, edgeOrient,
1686 pIt->second);
1687 edgeOrient = idOrient.second;
1688 }
1689
1690 if (exp->GetGeom()->GetNumFaces())
1691 {
1692 exp->as<LocalRegions::Expansion3D>()
1693 ->GetEdgeInteriorToElementMap(j, edgeInteriorMap,
1694 edgeInteriorSign, edgeOrient);
1695 }
1696 else
1697 {
1698 exp->GetTraceInteriorToElementMap(j, edgeInteriorMap,
1699 edgeInteriorSign, edgeOrient);
1700 }
1701
1702 // Set the global DOF's for the interior modes of edge j
1703 for (k = 0; k < dofs[1][meshEdgeId]; ++k)
1704 {
1705 m_localToGlobalMap[cnt + edgeInteriorMap[k]] =
1706 graphVertOffset[graph[1][meshEdgeId]] + k;
1707 }
1708 for (k = dofs[1][meshEdgeId]; k < nEdgeInteriorCoeffs; ++k)
1709 {
1710 // set value to edge vertex on edge - just to id local - will be
1711 // zerod by sign
1712 m_localToGlobalMap[cnt + edgeInteriorMap[k]] =
1713 graphVertOffset[graph[0][meshEdgeVertId]];
1714 }
1715
1716 // Fill the sign vector if required
1717 if (m_signChange)
1718 {
1719 for (k = 0; k < dofs[1][meshEdgeId]; ++k)
1720 {
1721 m_localToGlobalSign[cnt + edgeInteriorMap[k]] =
1722 (NekDouble)edgeInteriorSign[k];
1723 }
1724 for (k = dofs[1][meshEdgeId]; k < nEdgeInteriorCoeffs; ++k)
1725 {
1726 m_localToGlobalSign[cnt + edgeInteriorMap[k]] = 0.0;
1727 }
1728 }
1729 }
1730
1731 for (j = 0; j < exp->GetGeom()->GetNumFaces(); ++j)
1732 {
1733 faceOrient = exp->GetGeom()->GetForient(j);
1734 meshFaceId = exp->GetGeom()->GetFid(j);
1735
1736 // obtain the vertex id attached with one of the vertices of the
1737 // face for variable P purposes
1738 auto meshFaceVertId = exp->GetGeom()->GetFace(j)->GetVid(0);
1739
1740 auto pIt = periodicFaces.find(meshFaceId);
1741
1742 if (pIt != periodicFaces.end() &&
1743 meshFaceId == min(meshFaceId, pIt->second[0].id))
1744 {
1745 faceOrient = DeterminePeriodicFaceOrient(faceOrient,
1746 pIt->second[0].orient);
1747 }
1748
1749 exp->GetTraceInteriorToElementMap(j, faceInteriorMap,
1750 faceInteriorSign, faceOrient);
1751
1752 // Set the global DOF's for the interior modes of face j
1753 exp->GetTraceNumModes(j, numModes0, numModes1, faceOrient);
1754 switch (faceType[meshFaceId])
1755 {
1757 {
1758 int kLoc = 0;
1759 k = 0;
1760 for (q = 2; q < numModes1; q++)
1761 {
1762 for (p = 2; p < numModes0; p++)
1763 {
1764 if ((p < faceModes[0][meshFaceId]) &&
1765 (q < faceModes[1][meshFaceId]))
1766 {
1767 m_localToGlobalMap[cnt +
1768 faceInteriorMap[kLoc]] =
1769 graphVertOffset[graph[2][meshFaceId]] + k;
1770 if (m_signChange)
1771 {
1773 faceInteriorMap[kLoc]] =
1774 (NekDouble)faceInteriorSign[kLoc];
1775 }
1776 k++;
1777 }
1778 else
1779 {
1780 // set value to edge vertex on face - just to id
1781 // local - will be zerod by sign
1782 m_localToGlobalMap[cnt +
1783 faceInteriorMap[kLoc]] =
1784 graphVertOffset[graph[0][meshFaceVertId]];
1785
1786 if (m_signChange)
1787 {
1789 faceInteriorMap[kLoc]] =
1790 0.0;
1791 }
1792 }
1793 kLoc++;
1794 }
1795 }
1796 }
1797 break;
1799 {
1800 int kLoc = 0;
1801 k = 0;
1802 for (p = 2; p < numModes0; p++)
1803 {
1804 for (q = 1; q < numModes1 - p; q++)
1805 {
1806 if ((p < faceModes[0][meshFaceId]) &&
1807 (p + q < faceModes[1][meshFaceId]))
1808 {
1809 m_localToGlobalMap[cnt +
1810 faceInteriorMap[kLoc]] =
1811 graphVertOffset[graph[2][meshFaceId]] + k;
1812 if (m_signChange)
1813 {
1815 faceInteriorMap[kLoc]] =
1816 (NekDouble)faceInteriorSign[kLoc];
1817 }
1818 k++;
1819 }
1820 else
1821 {
1822 // set value to edge vertex on face - just to id
1823 // local - will be zerod by sign
1824 m_localToGlobalMap[cnt +
1825 faceInteriorMap[kLoc]] =
1826 graphVertOffset[graph[0][meshFaceVertId]];
1827 if (m_signChange)
1828 {
1830 faceInteriorMap[kLoc]] =
1831 0.0;
1832 }
1833 }
1834 kLoc++;
1835 }
1836 }
1837 }
1838 break;
1839 default:
1840 ASSERTL0(false, "Shape not recognised");
1841 break;
1842 }
1843 }
1844 }
1845
1846 // Set up the mapping for the boundary conditions
1847 // Set up boundary mapping
1848 map<int, pair<int, int>> traceToElmtTraceMap;
1849 int id;
1850
1851 for (cnt = i = 0; i < locExpVector.size(); ++i)
1852 {
1853 exp = locExpVector[i];
1854
1855 for (j = 0; j < exp->GetNtraces(); ++j)
1856 {
1857 id = exp->GetGeom()->GetTid(j);
1858
1859 traceToElmtTraceMap[id] = pair<int, int>(i, j);
1860 }
1861 }
1862
1864 Array<OneD, int> signarray;
1865 map<int, pair<int, NekDouble>> GloDirBndCoeffToLocalCoeff;
1866 set<int> CoeffOnDirTrace;
1867
1868 cnt = 0;
1869 int offset = 0;
1870 for (i = 0; i < bndCondExp.size(); i++)
1871 {
1872 set<int> foundExtraVerts, foundExtraEdges;
1873 for (j = 0; j < bndCondExp[i]->GetNumElmts(); j++)
1874 {
1875 bndExp = bndCondExp[i]->GetExp(j);
1876 cnt = offset + bndCondExp[i]->GetCoeff_Offset(j);
1877
1878 int id = bndExp->GetGeom()->GetGlobalID();
1879
1880 ASSERTL1(traceToElmtTraceMap.count(id) > 0,
1881 "Failed to find trace id");
1882
1883 int eid = traceToElmtTraceMap[id].first;
1884 int tid = traceToElmtTraceMap[id].second;
1885
1886 exp = locExpVector[eid];
1887 int dim = exp->GetShapeDimension();
1888
1889 if (dim == 1)
1890 {
1892 locExp.GetCoeff_Offset(eid) + exp->GetVertexMap(tid);
1893 }
1894 else
1895 {
1896 if (dim == 2)
1897 {
1898 exp->GetTraceToElementMap(tid, maparray, signarray,
1899 exp->GetGeom()->GetEorient(tid),
1900 bndExp->GetBasisNumModes(0));
1901 }
1902 else if (dim == 3)
1903 {
1904 exp->GetTraceToElementMap(tid, maparray, signarray,
1905 exp->GetGeom()->GetForient(tid),
1906 bndExp->GetBasisNumModes(0),
1907 bndExp->GetBasisNumModes(1));
1908 }
1909
1910 for (k = 0; k < bndExp->GetNcoeffs(); k++)
1911 {
1913 locExp.GetCoeff_Offset(eid) + maparray[k];
1914 if (m_signChange)
1915 {
1917 signarray[k];
1918 }
1919 }
1920 }
1921
1922 // we now need some information to work out how to
1923 // handle vertices and edges that are only just
1924 // touching a dirichlet boundary (and not the
1925 // whole edge/face)
1926
1927 for (k = 0; k < bndExp->GetNcoeffs(); k++)
1928 {
1929 int locid = m_bndCondCoeffsToLocalCoeffsMap[cnt + k];
1930 int gloid = m_localToGlobalMap[locid];
1931 NekDouble sign = 1.0;
1932
1933 if (m_signChange)
1934 {
1936 }
1937
1938 if (bndConditions[i]->GetBoundaryConditionType() ==
1940 {
1941 bool addid =
1942 (m_signChange && m_localToGlobalSign[locid] == 0)
1943 ? false
1944 : true;
1945
1946 // only add point if sign is +/- 1 since if zero it
1947 // belongs to a mode that is not used in variable p
1948 // expansion
1949 if (addid)
1950 {
1951 CoeffOnDirTrace.insert(locid);
1952
1953 // store the local id and sign from global id
1954 // back to local space;
1955 GloDirBndCoeffToLocalCoeff[gloid] =
1956 pair<int, NekDouble>(locid, sign);
1957 }
1958 }
1959 }
1960 }
1961 offset += bndCondExp[i]->GetNcoeffs();
1962 }
1963
1964 globalId = Vmath::Vmax(m_numLocalCoeffs, &m_localToGlobalMap[0], 1) + 1;
1965 m_numGlobalBndCoeffs = globalId;
1966
1967 // Set up a mapping list of Dirichlet Local Dofs that
1968 // arise due to one vertex or edge just touching a
1969 // Dirichlet boundary and need the value from another
1970 // local coeff that has been filled by the boundary
1971 // coeffs.
1972
1973 Array<OneD, NekDouble> gloParaDirBnd(m_numGlobalBndCoeffs, -1.0);
1974
1976 cnt = 0;
1977 for (i = 0; i < locExpVector.size(); ++i)
1978 {
1979 int gloid;
1980
1981 exp = locExpVector[i];
1982
1983 exp->GetBoundaryMap(bndmap);
1984
1985 for (j = 0; j < bndmap.size(); ++j)
1986 {
1987 k = cnt + bndmap[j];
1988
1989 // exclude if Dirichlet boundary already included in partition
1990 if (CoeffOnDirTrace.count(k) == 0)
1991 {
1992 gloid = m_localToGlobalMap[k];
1993
1994 if (gloid < m_numGlobalDirBndCoeffs) // point on Dir BC
1995 {
1996 if (GloDirBndCoeffToLocalCoeff.count(gloid))
1997 {
1998 int locid = GloDirBndCoeffToLocalCoeff[gloid].first;
1999 NekDouble sign = 1.0;
2000
2001 if (m_signChange)
2002 {
2003 sign = m_localToGlobalSign[locid] *
2005 }
2006
2007 ExtraDirDof DirDofs(k, locid, sign);
2008 // could make same `structure as extraDirDof
2009 m_copyLocalDirDofs.insert(DirDofs);
2010 }
2011 else // else could be on another parallel partition.
2012 {
2013 gloParaDirBnd[gloid] = gloid;
2014 }
2015 }
2016 }
2017 }
2018
2019 cnt += exp->GetNcoeffs();
2020 }
2021
2022 /*
2023 * The boundary condition mapping is generated from the same vertex
2024 * renumbering.
2025 */
2026 cnt = 0;
2027 for (i = 0; i < m_numLocalCoeffs; ++i)
2028 {
2029 if (m_localToGlobalMap[i] == -1)
2030 {
2031 m_localToGlobalMap[i] = globalId++;
2032 }
2033 else
2034 {
2035 if (m_signChange)
2036 {
2038 }
2040 }
2041 }
2042
2043 m_numGlobalCoeffs = globalId;
2044
2045 SetUpUniversalC0ContMap(locExp, periodicVerts, periodicEdges,
2046 periodicFaces);
2047
2048 // Now that universal map is setup reset gloParaDirBnd to
2049 // 0 if no point communicated or universal value of not
2050 // equal to -1.0
2051 for (i = 0; i < m_numGlobalBndCoeffs; ++i)
2052 {
2053 int gloid = gloParaDirBnd[i];
2054 if (gloid == -1)
2055 {
2056 gloParaDirBnd[i] = 0.0;
2057 }
2058 else
2059 {
2060 gloParaDirBnd[i] = m_globalToUniversalMap[gloid];
2061 }
2062 }
2063
2064 // Use parallel boundary communication to set parallel
2065 // dirichlet values on all processors Needs to be after
2066 // SetupUuniversialC0ContMap
2067 Gs::Gather(gloParaDirBnd, Gs::gs_max, m_bndGsh);
2068
2069 // copy global ids back to local values in partition to
2070 // initialise gs communicator.
2072 for (i = 0; i < numLocalCoeffs; ++i)
2073 {
2074 paraDirBnd[i] = 0.0;
2075
2076 int id = m_localToGlobalMap[i];
2077
2078 if (id >= m_numGlobalDirBndCoeffs)
2079 {
2080 continue;
2081 }
2082
2083 paraDirBnd[i] = gloParaDirBnd[id];
2084
2085 if (gloParaDirBnd[id] > 0.0)
2086 {
2087 // gather any sign changes due to edge modes
2088 if (m_signChange)
2089 {
2090 if (m_localToGlobalSign[i] < 0)
2091 {
2092 m_parallelDirBndSign.insert(i);
2093 }
2094 }
2095 }
2096 }
2097
2098 m_dirBndGsh = Gs::Init(paraDirBnd, vRowComm, verbose);
2099
2100 // Set up the local to global map for the next level when using
2101 // multi-level static condensation
2106 nGraphVerts)
2107 {
2108 if (m_staticCondLevel < (bottomUpGraph->GetNlevels() - 1))
2109 {
2110 Array<OneD, int> vwgts_perm(graph[0].size() + graph[1].size() +
2111 graph[2].size() -
2112 firstNonDirGraphVertId);
2113
2114 for (i = 0; i < locExpVector.size(); ++i)
2115 {
2116 exp = locExpVector[i];
2117
2118 for (j = 0; j < exp->GetNverts(); ++j)
2119 {
2120 meshVertId = exp->GetGeom()->GetVid(j);
2121
2122 if (graph[0][meshVertId] >= firstNonDirGraphVertId)
2123 {
2124 vwgts_perm[graph[0][meshVertId] -
2125 firstNonDirGraphVertId] =
2126 dofs[0][meshVertId];
2127 }
2128 }
2129
2130 for (j = 0; j < exp->GetGeom()->GetNumEdges(); ++j)
2131 {
2132 meshEdgeId = exp->GetGeom()->GetEid(j);
2133
2134 if (graph[1][meshEdgeId] >= firstNonDirGraphVertId)
2135 {
2136 vwgts_perm[graph[1][meshEdgeId] -
2137 firstNonDirGraphVertId] =
2138 dofs[1][meshEdgeId];
2139 }
2140 }
2141
2142 for (j = 0; j < exp->GetGeom()->GetNumFaces(); ++j)
2143 {
2144 meshFaceId = exp->GetGeom()->GetFid(j);
2145
2146 if (graph[2][meshFaceId] >= firstNonDirGraphVertId)
2147 {
2148 vwgts_perm[graph[2][meshFaceId] -
2149 firstNonDirGraphVertId] =
2150 dofs[2][meshFaceId];
2151 }
2152 }
2153 }
2154
2155 bottomUpGraph->ExpandGraphWithVertexWeights(vwgts_perm);
2158 bottomUpGraph);
2159 }
2160 }
2161
2163
2164 // Add up hash values if parallel
2165 int hash = m_hash;
2166 vRowComm->AllReduce(hash, LibUtilities::ReduceSum);
2167 m_hash = hash;
2168
2171
2173}
2174
2175/**
2176 *
2177 */
2183
2184/**
2185 * @brief Determine orientation of an edge to its periodic equivalents,
2186 * as well as the ID of the representative edge.
2187 *
2188 * Since an edge may be periodic with more than one other edge (e.g. a
2189 * periodic cube has sets of four periodic edges in each coordinate
2190 * direction), we have to define a 'representative' edge. In this
2191 * assembly map we define it to be the one with the minimum ID. This
2192 * routine is set up to calculate the orientation of a given edge with
2193 * ID @p meshEdgeId with respect to the edge ID.
2194 *
2195 * @param meshEdgeId ID of a periodic edge.
2196 * @param edgeOrient Edge orientation of meshEdgeId with respect to
2197 * its parent element.
2198 * @param periodicEdges The map of all periodic edges.
2199 *
2200 * @return Pair containing the ID of the periodic edge and the
2201 * orientation of @p meshEdgeID with respect to this edge.
2202 */
2203pair<int, StdRegions::Orientation> DeterminePeriodicEdgeOrientId(
2204 int meshEdgeId, StdRegions::Orientation edgeOrient,
2205 const vector<PeriodicEntity> &periodicEdges)
2206{
2207 int minId = periodicEdges[0].id;
2208 int minIdK = 0;
2209 int k;
2210
2211 for (k = 1; k < periodicEdges.size(); ++k)
2212 {
2213 if (periodicEdges[k].id < minId)
2214 {
2215 minId = min(minId, periodicEdges[k].id);
2216 minIdK = k;
2217 }
2218 }
2219
2220 minId = min(minId, meshEdgeId);
2221
2222 if (meshEdgeId != minId)
2223 {
2224 if (periodicEdges[minIdK].orient == StdRegions::eBackwards)
2225 {
2226 // Swap edge orientation
2227 edgeOrient = (edgeOrient == StdRegions::eForwards)
2230 }
2231 }
2232
2233 return make_pair(minId, edgeOrient);
2234}
2235
2236/**
2237 * @brief Determine relative orientation between two faces.
2238 *
2239 * Given the orientation of a local element to its local face, defined
2240 * as @p faceOrient, and @p perFaceOrient which states the alignment of
2241 * one periodic face to the other global face, this routine determines
2242 * the orientation that takes this local element face to the
2243 * global/unique face.
2244 *
2245 * @param faceOrient Orientation of the face with respect to its
2246 * parent element.
2247 * @param perFaceOrient Orientation of the representative/global face.
2248 *
2249 * @return Orientation between the two faces.
2250 */
2252 StdRegions::Orientation faceOrient, StdRegions::Orientation perFaceOrient)
2253{
2254 StdRegions::Orientation returnval = faceOrient;
2255
2256 if (perFaceOrient != StdRegions::eDir1FwdDir1_Dir2FwdDir2)
2257 {
2258 int tmp1 = (int)faceOrient - 5;
2259 int tmp2 = (int)perFaceOrient - 5;
2260
2261 int flipDir1Map[8] = {2, 3, 0, 1, 6, 7, 4, 5};
2262 int flipDir2Map[8] = {1, 0, 3, 2, 5, 4, 7, 6};
2263 int transposeMap[8] = {4, 5, 6, 7, 0, 2, 1, 3};
2264
2265 // Transpose orientation
2266 if (tmp2 > 3)
2267 {
2268 tmp1 = transposeMap[tmp1];
2269 }
2270
2271 // Reverse orientation in direction 1.
2272 if (tmp2 == 2 || tmp2 == 3 || tmp2 == 6 || tmp2 == 7)
2273 {
2274 tmp1 = flipDir1Map[tmp1];
2275 }
2276
2277 // Reverse orientation in direction 2
2278 if (tmp2 % 2 == 1)
2279 {
2280 tmp1 = flipDir2Map[tmp1];
2281 }
2282
2283 returnval = (StdRegions::Orientation)(tmp1 + 5);
2284 }
2285 return returnval;
2286}
2287
2288/**
2289 * Sets up the global to universal mapping of degrees of freedom across
2290 * processors.
2291 */
2293 const PeriodicMap &perVerts,
2294 const PeriodicMap &perEdges,
2295 const PeriodicMap &perFaces)
2296{
2298 int nVert = 0;
2299 int nEdge = 0;
2300 int nFace = 0;
2301 int maxEdgeDof = 0;
2302 int maxFaceDof = 0;
2303 int maxIntDof = 0;
2304 int dof = 0;
2305 int cnt;
2306 int i, j, k, l;
2307 int meshVertId;
2308 int meshEdgeId;
2309 int meshFaceId;
2310 int elementId;
2311 int vGlobalId;
2312 int maxBndGlobalId = 0;
2313 StdRegions::Orientation edgeOrient;
2314 StdRegions::Orientation faceOrient;
2315 Array<OneD, unsigned int> edgeInteriorMap;
2316 Array<OneD, int> edgeInteriorSign;
2317 Array<OneD, unsigned int> faceInteriorMap;
2318 Array<OneD, int> faceInteriorSign;
2319 Array<OneD, unsigned int> interiorMap;
2320
2321 const LocalRegions::ExpansionVector &locExpVector = *(locExp.GetExp());
2322 LibUtilities::CommSharedPtr vRowComm = m_comm->GetRowComm();
2323 const bool verbose = locExp.GetSession()->DefinesCmdLineArgument("verbose");
2324
2332
2333 // Loop over all the elements in the domain to gather mesh data
2334 for (i = 0; i < locExpVector.size(); ++i)
2335 {
2336 exp = locExpVector[i];
2337
2338 int nv = exp->GetNverts();
2339 int ne = exp->GetGeom()->GetNumEdges();
2340 int nf = exp->GetGeom()->GetNumFaces();
2341
2342 nVert += nv;
2343 nEdge += ne;
2344 nFace += nf;
2345
2346 // Loop over all edges (and vertices) of element i
2347 for (j = 0; j < ne; ++j)
2348 {
2349 if (nf)
2350 {
2351 dof =
2352 exp->as<LocalRegions::Expansion3D>()->GetEdgeNcoeffs(j) - 2;
2353 }
2354 else
2355 {
2356 dof = exp->GetTraceNcoeffs(j) - 2;
2357 }
2358
2359 maxEdgeDof = (dof > maxEdgeDof ? dof : maxEdgeDof);
2360 }
2361 for (j = 0; j < nf; ++j)
2362 {
2363 dof = exp->GetTraceIntNcoeffs(j);
2364 maxFaceDof = (dof > maxFaceDof ? dof : maxFaceDof);
2365 }
2366 exp->GetInteriorMap(interiorMap);
2367 dof = interiorMap.size();
2368 maxIntDof = (dof > maxIntDof ? dof : maxIntDof);
2369 }
2370
2371 // Tell other processes about how many dof we have
2372 vRowComm->AllReduce(nVert, LibUtilities::ReduceSum);
2373 vRowComm->AllReduce(nEdge, LibUtilities::ReduceSum);
2374 vRowComm->AllReduce(nFace, LibUtilities::ReduceSum);
2375 vRowComm->AllReduce(maxEdgeDof, LibUtilities::ReduceMax);
2376 vRowComm->AllReduce(maxFaceDof, LibUtilities::ReduceMax);
2377 vRowComm->AllReduce(maxIntDof, LibUtilities::ReduceMax);
2378
2379 // Assemble global to universal mapping for this process
2380 for (i = 0; i < locExpVector.size(); ++i)
2381 {
2382 exp = locExpVector[i];
2383 cnt = locExp.GetCoeff_Offset(i);
2384
2385 int nf = exp->GetGeom()->GetNumFaces();
2386
2387 // Loop over all vertices of element i
2388 for (j = 0; j < exp->GetNverts(); ++j)
2389 {
2390 meshVertId = exp->GetGeom()->GetVid(j);
2391 vGlobalId = m_localToGlobalMap[cnt + exp->GetVertexMap(j)];
2392
2393 auto pIt = perVerts.find(meshVertId);
2394 if (pIt != perVerts.end())
2395 {
2396 for (k = 0; k < pIt->second.size(); ++k)
2397 {
2398 meshVertId = min(meshVertId, pIt->second[k].id);
2399 }
2400 }
2401
2402 m_globalToUniversalMap[vGlobalId] = meshVertId + 1;
2403 m_globalToUniversalBndMap[vGlobalId] =
2404 m_globalToUniversalMap[vGlobalId];
2405 maxBndGlobalId =
2406 (vGlobalId > maxBndGlobalId ? vGlobalId : maxBndGlobalId);
2407 }
2408
2409 // Loop over all edges of element i
2410 for (j = 0; j < exp->GetGeom()->GetNumEdges(); ++j)
2411 {
2412 meshEdgeId = exp->GetGeom()->GetEid(j);
2413 auto pIt = perEdges.find(meshEdgeId);
2414 edgeOrient = exp->GetGeom()->GetEorient(j);
2415
2416 if (pIt != perEdges.end())
2417 {
2418 pair<int, StdRegions::Orientation> idOrient =
2419 DeterminePeriodicEdgeOrientId(meshEdgeId, edgeOrient,
2420 pIt->second);
2421 meshEdgeId = idOrient.first;
2422 edgeOrient = idOrient.second;
2423 }
2424
2425 if (nf) // 3D version
2426 {
2427 exp->as<LocalRegions::Expansion3D>()
2428 ->GetEdgeInteriorToElementMap(j, edgeInteriorMap,
2429 edgeInteriorSign, edgeOrient);
2430 dof =
2431 exp->as<LocalRegions::Expansion3D>()->GetEdgeNcoeffs(j) - 2;
2432 }
2433 else // 2D version
2434 {
2435 exp->GetTraceInteriorToElementMap(j, edgeInteriorMap,
2436 edgeInteriorSign, edgeOrient);
2437 dof = exp->GetTraceNcoeffs(j) - 2;
2438 }
2439
2440 // Set the global DOF's for the interior modes of edge j
2441 // for varP, ignore modes with sign == 0
2442 for (k = 0, l = 0; k < dof; ++k)
2443 {
2444 if (m_signChange)
2445 {
2446 if (m_localToGlobalSign[cnt + edgeInteriorMap[k]] == 0)
2447 {
2448 continue;
2449 }
2450 }
2451 vGlobalId = m_localToGlobalMap[cnt + edgeInteriorMap[k]];
2452 m_globalToUniversalMap[vGlobalId] =
2453 nVert + meshEdgeId * maxEdgeDof + l + 1;
2454 m_globalToUniversalBndMap[vGlobalId] =
2455 m_globalToUniversalMap[vGlobalId];
2456 maxBndGlobalId =
2457 (vGlobalId > maxBndGlobalId ? vGlobalId : maxBndGlobalId);
2458 l++;
2459 }
2460 }
2461
2462 // Loop over all faces of element i
2463 for (j = 0; j < exp->GetGeom()->GetNumFaces(); ++j)
2464 {
2465 faceOrient = exp->GetGeom()->GetForient(j);
2466
2467 meshFaceId = exp->GetGeom()->GetFid(j);
2468
2469 auto pIt = perFaces.find(meshFaceId);
2470 if (pIt != perFaces.end())
2471 {
2472 if (meshFaceId == min(meshFaceId, pIt->second[0].id))
2473 {
2474 faceOrient = DeterminePeriodicFaceOrient(
2475 faceOrient, pIt->second[0].orient);
2476 }
2477 meshFaceId = min(meshFaceId, pIt->second[0].id);
2478 }
2479
2480 exp->GetTraceInteriorToElementMap(j, faceInteriorMap,
2481 faceInteriorSign, faceOrient);
2482 dof = exp->GetTraceIntNcoeffs(j);
2483
2484 for (k = 0, l = 0; k < dof; ++k)
2485 {
2486 if (m_signChange)
2487 {
2488 if (m_localToGlobalSign[cnt + faceInteriorMap[k]] == 0)
2489 {
2490 continue;
2491 }
2492 }
2493 vGlobalId = m_localToGlobalMap[cnt + faceInteriorMap[k]];
2494 m_globalToUniversalMap[vGlobalId] = nVert + nEdge * maxEdgeDof +
2495 meshFaceId * maxFaceDof +
2496 l + 1;
2497 m_globalToUniversalBndMap[vGlobalId] =
2498 m_globalToUniversalMap[vGlobalId];
2499
2500 maxBndGlobalId =
2501 (vGlobalId > maxBndGlobalId ? vGlobalId : maxBndGlobalId);
2502 l++;
2503 }
2504 }
2505
2506 // Add interior DOFs to complete universal numbering
2507 exp->GetInteriorMap(interiorMap);
2508 dof = interiorMap.size();
2509 elementId = (exp->GetGeom())->GetGlobalID();
2510 for (k = 0; k < dof; ++k)
2511 {
2512 vGlobalId = m_localToGlobalMap[cnt + interiorMap[k]];
2513 m_globalToUniversalMap[vGlobalId] = nVert + nEdge * maxEdgeDof +
2514 nFace * maxFaceDof +
2515 elementId * maxIntDof + k + 1;
2516 }
2517 }
2518
2519 // Set up the GSLib universal assemble mapping
2520 // Internal DOF do not participate in any data
2521 // exchange, so we keep these set to the special GSLib id=0 so
2522 // they are ignored.
2526 for (unsigned int i = 0; i < m_numGlobalBndCoeffs; ++i)
2527 {
2528 tmp[i] = m_globalToUniversalMap[i];
2529 }
2530
2531 m_gsh = Gs::Init(tmp, vRowComm, verbose);
2532 m_bndGsh = Gs::Init(tmp2, vRowComm, verbose);
2533 Gs::Unique(tmp, vRowComm);
2534 for (unsigned int i = 0; i < m_numGlobalCoeffs; ++i)
2535 {
2536 m_globalToUniversalMapUnique[i] = (tmp[i] >= 0 ? 1 : 0);
2537 }
2538 for (unsigned int i = 0; i < m_numGlobalBndCoeffs; ++i)
2539 {
2540 m_globalToUniversalBndMapUnique[i] = (tmp2[i] >= 0 ? 1 : 0);
2541 }
2542}
2543
2544/**
2545 * @brief Construct an AssemblyMapCG object which corresponds to the
2546 * linear space of the current object.
2547 *
2548 * This function is used to create a linear-space assembly map, which is
2549 * then used in the linear space preconditioner in the conjugate
2550 * gradient solve.
2551 */
2553 GlobalSysSolnType solnType)
2554{
2555 AssemblyMapCGSharedPtr returnval;
2556
2557 int i, j;
2558 int nverts = 0;
2559 const std::shared_ptr<LocalRegions::ExpansionVector> exp = locexp.GetExp();
2560 int nelmts = exp->size();
2561 const bool verbose = locexp.GetSession()->DefinesCmdLineArgument("verbose");
2562
2563 // Get Default Map and turn off any searched values.
2565 m_session, locexp.GetComm());
2566 returnval->m_solnType = solnType;
2567 returnval->m_preconType = "Null";
2568 returnval->m_maxStaticCondLevel = 0;
2569 returnval->m_signChange = false;
2570 returnval->m_comm = m_comm;
2571
2572 // Count the number of vertices
2573 for (i = 0; i < nelmts; ++i)
2574 {
2575 nverts += (*exp)[i]->GetNverts();
2576 }
2577
2578 returnval->m_numLocalCoeffs = nverts;
2579 returnval->m_localToGlobalMap = Array<OneD, int>(nverts, -1);
2580
2581 // Store original global ids in this map
2582 returnval->m_localToGlobalBndMap = Array<OneD, int>(nverts, -1);
2583
2584 int cnt = 0;
2585 int cnt1 = 0;
2586 Array<OneD, int> GlobCoeffs(m_numGlobalCoeffs, -1);
2587
2588 // Set up local to global map;
2589 for (i = 0; i < nelmts; ++i)
2590 {
2591 for (j = 0; j < (*exp)[i]->GetNverts(); ++j)
2592 {
2593 returnval->m_localToGlobalMap[cnt] =
2594 returnval->m_localToGlobalBndMap[cnt] =
2595 m_localToGlobalMap[cnt1 + (*exp)[i]->GetVertexMap(j, true)];
2596 GlobCoeffs[returnval->m_localToGlobalMap[cnt]] = 1;
2597
2598 // Set up numLocalDirBndCoeffs
2599 if ((returnval->m_localToGlobalMap[cnt]) < m_numGlobalDirBndCoeffs)
2600 {
2601 returnval->m_numLocalDirBndCoeffs++;
2602 }
2603 cnt++;
2604 }
2605 cnt1 += (*exp)[i]->GetNcoeffs();
2606 }
2607
2608 cnt = 0;
2609 // Reset global numbering and count number of dofs
2610 for (i = 0; i < m_numGlobalCoeffs; ++i)
2611 {
2612 if (GlobCoeffs[i] != -1)
2613 {
2614 GlobCoeffs[i] = cnt++;
2615 }
2616 }
2617
2618 // Set up number of globalCoeffs;
2619 returnval->m_numGlobalCoeffs = cnt;
2620
2621 // Set up number of global Dirichlet boundary coefficients
2622 for (i = 0; i < m_numGlobalDirBndCoeffs; ++i)
2623 {
2624 if (GlobCoeffs[i] != -1)
2625 {
2626 returnval->m_numGlobalDirBndCoeffs++;
2627 }
2628 }
2629
2630 // Set up global to universal map
2631 if (m_globalToUniversalMap.size())
2632 {
2634 m_session->GetComm()->GetRowComm();
2635 int nglocoeffs = returnval->m_numGlobalCoeffs;
2636 returnval->m_globalToUniversalMap = Array<OneD, int>(nglocoeffs);
2637 returnval->m_globalToUniversalMapUnique = Array<OneD, int>(nglocoeffs);
2638
2639 // Reset local to global map and setup universal map
2640 for (i = 0; i < nverts; ++i)
2641 {
2642 cnt = returnval->m_localToGlobalMap[i];
2643 returnval->m_localToGlobalMap[i] = GlobCoeffs[cnt];
2644
2645 returnval->m_globalToUniversalMap[GlobCoeffs[cnt]] =
2647 }
2648
2649 Nektar::Array<OneD, long> tmp(nglocoeffs);
2650 Vmath::Zero(nglocoeffs, tmp, 1);
2651 for (unsigned int i = 0; i < nglocoeffs; ++i)
2652 {
2653 tmp[i] = returnval->m_globalToUniversalMap[i];
2654 }
2655 returnval->m_gsh = Gs::Init(tmp, vRowComm, verbose);
2656 Gs::Unique(tmp, vRowComm);
2657 for (unsigned int i = 0; i < nglocoeffs; ++i)
2658 {
2659 returnval->m_globalToUniversalMapUnique[i] = (tmp[i] >= 0 ? 1 : 0);
2660 }
2661 }
2662 else // not sure this option is ever needed.
2663 {
2664 for (i = 0; i < nverts; ++i)
2665 {
2666 cnt = returnval->m_localToGlobalMap[i];
2667 returnval->m_localToGlobalMap[i] = GlobCoeffs[cnt];
2668 }
2669 }
2670
2671 return returnval;
2672}
2673
2674/**
2675 * The bandwidth calculated here corresponds to what is referred to as
2676 * half-bandwidth. If the elements of the matrix are designated as
2677 * a_ij, it corresponds to the maximum value of |i-j| for non-zero
2678 * a_ij. As a result, the value also corresponds to the number of
2679 * sub- or super-diagonals.
2680 *
2681 * The bandwith can be calculated elementally as it corresponds to the
2682 * maximal elemental bandwith (i.e. the maximal difference in global
2683 * DOF index for every element).
2684 *
2685 * We caluclate here the bandwith of the full global system.
2686 */
2688{
2689 int i, j;
2690 int cnt = 0;
2691 int locSize;
2692 int maxId;
2693 int minId;
2694 int bwidth = -1;
2695 for (i = 0; i < m_numPatches; ++i)
2696 {
2697 locSize =
2699 maxId = -1;
2700 minId = m_numLocalCoeffs + 1;
2701 for (j = 0; j < locSize; j++)
2702 {
2704 {
2705 if (m_localToGlobalMap[cnt + j] > maxId)
2706 {
2707 maxId = m_localToGlobalMap[cnt + j];
2708 }
2709
2710 if (m_localToGlobalMap[cnt + j] < minId)
2711 {
2712 minId = m_localToGlobalMap[cnt + j];
2713 }
2714 }
2715 }
2716 bwidth = (bwidth > (maxId - minId)) ? bwidth : (maxId - minId);
2717
2718 cnt += locSize;
2719 }
2720
2721 m_fullSystemBandWidth = bwidth;
2722}
2723
2725{
2726 return m_localToGlobalMap[i];
2727}
2728
2730{
2731 return m_globalToUniversalMap[i];
2732}
2733
2735{
2737}
2738
2743
2748
2754
2756{
2757 if (m_signChange)
2758 {
2759 return m_localToGlobalSign[i];
2760 }
2761 else
2762 {
2763 return 1.0;
2764 }
2765}
2766
2771
2773 Array<OneD, NekDouble> &global,
2774 bool useComm) const
2775{
2777 if (global.data() == loc.data())
2778 {
2779 local = Array<OneD, NekDouble>(m_numLocalCoeffs, loc.data());
2780 }
2781 else
2782 {
2783 local = loc; // create reference
2784 }
2785
2786 if (m_signChange)
2787 {
2789 m_localToGlobalMap.data(), global.data());
2790 }
2791 else
2792 {
2793 Vmath::Scatr(m_numLocalCoeffs, local.data(), m_localToGlobalMap.data(),
2794 global.data());
2795 }
2796
2797 // ensure all values are unique by calling a max
2798 if (useComm)
2799 {
2800 Gs::Gather(global, Gs::gs_max, m_gsh);
2801 }
2802}
2803
2805 Array<OneD, NekDouble> &loc) const
2806{
2808 if (global.data() == loc.data())
2809 {
2810 glo = Array<OneD, NekDouble>(m_numGlobalCoeffs, global.data());
2811 }
2812 else
2813 {
2814 glo = global; // create reference
2815 }
2816
2817 if (m_signChange)
2818 {
2820 m_localToGlobalMap.data(), loc.data());
2821 }
2822 else
2823 {
2825 loc.data());
2826 }
2827}
2828
2830 NekVector<NekDouble> &loc) const
2831{
2832 GlobalToLocal(global.GetPtr(), loc.GetPtr());
2833}
2834
2836 Array<OneD, NekDouble> &global,
2837 bool useComm) const
2838{
2840 if (global.data() == loc.data())
2841 {
2842 local = Array<OneD, NekDouble>(m_numLocalCoeffs, loc.data());
2843 }
2844 else
2845 {
2846 local = loc; // create reference
2847 }
2848
2849 Vmath::Zero(m_numGlobalCoeffs, global.data(), 1);
2850
2852 local.data(), m_localToGlobalMap.data(), global.data());
2853
2854 if (useComm)
2855 {
2856 UniversalAssemble(global);
2857 }
2858}
2859
2861 Array<OneD, NekDouble> &global) const
2862{
2864 if (global.data() == loc.data())
2865 {
2866 local = Array<OneD, NekDouble>(m_numLocalCoeffs, loc.data());
2867 }
2868 else
2869 {
2870 local = loc; // create reference
2871 }
2872
2873 Vmath::Zero(m_numGlobalCoeffs, global.data(), 1);
2874
2875 if (m_signChange)
2876 {
2878 m_localToGlobalMap.data(), global.data());
2879 }
2880 else
2881 {
2882 Vmath::Assmb(m_numLocalCoeffs, local.data(), m_localToGlobalMap.data(),
2883 global.data());
2884 }
2885 UniversalAssemble(global);
2886}
2887
2889 NekVector<NekDouble> &global) const
2890{
2891 Assemble(loc.GetPtr(), global.GetPtr());
2892}
2893
2898
2900 int offset) const
2901{
2902 Array<OneD, NekDouble> tmp(offset);
2903 Vmath::Vcopy(offset, pGlobal, 1, tmp, 1);
2904 UniversalAssemble(pGlobal);
2905 Vmath::Vcopy(offset, tmp, 1, pGlobal, 1);
2906}
2907
2912
2917
2922
2927
2929{
2930 return m_numDirEdges;
2931}
2932
2934{
2935 return m_numDirFaces;
2936}
2937
2939{
2940 return m_numNonDirEdges;
2941}
2942
2944{
2945 return m_numNonDirFaces;
2946}
2947
2952
2954{
2956
2957 Array<OneD, NekDouble> l2gSign;
2958
2959 if (m_localToGlobalSign.size())
2960 {
2961 l2gSign = m_localToGlobalSign;
2962 }
2963 else // case that does not need to have sign array set up
2964 {
2966 }
2967
2970 for (unsigned i = 0; i < m_numLocalCoeffs; ++i)
2971 {
2972 // for variable order mult might be near zero. All non-zero
2973 // values shoudl be one or bigger so large tolreance shoudl
2974 // be fine
2975 if (fabs(m_invMultiplicityWithSign[i]) < 0.1)
2976 {
2978 }
2979 else
2980 {
2982 }
2983 }
2984}
2985} // namespace Nektar::MultiRegions
#define ASSERTL0(condition, msg)
#define ASSERTL1(condition, msg)
Assert Level 1 – Debugging which is used whether in FULLDEBUG or DEBUG compilation mode....
#define sign(a, b)
return the sign(b)*a
Definition Polylib.cpp:47
SpatialDomains::Geometry * GetGeom() const
static std::shared_ptr< DataType > AllocateSharedPtr(const Args &...args)
Allocate a shared pointer from the memory pool.
int v_GetFullSystemBandWidth() const override
Array< OneD, NekDouble > m_invMultiplicityWithSign
Inverse of multiplicity with sign.
int m_maxStaticCondLevel
Maximum static condensation level.
int m_numNonDirVertexModes
Number of non Dirichlet vertex modes.
const Array< OneD, const int > & v_GetLocalToGlobalMap() override
int CreateGraph(const ExpList &locExp, const BndCondExp &bndCondExp, const Array< OneD, const BndCond > &bndConditions, const bool checkIfSystemSingular, const PeriodicMap &periodicVerts, const PeriodicMap &periodicEdges, const PeriodicMap &periodicFaces, DofGraph &graph, BottomUpSubStructuredGraphSharedPtr &bottomUpGraph, std::set< int > &extraDirVerts, std::set< int > &extraDirEdges, int &firstNonDirGraphVertId, int &nExtraDirichlet, int mdswitch=1)
const Array< OneD, const int > & v_GetGlobalToUniversalMap() override
void v_GlobalToLocal(const Array< OneD, const NekDouble > &global, Array< OneD, NekDouble > &loc) const override
const Array< OneD, const int > & v_GetExtraDirEdges() override
const Array< OneD, const int > & v_GetGlobalToUniversalMapUnique() override
int m_numNonDirEdges
Number of Dirichlet edges.
void v_Assemble(const Array< OneD, const NekDouble > &loc, Array< OneD, NekDouble > &global) const override
std::set< int > m_parallelDirBndSign
Set indicating the local coeffs just touching parallel dirichlet boundary that have a sign change.
Array< OneD, int > m_globalToUniversalMapUnique
Integer map of unique process coeffs to universal space (signed)
Array< OneD, int > m_extraDirEdges
Extra dirichlet edges in parallel.
int v_GetNumNonDirVertexModes() const override
int m_numNonDirFaceModes
Number of non Dirichlet face modes.
void v_UniversalAssemble(Array< OneD, NekDouble > &pGlobal) const override
int m_numNonDirEdgeModes
Number of non Dirichlet edge modes.
int m_numDirEdges
Number of Dirichlet edges.
AssemblyMapSharedPtr v_LinearSpaceMap(const ExpList &locexp, GlobalSysSolnType solnType) override
Construct an AssemblyMapCG object which corresponds to the linear space of the current object.
int v_GetNumNonDirEdgeModes() const override
int m_fullSystemBandWidth
Bandwith of the full matrix system (no static condensation).
Array< OneD, int > m_localToGlobalMap
Integer map of local coeffs to global space.
AssemblyMapCG(const LibUtilities::SessionReaderSharedPtr &pSession, const LibUtilities::CommSharedPtr &comm, const std::string variable="DefaultVar")
Default constructor.
int m_numDirFaces
Number of Dirichlet faces.
void v_AvgAssemble(const Array< OneD, const NekDouble > &loc, Array< OneD, NekDouble > &global, bool useComm) const override
void v_LocalToGlobal(const Array< OneD, const NekDouble > &loc, Array< OneD, NekDouble > &global, bool useComm) const override
void SetUpUniversalC0ContMap(const ExpList &locExp, const PeriodicMap &perVerts=NullPeriodicMap, const PeriodicMap &perEdges=NullPeriodicMap, const PeriodicMap &perFaces=NullPeriodicMap)
Array< OneD, NekDouble > m_localToGlobalSign
Integer sign of local coeffs to global space.
void CalculateFullSystemBandWidth()
Calculate the bandwith of the full matrix system.
std::set< ExtraDirDof > m_copyLocalDirDofs
Set indicating degrees of freedom which are Dirichlet but whose value is stored on another processor.
int m_numNonDirFaces
Number of Dirichlet faces.
Array< OneD, int > m_globalToUniversalMap
Integer map of process coeffs to universal space.
int v_GetNumNonDirFaceModes() const override
const Array< OneD, NekDouble > & v_GetLocalToGlobalSign() const override
int m_numLocalBndCondCoeffs
Number of local boundary condition coefficients.
Base class for constructing local to global mapping of degrees of freedom.
Definition AssemblyMap.h:56
int m_lowestStaticCondLevel
Lowest static condensation level.
GlobalSysSolnType m_solnType
The solution type of the global system.
int m_numLocalCoeffs
Total number of local coefficients.
Array< OneD, int > m_bndCondCoeffsToLocalCoeffsMap
Integer map of bnd cond coeffs to local coefficients.
bool m_signChange
Flag indicating if modes require sign reversal.
void Assemble(const Array< OneD, const NekDouble > &loc, Array< OneD, NekDouble > &global) const
Array< OneD, int > m_localToLocalIntMap
Integer map of local boundary coeffs to local interior system numbering.
int m_numGlobalCoeffs
Total number of global coefficients.
Array< OneD, int > m_globalToUniversalBndMap
Integer map of process coeffs to universal space.
void CalculateBndSystemBandWidth()
Calculates the bandwidth of the boundary system.
Array< OneD, NekDouble > m_localToGlobalBndSign
Integer sign of local boundary coeffs to global space.
Array< OneD, unsigned int > m_numLocalBndCoeffsPerPatch
The number of bnd dofs per patch.
LibUtilities::SessionReaderSharedPtr m_session
Session object.
int m_numLocalBndCoeffs
Number of local boundary coefficients.
AssemblyMapSharedPtr m_nextLevelLocalToGlobalMap
Map from the patches of the previous level to the patches of the current level.
int m_staticCondLevel
The level of recursion in the case of multi-level static condensation.
void GlobalToLocal(const Array< OneD, const NekDouble > &global, Array< OneD, NekDouble > &loc) const
int m_numLocalDirBndCoeffs
Number of Local Dirichlet Boundary Coefficients.
int m_numGlobalDirBndCoeffs
Number of Global Dirichlet Boundary Coefficients.
Array< OneD, unsigned int > m_numLocalIntCoeffsPerPatch
The number of int dofs per patch.
bool m_systemSingular
Flag indicating if the system is singular or not.
Array< OneD, int > m_localToGlobalBndMap
Integer map of local coeffs to global Boundary Dofs.
Gs::gs_data * m_dirBndGsh
gs gather communication to impose Dirhichlet BCs.
Array< OneD, int > m_globalToUniversalBndMapUnique
Integer map of unique process coeffs to universal space (signed)
LibUtilities::CommSharedPtr m_comm
Communicator.
Array< OneD, int > m_localToLocalBndMap
Integer map of local boundary coeffs to local boundary system numbering.
Array< OneD, NekDouble > m_bndCondCoeffsToLocalCoeffsSign
Integer map of sign of bnd cond coeffs to local coefficients.
void UniversalAssemble(Array< OneD, NekDouble > &pGlobal) const
int m_numPatches
The number of patches (~elements) in the current level.
int m_numGlobalBndCoeffs
Total number of global boundary coefficients.
Base class for all multi-elemental spectral/hp expansions.
Definition ExpList.h:98
int GetCoeff_Offset(int n) const
Get the start offset position for a local contiguous list of coeffs correspoinding to element n.
Definition ExpList.h:2197
std::shared_ptr< LibUtilities::SessionReader > GetSession() const
Returns the session object.
Definition ExpList.h:990
const std::shared_ptr< LocalRegions::ExpansionVector > GetExp() const
This function returns the vector of elements in the expansion.
Definition ExpList.h:2189
std::shared_ptr< LibUtilities::Comm > GetComm() const
Returns the comm object.
Definition ExpList.h:995
Array< OneD, DataType > & GetPtr()
int GetVid(int i) const
Returns global id of vertex i of this object.
Definition Geometry.h:345
void GetTraceInteriorToElementMap(const int tid, Array< OneD, unsigned int > &maparray, Array< OneD, int > &signarray, const Orientation traceOrient=eForwards)
int GetTraceNcoeffs(const int i) const
This function returns the number of expansion coefficients belonging to the i-th trace.
static gs_data * Init(const Nektar::Array< OneD, long > &pId, const LibUtilities::CommSharedPtr &pComm, bool verbose=true)
Initialise Gather-Scatter map.
Definition GsLib.hpp:190
static void Gather(Nektar::Array< OneD, NekDouble > pU, gs_op pOp, gs_data *pGsh, Nektar::Array< OneD, NekDouble > pBuffer=NullNekDouble1DArray)
Performs a gather-scatter operation of the provided values.
Definition GsLib.hpp:278
@ gs_add
Definition GsLib.hpp:60
@ gs_max
Definition GsLib.hpp:63
@ gs_min
Definition GsLib.hpp:62
static void Unique(const Nektar::Array< OneD, long > &pId, const LibUtilities::CommSharedPtr &pComm)
Updates pId to negate all-but-one references to each universal ID.
Definition GsLib.hpp:225
static void Finalise(gs_data *pGsh)
Deallocates the GSLib mapping data.
Definition GsLib.hpp:248
constexpr int getNumberOfBndCoefficients(int Na, int Nb)
constexpr int getNumberOfCoefficients(int Na, int Nb)
constexpr int getNumberOfBndCoefficients(int Na, int Nb)
constexpr int getNumberOfCoefficients(int Na, int Nb)
std::shared_ptr< SessionReader > SessionReaderSharedPtr
std::shared_ptr< Comm > CommSharedPtr
Pointer to a Communicator object.
Definition Comm.h:55
@ eModified_B
Principle Modified Functions .
Definition BasisType.h:49
@ eModified_C
Principle Modified Functions .
Definition BasisType.h:50
@ eModifiedPyr_C
Principle Modified Functions.
Definition BasisType.h:53
@ eModified_A
Principle Modified Functions .
Definition BasisType.h:48
std::shared_ptr< Expansion > ExpansionSharedPtr
Definition Expansion.h:66
std::vector< ExpansionSharedPtr > ExpansionVector
Definition Expansion.h:68
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
std::shared_ptr< AssemblyMapCG > AssemblyMapCGSharedPtr
const char *const GlobalSysSolnTypeMap[]
std::tuple< int, int, NekDouble > ExtraDirDof
std::vector< std::map< int, int > > DofGraph
pair< int, StdRegions::Orientation > DeterminePeriodicEdgeOrientId(int meshEdgeId, StdRegions::Orientation edgeOrient, const vector< PeriodicEntity > &periodicEdges)
Determine orientation of an edge to its periodic equivalents, as well as the ID of the representative...
std::shared_ptr< AssemblyMap > AssemblyMapSharedPtr
Definition AssemblyMap.h:50
std::map< int, std::vector< PeriodicEntity > > PeriodicMap
StdRegions::Orientation DeterminePeriodicFaceOrient(StdRegions::Orientation faceOrient, StdRegions::Orientation perFaceOrient)
Determine relative orientation between two faces.
std::size_t hash_range(Iter first, Iter last)
Definition HashUtils.hpp:64
void Gathr(I n, const T *x, const I *y, T *z)
Gather vector z[i] = x[y[i]].
Definition Vmath.hpp:507
T Vsum(int n, const T *x, const int incx)
Subtract return sum(x)
Definition Vmath.hpp:608
void Scatr(int n, const T *x, const int *y, T *z)
Scatter vector z[y[i]] = x[i].
Definition Vmath.hpp:539
void Assmb(int n, const T *x, const int *y, T *z)
Assemble z[y[i]] += x[i]; z should be zero'd first.
Definition Vmath.hpp:577
int Imax(int n, const T *x, const int incx)
Return the index of the maximum element in x.
Definition Vmath.hpp:623
void Zero(int n, T *x, const int incx)
Zero vector.
Definition Vmath.hpp:273
T Vmax(int n, const T *x, const int incx)
Return the maximum element in x – called vmax to avoid conflict with max.
Definition Vmath.hpp:644
void Vcopy(int n, const T *x, const int incx, T *y, const int incy)
Definition Vmath.hpp:825
STL namespace.
scalarT< T > min(scalarT< T > lhs, scalarT< T > rhs)
Definition scalar.hpp:300