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