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 break;
951 }
952 // associate to other vertices
953 for (k = 0; k < nVerts; k++)
954 {
955 if (localVerts[k + localVertOffset] == -1)
956 {
957 break;
958 }
959 if (k != j)
960 {
961 boost::add_edge((size_t)localVerts[j + localVertOffset],
962 (size_t)localVerts[k + localVertOffset],
963 boostGraphObj);
964 }
965 }
966 // associate to other edges
967 for (k = 0; k < nEdges; k++)
968 {
969 if (localEdges[k + localEdgeOffset] == -1)
970 {
971 break;
972 }
973 boost::add_edge((size_t)localVerts[j + localVertOffset],
974 (size_t)localEdges[k + localEdgeOffset],
975 boostGraphObj);
976 }
977 // associate to other faces
978 for (k = 0; k < nFaces; k++)
979 {
980 if (localFaces[k + localFaceOffset] == -1)
981 {
982 break;
983 }
984 boost::add_edge((size_t)localVerts[j + localVertOffset],
985 (size_t)localFaces[k + localFaceOffset],
986 boostGraphObj);
987 }
988 }
989
990 // Edges
991 for (j = 0; j < nEdges; j++)
992 {
993 if (localEdges[j + localEdgeOffset] == -1)
994 {
995 break;
996 }
997 // Associate to other edges
998 for (k = 0; k < nEdges; k++)
999 {
1000 if (localEdges[k + localEdgeOffset] == -1)
1001 {
1002 break;
1003 }
1004 if (k != j)
1005 {
1006 boost::add_edge((size_t)localEdges[j + localEdgeOffset],
1007 (size_t)localEdges[k + localEdgeOffset],
1008 boostGraphObj);
1009 }
1010 }
1011 // Associate to vertices
1012 for (k = 0; k < nVerts; k++)
1013 {
1014 if (localVerts[k + localVertOffset] == -1)
1015 {
1016 break;
1017 }
1018 boost::add_edge((size_t)localEdges[j + localEdgeOffset],
1019 (size_t)localVerts[k + localVertOffset],
1020 boostGraphObj);
1021 }
1022 // Associate to faces
1023 for (k = 0; k < nFaces; k++)
1024 {
1025 if (localFaces[k + localFaceOffset] == -1)
1026 {
1027 break;
1028 }
1029 boost::add_edge((size_t)localEdges[j + localEdgeOffset],
1030 (size_t)localFaces[k + localFaceOffset],
1031 boostGraphObj);
1032 }
1033 }
1034
1035 // Faces
1036 for (j = 0; j < nFaces; j++)
1037 {
1038 if (localFaces[j + localFaceOffset] == -1)
1039 {
1040 break;
1041 }
1042 // Associate to other faces
1043 for (k = 0; k < nFaces; k++)
1044 {
1045 if (localFaces[k + localFaceOffset] == -1)
1046 {
1047 break;
1048 }
1049 if (k != j)
1050 {
1051 boost::add_edge((size_t)localFaces[j + localFaceOffset],
1052 (size_t)localFaces[k + localFaceOffset],
1053 boostGraphObj);
1054 }
1055 }
1056 // Associate to vertices
1057 for (k = 0; k < nVerts; k++)
1058 {
1059 if (localVerts[k + localVertOffset] == -1)
1060 {
1061 break;
1062 }
1063 boost::add_edge((size_t)localFaces[j + localFaceOffset],
1064 (size_t)localVerts[k + localVertOffset],
1065 boostGraphObj);
1066 }
1067 // Associate to edges
1068 for (k = 0; k < nEdges; k++)
1069 {
1070 if (localEdges[k + localEdgeOffset] == -1)
1071 {
1072 break;
1073 }
1074 boost::add_edge((size_t)localFaces[j + localFaceOffset],
1075 (size_t)localEdges[k + localEdgeOffset],
1076 boostGraphObj);
1077 }
1078 }
1079
1080 localVertOffset += nVerts;
1081 localEdgeOffset += nEdges;
1082 localFaceOffset += nFaces;
1083 }
1084
1085 // Container to store vertices of the graph which correspond to
1086 // degrees of freedom along the boundary and periodic BCs.
1087 set<int> partVerts;
1088
1091 {
1092 vector<long> procVerts, procEdges, procFaces;
1093 set<int> foundVerts, foundEdges, foundFaces;
1094
1095 // Loop over element and construct the procVerts and procEdges
1096 // vectors, which store the geometry IDs of mesh vertices and
1097 // edges respectively which are local to this process.
1098 for (i = cnt = 0; i < locExpVector.size(); ++i)
1099 {
1100 int elmtid = i;
1101 exp = locExpVector[elmtid];
1102 for (j = 0; j < exp->GetNverts(); ++j)
1103 {
1104 int vid = exp->GetGeom()->GetVid(j) + 1;
1105 if (foundVerts.count(vid) == 0)
1106 {
1107 procVerts.push_back(vid);
1108 foundVerts.insert(vid);
1109 }
1110 }
1111
1112 for (j = 0; j < exp->GetGeom()->GetNumEdges(); ++j)
1113 {
1114 int eid = exp->GetGeom()->GetEid(j) + 1;
1115
1116 if (foundEdges.count(eid) == 0)
1117 {
1118 procEdges.push_back(eid);
1119 foundEdges.insert(eid);
1120 }
1121 }
1122
1123 for (j = 0; j < exp->GetGeom()->GetNumFaces(); ++j)
1124 {
1125 int fid = exp->GetGeom()->GetFid(j) + 1;
1126
1127 if (foundFaces.count(fid) == 0)
1128 {
1129 procFaces.push_back(fid);
1130 foundFaces.insert(fid);
1131 }
1132 }
1133 }
1134
1135 int unique_verts = foundVerts.size();
1136 int unique_edges = foundEdges.size();
1137 int unique_faces = foundFaces.size();
1138
1139 bool verbose = m_session->DefinesCmdLineArgument("verbose");
1140
1141 // Now construct temporary GS objects. These will be used to
1142 // populate the arrays tmp3 and tmp4 with the multiplicity of
1143 // the vertices and edges respectively to identify those
1144 // vertices and edges which are located on partition boundary.
1145 Array<OneD, long> vertArray(unique_verts, &procVerts[0]);
1146 Gs::gs_data *tmp1 = Gs::Init(vertArray, vRowComm, verbose);
1147 Array<OneD, NekDouble> tmp4(unique_verts, 1.0);
1148 Array<OneD, NekDouble> tmp5(unique_edges, 1.0);
1149 Array<OneD, NekDouble> tmp6(unique_faces, 1.0);
1150 Gs::Gather(tmp4, Gs::gs_add, tmp1);
1151 Gs::Finalise(tmp1);
1152
1153 if (unique_edges > 0)
1154 {
1155 Array<OneD, long> edgeArray(unique_edges, &procEdges[0]);
1156 Gs::gs_data *tmp2 = Gs::Init(edgeArray, vRowComm, verbose);
1157 Gs::Gather(tmp5, Gs::gs_add, tmp2);
1158 Gs::Finalise(tmp2);
1159 }
1160
1161 if (unique_faces > 0)
1162 {
1163 Array<OneD, long> faceArray(unique_faces, &procFaces[0]);
1164 Gs::gs_data *tmp3 = Gs::Init(faceArray, vRowComm, verbose);
1165 Gs::Gather(tmp6, Gs::gs_add, tmp3);
1166 Gs::Finalise(tmp3);
1167 }
1168
1169 // Finally, fill the partVerts set with all non-Dirichlet
1170 // vertices which lie on a partition boundary.
1171 for (i = 0; i < unique_verts; ++i)
1172 {
1173 if (tmp4[i] > 1.0)
1174 {
1175 if (graph[0].count(procVerts[i] - 1) == 0)
1176 {
1177 partVerts.insert(tempGraph[0][procVerts[i] - 1]);
1178 }
1179 }
1180 }
1181
1182 for (i = 0; i < unique_edges; ++i)
1183 {
1184 if (tmp5[i] > 1.0)
1185 {
1186 if (graph[1].count(procEdges[i] - 1) == 0)
1187 {
1188 partVerts.insert(tempGraph[1][procEdges[i] - 1]);
1189 }
1190 }
1191 }
1192
1193 for (i = 0; i < unique_faces; ++i)
1194 {
1195 if (tmp6[i] > 1.0)
1196 {
1197 if (graph[2].count(procFaces[i] - 1) == 0)
1198 {
1199 partVerts.insert(tempGraph[2][procFaces[i] - 1]);
1200 }
1201 }
1202 }
1203
1204 // Now fill with all vertices on periodic BCs
1205 for (auto &pIt : periodicVerts)
1206 {
1207 if (graph[0].count(pIt.first) == 0)
1208 {
1209 partVerts.insert(tempGraph[0][pIt.first]);
1210 }
1211 }
1212 for (auto &pIt : periodicEdges)
1213 {
1214 if (graph[1].count(pIt.first) == 0)
1215 {
1216 partVerts.insert(tempGraph[1][pIt.first]);
1217 }
1218 }
1219 for (auto &pIt : periodicFaces)
1220 {
1221 if (graph[2].count(pIt.first) == 0)
1222 {
1223 partVerts.insert(tempGraph[2][pIt.first]);
1224 }
1225 }
1226 }
1227
1228 int nGraphVerts = tempGraphVertId;
1229 Array<OneD, int> perm(nGraphVerts);
1230 Array<OneD, int> iperm(nGraphVerts);
1231
1232 if (nGraphVerts)
1233 {
1234 switch (m_solnType)
1235 {
1236 case eDirectFullMatrix:
1237 case eIterativeFull:
1239 case ePETScStaticCond:
1240 case ePETScFullMatrix:
1241 case eXxtFullMatrix:
1242 case eXxtStaticCond:
1243 {
1244 NoReordering(boostGraphObj, perm, iperm);
1245 break;
1246 }
1247
1248 case eDirectStaticCond:
1249 {
1250 CuthillMckeeReordering(boostGraphObj, perm, iperm);
1251 break;
1252 }
1253
1258 {
1259 MultiLevelBisectionReordering(boostGraphObj, perm, iperm,
1260 bottomUpGraph, partVerts,
1261 mdswitch);
1262 break;
1263 }
1264 default:
1265 {
1266 ASSERTL0(false,
1267 "Unrecognised solution type: " +
1268 std::string(GlobalSysSolnTypeMap[m_solnType]));
1269 }
1270 }
1271 }
1272
1273 // For parallel multi-level static condensation determine the lowest
1274 // static condensation level amongst processors.
1279 bottomUpGraph)
1280 {
1281 m_lowestStaticCondLevel = bottomUpGraph->GetNlevels() - 1;
1283 }
1284 else
1285 {
1287 }
1288
1289 /**
1290 * STEP 4: Fill the #graph[0] and
1291 * #graph[1] with the optimal ordering from boost.
1292 */
1293 for (auto &mapIt : tempGraph[0])
1294 {
1295 graph[0][mapIt.first] = iperm[mapIt.second] + graphVertId;
1296 }
1297 for (auto &mapIt : tempGraph[1])
1298 {
1299 graph[1][mapIt.first] = iperm[mapIt.second] + graphVertId;
1300 }
1301 for (auto &mapIt : tempGraph[2])
1302 {
1303 graph[2][mapIt.first] = iperm[mapIt.second] + graphVertId;
1304 }
1305
1306 return nGraphVerts;
1307}
1308
1309/**
1310 *
1311 */
1314 const int numLocalCoeffs, const ExpList &locExp,
1315 const BndCondExp &bndCondExp, const BndCond &bndConditions,
1316 const bool checkIfSystemSingular, const std::string variable,
1317 const PeriodicMap &periodicVerts, const PeriodicMap &periodicEdges,
1318 const PeriodicMap &periodicFaces)
1319 : AssemblyMap(pSession, locExp.GetComm(), variable)
1320{
1321 int i, j, k;
1322 int p, q, numModes0, numModes1;
1323 int cnt = 0;
1324 int meshVertId, meshEdgeId, meshEdgeId2, meshFaceId, meshFaceId2;
1325 int globalId;
1326 int nEdgeInteriorCoeffs;
1327 int firstNonDirGraphVertId;
1328 LibUtilities::CommSharedPtr vRowComm = m_comm->GetRowComm();
1330 StdRegions::Orientation edgeOrient;
1331 StdRegions::Orientation faceOrient;
1332 Array<OneD, unsigned int> edgeInteriorMap;
1333 Array<OneD, int> edgeInteriorSign;
1334 Array<OneD, unsigned int> faceInteriorMap;
1335 Array<OneD, int> faceInteriorSign;
1336
1337 const LocalRegions::ExpansionVector &locExpVector = *(locExp.GetExp());
1338
1339 bool verbose = m_session->DefinesCmdLineArgument("verbose");
1340
1341 m_signChange = false;
1342
1343 // Stores vertex, edge and face reordered vertices.
1344 DofGraph graph(3);
1345 DofGraph dofs(3);
1346 vector<map<int, int>> faceModes(2);
1347 map<int, LibUtilities::ShapeType> faceType;
1348
1349 set<int> extraDirVerts, extraDirEdges;
1351
1352 // Construct list of number of degrees of freedom for each vertex,
1353 // edge and face.
1354 for (i = 0; i < locExpVector.size(); ++i)
1355 {
1356 exp = locExpVector[i];
1357
1358 for (j = 0; j < exp->GetNverts(); ++j)
1359 {
1360 dofs[0][exp->GetGeom()->GetVid(j)] = 1;
1361 }
1362
1363 for (j = 0; j < exp->GetGeom()->GetNumEdges(); ++j)
1364 {
1365 int nEdgeInt;
1366 if (exp->GetGeom()->GetNumFaces())
1367 {
1368 nEdgeInt =
1369 exp->as<LocalRegions::Expansion3D>()->GetEdgeNcoeffs(j) - 2;
1370 }
1371 else
1372 {
1373 nEdgeInt = exp->GetTraceNcoeffs(j) - 2;
1374 }
1375
1376 if (dofs[1].count(exp->GetGeom()->GetEid(j)) > 0)
1377 {
1378 if (dofs[1][exp->GetGeom()->GetEid(j)] != nEdgeInt)
1379 {
1380 ASSERTL0(
1381 (exp->GetBasisType(0) == LibUtilities::eModified_A) ||
1382 (exp->GetBasisType(1) ==
1384 (exp->GetBasisType(2) ==
1386 (exp->GetBasisType(2) ==
1388 "CG with variable order only available with "
1389 "modal expansion");
1390 }
1391 dofs[1][exp->GetGeom()->GetEid(j)] =
1392 min(dofs[1][exp->GetGeom()->GetEid(j)], nEdgeInt);
1393 }
1394 else
1395 {
1396 dofs[1][exp->GetGeom()->GetEid(j)] = nEdgeInt;
1397 }
1398 }
1399
1400 for (j = 0; j < exp->GetGeom()->GetNumFaces(); ++j)
1401 {
1402 faceOrient = exp->GetGeom()->GetForient(j);
1403 meshFaceId = exp->GetGeom()->GetFid(j);
1404 exp->GetTraceNumModes(j, numModes0, numModes1, faceOrient);
1405
1406 if (faceModes[0].count(meshFaceId) > 0)
1407 {
1408 faceModes[0][meshFaceId] =
1409 min(faceModes[0][meshFaceId], numModes0);
1410
1411 faceModes[1][meshFaceId] =
1412 min(faceModes[1][meshFaceId], numModes1);
1413 }
1414 else
1415 {
1416 faceModes[0][meshFaceId] = numModes0;
1417 faceModes[1][meshFaceId] = numModes1;
1418
1419 // Get shape of this face
1420 faceType[meshFaceId] =
1421 exp->GetGeom()->GetFace(j)->GetShapeType();
1422 }
1423 }
1424 }
1425
1426 // Add non-local periodic dofs to the map
1427 for (auto &pIt : periodicEdges)
1428 {
1429 for (i = 0; i < pIt.second.size(); ++i)
1430 {
1431 meshEdgeId2 = pIt.second[i].id;
1432 if (dofs[1].count(meshEdgeId2) == 0)
1433 {
1434 dofs[1][meshEdgeId2] = 1e6;
1435 }
1436 }
1437 }
1438 for (auto &pIt : periodicFaces)
1439 {
1440 for (i = 0; i < pIt.second.size(); ++i)
1441 {
1442 meshFaceId2 = pIt.second[i].id;
1443 if (faceModes[0].count(meshFaceId2) == 0)
1444 {
1445 faceModes[0][meshFaceId2] = 1e6;
1446 faceModes[1][meshFaceId2] = 1e6;
1447 }
1448 }
1449 }
1450
1451 // Now use information from all partitions to determine the correct
1452 // size
1453
1454 // edges
1455 Array<OneD, long> edgeId(dofs[1].size());
1456 Array<OneD, NekDouble> edgeDof(dofs[1].size());
1457 i = 0;
1458 for (auto &dofIt : dofs[1])
1459 {
1460 edgeId[i] = dofIt.first + 1;
1461 edgeDof[i++] = (NekDouble)dofIt.second;
1462 }
1463 Gs::gs_data *tmp = Gs::Init(edgeId, vRowComm, verbose);
1464 Gs::Gather(edgeDof, Gs::gs_min, tmp);
1465 Gs::Finalise(tmp);
1466 for (i = 0; i < dofs[1].size(); i++)
1467 {
1468 dofs[1][edgeId[i] - 1] = (int)(edgeDof[i] + 0.5);
1469 }
1470 // Periodic edges
1471 for (auto &pIt : periodicEdges)
1472 {
1473 meshEdgeId = pIt.first;
1474 for (i = 0; i < pIt.second.size(); ++i)
1475 {
1476 meshEdgeId2 = pIt.second[i].id;
1477 if (dofs[1][meshEdgeId2] < dofs[1][meshEdgeId])
1478 {
1479 dofs[1][meshEdgeId] = dofs[1][meshEdgeId2];
1480 }
1481 }
1482 }
1483 // faces
1484 Array<OneD, long> faceId(faceModes[0].size());
1485 Array<OneD, NekDouble> faceP(faceModes[0].size());
1486 Array<OneD, NekDouble> faceQ(faceModes[0].size());
1487
1488 i = 0;
1489 for (auto dofIt = faceModes[0].begin(), dofIt2 = faceModes[1].begin();
1490 dofIt != faceModes[0].end(); dofIt++, dofIt2++, i++)
1491 {
1492 faceId[i] = dofIt->first + 1;
1493 faceP[i] = (NekDouble)dofIt->second;
1494 faceQ[i] = (NekDouble)dofIt2->second;
1495 }
1496 Gs::gs_data *tmp2 = Gs::Init(faceId, vRowComm, verbose);
1497 Gs::Gather(faceP, Gs::gs_min, tmp2);
1498 Gs::Gather(faceQ, Gs::gs_min, tmp2);
1499 Gs::Finalise(tmp2);
1500 for (i = 0; i < faceModes[0].size(); i++)
1501 {
1502 faceModes[0][faceId[i] - 1] = (int)(faceP[i] + 0.5);
1503 faceModes[1][faceId[i] - 1] = (int)(faceQ[i] + 0.5);
1504 }
1505 // Periodic faces
1506 for (auto &pIt : periodicFaces)
1507 {
1508 meshFaceId = pIt.first;
1509 for (i = 0; i < pIt.second.size(); ++i)
1510 {
1511 meshFaceId2 = pIt.second[i].id;
1512 if (faceModes[0][meshFaceId2] < faceModes[0][meshFaceId])
1513 {
1514 faceModes[0][meshFaceId] = faceModes[0][meshFaceId2];
1515 }
1516 if (faceModes[1][meshFaceId2] < faceModes[1][meshFaceId])
1517 {
1518 faceModes[1][meshFaceId] = faceModes[1][meshFaceId2];
1519 }
1520 }
1521 }
1522 // Calculate number of dof in each face
1523 int P, Q;
1524 for (i = 0; i < faceModes[0].size(); i++)
1525 {
1526 P = faceModes[0][faceId[i] - 1];
1527 Q = faceModes[1][faceId[i] - 1];
1528 if (faceType[faceId[i] - 1] == LibUtilities::eQuadrilateral)
1529 {
1530 // Quad face
1531 dofs[2][faceId[i] - 1] =
1534 }
1535 else
1536 {
1537 // Tri face
1538 dofs[2][faceId[i] - 1] =
1541 }
1542 }
1543
1544 Array<OneD, const BndCond> bndCondVec(1, bndConditions);
1545
1546 // Note that nExtraDirichlet is not used in the logic below; it just
1547 // needs to be set so that the coupled solver in
1548 // IncNavierStokesSolver can work.
1549 int nExtraDirichlet;
1550 int mdswitch;
1551 m_session->LoadParameter("MDSwitch", mdswitch, 10);
1552
1553 int nGraphVerts = CreateGraph(
1554 locExp, bndCondExp, bndCondVec, checkIfSystemSingular, periodicVerts,
1555 periodicEdges, periodicFaces, graph, bottomUpGraph, extraDirVerts,
1556 extraDirEdges, firstNonDirGraphVertId, nExtraDirichlet, mdswitch);
1557
1558 /*
1559 * Set up an array which contains the offset information of the
1560 * different graph vertices.
1561 *
1562 * This basically means to identify to how many global degrees of
1563 * freedom the individual graph vertices correspond. Obviously,
1564 * the graph vertices corresponding to the mesh-vertices account
1565 * for a single global DOF. However, the graph vertices
1566 * corresponding to the element edges correspond to N-2 global DOF
1567 * where N is equal to the number of boundary modes on this edge.
1568 */
1569 Array<OneD, int> graphVertOffset(
1570 graph[0].size() + graph[1].size() + graph[2].size() + 1, 0);
1571
1572 graphVertOffset[0] = 0;
1573
1574 for (i = 0; i < locExpVector.size(); ++i)
1575 {
1576 exp = locExpVector[i];
1577
1578 for (j = 0; j < exp->GetNverts(); ++j)
1579 {
1580 meshVertId = exp->GetGeom()->GetVid(j);
1581 graphVertOffset[graph[0][meshVertId] + 1] = 1;
1582 }
1583
1584 for (j = 0; j < exp->GetGeom()->GetNumEdges(); ++j)
1585 {
1586 if (exp->GetGeom()->GetNumFaces()) // 3D version
1587 {
1588 nEdgeInteriorCoeffs =
1589 exp->as<LocalRegions::Expansion3D>()->GetEdgeNcoeffs(j) - 2;
1590 }
1591 else
1592 {
1593 nEdgeInteriorCoeffs = exp->GetTraceNcoeffs(j) - 2;
1594 }
1595 meshEdgeId = exp->GetGeom()->GetEid(j);
1596 graphVertOffset[graph[1][meshEdgeId] + 1] = dofs[1][meshEdgeId];
1597
1598 // Need a sign vector for modal expansions if nEdgeCoeffs
1599 // >=3 (not 4 because of variable order case)
1600 if (nEdgeInteriorCoeffs &&
1601 (exp->GetBasisType(0) == LibUtilities::eModified_A))
1602 {
1603 m_signChange = true;
1604 }
1605 }
1606
1607 for (j = 0; j < exp->GetGeom()->GetNumFaces(); ++j)
1608 {
1609 meshFaceId = exp->GetGeom()->GetFid(j);
1610 graphVertOffset[graph[2][meshFaceId] + 1] = dofs[2][meshFaceId];
1611 }
1612 }
1613
1614 for (i = 1; i < graphVertOffset.size(); i++)
1615 {
1616 graphVertOffset[i] += graphVertOffset[i - 1];
1617 }
1618
1619 // Allocate the proper amount of space for the class-data
1620 m_numLocalCoeffs = numLocalCoeffs;
1621 m_numGlobalDirBndCoeffs = graphVertOffset[firstNonDirGraphVertId];
1629
1630 // If required, set up the sign-vector
1631 if (m_signChange)
1632 {
1638 }
1639
1641 m_numPatches = locExpVector.size();
1644 for (i = 0; i < m_numPatches; ++i)
1645 {
1647 (unsigned int)locExpVector[i]->NumBndryCoeffs();
1649 (unsigned int)locExpVector[i]->GetNcoeffs() -
1650 locExpVector[i]->NumBndryCoeffs();
1651 }
1652
1653 /**
1654 * STEP 6: Now, all ingredients are ready to set up the actual
1655 * local to global mapping.
1656 *
1657 * The remainder of the map consists of the element-interior
1658 * degrees of freedom. This leads to the block-diagonal submatrix
1659 * as each element-interior mode is globally orthogonal to modes
1660 * in all other elements.
1661 */
1662 cnt = 0;
1663
1664 // Loop over all the elements in the domain
1665 int cntbdry = 0;
1666 int cntint = 0;
1667 for (i = 0; i < locExpVector.size(); ++i)
1668 {
1669 exp = locExpVector[i];
1670 cnt = locExp.GetCoeff_Offset(i);
1671
1672 int nbdry = exp->NumBndryCoeffs();
1673 int nint = exp->GetNcoeffs() - nbdry;
1674
1675 Array<OneD, unsigned int> bmap(nbdry);
1676 Array<OneD, unsigned int> imap(nint);
1677
1678 exp->GetBoundaryMap(bmap);
1679 exp->GetInteriorMap(imap);
1680
1681 for (j = 0; j < nbdry; ++j)
1682 {
1683 m_localToLocalBndMap[cntbdry++] = cnt + bmap[j];
1684 }
1685
1686 for (j = 0; j < nint; ++j)
1687 {
1688 m_localToLocalIntMap[cntint++] = cnt + imap[j];
1689 }
1690
1691 for (j = 0; j < exp->GetNverts(); ++j)
1692 {
1693 meshVertId = exp->GetGeom()->GetVid(j);
1694
1695 // Set the global DOF for vertex j of element i
1696 m_localToGlobalMap[cnt + exp->GetVertexMap(j)] =
1697 graphVertOffset[graph[0][meshVertId]];
1698 }
1699
1700 for (j = 0; j < exp->GetGeom()->GetNumEdges(); ++j)
1701 {
1702 if (exp->GetGeom()->GetNumFaces())
1703 {
1704 nEdgeInteriorCoeffs =
1705 exp->as<LocalRegions::Expansion3D>()->GetEdgeNcoeffs(j) - 2;
1706 }
1707 else
1708 {
1709 nEdgeInteriorCoeffs = exp->GetTraceNcoeffs(j) - 2;
1710 }
1711 edgeOrient = exp->GetGeom()->GetEorient(j);
1712 meshEdgeId = exp->GetGeom()->GetEid(j);
1713
1714 auto pIt = periodicEdges.find(meshEdgeId);
1715
1716 // See if this edge is periodic. If it is, then we map all
1717 // edges to the one with lowest ID, and align all
1718 // coefficients to this edge orientation.
1719 if (pIt != periodicEdges.end())
1720 {
1721 pair<int, StdRegions::Orientation> idOrient =
1722 DeterminePeriodicEdgeOrientId(meshEdgeId, edgeOrient,
1723 pIt->second);
1724 edgeOrient = idOrient.second;
1725 }
1726
1727 if (exp->GetGeom()->GetNumFaces())
1728 {
1729 exp->as<LocalRegions::Expansion3D>()
1730 ->GetEdgeInteriorToElementMap(j, edgeInteriorMap,
1731 edgeInteriorSign, edgeOrient);
1732 }
1733 else
1734 {
1735 exp->GetTraceInteriorToElementMap(j, edgeInteriorMap,
1736 edgeInteriorSign, edgeOrient);
1737 }
1738
1739 // Set the global DOF's for the interior modes of edge j
1740 for (k = 0; k < dofs[1][meshEdgeId]; ++k)
1741 {
1742 m_localToGlobalMap[cnt + edgeInteriorMap[k]] =
1743 graphVertOffset[graph[1][meshEdgeId]] + k;
1744 }
1745 for (k = dofs[1][meshEdgeId]; k < nEdgeInteriorCoeffs; ++k)
1746 {
1747 m_localToGlobalMap[cnt + edgeInteriorMap[k]] = 0;
1748 }
1749
1750 // Fill the sign vector if required
1751 if (m_signChange)
1752 {
1753 for (k = 0; k < dofs[1][meshEdgeId]; ++k)
1754 {
1755 m_localToGlobalSign[cnt + edgeInteriorMap[k]] =
1756 (NekDouble)edgeInteriorSign[k];
1757 }
1758 for (k = dofs[1][meshEdgeId]; k < nEdgeInteriorCoeffs; ++k)
1759 {
1760 m_localToGlobalSign[cnt + edgeInteriorMap[k]] = 0.0;
1761 }
1762 }
1763 }
1764
1765 for (j = 0; j < exp->GetGeom()->GetNumFaces(); ++j)
1766 {
1767 faceOrient = exp->GetGeom()->GetForient(j);
1768 meshFaceId = exp->GetGeom()->GetFid(j);
1769
1770 auto pIt = periodicFaces.find(meshFaceId);
1771
1772 if (pIt != periodicFaces.end() &&
1773 meshFaceId == min(meshFaceId, pIt->second[0].id))
1774 {
1775 faceOrient = DeterminePeriodicFaceOrient(faceOrient,
1776 pIt->second[0].orient);
1777 }
1778
1779 exp->GetTraceInteriorToElementMap(j, faceInteriorMap,
1780 faceInteriorSign, faceOrient);
1781
1782 // Set the global DOF's for the interior modes of face j
1783 exp->GetTraceNumModes(j, numModes0, numModes1, faceOrient);
1784 switch (faceType[meshFaceId])
1785 {
1787 {
1788 int kLoc = 0;
1789 k = 0;
1790 for (q = 2; q < numModes1; q++)
1791 {
1792 for (p = 2; p < numModes0; p++)
1793 {
1794 if ((p < faceModes[0][meshFaceId]) &&
1795 (q < faceModes[1][meshFaceId]))
1796 {
1797 m_localToGlobalMap[cnt +
1798 faceInteriorMap[kLoc]] =
1799 graphVertOffset[graph[2][meshFaceId]] + k;
1800 if (m_signChange)
1801 {
1803 faceInteriorMap[kLoc]] =
1804 (NekDouble)faceInteriorSign[kLoc];
1805 }
1806 k++;
1807 }
1808 else
1809 {
1810 m_localToGlobalMap[cnt +
1811 faceInteriorMap[kLoc]] = 0;
1812 if (m_signChange)
1813 {
1815 faceInteriorMap[kLoc]] =
1816 0.0;
1817 }
1818 }
1819 kLoc++;
1820 }
1821 }
1822 }
1823 break;
1825 {
1826 int kLoc = 0;
1827 k = 0;
1828 for (p = 2; p < numModes0; p++)
1829 {
1830 for (q = 1; q < numModes1 - p; q++)
1831 {
1832 if ((p < faceModes[0][meshFaceId]) &&
1833 (p + q < faceModes[1][meshFaceId]))
1834 {
1835 m_localToGlobalMap[cnt +
1836 faceInteriorMap[kLoc]] =
1837 graphVertOffset[graph[2][meshFaceId]] + k;
1838 if (m_signChange)
1839 {
1841 faceInteriorMap[kLoc]] =
1842 (NekDouble)faceInteriorSign[kLoc];
1843 }
1844 k++;
1845 }
1846 else
1847 {
1848 m_localToGlobalMap[cnt +
1849 faceInteriorMap[kLoc]] = 0;
1850 if (m_signChange)
1851 {
1853 faceInteriorMap[kLoc]] =
1854 0.0;
1855 }
1856 }
1857 kLoc++;
1858 }
1859 }
1860 }
1861 break;
1862 default:
1863 ASSERTL0(false, "Shape not recognised");
1864 break;
1865 }
1866 }
1867 }
1868
1869 // Set up the mapping for the boundary conditions
1870 // Set up boundary mapping
1871 map<int, pair<int, int>> traceToElmtTraceMap;
1872 int id;
1873
1874 for (cnt = i = 0; i < locExpVector.size(); ++i)
1875 {
1876 exp = locExpVector[i];
1877
1878 for (j = 0; j < exp->GetNtraces(); ++j)
1879 {
1880 id = exp->GetGeom()->GetTid(j);
1881
1882 traceToElmtTraceMap[id] = pair<int, int>(i, j);
1883 }
1884 }
1885
1887 Array<OneD, int> signarray;
1888 map<int, pair<int, NekDouble>> GloDirBndCoeffToLocalCoeff;
1889 set<int> CoeffOnDirTrace;
1890
1891 cnt = 0;
1892 int offset = 0;
1893 for (i = 0; i < bndCondExp.size(); i++)
1894 {
1895 set<int> foundExtraVerts, foundExtraEdges;
1896 for (j = 0; j < bndCondExp[i]->GetNumElmts(); j++)
1897 {
1898 bndExp = bndCondExp[i]->GetExp(j);
1899 cnt = offset + bndCondExp[i]->GetCoeff_Offset(j);
1900
1901 int id = bndExp->GetGeom()->GetGlobalID();
1902
1903 ASSERTL1(traceToElmtTraceMap.count(id) > 0,
1904 "Failed to find trace id");
1905
1906 int eid = traceToElmtTraceMap[id].first;
1907 int tid = traceToElmtTraceMap[id].second;
1908
1909 exp = locExpVector[eid];
1910 int dim = exp->GetShapeDimension();
1911
1912 if (dim == 1)
1913 {
1915 locExp.GetCoeff_Offset(eid) + exp->GetVertexMap(tid);
1916 }
1917 else
1918 {
1919 if (dim == 2)
1920 {
1921 exp->GetTraceToElementMap(tid, maparray, signarray,
1922 exp->GetGeom()->GetEorient(tid),
1923 bndExp->GetBasisNumModes(0));
1924 }
1925 else if (dim == 3)
1926 {
1927 exp->GetTraceToElementMap(tid, maparray, signarray,
1928 exp->GetGeom()->GetForient(tid),
1929 bndExp->GetBasisNumModes(0),
1930 bndExp->GetBasisNumModes(1));
1931 }
1932
1933 for (k = 0; k < bndExp->GetNcoeffs(); k++)
1934 {
1936 locExp.GetCoeff_Offset(eid) + maparray[k];
1937 if (m_signChange)
1938 {
1940 signarray[k];
1941 }
1942 }
1943 }
1944
1945 // we now need some information to work out how to
1946 // handle vertices and edges that are only just
1947 // touching a dirichlet boundary (and not the
1948 // whole edge/face)
1949
1950 for (k = 0; k < bndExp->GetNcoeffs(); k++)
1951 {
1952 int locid = m_bndCondCoeffsToLocalCoeffsMap[cnt + k];
1953 int gloid = m_localToGlobalMap[locid];
1954 NekDouble sign = 1.0;
1955
1956 if (m_signChange)
1957 {
1959 }
1960
1961 if (bndConditions[i]->GetBoundaryConditionType() ==
1963 {
1964 bool addid =
1965 (m_signChange && m_localToGlobalSign[locid] == 0)
1966 ? false
1967 : true;
1968
1969 // only add point if sign is +/- 1 since if zero it
1970 // belongs to a mode that is not used in variable p
1971 // expansion
1972 if (addid)
1973 {
1974 CoeffOnDirTrace.insert(locid);
1975
1976 // store the local id and sign from global id
1977 // back to local space;
1978 GloDirBndCoeffToLocalCoeff[gloid] =
1979 pair<int, NekDouble>(locid, sign);
1980 }
1981 }
1982 }
1983 }
1984 offset += bndCondExp[i]->GetNcoeffs();
1985 }
1986
1987 globalId = Vmath::Vmax(m_numLocalCoeffs, &m_localToGlobalMap[0], 1) + 1;
1988 m_numGlobalBndCoeffs = globalId;
1989
1990 // Set up a mapping list of Dirichlet Local Dofs that
1991 // arise due to one vertex or edge just touching a
1992 // Dirichlet boundary and need the value from another
1993 // local coeff that has been filled by the boundary
1994 // coeffs.
1995
1996 Array<OneD, NekDouble> gloParaDirBnd(m_numGlobalBndCoeffs, -1.0);
1997
1999 cnt = 0;
2000 for (i = 0; i < locExpVector.size(); ++i)
2001 {
2002 int gloid;
2003
2004 exp = locExpVector[i];
2005
2006 exp->GetBoundaryMap(bndmap);
2007
2008 for (j = 0; j < bndmap.size(); ++j)
2009 {
2010 k = cnt + bndmap[j];
2011
2012 // exclude if Dirichlet boundary already included in partition
2013 if (CoeffOnDirTrace.count(k) == 0)
2014 {
2015 gloid = m_localToGlobalMap[k];
2016
2017 if (gloid < m_numGlobalDirBndCoeffs) // point on Dir BC
2018 {
2019 if (GloDirBndCoeffToLocalCoeff.count(gloid))
2020 {
2021 int locid = GloDirBndCoeffToLocalCoeff[gloid].first;
2022 NekDouble sign = 1.0;
2023
2024 if (m_signChange)
2025 {
2026 sign = m_localToGlobalSign[locid] *
2028 }
2029
2030 ExtraDirDof DirDofs(k, locid, sign);
2031 // could make same `structure as extraDirDof
2032 m_copyLocalDirDofs.insert(DirDofs);
2033 }
2034 else // else could be on another parallel partition.
2035 {
2036 gloParaDirBnd[gloid] = gloid;
2037 }
2038 }
2039 }
2040 }
2041
2042 cnt += exp->GetNcoeffs();
2043 }
2044
2045 /*
2046 * The boundary condition mapping is generated from the same vertex
2047 * renumbering.
2048 */
2049 cnt = 0;
2050 for (i = 0; i < m_numLocalCoeffs; ++i)
2051 {
2052 if (m_localToGlobalMap[i] == -1)
2053 {
2054 m_localToGlobalMap[i] = globalId++;
2055 }
2056 else
2057 {
2058 if (m_signChange)
2059 {
2061 }
2063 }
2064 }
2065
2066 m_numGlobalCoeffs = globalId;
2067
2068 SetUpUniversalC0ContMap(locExp, periodicVerts, periodicEdges,
2069 periodicFaces);
2070
2071 // Now that universal map is setup reset gloParaDirBnd to
2072 // 0 if no point communicated or universal value of not
2073 // equal to -1.0
2074 for (i = 0; i < m_numGlobalBndCoeffs; ++i)
2075 {
2076 int gloid = gloParaDirBnd[i];
2077 if (gloid == -1)
2078 {
2079 gloParaDirBnd[i] = 0.0;
2080 }
2081 else
2082 {
2083 gloParaDirBnd[i] = m_globalToUniversalMap[gloid];
2084 }
2085 }
2086
2087 // Use parallel boundary communication to set parallel
2088 // dirichlet values on all processors Needs to be after
2089 // SetupUuniversialC0ContMap
2090 Gs::Gather(gloParaDirBnd, Gs::gs_max, m_bndGsh);
2091
2092 // copy global ids back to local values in partition to
2093 // initialise gs communicator.
2095 for (i = 0; i < numLocalCoeffs; ++i)
2096 {
2097 paraDirBnd[i] = 0.0;
2098
2099 int id = m_localToGlobalMap[i];
2100
2101 if (id >= m_numGlobalDirBndCoeffs)
2102 {
2103 continue;
2104 }
2105
2106 paraDirBnd[i] = gloParaDirBnd[id];
2107
2108 if (gloParaDirBnd[id] > 0.0)
2109 {
2110 // gather any sign changes due to edge modes
2111 if (m_signChange)
2112 {
2113 if (m_localToGlobalSign[i] < 0)
2114 {
2115 m_parallelDirBndSign.insert(i);
2116 }
2117 }
2118 }
2119 }
2120
2121 m_dirBndGsh = Gs::Init(paraDirBnd, vRowComm, verbose);
2122
2123 // Set up the local to global map for the next level when using
2124 // multi-level static condensation
2129 nGraphVerts)
2130 {
2131 if (m_staticCondLevel < (bottomUpGraph->GetNlevels() - 1))
2132 {
2133 Array<OneD, int> vwgts_perm(graph[0].size() + graph[1].size() +
2134 graph[2].size() -
2135 firstNonDirGraphVertId);
2136
2137 for (i = 0; i < locExpVector.size(); ++i)
2138 {
2139 exp = locExpVector[i];
2140
2141 for (j = 0; j < exp->GetNverts(); ++j)
2142 {
2143 meshVertId = exp->GetGeom()->GetVid(j);
2144
2145 if (graph[0][meshVertId] >= firstNonDirGraphVertId)
2146 {
2147 vwgts_perm[graph[0][meshVertId] -
2148 firstNonDirGraphVertId] =
2149 dofs[0][meshVertId];
2150 }
2151 }
2152
2153 for (j = 0; j < exp->GetGeom()->GetNumEdges(); ++j)
2154 {
2155 meshEdgeId = exp->GetGeom()->GetEid(j);
2156
2157 if (graph[1][meshEdgeId] >= firstNonDirGraphVertId)
2158 {
2159 vwgts_perm[graph[1][meshEdgeId] -
2160 firstNonDirGraphVertId] =
2161 dofs[1][meshEdgeId];
2162 }
2163 }
2164
2165 for (j = 0; j < exp->GetGeom()->GetNumFaces(); ++j)
2166 {
2167 meshFaceId = exp->GetGeom()->GetFid(j);
2168
2169 if (graph[2][meshFaceId] >= firstNonDirGraphVertId)
2170 {
2171 vwgts_perm[graph[2][meshFaceId] -
2172 firstNonDirGraphVertId] =
2173 dofs[2][meshFaceId];
2174 }
2175 }
2176 }
2177
2178 bottomUpGraph->ExpandGraphWithVertexWeights(vwgts_perm);
2181 bottomUpGraph);
2182 }
2183 }
2184
2186
2187 // Add up hash values if parallel
2188 int hash = m_hash;
2189 vRowComm->AllReduce(hash, LibUtilities::ReduceSum);
2190 m_hash = hash;
2191
2194}
2195
2196/**
2197 *
2198 */
2204
2205/**
2206 * @brief Determine orientation of an edge to its periodic equivalents,
2207 * as well as the ID of the representative edge.
2208 *
2209 * Since an edge may be periodic with more than one other edge (e.g. a
2210 * periodic cube has sets of four periodic edges in each coordinate
2211 * direction), we have to define a 'representative' edge. In this
2212 * assembly map we define it to be the one with the minimum ID. This
2213 * routine is set up to calculate the orientation of a given edge with
2214 * ID @p meshEdgeId with respect to the edge ID.
2215 *
2216 * @param meshEdgeId ID of a periodic edge.
2217 * @param edgeOrient Edge orientation of meshEdgeId with respect to
2218 * its parent element.
2219 * @param periodicEdges The map of all periodic edges.
2220 *
2221 * @return Pair containing the ID of the periodic edge and the
2222 * orientation of @p meshEdgeID with respect to this edge.
2223 */
2224pair<int, StdRegions::Orientation> DeterminePeriodicEdgeOrientId(
2225 int meshEdgeId, StdRegions::Orientation edgeOrient,
2226 const vector<PeriodicEntity> &periodicEdges)
2227{
2228 int minId = periodicEdges[0].id;
2229 int minIdK = 0;
2230 int k;
2231
2232 for (k = 1; k < periodicEdges.size(); ++k)
2233 {
2234 if (periodicEdges[k].id < minId)
2235 {
2236 minId = min(minId, periodicEdges[k].id);
2237 minIdK = k;
2238 }
2239 }
2240
2241 minId = min(minId, meshEdgeId);
2242
2243 if (meshEdgeId != minId)
2244 {
2245 if (periodicEdges[minIdK].orient == StdRegions::eBackwards)
2246 {
2247 // Swap edge orientation
2248 edgeOrient = (edgeOrient == StdRegions::eForwards)
2251 }
2252 }
2253
2254 return make_pair(minId, edgeOrient);
2255}
2256
2257/**
2258 * @brief Determine relative orientation between two faces.
2259 *
2260 * Given the orientation of a local element to its local face, defined
2261 * as @p faceOrient, and @p perFaceOrient which states the alignment of
2262 * one periodic face to the other global face, this routine determines
2263 * the orientation that takes this local element face to the
2264 * global/unique face.
2265 *
2266 * @param faceOrient Orientation of the face with respect to its
2267 * parent element.
2268 * @param perFaceOrient Orientation of the representative/global face.
2269 *
2270 * @return Orientation between the two faces.
2271 */
2273 StdRegions::Orientation faceOrient, StdRegions::Orientation perFaceOrient)
2274{
2275 StdRegions::Orientation returnval = faceOrient;
2276
2277 if (perFaceOrient != StdRegions::eDir1FwdDir1_Dir2FwdDir2)
2278 {
2279 int tmp1 = (int)faceOrient - 5;
2280 int tmp2 = (int)perFaceOrient - 5;
2281
2282 int flipDir1Map[8] = {2, 3, 0, 1, 6, 7, 4, 5};
2283 int flipDir2Map[8] = {1, 0, 3, 2, 5, 4, 7, 6};
2284 int transposeMap[8] = {4, 5, 6, 7, 0, 2, 1, 3};
2285
2286 // Transpose orientation
2287 if (tmp2 > 3)
2288 {
2289 tmp1 = transposeMap[tmp1];
2290 }
2291
2292 // Reverse orientation in direction 1.
2293 if (tmp2 == 2 || tmp2 == 3 || tmp2 == 6 || tmp2 == 7)
2294 {
2295 tmp1 = flipDir1Map[tmp1];
2296 }
2297
2298 // Reverse orientation in direction 2
2299 if (tmp2 % 2 == 1)
2300 {
2301 tmp1 = flipDir2Map[tmp1];
2302 }
2303
2304 returnval = (StdRegions::Orientation)(tmp1 + 5);
2305 }
2306 return returnval;
2307}
2308
2309/**
2310 * Sets up the global to universal mapping of degrees of freedom across
2311 * processors.
2312 */
2314 const PeriodicMap &perVerts,
2315 const PeriodicMap &perEdges,
2316 const PeriodicMap &perFaces)
2317{
2319 int nVert = 0;
2320 int nEdge = 0;
2321 int nFace = 0;
2322 int maxEdgeDof = 0;
2323 int maxFaceDof = 0;
2324 int maxIntDof = 0;
2325 int dof = 0;
2326 int cnt;
2327 int i, j, k, l;
2328 int meshVertId;
2329 int meshEdgeId;
2330 int meshFaceId;
2331 int elementId;
2332 int vGlobalId;
2333 int maxBndGlobalId = 0;
2334 StdRegions::Orientation edgeOrient;
2335 StdRegions::Orientation faceOrient;
2336 Array<OneD, unsigned int> edgeInteriorMap;
2337 Array<OneD, int> edgeInteriorSign;
2338 Array<OneD, unsigned int> faceInteriorMap;
2339 Array<OneD, int> faceInteriorSign;
2340 Array<OneD, unsigned int> interiorMap;
2341
2342 const LocalRegions::ExpansionVector &locExpVector = *(locExp.GetExp());
2343 LibUtilities::CommSharedPtr vRowComm = m_comm->GetRowComm();
2344 const bool verbose = locExp.GetSession()->DefinesCmdLineArgument("verbose");
2345
2353
2354 // Loop over all the elements in the domain to gather mesh data
2355 for (i = 0; i < locExpVector.size(); ++i)
2356 {
2357 exp = locExpVector[i];
2358
2359 int nv = exp->GetNverts();
2360 int ne = exp->GetGeom()->GetNumEdges();
2361 int nf = exp->GetGeom()->GetNumFaces();
2362
2363 nVert += nv;
2364 nEdge += ne;
2365 nFace += nf;
2366
2367 // Loop over all edges (and vertices) of element i
2368 for (j = 0; j < ne; ++j)
2369 {
2370 if (nf)
2371 {
2372 dof =
2373 exp->as<LocalRegions::Expansion3D>()->GetEdgeNcoeffs(j) - 2;
2374 }
2375 else
2376 {
2377 dof = exp->GetTraceNcoeffs(j) - 2;
2378 }
2379
2380 maxEdgeDof = (dof > maxEdgeDof ? dof : maxEdgeDof);
2381 }
2382 for (j = 0; j < nf; ++j)
2383 {
2384 dof = exp->GetTraceIntNcoeffs(j);
2385 maxFaceDof = (dof > maxFaceDof ? dof : maxFaceDof);
2386 }
2387 exp->GetInteriorMap(interiorMap);
2388 dof = interiorMap.size();
2389 maxIntDof = (dof > maxIntDof ? dof : maxIntDof);
2390 }
2391
2392 // Tell other processes about how many dof we have
2393 vRowComm->AllReduce(nVert, LibUtilities::ReduceSum);
2394 vRowComm->AllReduce(nEdge, LibUtilities::ReduceSum);
2395 vRowComm->AllReduce(nFace, LibUtilities::ReduceSum);
2396 vRowComm->AllReduce(maxEdgeDof, LibUtilities::ReduceMax);
2397 vRowComm->AllReduce(maxFaceDof, LibUtilities::ReduceMax);
2398 vRowComm->AllReduce(maxIntDof, LibUtilities::ReduceMax);
2399
2400 // Assemble global to universal mapping for this process
2401 for (i = 0; i < locExpVector.size(); ++i)
2402 {
2403 exp = locExpVector[i];
2404 cnt = locExp.GetCoeff_Offset(i);
2405
2406 int nf = exp->GetGeom()->GetNumFaces();
2407
2408 // Loop over all vertices of element i
2409 for (j = 0; j < exp->GetNverts(); ++j)
2410 {
2411 meshVertId = exp->GetGeom()->GetVid(j);
2412 vGlobalId = m_localToGlobalMap[cnt + exp->GetVertexMap(j)];
2413
2414 auto pIt = perVerts.find(meshVertId);
2415 if (pIt != perVerts.end())
2416 {
2417 for (k = 0; k < pIt->second.size(); ++k)
2418 {
2419 meshVertId = min(meshVertId, pIt->second[k].id);
2420 }
2421 }
2422
2423 m_globalToUniversalMap[vGlobalId] = meshVertId + 1;
2424 m_globalToUniversalBndMap[vGlobalId] =
2425 m_globalToUniversalMap[vGlobalId];
2426 maxBndGlobalId =
2427 (vGlobalId > maxBndGlobalId ? vGlobalId : maxBndGlobalId);
2428 }
2429
2430 // Loop over all edges of element i
2431 for (j = 0; j < exp->GetGeom()->GetNumEdges(); ++j)
2432 {
2433 meshEdgeId = exp->GetGeom()->GetEid(j);
2434 auto pIt = perEdges.find(meshEdgeId);
2435 edgeOrient = exp->GetGeom()->GetEorient(j);
2436
2437 if (pIt != perEdges.end())
2438 {
2439 pair<int, StdRegions::Orientation> idOrient =
2440 DeterminePeriodicEdgeOrientId(meshEdgeId, edgeOrient,
2441 pIt->second);
2442 meshEdgeId = idOrient.first;
2443 edgeOrient = idOrient.second;
2444 }
2445
2446 if (nf) // 3D version
2447 {
2448 exp->as<LocalRegions::Expansion3D>()
2449 ->GetEdgeInteriorToElementMap(j, edgeInteriorMap,
2450 edgeInteriorSign, edgeOrient);
2451 dof =
2452 exp->as<LocalRegions::Expansion3D>()->GetEdgeNcoeffs(j) - 2;
2453 }
2454 else // 2D version
2455 {
2456 exp->GetTraceInteriorToElementMap(j, edgeInteriorMap,
2457 edgeInteriorSign, edgeOrient);
2458 dof = exp->GetTraceNcoeffs(j) - 2;
2459 }
2460
2461 // Set the global DOF's for the interior modes of edge j
2462 // for varP, ignore modes with sign == 0
2463 for (k = 0, l = 0; k < dof; ++k)
2464 {
2465 if (m_signChange)
2466 {
2467 if (m_localToGlobalSign[cnt + edgeInteriorMap[k]] == 0)
2468 {
2469 continue;
2470 }
2471 }
2472 vGlobalId = m_localToGlobalMap[cnt + edgeInteriorMap[k]];
2473 m_globalToUniversalMap[vGlobalId] =
2474 nVert + meshEdgeId * maxEdgeDof + l + 1;
2475 m_globalToUniversalBndMap[vGlobalId] =
2476 m_globalToUniversalMap[vGlobalId];
2477 maxBndGlobalId =
2478 (vGlobalId > maxBndGlobalId ? vGlobalId : maxBndGlobalId);
2479 l++;
2480 }
2481 }
2482
2483 // Loop over all faces of element i
2484 for (j = 0; j < exp->GetGeom()->GetNumFaces(); ++j)
2485 {
2486 faceOrient = exp->GetGeom()->GetForient(j);
2487
2488 meshFaceId = exp->GetGeom()->GetFid(j);
2489
2490 auto pIt = perFaces.find(meshFaceId);
2491 if (pIt != perFaces.end())
2492 {
2493 if (meshFaceId == min(meshFaceId, pIt->second[0].id))
2494 {
2495 faceOrient = DeterminePeriodicFaceOrient(
2496 faceOrient, pIt->second[0].orient);
2497 }
2498 meshFaceId = min(meshFaceId, pIt->second[0].id);
2499 }
2500
2501 exp->GetTraceInteriorToElementMap(j, faceInteriorMap,
2502 faceInteriorSign, faceOrient);
2503 dof = exp->GetTraceIntNcoeffs(j);
2504
2505 for (k = 0, l = 0; k < dof; ++k)
2506 {
2507 if (m_signChange)
2508 {
2509 if (m_localToGlobalSign[cnt + faceInteriorMap[k]] == 0)
2510 {
2511 continue;
2512 }
2513 }
2514 vGlobalId = m_localToGlobalMap[cnt + faceInteriorMap[k]];
2515 m_globalToUniversalMap[vGlobalId] = nVert + nEdge * maxEdgeDof +
2516 meshFaceId * maxFaceDof +
2517 l + 1;
2518 m_globalToUniversalBndMap[vGlobalId] =
2519 m_globalToUniversalMap[vGlobalId];
2520
2521 maxBndGlobalId =
2522 (vGlobalId > maxBndGlobalId ? vGlobalId : maxBndGlobalId);
2523 l++;
2524 }
2525 }
2526
2527 // Add interior DOFs to complete universal numbering
2528 exp->GetInteriorMap(interiorMap);
2529 dof = interiorMap.size();
2530 elementId = (exp->GetGeom())->GetGlobalID();
2531 for (k = 0; k < dof; ++k)
2532 {
2533 vGlobalId = m_localToGlobalMap[cnt + interiorMap[k]];
2534 m_globalToUniversalMap[vGlobalId] = nVert + nEdge * maxEdgeDof +
2535 nFace * maxFaceDof +
2536 elementId * maxIntDof + k + 1;
2537 }
2538 }
2539
2540 // Set up the GSLib universal assemble mapping
2541 // Internal DOF do not participate in any data
2542 // exchange, so we keep these set to the special GSLib id=0 so
2543 // they are ignored.
2547 for (unsigned int i = 0; i < m_numGlobalBndCoeffs; ++i)
2548 {
2549 tmp[i] = m_globalToUniversalMap[i];
2550 }
2551
2552 m_gsh = Gs::Init(tmp, vRowComm, verbose);
2553 m_bndGsh = Gs::Init(tmp2, vRowComm, verbose);
2554 Gs::Unique(tmp, vRowComm);
2555 for (unsigned int i = 0; i < m_numGlobalCoeffs; ++i)
2556 {
2557 m_globalToUniversalMapUnique[i] = (tmp[i] >= 0 ? 1 : 0);
2558 }
2559 for (unsigned int i = 0; i < m_numGlobalBndCoeffs; ++i)
2560 {
2561 m_globalToUniversalBndMapUnique[i] = (tmp2[i] >= 0 ? 1 : 0);
2562 }
2563}
2564
2565/**
2566 * @brief Construct an AssemblyMapCG object which corresponds to the
2567 * linear space of the current object.
2568 *
2569 * This function is used to create a linear-space assembly map, which is
2570 * then used in the linear space preconditioner in the conjugate
2571 * gradient solve.
2572 */
2574 GlobalSysSolnType solnType)
2575{
2576 AssemblyMapCGSharedPtr returnval;
2577
2578 int i, j;
2579 int nverts = 0;
2580 const std::shared_ptr<LocalRegions::ExpansionVector> exp = locexp.GetExp();
2581 int nelmts = exp->size();
2582 const bool verbose = locexp.GetSession()->DefinesCmdLineArgument("verbose");
2583
2584 // Get Default Map and turn off any searched values.
2586 m_session, locexp.GetComm());
2587 returnval->m_solnType = solnType;
2588 returnval->m_preconType = "Null";
2589 returnval->m_maxStaticCondLevel = 0;
2590 returnval->m_signChange = false;
2591 returnval->m_comm = m_comm;
2592
2593 // Count the number of vertices
2594 for (i = 0; i < nelmts; ++i)
2595 {
2596 nverts += (*exp)[i]->GetNverts();
2597 }
2598
2599 returnval->m_numLocalCoeffs = nverts;
2600 returnval->m_localToGlobalMap = Array<OneD, int>(nverts, -1);
2601
2602 // Store original global ids in this map
2603 returnval->m_localToGlobalBndMap = Array<OneD, int>(nverts, -1);
2604
2605 int cnt = 0;
2606 int cnt1 = 0;
2607 Array<OneD, int> GlobCoeffs(m_numGlobalCoeffs, -1);
2608
2609 // Set up local to global map;
2610 for (i = 0; i < nelmts; ++i)
2611 {
2612 for (j = 0; j < (*exp)[i]->GetNverts(); ++j)
2613 {
2614 returnval->m_localToGlobalMap[cnt] =
2615 returnval->m_localToGlobalBndMap[cnt] =
2616 m_localToGlobalMap[cnt1 + (*exp)[i]->GetVertexMap(j, true)];
2617 GlobCoeffs[returnval->m_localToGlobalMap[cnt]] = 1;
2618
2619 // Set up numLocalDirBndCoeffs
2620 if ((returnval->m_localToGlobalMap[cnt]) < m_numGlobalDirBndCoeffs)
2621 {
2622 returnval->m_numLocalDirBndCoeffs++;
2623 }
2624 cnt++;
2625 }
2626 cnt1 += (*exp)[i]->GetNcoeffs();
2627 }
2628
2629 cnt = 0;
2630 // Reset global numbering and count number of dofs
2631 for (i = 0; i < m_numGlobalCoeffs; ++i)
2632 {
2633 if (GlobCoeffs[i] != -1)
2634 {
2635 GlobCoeffs[i] = cnt++;
2636 }
2637 }
2638
2639 // Set up number of globalCoeffs;
2640 returnval->m_numGlobalCoeffs = cnt;
2641
2642 // Set up number of global Dirichlet boundary coefficients
2643 for (i = 0; i < m_numGlobalDirBndCoeffs; ++i)
2644 {
2645 if (GlobCoeffs[i] != -1)
2646 {
2647 returnval->m_numGlobalDirBndCoeffs++;
2648 }
2649 }
2650
2651 // Set up global to universal map
2652 if (m_globalToUniversalMap.size())
2653 {
2655 m_session->GetComm()->GetRowComm();
2656 int nglocoeffs = returnval->m_numGlobalCoeffs;
2657 returnval->m_globalToUniversalMap = Array<OneD, int>(nglocoeffs);
2658 returnval->m_globalToUniversalMapUnique = Array<OneD, int>(nglocoeffs);
2659
2660 // Reset local to global map and setup universal map
2661 for (i = 0; i < nverts; ++i)
2662 {
2663 cnt = returnval->m_localToGlobalMap[i];
2664 returnval->m_localToGlobalMap[i] = GlobCoeffs[cnt];
2665
2666 returnval->m_globalToUniversalMap[GlobCoeffs[cnt]] =
2668 }
2669
2670 Nektar::Array<OneD, long> tmp(nglocoeffs);
2671 Vmath::Zero(nglocoeffs, tmp, 1);
2672 for (unsigned int i = 0; i < nglocoeffs; ++i)
2673 {
2674 tmp[i] = returnval->m_globalToUniversalMap[i];
2675 }
2676 returnval->m_gsh = Gs::Init(tmp, vRowComm, verbose);
2677 Gs::Unique(tmp, vRowComm);
2678 for (unsigned int i = 0; i < nglocoeffs; ++i)
2679 {
2680 returnval->m_globalToUniversalMapUnique[i] = (tmp[i] >= 0 ? 1 : 0);
2681 }
2682 }
2683 else // not sure this option is ever needed.
2684 {
2685 for (i = 0; i < nverts; ++i)
2686 {
2687 cnt = returnval->m_localToGlobalMap[i];
2688 returnval->m_localToGlobalMap[i] = GlobCoeffs[cnt];
2689 }
2690 }
2691
2692 return returnval;
2693}
2694
2695/**
2696 * The bandwidth calculated here corresponds to what is referred to as
2697 * half-bandwidth. If the elements of the matrix are designated as
2698 * a_ij, it corresponds to the maximum value of |i-j| for non-zero
2699 * a_ij. As a result, the value also corresponds to the number of
2700 * sub- or super-diagonals.
2701 *
2702 * The bandwith can be calculated elementally as it corresponds to the
2703 * maximal elemental bandwith (i.e. the maximal difference in global
2704 * DOF index for every element).
2705 *
2706 * We caluclate here the bandwith of the full global system.
2707 */
2709{
2710 int i, j;
2711 int cnt = 0;
2712 int locSize;
2713 int maxId;
2714 int minId;
2715 int bwidth = -1;
2716 for (i = 0; i < m_numPatches; ++i)
2717 {
2718 locSize =
2720 maxId = -1;
2721 minId = m_numLocalCoeffs + 1;
2722 for (j = 0; j < locSize; j++)
2723 {
2725 {
2726 if (m_localToGlobalMap[cnt + j] > maxId)
2727 {
2728 maxId = m_localToGlobalMap[cnt + j];
2729 }
2730
2731 if (m_localToGlobalMap[cnt + j] < minId)
2732 {
2733 minId = m_localToGlobalMap[cnt + j];
2734 }
2735 }
2736 }
2737 bwidth = (bwidth > (maxId - minId)) ? bwidth : (maxId - minId);
2738
2739 cnt += locSize;
2740 }
2741
2742 m_fullSystemBandWidth = bwidth;
2743}
2744
2746{
2747 return m_localToGlobalMap[i];
2748}
2749
2751{
2752 return m_globalToUniversalMap[i];
2753}
2754
2756{
2758}
2759
2764
2769
2775
2777{
2778 if (m_signChange)
2779 {
2780 return m_localToGlobalSign[i];
2781 }
2782 else
2783 {
2784 return 1.0;
2785 }
2786}
2787
2792
2794 Array<OneD, NekDouble> &global,
2795 bool useComm) const
2796{
2798 if (global.data() == loc.data())
2799 {
2800 local = Array<OneD, NekDouble>(m_numLocalCoeffs, loc.data());
2801 }
2802 else
2803 {
2804 local = loc; // create reference
2805 }
2806
2807 if (m_signChange)
2808 {
2810 m_localToGlobalMap.data(), global.data());
2811 }
2812 else
2813 {
2814 Vmath::Scatr(m_numLocalCoeffs, local.data(), m_localToGlobalMap.data(),
2815 global.data());
2816 }
2817
2818 // ensure all values are unique by calling a max
2819 if (useComm)
2820 {
2821 Gs::Gather(global, Gs::gs_max, m_gsh);
2822 }
2823}
2824
2826 Array<OneD, NekDouble> &loc) const
2827{
2829 if (global.data() == loc.data())
2830 {
2831 glo = Array<OneD, NekDouble>(m_numGlobalCoeffs, global.data());
2832 }
2833 else
2834 {
2835 glo = global; // create reference
2836 }
2837
2838 if (m_signChange)
2839 {
2841 m_localToGlobalMap.data(), loc.data());
2842 }
2843 else
2844 {
2846 loc.data());
2847 }
2848}
2849
2851 NekVector<NekDouble> &loc) const
2852{
2853 GlobalToLocal(global.GetPtr(), loc.GetPtr());
2854}
2855
2857 Array<OneD, NekDouble> &global) const
2858{
2860 if (global.data() == loc.data())
2861 {
2862 local = Array<OneD, NekDouble>(m_numLocalCoeffs, loc.data());
2863 }
2864 else
2865 {
2866 local = loc; // create reference
2867 }
2868
2869 Vmath::Zero(m_numGlobalCoeffs, global.data(), 1);
2870
2871 if (m_signChange)
2872 {
2874 m_localToGlobalMap.data(), global.data());
2875 }
2876 else
2877 {
2878 Vmath::Assmb(m_numLocalCoeffs, local.data(), m_localToGlobalMap.data(),
2879 global.data());
2880 }
2881 UniversalAssemble(global);
2882}
2883
2885 NekVector<NekDouble> &global) const
2886{
2887 Assemble(loc.GetPtr(), global.GetPtr());
2888}
2889
2894
2896 int offset) const
2897{
2898 Array<OneD, NekDouble> tmp(offset);
2899 Vmath::Vcopy(offset, pGlobal, 1, tmp, 1);
2900 UniversalAssemble(pGlobal);
2901 Vmath::Vcopy(offset, tmp, 1, pGlobal, 1);
2902}
2903
2908
2913
2918
2923
2925{
2926 return m_numDirEdges;
2927}
2928
2930{
2931 return m_numDirFaces;
2932}
2933
2935{
2936 return m_numNonDirEdges;
2937}
2938
2940{
2941 return m_numNonDirFaces;
2942}
2943
2948} // 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
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_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:99
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:2159
std::shared_ptr< LibUtilities::SessionReader > GetSession() const
Returns the session object.
Definition ExpList.h:981
const std::shared_ptr< LocalRegions::ExpansionVector > GetExp() const
This function returns the vector of elements in the expansion.
Definition ExpList.h:2151
std::shared_ptr< LibUtilities::Comm > GetComm() const
Returns the comm object.
Definition ExpList.h:986
Array< OneD, DataType > & GetPtr()
int GetVid(int i) const
Returns global id of vertex i of this object.
Definition Geometry.h:353
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
int getNumberOfCoefficients(int Na, int Nb)
int getNumberOfBndCoefficients(int Na, int Nb)
int getNumberOfCoefficients(int Na, int Nb)
int getNumberOfBndCoefficients(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
@ P
Monomial polynomials .
Definition BasisType.h:62
@ 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