Nektar++
PreconditionerBlock.cpp
Go to the documentation of this file.
1///////////////////////////////////////////////////////////////////////////////
2//
3// File: PreconditionerBlock.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: Block Preconditioner definition
32//
33///////////////////////////////////////////////////////////////////////////////
34
36#include <LocalRegions/SegExp.h>
42#include <cmath>
43
44using namespace std;
45
46namespace Nektar
47{
48namespace MultiRegions
49{
50/**
51 * Registers the class with the Factory.
52 */
55 "Block", PreconditionerBlock::create, "Block Diagonal Preconditioning");
56/**
57 * @class Block Preconditioner
58 *
59 * This class implements Block preconditioning for the conjugate
60 * gradient matrix solver.
61 */
62
64 const std::shared_ptr<GlobalLinSys> &plinsys,
65 const AssemblyMapSharedPtr &pLocToGloMap)
66 : Preconditioner(plinsys, pLocToGloMap)
67{
68}
69
71{
72 GlobalSysSolnType solverType = m_locToGloMap.lock()->GetGlobalSysSolnType();
73 m_isFull =
74 (solverType == eIterativeFull) || (solverType == ePETScFullMatrix);
75
78 "Solver type not valid");
79}
80
82{
83 GlobalLinSysKey key = m_linsys.lock()->GetKey();
84
85 // Different setup for HDG and CG.
87 {
89 }
90 else
91 {
93 }
94}
95
96/**
97 * \brief Construct a block preconditioner from \f$\mathbf{S}_{1}\f$ for
98 * the continuous Galerkin system.
99 *
100 * The preconditioner for the statically-condensed system is defined as:
101 *
102 * \f[\mathbf{M}^{-1}=\left[\begin{array}{ccc}
103 * \mathrm{Diag}[(\mathbf{S_{1}})_{vv}] & & \\
104 * & (\mathbf{S}_{1})_{eb} & \\
105 * & & (\mathbf{S}_{1})_{fb} \end{array}\right] \f]
106 *
107 * where \f$\mathbf{S}_{1}\f$ is the local Schur complement matrix for
108 * each element and the subscript denotes the portion of the Schur
109 * complement associated with the vertex (vv), edge (eb) and face
110 * blocks (fb) respectively.
111 *
112 * In case of the full system \f$\mathbf{A}\f$, the preconditioner
113 * includes the interior blocks as well. The full block preconditioner
114 * is defined as:
115 *
116 * \f[\mathbf{M}^{-1}=\left[\begin{array}{cccc}
117 * \mathrm{Diag}[(\mathbf{A})_{vv}] & & & \\
118 * & (\mathbf{A})_{eb} & & \\
119 * & & (\mathbf{A})_{fb} & \\
120 * & & & (\mathbf{A})_{int} \end{array}\right] \f]
121 *
122 * where the interior portion is added at the end following the
123 * ordering inside data structures.
124 */
126{
127 ExpListSharedPtr expList = m_linsys.lock()->GetLocMat().lock();
130 DNekScalMatSharedPtr bnd_mat;
131
132 int i, j, k, n, cnt, gId;
133 int meshVertId, meshEdgeId, meshFaceId;
134
135 auto asmMap = m_locToGloMap.lock();
136
137 const int nExp = expList->GetExpSize();
138 const int nDirBnd = asmMap->GetNumGlobalDirBndCoeffs();
139
140 // Grab periodic geometry information.
141 PeriodicMap periodicVerts, periodicEdges, periodicFaces;
142 expList->GetPeriodicEntities(periodicVerts, periodicEdges, periodicFaces);
143
144 // The vectors below are of size 3(+1) to have separate storage for
145 // vertices, edges and faces.
146 // For the full matrix approach additional storage for the
147 // interior matrices is required
148 int nStorage = m_isFull ? 4 : 3;
149
150 // Maps from geometry ID to the matrix representing the extracted
151 // portion of S_1. For example idMats[2] folds the S_1 face blocks.
152 vector<map<int, vector<NekDouble>>> idMats(nStorage);
153
154 // Maps from the global ID, as obtained from AssemblyMapCG's
155 // localToGlobalMap, to the geometry ID.
156 vector<map<int, int>> gidMeshIds(3);
157
158 // Maps from the global ID to the number of degrees of freedom for
159 // this geometry object.
160 vector<map<int, int>> gidDofs(nStorage);
161
162 // Array containing maximum information needed for the universal
163 // numbering later. For i = 0,1,2 for each geometry dimension:
164 //
165 // maxVertIds[2*i] = maximum geometry ID at dimension i
166 // maxVertIds[2*i+1] = maximum number of degrees of freedom for all
167 // elements of dimension i.
168 Array<OneD, int> maxVertIds(6, -1);
169
170 // Figure out mapping from each elemental contribution to offset in
171 // (vert,edge,face) triples.
172 for (cnt = n = 0; n < nExp; ++n)
173 {
174 exp = expList->GetExp(n);
175
176 // Grab reference to
177 // StaticCond: Local Schur complement matrix
178 // Full: Boundary and interior matrix
179 DNekScalMatSharedPtr schurMat;
180
181 if (m_isFull)
182 {
183 // Get local matrix
184 auto tmpMat = m_linsys.lock()->GetBlock(n);
185
186 // Get number of boundary dofs
187 int nBndDofs = exp->NumBndryCoeffs();
188 int nIntDofs = exp->GetNcoeffs() - nBndDofs;
189
190 // Get boundary & interior maps
191 Array<OneD, unsigned int> bndMap(nBndDofs), intMap(nIntDofs);
192 exp->GetBoundaryMap(bndMap);
193 exp->GetInteriorMap(intMap);
194
195 // Create temporary matrices
196 DNekMatSharedPtr bndryMat =
198
199 // Extract boundary and send to StaticCond (Schur)
200 // framework
201 for (int i = 0; i < nBndDofs; ++i)
202 {
203 for (int j = 0; j < nBndDofs; ++j)
204 {
205 (*bndryMat)(i, j) = (*tmpMat)(bndMap[i], bndMap[j]);
206 }
207 }
208
209 schurMat =
211
212 // Extract interior matrix and save for block matrix setting
213 vector<NekDouble> tmpStore(nIntDofs * nIntDofs);
214 for (int i = 0; i < nIntDofs; ++i)
215 {
216 for (int j = 0; j < nIntDofs; ++j)
217 {
218 tmpStore[j + i * nIntDofs] =
219 (*tmpMat)(intMap[i], intMap[j]);
220 }
221 }
222
223 // Save interior mats and nDofs
224 idMats[3][n] = tmpStore;
225 gidDofs[3][n] = nIntDofs;
226 // then intMat gets added to a block diagonal matrix that
227 // handles the extra interior dofs no need to do any
228 // communication for that because all interior dofs are
229 // local to each element
230 }
231 else
232 {
233 schurMat = m_linsys.lock()->GetStaticCondBlock(n)->GetBlock(0, 0);
234 }
235
236 // Process vertices to extract relevant portion of the Schur
237 // complement matrix.
238 for (i = 0; i < exp->GetNverts(); ++i)
239 {
240 meshVertId = exp->GetGeom()->GetVid(i);
241 int locId = exp->GetVertexMap(i);
242
243 // Get the global ID of this vertex.
244 gId = asmMap->GetLocalToGlobalMap(cnt + locId) - nDirBnd;
245
246 // Ignore all Dirichlet vertices.
247 if (gId < 0)
248 {
249 continue;
250 }
251
252 gidDofs[0][gId] = 1;
253
254 // Extract vertex value from Schur complement matrix.
255 NekDouble vertVal = (*schurMat)(locId, locId);
256
257 // See if we have processed this vertex from another
258 // element.
259 auto gIt = idMats[0].find(gId);
260
261 if (gIt == idMats[0].end())
262 {
263 // If not then put our 'matrix' inside idMats.
264 idMats[0][gId] = vector<NekDouble>(1, vertVal);
265 }
266 else
267 {
268 // Otherwise combine with the value that is already
269 // there (i.e. do assembly on this degree of freedom).
270 gIt->second[0] += vertVal;
271 }
272
273 // Now check to see if the vertex is periodic. If it is,
274 // then we change meshVertId to be the minimum of all the
275 // other periodic vertices, so that we don't end up
276 // duplicating the matrix in our final block matrix.
277 auto pIt = periodicVerts.find(meshVertId);
278 if (pIt != periodicVerts.end())
279 {
280 for (j = 0; j < pIt->second.size(); ++j)
281 {
282 meshVertId = min(meshVertId, pIt->second[j].id);
283 }
284 }
285
286 // Finally record the other information we need into the
287 // other matrices.
288 gidMeshIds[0][gId] = meshVertId;
289 maxVertIds[0] = max(maxVertIds[0], meshVertId);
290 maxVertIds[1] = 1;
291 }
292
293 // Process edges. This logic is mostly the same as the previous
294 // block.
295 for (i = 0; i < exp->GetGeom()->GetNumEdges(); ++i)
296 {
297 meshEdgeId = exp->GetGeom()->GetEid(i);
298
299 Array<OneD, unsigned int> bmap, bmap2;
301 StdRegions::Orientation edgeOrient = exp->GetGeom()->GetEorient(i);
302
303 // Check if this edge is periodic. We may need to flip
304 // orientation if it is.
305 auto pIt = periodicEdges.find(meshEdgeId);
306 if (pIt != periodicEdges.end())
307 {
308 pair<int, StdRegions::Orientation> idOrient =
309 DeterminePeriodicEdgeOrientId(meshEdgeId, edgeOrient,
310 pIt->second);
311 meshEdgeId = idOrient.first;
312 edgeOrient = idOrient.second;
313 }
314
315 // Grab edge interior map, and the edge inverse boundary
316 // map, so that we can extract this edge from the Schur
317 // complement matrix.
318 if (exp->GetGeom()->GetNumFaces()) // 3D Element calls
319 {
321 ->GetEdgeInteriorToElementMap(i, bmap, sign, edgeOrient);
322 bmap2 = exp->as<LocalRegions::Expansion3D>()
323 ->GetEdgeInverseBoundaryMap(i);
324 }
325 else
326 {
327 exp->GetTraceInteriorToElementMap(i, bmap, sign, edgeOrient);
328 bmap2 = exp->as<LocalRegions::Expansion2D>()
329 ->GetTraceInverseBoundaryMap(i);
330 }
331
332 // Allocate temporary storage for the extracted edge matrix.
333 const int nEdgeCoeffs = bmap.size();
334 vector<NekDouble> tmpStore(nEdgeCoeffs * nEdgeCoeffs);
335
336 gId = asmMap->GetLocalToGlobalMap(cnt + bmap[0]);
337
338 for (j = 0; j < nEdgeCoeffs; ++j)
339 {
340 // We record the minimum ID from the edge for our
341 // maps. This follows the logic that the assembly map
342 // ordering will always give us a contiguous ordering of
343 // global degrees of freedom for edge interior
344 // coefficients.
345 gId = min(gId,
346 asmMap->GetLocalToGlobalMap(cnt + bmap[j]) - nDirBnd);
347
348 // Ignore Dirichlet edges.
349 if (gId < 0)
350 {
351 continue;
352 }
353
354 const NekDouble sign1 = sign[j];
355
356 // Extract this edge, along with sign array for assembly
357 // later.
358 for (k = 0; k < nEdgeCoeffs; ++k)
359 {
360 tmpStore[k + j * nEdgeCoeffs] =
361 sign1 * sign[k] * (*schurMat)(bmap2[j], bmap2[k]);
362 }
363 }
364
365 if (gId < 0)
366 {
367 continue;
368 }
369
370 gidDofs[1][gId] = nEdgeCoeffs;
371
372 // Assemble this edge matrix with another one, if it exists.
373 auto gIt = idMats[1].find(gId);
374 if (gIt == idMats[1].end())
375 {
376 idMats[1][gId] = tmpStore;
377 }
378 else
379 {
380 ASSERTL1(tmpStore.size() == gIt->second.size(),
381 "Number of modes mismatch");
382 Vmath::Vadd(nEdgeCoeffs * nEdgeCoeffs, &gIt->second[0], 1,
383 &tmpStore[0], 1, &gIt->second[0], 1);
384 }
385
386 gidMeshIds[1][gId] = meshEdgeId;
387 maxVertIds[2] = max(maxVertIds[2], meshEdgeId);
388 maxVertIds[3] = max(maxVertIds[3], nEdgeCoeffs);
389 }
390
391 // Process faces. This logic is mostly the same as the previous
392 // block.
393 for (i = 0; i < exp->GetGeom()->GetNumFaces(); ++i)
394 {
395 meshFaceId = exp->GetGeom()->GetFid(i);
396
397 Array<OneD, unsigned int> bmap, bmap2;
399 StdRegions::Orientation faceOrient = exp->GetGeom()->GetForient(i);
400
401 // Check if this face is periodic. We may need to flip
402 // orientation if it is.
403 auto pIt = periodicFaces.find(meshFaceId);
404 if (pIt != periodicFaces.end())
405 {
406 meshFaceId = min(meshFaceId, pIt->second[0].id);
407 faceOrient = DeterminePeriodicFaceOrient(faceOrient,
408 pIt->second[0].orient);
409 }
410
411 exp->GetTraceInteriorToElementMap(i, bmap, sign, faceOrient);
412 bmap2 = exp->as<LocalRegions::Expansion3D>()
413 ->GetTraceInverseBoundaryMap(i);
414
415 // Allocate temporary storage for the extracted face matrix.
416 const int nFaceCoeffs = bmap.size();
417 vector<NekDouble> tmpStore(nFaceCoeffs * nFaceCoeffs);
418
419 gId = asmMap->GetLocalToGlobalMap(cnt + bmap[0]);
420
421 for (j = 0; j < nFaceCoeffs; ++j)
422 {
423 gId = min(gId,
424 asmMap->GetLocalToGlobalMap(cnt + bmap[j]) - nDirBnd);
425
426 // Ignore Dirichlet faces.
427 if (gId < 0)
428 {
429 continue;
430 }
431
432 const NekDouble sign1 = sign[j];
433
434 // Extract this face, along with sign array for assembly
435 // later.
436 for (k = 0; k < nFaceCoeffs; ++k)
437 {
438 tmpStore[k + j * nFaceCoeffs] =
439 sign1 * sign[k] * (*schurMat)(bmap2[j], bmap2[k]);
440 }
441 }
442
443 if (gId < 0)
444 {
445 continue;
446 }
447
448 gidDofs[2][gId] = nFaceCoeffs;
449
450 // Assemble this face matrix with another one, if it exists.
451 auto gIt = idMats[2].find(gId);
452 if (gIt == idMats[2].end())
453 {
454 idMats[2][gId] = tmpStore;
455 }
456 else
457 {
458 ASSERTL1(tmpStore.size() == gIt->second.size(),
459 "Number of modes mismatch");
460 Vmath::Vadd(nFaceCoeffs * nFaceCoeffs, &gIt->second[0], 1,
461 &tmpStore[0], 1, &gIt->second[0], 1);
462 }
463
464 gidMeshIds[2][gId] = meshFaceId;
465 maxVertIds[4] = max(maxVertIds[4], meshFaceId);
466 maxVertIds[5] = max(maxVertIds[5], nFaceCoeffs);
467 }
468
469 cnt += exp->GetNcoeffs();
470 }
471
472 // Perform a reduction to find maximum vertex, edge and face
473 // geometry IDs.
474 m_comm = expList->GetSession()->GetComm()->GetRowComm();
475 m_comm->AllReduce(maxVertIds, LibUtilities::ReduceMax);
476
477 // Concatenate all matrices into contiguous storage and figure out
478 // universal ID numbering.
479 vector<NekDouble> storageBuf;
480 vector<long> globalToUniversal;
481
482 for (i = 0, cnt = 1; i < 3; ++i)
483 {
484 const int maxDofs = maxVertIds[2 * i + 1];
485
486 // Note that iterating over the map uses the property that keys
487 // are accessed in order of ascending order, putting everything
488 // in the right order for the global system.
489 for (auto &gIt : idMats[i])
490 {
491 // Copy matrix into storage.
492 storageBuf.insert(storageBuf.end(), gIt.second.begin(),
493 gIt.second.end());
494
495 // Get mesh ID from global ID number.
496 ASSERTL1(gidMeshIds[i].count(gIt.first) > 0,
497 "Unable to find global ID " +
498 boost::lexical_cast<string>(gIt.first) +
499 " inside map");
500 meshVertId = gidMeshIds[i][gIt.first];
501
502 for (j = 0; j < gIt.second.size(); ++j)
503 {
504 globalToUniversal.push_back(cnt +
505 meshVertId * maxDofs * maxDofs + j);
506 }
507
508 // Free up the temporary storage.
509 gIt.second.clear();
510 }
511
512 cnt += (maxVertIds[2 * i] + 1) * maxDofs * maxDofs;
513 }
514
515 ASSERTL1(storageBuf.size() == globalToUniversal.size(),
516 "Storage buffer and global to universal map size does "
517 "not match");
518
519 Array<OneD, NekDouble> storageData(storageBuf.size(), &storageBuf[0]);
520 Array<OneD, long> globalToUniversalMap(globalToUniversal.size(),
521 &globalToUniversal[0]);
522
523 // Use GS to assemble data between processors.
524 Gs::gs_data *tmpGs =
525 Gs::Init(globalToUniversalMap, m_comm,
526 expList->GetSession()->DefinesCmdLineArgument("verbose"));
527 Gs::Gather(storageData, Gs::gs_add, tmpGs);
528
529 // Figure out what storage we need in the block matrix.
530 int nblksSize = 1 + idMats[1].size() + idMats[2].size();
531 if (m_isFull)
532 {
533 nblksSize += idMats[3].size();
534 }
535 Array<OneD, unsigned int> n_blks(nblksSize);
536
537 // Vertex block is a diagonal matrix.
538 n_blks[0] = idMats[0].size();
539
540 // Now extract number of rows in each edge and face block from the
541 // gidDofs map.
542 cnt = 1;
543 for (i = 1; i < nStorage; ++i)
544 {
545 for (auto &gIt : idMats[i])
546 {
547 ASSERTL1(gidDofs[i].count(gIt.first) > 0,
548 "Unable to find number of degrees of freedom for "
549 "global ID " +
550 boost::lexical_cast<string>(gIt.first));
551
552 n_blks[cnt++] = gidDofs[i][gIt.first];
553 }
554 }
555
556 // Allocate storage for the block matrix.
557 m_blkMat =
559
560 // We deal with the vertex matrix separately since all vertices can
561 // be condensed into a single, block-diagonal matrix.
563 n_blks[0], n_blks[0], 0.0, eDIAGONAL);
564
565 // Fill the vertex matrix with the inverse of each vertex value.
566 cnt = 0;
567 for (auto gIt = idMats[0].begin(); gIt != idMats[0].end(); ++gIt, ++cnt)
568 {
569 (*vertMat)(cnt, cnt) = 1.0 / storageData[cnt];
570 }
571
572 // Put the vertex matrix in the block matrix.
573 m_blkMat->SetBlock(0, 0, vertMat);
574
575 // Finally, grab the matrices from the block storage, invert them
576 // and place them in the correct position inside the block matrix.
577 int cnt2 = 1;
578 for (i = 1; i < nStorage; ++i)
579 {
580 for (auto &gIt : idMats[i])
581 {
582 int nDofs = gidDofs[i][gIt.first];
583
584 DNekMatSharedPtr tmp =
586
587 for (j = 0; j < nDofs; ++j)
588 {
589 for (k = 0; k < nDofs; ++k)
590 {
591 // Interior matrices do not need mapping
592 // and are stored in idMats[3]
593 if (m_isFull && i == 3)
594 {
595 (*tmp)(j, k) = gIt.second[k + j * nDofs];
596 }
597 // Boundary matrices
598 else
599 {
600 (*tmp)(j, k) = storageData[k + j * nDofs + cnt];
601 }
602 }
603 }
604
605 cnt += nDofs * nDofs;
606
607 tmp->Invert();
608 m_blkMat->SetBlock(cnt2, cnt2, tmp);
609 ++cnt2;
610 }
611 }
612}
613
614/**
615 * @brief Construct a block preconditioner for the hybridized
616 * discontinuous Galerkin system.
617 *
618 * This system is built in a similar fashion to its continuous variant
619 * found in PreconditionerBlock::BlockPreconditionerCG. In this setting
620 * however, the matrix is constructed as:
621 *
622 * \f[ M^{-1} = \mathrm{Diag}[ (\mathbf{S_{1}})_{f}^{-1} ] \f]
623 *
624 * where each matrix is the Schur complement system restricted to a
625 * single face of the trace system.
626 */
628{
629 std::shared_ptr<MultiRegions::ExpList> expList =
630 ((m_linsys.lock())->GetLocMat()).lock();
631 std::shared_ptr<MultiRegions::ExpList> trace = expList->GetTrace();
634 DNekScalMatSharedPtr bnd_mat;
635
637 std::dynamic_pointer_cast<AssemblyMapDG>(m_locToGloMap.lock());
638
639 int i, j, k, n, cnt, cnt2;
640
641 // Figure out number of Dirichlet trace elements
642 int nTrace = expList->GetTrace()->GetExpSize();
643 int nDir = asmMap->GetNumGlobalDirBndCoeffs();
644
645 for (cnt = n = 0; n < nTrace; ++n)
646 {
647 if (cnt >= nDir)
648 {
649 break;
650 }
651
652 cnt += trace->GetExp(n)->GetNcoeffs();
653 }
654
655 nDir = n;
656
657 // Allocate storage for block matrix. Need number of unique faces in
658 // trace space.
659 int maxTraceSize = -1;
660 map<int, int> arrayOffsets;
661
662 for (cnt = 0, n = nDir; n < nTrace; ++n)
663 {
664 int nCoeffs = trace->GetExp(n)->GetNcoeffs();
665 int nCoeffs2 = nCoeffs * nCoeffs;
666
667 arrayOffsets[n] = cnt;
668 cnt += nCoeffs2;
669
670 if (maxTraceSize < nCoeffs2)
671 {
672 maxTraceSize = nCoeffs2;
673 }
674 }
675
676 // Find maximum trace size.
677 m_comm = expList->GetSession()->GetComm()->GetRowComm();
678 m_comm->AllReduce(maxTraceSize, LibUtilities::ReduceMax);
679
680 // Zero matrix storage.
681 Array<OneD, NekDouble> tmpStore(cnt, 0.0);
682
683 // Assemble block matrices for each trace element.
684 for (cnt = n = 0; n < expList->GetExpSize(); ++n)
685 {
686 int elmt = n;
687 locExpansion = expList->GetExp(elmt);
688
690 asmMap->GetElmtToTrace()[elmt];
691
692 // Block matrix (lambda)
693 loc_mat = (m_linsys.lock())->GetStaticCondBlock(n);
694 bnd_mat = loc_mat->GetBlock(0, 0);
695
696 int nFacets = locExpansion->GetNtraces();
697
698 for (cnt2 = i = 0; i < nFacets; ++i)
699 {
700 int nCoeffs = elmtToTraceMap[i]->GetNcoeffs();
701 int elmtId = elmtToTraceMap[i]->GetElmtId();
702
703 // Ignore Dirichlet edges.
704 if (elmtId < nDir)
705 {
706 cnt += nCoeffs;
707 cnt2 += nCoeffs;
708 continue;
709 }
710
711 NekDouble *off = &tmpStore[arrayOffsets[elmtId]];
712
713 for (j = 0; j < nCoeffs; ++j)
714 {
715 NekDouble sign1 = asmMap->GetLocalToGlobalBndSign(cnt + j);
716 for (k = 0; k < nCoeffs; ++k)
717 {
718 NekDouble sign2 = asmMap->GetLocalToGlobalBndSign(cnt + k);
719 off[j * nCoeffs + k] +=
720 (*bnd_mat)(cnt2 + j, cnt2 + k) * sign1 * sign2;
721 }
722 }
723
724 cnt += nCoeffs;
725 cnt2 += nCoeffs;
726 }
727 }
728
729 // Set up IDs for universal numbering.
730 Array<OneD, long> uniIds(tmpStore.size());
731 for (cnt = 0, n = nDir; n < nTrace; ++n)
732 {
733 LocalRegions::ExpansionSharedPtr traceExp = trace->GetExp(n);
734 int nCoeffs = traceExp->GetNcoeffs();
735 int geomId = traceExp->GetGeom()->GetGlobalID();
736
737 for (i = 0; i < nCoeffs * nCoeffs; ++i)
738 {
739 uniIds[cnt++] = geomId * maxTraceSize + i + 1;
740 }
741 }
742
743 // Assemble matrices across partitions.
744 Gs::gs_data *gsh =
745 Gs::Init(uniIds, m_comm,
746 expList->GetSession()->DefinesCmdLineArgument("verbose"));
747 Gs::Gather(tmpStore, Gs::gs_add, gsh);
748
749 // Set up diagonal block matrix
750 Array<OneD, unsigned int> n_blks(nTrace - nDir);
751 for (n = 0; n < nTrace - nDir; ++n)
752 {
753 n_blks[n] = trace->GetExp(n + nDir)->GetNcoeffs();
754 }
755
756 m_blkMat =
758
759 for (n = 0; n < nTrace - nDir; ++n)
760 {
761 int nCoeffs = trace->GetExp(n + nDir)->GetNcoeffs();
762 DNekMatSharedPtr tmp =
764 NekDouble *off = &tmpStore[arrayOffsets[n + nDir]];
765
766 for (i = 0; i < nCoeffs; ++i)
767 {
768 for (j = 0; j < nCoeffs; ++j)
769 {
770 (*tmp)(i, j) = off[i * nCoeffs + j];
771 }
772 }
773
774 tmp->Invert();
775 m_blkMat->SetBlock(n, n, tmp);
776 }
777}
778
779/**
780 * @brief Apply preconditioner to \p pInput and store the result in \p
781 * pOutput.
782 */
784 const Array<OneD, NekDouble> &pInput, Array<OneD, NekDouble> &pOutput,
785 const bool &isLocal)
786{
787 // Get assembly map and solver type
788 auto asmMap = m_locToGloMap.lock();
789
790 // Determine matrix size
791 int nGlobal = m_isFull ? asmMap->GetNumGlobalCoeffs()
792 : asmMap->GetNumGlobalBndCoeffs();
793 int nDir = asmMap->GetNumGlobalDirBndCoeffs();
794 int nNonDir = nGlobal - nDir;
795
796 // Apply preconditioner
797 DNekBlkMat &M = (*m_blkMat);
798
799 if (isLocal)
800 {
801 Array<OneD, NekDouble> wk(nGlobal);
802 Array<OneD, NekDouble> wk1(nGlobal);
803 asmMap->Assemble(pInput, wk);
804
805 NekVector<NekDouble> r(nNonDir, wk + nDir, eWrapper);
806 NekVector<NekDouble> z(nNonDir, wk1 + nDir, eWrapper);
807
808 z = M * r;
809 Vmath::Zero(nDir, wk1, 1);
810
811 asmMap->GlobalToLocal(wk1, pOutput);
812 }
813 else
814 {
815 NekVector<NekDouble> r(nNonDir, pInput, eWrapper);
816 NekVector<NekDouble> z(nNonDir, pOutput, eWrapper);
817 z = M * r;
818 }
819}
820} // namespace MultiRegions
821} // namespace Nektar
#define ASSERTL0(condition, msg)
Definition: ErrorUtil.hpp:215
#define ASSERTL1(condition, msg)
Assert Level 1 – Debugging which is used whether in FULLDEBUG or DEBUG compilation mode....
Definition: ErrorUtil.hpp:249
#define sign(a, b)
return the sign(b)*a
Definition: Polylib.cpp:49
tKey RegisterCreatorFunction(tKey idKey, CreatorFunction classCreator, std::string pDesc="")
Register a class with the factory.
Definition: NekFactory.hpp:198
static std::shared_ptr< DataType > AllocateSharedPtr(const Args &...args)
Allocate a shared pointer from the memory pool.
StdRegions::MatrixType GetMatrixType() const
Return the matrix type.
void BlockPreconditionerCG(void)
Construct a block preconditioner from for the continuous Galerkin system.
static PreconditionerSharedPtr create(const std::shared_ptr< GlobalLinSys > &plinsys, const std::shared_ptr< AssemblyMap > &pLocToGloMap)
Creates an instance of this class.
virtual void v_DoPreconditioner(const Array< OneD, NekDouble > &pInput, Array< OneD, NekDouble > &pOutput, const bool &isLocal=false) override
Apply preconditioner to pInput and store the result in pOutput.
void BlockPreconditionerHDG(void)
Construct a block preconditioner for the hybridized discontinuous Galerkin system.
PreconditionerBlock(const std::shared_ptr< GlobalLinSys > &plinsys, const AssemblyMapSharedPtr &pLocToGloMap)
static std::string className
Name of class.
LibUtilities::CommSharedPtr m_comm
const std::weak_ptr< GlobalLinSys > m_linsys
std::weak_ptr< AssemblyMap > m_locToGloMap
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:270
static gs_data * Init(const Nektar::Array< OneD, long > pId, const LibUtilities::CommSharedPtr &pComm, bool verbose=true)
Initialise Gather-Scatter map.
Definition: GsLib.hpp:192
@ gs_add
Definition: GsLib.hpp:62
std::shared_ptr< Expansion > ExpansionSharedPtr
Definition: Expansion.h:68
std::shared_ptr< AssemblyMapDG > AssemblyMapDGSharedPtr
Definition: AssemblyMapDG.h:48
std::shared_ptr< ExpList > ExpListSharedPtr
Shared pointer to an ExpList object.
PreconFactory & GetPreconFactory()
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:52
std::map< int, std::vector< PeriodicEntity > > PeriodicMap
StdRegions::Orientation DeterminePeriodicFaceOrient(StdRegions::Orientation faceOrient, StdRegions::Orientation perFaceOrient)
Determine relative orientation between two faces.
std::vector< double > z(NPUPPER)
The above copyright notice and this permission notice shall be included.
Definition: CoupledSolver.h:2
std::shared_ptr< DNekScalMat > DNekScalMatSharedPtr
std::shared_ptr< DNekScalBlkMat > DNekScalBlkMatSharedPtr
Definition: NekTypeDefs.hpp:79
std::shared_ptr< DNekMat > DNekMatSharedPtr
Definition: NekTypeDefs.hpp:75
double NekDouble
void Vadd(int n, const T *x, const int incx, const T *y, const int incy, T *z, const int incz)
Add vector z = x+y.
Definition: Vmath.cpp:354
void Zero(int n, T *x, const int incx)
Zero vector.
Definition: Vmath.cpp:487