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