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/MatrixKey.h>
37 #include <LocalRegions/SegExp.h>
43 #include <cmath>
44 
45 using namespace std;
46 
47 namespace Nektar
48 {
49 namespace MultiRegions
50 {
51 /**
52  * Registers the class with the Factory.
53  */
54 string PreconditionerBlock::className =
56  "Block", PreconditionerBlock::create, "Block Diagonal Preconditioning");
57 /**
58  * @class Block Preconditioner
59  *
60  * This class implements Block preconditioning for the conjugate
61  * gradient matrix solver.
62  */
63 
64 PreconditionerBlock::PreconditionerBlock(
65  const std::shared_ptr<GlobalLinSys> &plinsys,
66  const AssemblyMapSharedPtr &pLocToGloMap)
67  : Preconditioner(plinsys, pLocToGloMap)
68 {
69 }
70 
72 {
73  GlobalSysSolnType solvertype = m_locToGloMap.lock()->GetGlobalSysSolnType();
75  solvertype == MultiRegions::ePETScStaticCond,
76  "Solver type not valid");
77 }
78 
80 {
81  GlobalLinSysKey key = m_linsys.lock()->GetKey();
82 
83  // Different setup for HDG and CG.
85  {
87  }
88  else
89  {
91  }
92 }
93 
94 /**
95  * \brief Construct a block preconditioner from \f$\mathbf{S}_{1}\f$ for
96  * the continuous Galerkin system.
97  *
98  * The preconditioner is defined as:
99  *
100  * \f[\mathbf{M}^{-1}=\left[\begin{array}{ccc}
101  * \mathrm{Diag}[(\mathbf{S_{1}})_{vv}] & & \\
102  * & (\mathbf{S}_{1})_{eb} & \\
103  * & & (\mathbf{S}_{1})_{fb} \end{array}\right] \f]
104  *
105  * where \f$\mathbf{S}_{1}\f$ is the local Schur complement matrix for
106  * each element and the subscript denotes the portion of the Schur
107  * complement associated with the vertex, edge and face blocks
108  * respectively.
109  */
111 {
112  ExpListSharedPtr expList = m_linsys.lock()->GetLocMat().lock();
114  DNekScalBlkMatSharedPtr loc_mat;
115  DNekScalMatSharedPtr bnd_mat;
116 
117  int i, j, k, n, cnt, gId;
118  int meshVertId, meshEdgeId, meshFaceId;
119 
120  auto asmMap = m_locToGloMap.lock();
121 
122  const int nExp = expList->GetExpSize();
123  const int nDirBnd = asmMap->GetNumGlobalDirBndCoeffs();
124 
125  // Grab periodic geometry information.
126  PeriodicMap periodicVerts, periodicEdges, periodicFaces;
127  expList->GetPeriodicEntities(periodicVerts, periodicEdges, periodicFaces);
128 
129  // The vectors below are of size 3 to have separate storage for
130  // vertices, edges and faces.
131 
132  // Maps from geometry ID to the matrix representing the extracted
133  // portion of S_1. For example idMats[2] folds the S_1 face blocks.
134  vector<map<int, vector<NekDouble>>> idMats(3);
135 
136  // Maps from the global ID, as obtained from AssemblyMapCG's
137  // localToGlobalMap, to the geometry ID.
138  vector<map<int, int>> gidMeshIds(3);
139 
140  // Maps from the global ID to the number of degrees of freedom for
141  // this geometry object.
142  vector<map<int, int>> gidDofs(3);
143 
144  // Array containing maximum information needed for the universal
145  // numbering later. For i = 0,1,2 for each geometry dimension:
146  //
147  // maxVertIds[2*i] = maximum geometry ID at dimension i
148  // maxVertIds[2*i+1] = maximum number of degrees of freedom for all
149  // elements of dimension i.
150  Array<OneD, int> maxVertIds(6, -1);
151 
152  // Figure out mapping from each elemental contribution to offset in
153  // (vert,edge,face) triples.
154  for (cnt = n = 0; n < nExp; ++n)
155  {
156  exp = expList->GetExp(n);
157 
158  // Grab reference to local Schur complement matrix.
159  DNekScalMatSharedPtr schurMat =
160  m_linsys.lock()->GetStaticCondBlock(n)->GetBlock(0, 0);
161 
162  // Process vertices to extract relevant portion of the Schur
163  // complement matrix.
164  for (i = 0; i < exp->GetNverts(); ++i)
165  {
166  meshVertId = exp->GetGeom()->GetVid(i);
167  int locId = exp->GetVertexMap(i);
168 
169  // Get the global ID of this vertex.
170  gId = asmMap->GetLocalToGlobalMap(cnt + locId) - nDirBnd;
171 
172  // Ignore all Dirichlet vertices.
173  if (gId < 0)
174  {
175  continue;
176  }
177 
178  gidDofs[0][gId] = 1;
179 
180  // Extract vertex value from Schur complement matrix.
181  NekDouble vertVal = (*schurMat)(locId, locId);
182 
183  // See if we have processed this vertex from another
184  // element.
185  auto gIt = idMats[0].find(gId);
186 
187  if (gIt == idMats[0].end())
188  {
189  // If not then put our 'matrix' inside idMats.
190  idMats[0][gId] = vector<NekDouble>(1, vertVal);
191  }
192  else
193  {
194  // Otherwise combine with the value that is already
195  // there (i.e. do assembly on this degree of freedom).
196  gIt->second[0] += vertVal;
197  }
198 
199  // Now check to see if the vertex is periodic. If it is,
200  // then we change meshVertId to be the minimum of all the
201  // other periodic vertices, so that we don't end up
202  // duplicating the matrix in our final block matrix.
203  auto pIt = periodicVerts.find(meshVertId);
204  if (pIt != periodicVerts.end())
205  {
206  for (j = 0; j < pIt->second.size(); ++j)
207  {
208  meshVertId = min(meshVertId, pIt->second[j].id);
209  }
210  }
211 
212  // Finally record the other information we need into the
213  // other matrices.
214  gidMeshIds[0][gId] = meshVertId;
215  maxVertIds[0] = max(maxVertIds[0], meshVertId);
216  maxVertIds[1] = 1;
217  }
218 
219  // Process edges. This logic is mostly the same as the previous
220  // block.
221  for (i = 0; i < exp->GetGeom()->GetNumEdges(); ++i)
222  {
223  meshEdgeId = exp->GetGeom()->GetEid(i);
224 
225  Array<OneD, unsigned int> bmap, bmap2;
227  StdRegions::Orientation edgeOrient = exp->GetGeom()->GetEorient(i);
228 
229  // Check if this edge is periodic. We may need to flip
230  // orientation if it is.
231  auto pIt = periodicEdges.find(meshEdgeId);
232  if (pIt != periodicEdges.end())
233  {
234  pair<int, StdRegions::Orientation> idOrient =
235  DeterminePeriodicEdgeOrientId(meshEdgeId, edgeOrient,
236  pIt->second);
237  meshEdgeId = idOrient.first;
238  edgeOrient = idOrient.second;
239  }
240 
241  // Grab edge interior map, and the edge inverse boundary
242  // map, so that we can extract this edge from the Schur
243  // complement matrix.
244  if (exp->GetGeom()->GetNumFaces()) // 3D Element calls
245  {
246  exp->as<LocalRegions::Expansion3D>()
247  ->GetEdgeInteriorToElementMap(i, bmap, sign, edgeOrient);
248  bmap2 = exp->as<LocalRegions::Expansion3D>()
249  ->GetEdgeInverseBoundaryMap(i);
250  }
251  else
252  {
253  exp->GetTraceInteriorToElementMap(i, bmap, sign, edgeOrient);
254  bmap2 = exp->as<LocalRegions::Expansion2D>()
255  ->GetTraceInverseBoundaryMap(i);
256  }
257 
258  // Allocate temporary storage for the extracted edge matrix.
259  const int nEdgeCoeffs = bmap.size();
260  vector<NekDouble> tmpStore(nEdgeCoeffs * nEdgeCoeffs);
261 
262  gId = asmMap->GetLocalToGlobalMap(cnt + bmap[0]);
263 
264  for (j = 0; j < nEdgeCoeffs; ++j)
265  {
266  // We record the minimum ID from the edge for our
267  // maps. This follows the logic that the assembly map
268  // ordering will always give us a contiguous ordering of
269  // global degrees of freedom for edge interior
270  // coefficients.
271  gId = min(gId,
272  asmMap->GetLocalToGlobalMap(cnt + bmap[j]) - nDirBnd);
273 
274  // Ignore Dirichlet edges.
275  if (gId < 0)
276  {
277  continue;
278  }
279 
280  const NekDouble sign1 = sign[j];
281 
282  // Extract this edge, along with sign array for assembly
283  // later.
284  for (k = 0; k < nEdgeCoeffs; ++k)
285  {
286  tmpStore[k + j * nEdgeCoeffs] =
287  sign1 * sign[k] * (*schurMat)(bmap2[j], bmap2[k]);
288  }
289  }
290 
291  if (gId < 0)
292  {
293  continue;
294  }
295 
296  gidDofs[1][gId] = nEdgeCoeffs;
297 
298  // Assemble this edge matrix with another one, if it exists.
299  auto gIt = idMats[1].find(gId);
300  if (gIt == idMats[1].end())
301  {
302  idMats[1][gId] = tmpStore;
303  }
304  else
305  {
306  ASSERTL1(tmpStore.size() == gIt->second.size(),
307  "Number of modes mismatch");
308  Vmath::Vadd(nEdgeCoeffs * nEdgeCoeffs, &gIt->second[0], 1,
309  &tmpStore[0], 1, &gIt->second[0], 1);
310  }
311 
312  gidMeshIds[1][gId] = meshEdgeId;
313  maxVertIds[2] = max(maxVertIds[2], meshEdgeId);
314  maxVertIds[3] = max(maxVertIds[3], nEdgeCoeffs);
315  }
316 
317  // Process faces. This logic is mostly the same as the previous
318  // block.
319  for (i = 0; i < exp->GetGeom()->GetNumFaces(); ++i)
320  {
321  meshFaceId = exp->GetGeom()->GetFid(i);
322 
323  Array<OneD, unsigned int> bmap, bmap2;
325  StdRegions::Orientation faceOrient = exp->GetGeom()->GetForient(i);
326 
327  // Check if this face is periodic. We may need to flip
328  // orientation if it is.
329  auto pIt = periodicFaces.find(meshFaceId);
330  if (pIt != periodicFaces.end())
331  {
332  meshFaceId = min(meshFaceId, pIt->second[0].id);
333  faceOrient = DeterminePeriodicFaceOrient(faceOrient,
334  pIt->second[0].orient);
335  }
336 
337  exp->GetTraceInteriorToElementMap(i, bmap, sign, faceOrient);
338  bmap2 = exp->as<LocalRegions::Expansion3D>()
339  ->GetTraceInverseBoundaryMap(i);
340 
341  // Allocate temporary storage for the extracted face matrix.
342  const int nFaceCoeffs = bmap.size();
343  vector<NekDouble> tmpStore(nFaceCoeffs * nFaceCoeffs);
344 
345  gId = asmMap->GetLocalToGlobalMap(cnt + bmap[0]);
346 
347  for (j = 0; j < nFaceCoeffs; ++j)
348  {
349  gId = min(gId,
350  asmMap->GetLocalToGlobalMap(cnt + bmap[j]) - nDirBnd);
351 
352  // Ignore Dirichlet faces.
353  if (gId < 0)
354  {
355  continue;
356  }
357 
358  const NekDouble sign1 = sign[j];
359 
360  // Extract this face, along with sign array for assembly
361  // later.
362  for (k = 0; k < nFaceCoeffs; ++k)
363  {
364  tmpStore[k + j * nFaceCoeffs] =
365  sign1 * sign[k] * (*schurMat)(bmap2[j], bmap2[k]);
366  }
367  }
368 
369  if (gId < 0)
370  {
371  continue;
372  }
373 
374  gidDofs[2][gId] = nFaceCoeffs;
375 
376  // Assemble this face matrix with another one, if it exists.
377  auto gIt = idMats[2].find(gId);
378  if (gIt == idMats[2].end())
379  {
380  idMats[2][gId] = tmpStore;
381  }
382  else
383  {
384  ASSERTL1(tmpStore.size() == gIt->second.size(),
385  "Number of modes mismatch");
386  Vmath::Vadd(nFaceCoeffs * nFaceCoeffs, &gIt->second[0], 1,
387  &tmpStore[0], 1, &gIt->second[0], 1);
388  }
389 
390  gidMeshIds[2][gId] = meshFaceId;
391  maxVertIds[4] = max(maxVertIds[4], meshFaceId);
392  maxVertIds[5] = max(maxVertIds[5], nFaceCoeffs);
393  }
394 
395  cnt += exp->GetNcoeffs();
396  }
397 
398  // Perform a reduction to find maximum vertex, edge and face
399  // geometry IDs.
400  m_comm = expList->GetSession()->GetComm()->GetRowComm();
401  m_comm->AllReduce(maxVertIds, LibUtilities::ReduceMax);
402 
403  // Concatenate all matrices into contiguous storage and figure out
404  // universal ID numbering.
405  vector<NekDouble> storageBuf;
406  vector<long> globalToUniversal;
407 
408  for (i = 0, cnt = 1; i < 3; ++i)
409  {
410  const int maxDofs = maxVertIds[2 * i + 1];
411 
412  // Note that iterating over the map uses the property that keys
413  // are accessed in order of ascending order, putting everything
414  // in the right order for the global system.
415  for (auto &gIt : idMats[i])
416  {
417  // Copy matrix into storage.
418  storageBuf.insert(storageBuf.end(), gIt.second.begin(),
419  gIt.second.end());
420 
421  // Get mesh ID from global ID number.
422  ASSERTL1(gidMeshIds[i].count(gIt.first) > 0,
423  "Unable to find global ID " +
424  boost::lexical_cast<string>(gIt.first) +
425  " inside map");
426  meshVertId = gidMeshIds[i][gIt.first];
427 
428  for (j = 0; j < gIt.second.size(); ++j)
429  {
430  globalToUniversal.push_back(cnt +
431  meshVertId * maxDofs * maxDofs + j);
432  }
433 
434  // Free up the temporary storage.
435  gIt.second.clear();
436  }
437 
438  cnt += (maxVertIds[2 * i] + 1) * maxDofs * maxDofs;
439  }
440 
441  ASSERTL1(storageBuf.size() == globalToUniversal.size(),
442  "Storage buffer and global to universal map size does "
443  "not match");
444 
445  Array<OneD, NekDouble> storageData(storageBuf.size(), &storageBuf[0]);
446  Array<OneD, long> globalToUniversalMap(globalToUniversal.size(),
447  &globalToUniversal[0]);
448 
449  // Use GS to assemble data between processors.
450  Gs::gs_data *tmpGs =
451  Gs::Init(globalToUniversalMap, m_comm,
452  expList->GetSession()->DefinesCmdLineArgument("verbose"));
453  Gs::Gather(storageData, Gs::gs_add, tmpGs);
454 
455  // Figure out what storage we need in the block matrix.
456  Array<OneD, unsigned int> n_blks(1 + idMats[1].size() + idMats[2].size());
457 
458  // Vertex block is a diagonal matrix.
459  n_blks[0] = idMats[0].size();
460 
461  // Now extract number of rows in each edge and face block from the
462  // gidDofs map.
463  cnt = 1;
464  for (i = 1; i < 3; ++i)
465  {
466  for (auto &gIt : idMats[i])
467  {
468  ASSERTL1(gidDofs[i].count(gIt.first) > 0,
469  "Unable to find number of degrees of freedom for "
470  "global ID " +
471  boost::lexical_cast<string>(gIt.first));
472 
473  n_blks[cnt++] = gidDofs[i][gIt.first];
474  }
475  }
476 
477  // Allocate storage for the block matrix.
478  m_blkMat =
480 
481  // We deal with the vertex matrix separately since all vertices can
482  // be condensed into a single, block-diagonal matrix.
484  n_blks[0], n_blks[0], 0.0, eDIAGONAL);
485 
486  // Fill the vertex matrix with the inverse of each vertex value.
487  cnt = 0;
488  for (auto gIt = idMats[0].begin(); gIt != idMats[0].end(); ++gIt, ++cnt)
489  {
490  (*vertMat)(cnt, cnt) = 1.0 / storageData[cnt];
491  }
492 
493  // Put the vertex matrix in the block matrix.
494  m_blkMat->SetBlock(0, 0, vertMat);
495 
496  // Finally, grab the matrices from the block storage, invert them
497  // and place them in the correct position inside the block matrix.
498  int cnt2 = 1;
499  for (i = 1; i < 3; ++i)
500  {
501  for (auto &gIt : idMats[i])
502  {
503  int nDofs = gidDofs[i][gIt.first];
504 
505  DNekMatSharedPtr tmp =
507 
508  for (j = 0; j < nDofs; ++j)
509  {
510  for (k = 0; k < nDofs; ++k)
511  {
512  (*tmp)(j, k) = storageData[k + j * nDofs + cnt];
513  }
514  }
515 
516  cnt += nDofs * nDofs;
517 
518  tmp->Invert();
519  m_blkMat->SetBlock(cnt2, cnt2, tmp);
520  ++cnt2;
521  }
522  }
523 }
524 
525 /**
526  * @brief Construct a block preconditioner for the hybridized
527  * discontinuous Galerkin system.
528  *
529  * This system is built in a similar fashion to its continuous variant
530  * found in PreconditionerBlock::BlockPreconditionerCG. In this setting
531  * however, the matrix is constructed as:
532  *
533  * \f[ M^{-1} = \mathrm{Diag}[ (\mathbf{S_{1}})_{f}^{-1} ] \f]
534  *
535  * where each matrix is the Schur complement system restricted to a
536  * single face of the trace system.
537  */
539 {
540  std::shared_ptr<MultiRegions::ExpList> expList =
541  ((m_linsys.lock())->GetLocMat()).lock();
542  std::shared_ptr<MultiRegions::ExpList> trace = expList->GetTrace();
544  DNekScalBlkMatSharedPtr loc_mat;
545  DNekScalMatSharedPtr bnd_mat;
546 
547  AssemblyMapDGSharedPtr asmMap =
548  std::dynamic_pointer_cast<AssemblyMapDG>(m_locToGloMap.lock());
549 
550  int i, j, k, n, cnt, cnt2;
551 
552  // Figure out number of Dirichlet trace elements
553  int nTrace = expList->GetTrace()->GetExpSize();
554  int nDir = asmMap->GetNumGlobalDirBndCoeffs();
555 
556  for (cnt = n = 0; n < nTrace; ++n)
557  {
558  if (cnt >= nDir)
559  {
560  break;
561  }
562 
563  cnt += trace->GetExp(n)->GetNcoeffs();
564  }
565 
566  nDir = n;
567 
568  // Allocate storage for block matrix. Need number of unique faces in
569  // trace space.
570  int maxTraceSize = -1;
571  map<int, int> arrayOffsets;
572 
573  for (cnt = 0, n = nDir; n < nTrace; ++n)
574  {
575  int nCoeffs = trace->GetExp(n)->GetNcoeffs();
576  int nCoeffs2 = nCoeffs * nCoeffs;
577 
578  arrayOffsets[n] = cnt;
579  cnt += nCoeffs2;
580 
581  if (maxTraceSize < nCoeffs2)
582  {
583  maxTraceSize = nCoeffs2;
584  }
585  }
586 
587  // Find maximum trace size.
588  m_comm = expList->GetSession()->GetComm()->GetRowComm();
589  m_comm->AllReduce(maxTraceSize, LibUtilities::ReduceMax);
590 
591  // Zero matrix storage.
592  Array<OneD, NekDouble> tmpStore(cnt, 0.0);
593 
594  // Assemble block matrices for each trace element.
595  for (cnt = n = 0; n < expList->GetExpSize(); ++n)
596  {
597  int elmt = n;
598  locExpansion = expList->GetExp(elmt);
599 
601  asmMap->GetElmtToTrace()[elmt];
602 
603  // Block matrix (lambda)
604  loc_mat = (m_linsys.lock())->GetStaticCondBlock(n);
605  bnd_mat = loc_mat->GetBlock(0, 0);
606 
607  int nFacets = locExpansion->GetNtraces();
608 
609  for (cnt2 = i = 0; i < nFacets; ++i)
610  {
611  int nCoeffs = elmtToTraceMap[i]->GetNcoeffs();
612  int elmtId = elmtToTraceMap[i]->GetElmtId();
613 
614  // Ignore Dirichlet edges.
615  if (elmtId < nDir)
616  {
617  cnt += nCoeffs;
618  cnt2 += nCoeffs;
619  continue;
620  }
621 
622  NekDouble *off = &tmpStore[arrayOffsets[elmtId]];
623 
624  for (j = 0; j < nCoeffs; ++j)
625  {
626  NekDouble sign1 = asmMap->GetLocalToGlobalBndSign(cnt + j);
627  for (k = 0; k < nCoeffs; ++k)
628  {
629  NekDouble sign2 = asmMap->GetLocalToGlobalBndSign(cnt + k);
630  off[j * nCoeffs + k] +=
631  (*bnd_mat)(cnt2 + j, cnt2 + k) * sign1 * sign2;
632  }
633  }
634 
635  cnt += nCoeffs;
636  cnt2 += nCoeffs;
637  }
638  }
639 
640  // Set up IDs for universal numbering.
641  Array<OneD, long> uniIds(tmpStore.size());
642  for (cnt = 0, n = nDir; n < nTrace; ++n)
643  {
644  LocalRegions::ExpansionSharedPtr traceExp = trace->GetExp(n);
645  int nCoeffs = traceExp->GetNcoeffs();
646  int geomId = traceExp->GetGeom()->GetGlobalID();
647 
648  for (i = 0; i < nCoeffs * nCoeffs; ++i)
649  {
650  uniIds[cnt++] = geomId * maxTraceSize + i + 1;
651  }
652  }
653 
654  // Assemble matrices across partitions.
655  Gs::gs_data *gsh =
656  Gs::Init(uniIds, m_comm,
657  expList->GetSession()->DefinesCmdLineArgument("verbose"));
658  Gs::Gather(tmpStore, Gs::gs_add, gsh);
659 
660  // Set up diagonal block matrix
661  Array<OneD, unsigned int> n_blks(nTrace - nDir);
662  for (n = 0; n < nTrace - nDir; ++n)
663  {
664  n_blks[n] = trace->GetExp(n + nDir)->GetNcoeffs();
665  }
666 
667  m_blkMat =
669 
670  for (n = 0; n < nTrace - nDir; ++n)
671  {
672  int nCoeffs = trace->GetExp(n + nDir)->GetNcoeffs();
673  DNekMatSharedPtr tmp =
675  NekDouble *off = &tmpStore[arrayOffsets[n + nDir]];
676 
677  for (i = 0; i < nCoeffs; ++i)
678  {
679  for (j = 0; j < nCoeffs; ++j)
680  {
681  (*tmp)(i, j) = off[i * nCoeffs + j];
682  }
683  }
684 
685  tmp->Invert();
686  m_blkMat->SetBlock(n, n, tmp);
687  }
688 }
689 
690 /**
691  * @brief Apply preconditioner to \p pInput and store the result in \p
692  * pOutput.
693  */
695  const Array<OneD, NekDouble> &pInput, Array<OneD, NekDouble> &pOutput)
696 {
697  int nDir = m_locToGloMap.lock()->GetNumGlobalDirBndCoeffs();
698  int nGlobal = m_locToGloMap.lock()->GetNumGlobalBndCoeffs();
699  int nNonDir = nGlobal - nDir;
700  DNekBlkMat &M = (*m_blkMat);
701  NekVector<NekDouble> r(nNonDir, pInput, eWrapper);
702  NekVector<NekDouble> z(nNonDir, pOutput, eWrapper);
703  z = M * r;
704 }
705 } // namespace MultiRegions
706 } // 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:15
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.
void BlockPreconditionerHDG(void)
Construct a block preconditioner for the hybridized discontinuous Galerkin system.
virtual void v_DoPreconditioner(const Array< OneD, NekDouble > &pInput, Array< OneD, NekDouble > &pOutput)
Apply preconditioner to pInput and store the result in pOutput.
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
@ gs_add
Definition: GsLib.hpp:62
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
std::shared_ptr< Expansion > ExpansionSharedPtr
Definition: Expansion.h:68
std::shared_ptr< AssemblyMapDG > AssemblyMapDGSharedPtr
Definition: AssemblyMapDG.h:47
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:51
std::map< int, std::vector< PeriodicEntity > > PeriodicMap
StdRegions::Orientation DeterminePeriodicFaceOrient(StdRegions::Orientation faceOrient, StdRegions::Orientation perFaceOrient)
Determine relative orientation between two faces.
The above copyright notice and this permission notice shall be included.
Definition: CoupledSolver.h:1
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:359