Nektar++
PreconditionerLowEnergy.cpp
Go to the documentation of this file.
1 ///////////////////////////////////////////////////////////////////////////////
2 //
3 // File Preconditioner.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: Preconditioner definition
32 //
33 ///////////////////////////////////////////////////////////////////////////////
34 
40 #include <LocalRegions/MatrixKey.h>
41 #include <math.h>
42 
43 using namespace std;
44 
45 namespace Nektar
46 {
47  using namespace LibUtilities;
48 
49  namespace MultiRegions
50  {
51  /**
52  * Registers the class with the Factory.
53  */
54  string PreconditionerLowEnergy::className
56  "LowEnergyBlock",
57  PreconditionerLowEnergy::create,
58  "LowEnergy Preconditioning");
59 
60  /**
61  * @class PreconditionerLowEnergy
62  *
63  * This class implements low energy preconditioning for the conjugate
64  * gradient matrix solver.
65  */
66 
67  PreconditionerLowEnergy::PreconditionerLowEnergy(
68  const std::shared_ptr<GlobalLinSys> &plinsys,
69  const AssemblyMapSharedPtr &pLocToGloMap)
70  : Preconditioner(plinsys, pLocToGloMap)
71  {
72  }
73 
75  {
76  GlobalSysSolnType solvertype =
77  m_locToGloMap.lock()->GetGlobalSysSolnType();
78 
79  ASSERTL0(solvertype == eIterativeStaticCond ||
80  solvertype == ePETScStaticCond, "Solver type not valid");
81 
82  std::shared_ptr<MultiRegions::ExpList>
83  expList=((m_linsys.lock())->GetLocMat()).lock();
84 
85  m_comm = expList->GetComm();
86 
88 
89  locExpansion = expList->GetExp(0);
90 
91  int nDim = locExpansion->GetShapeDimension();
92 
93  ASSERTL0(nDim==3,
94  "Preconditioner type only valid in 3D");
95 
96  //Set up block transformation matrix
98 
99  //Sets up multiplicity map for transformation from global to local
101  }
102 
103 
104  /**
105  * \brief Construct the low energy preconditioner from
106  * \f$\mathbf{S}_{2}\f$
107  *
108  * \f[\mathbf{M}^{-1}=\left[\begin{array}{ccc}
109  * Diag[(\mathbf{S_{2}})_{vv}] & & \\ & (\mathbf{S}_{2})_{eb} & \\ & &
110  * (\mathbf{S}_{2})_{fb} \end{array}\right] \f]
111  *
112  * where \f$\mathbf{R}\f$ is the transformation matrix and
113  * \f$\mathbf{S}_{2}\f$ the Schur complement of the modified basis,
114  * given by
115  *
116  * \f[\mathbf{S}_{2}=\mathbf{R}\mathbf{S}_{1}\mathbf{R}^{T}\f]
117  *
118  * where \f$\mathbf{S}_{1}\f$ is the local schur complement matrix for
119  * each element.
120  */
122  {
123  std::shared_ptr<MultiRegions::ExpList>
124  expList=((m_linsys.lock())->GetLocMat()).lock();
126  GlobalLinSysKey linSysKey=(m_linsys.lock())->GetKey();
127 
128  int i, j, k;
129  int nVerts, nEdges,nFaces;
130  int eid, fid, n, cnt, nmodes, nedgemodes, nfacemodes;
131  int nedgemodesloc;
132  NekDouble zero = 0.0;
133 
134  int vMap1, vMap2, sign1, sign2;
135  int m, v, eMap1, eMap2, fMap1, fMap2;
136  int offset, globalrow, globalcol, nCoeffs;
137 
138  // Periodic information
139  PeriodicMap periodicVerts;
140  PeriodicMap periodicEdges;
141  PeriodicMap periodicFaces;
142  expList->GetPeriodicEntities(periodicVerts,periodicEdges,periodicFaces);
143 
144  //matrix storage
145  MatrixStorage storage = eFULL;
146  MatrixStorage vertstorage = eDIAGONAL;
147  MatrixStorage blkmatStorage = eDIAGONAL;
148 
149  //local element static condensed matrices
150  DNekScalBlkMatSharedPtr loc_mat;
151  DNekScalMatSharedPtr bnd_mat;
152 
153  DNekMatSharedPtr pRSRT;
154 
155  DNekMat RS;
156  DNekMat RSRT;
157 
158  auto asmMap = m_locToGloMap.lock();
159 
160  int nDirBnd = asmMap->GetNumGlobalDirBndCoeffs();
161  int nNonDirVerts = asmMap->GetNumNonDirVertexModes();
162 
163  //Vertex, edge and face preconditioner matrices
165  AllocateSharedPtr(nNonDirVerts,nNonDirVerts,zero,vertstorage);
166 
167  Array<OneD, NekDouble> vertArray(nNonDirVerts,0.0);
168  Array<OneD, long> VertBlockToUniversalMap(nNonDirVerts,-1);
169 
170  //maps for different element types
171  int n_exp = expList->GetNumElmts();
172  int nNonDirEdgeIDs=asmMap->GetNumNonDirEdges();
173  int nNonDirFaceIDs=asmMap->GetNumNonDirFaces();
174 
175  set<int> edgeDirMap;
176  set<int> faceDirMap;
177  map<int,int> uniqueEdgeMap;
178  map<int,int> uniqueFaceMap;
179 
180  //this should be of size total number of local edges + faces
181  Array<OneD, int> modeoffset(1 + nNonDirEdgeIDs + nNonDirFaceIDs,0);
182  Array<OneD, int> globaloffset(1 + nNonDirEdgeIDs + nNonDirFaceIDs,0);
183 
184  const Array<OneD, const ExpListSharedPtr>& bndCondExp = expList->GetBndCondExpansions();
185  LocalRegions::Expansion2DSharedPtr bndCondFaceExp;
187  bndConditions = expList->GetBndConditions();
188 
189  int meshVertId;
190  int meshEdgeId;
191  int meshFaceId;
192 
193  const Array<OneD, const int> &extradiredges
194  = asmMap->GetExtraDirEdges();
195  for(i=0; i<extradiredges.num_elements(); ++i)
196  {
197  meshEdgeId=extradiredges[i];
198  edgeDirMap.insert(meshEdgeId);
199  }
200 
201  //Determine which boundary edges and faces have dirichlet values
202  for(i = 0; i < bndCondExp.num_elements(); i++)
203  {
204  cnt = 0;
205  for(j = 0; j < bndCondExp[i]->GetNumElmts(); j++)
206  {
207  bndCondFaceExp = std::dynamic_pointer_cast<
208  LocalRegions::Expansion2D>(bndCondExp[i]->GetExp(j));
209  if (bndConditions[i]->GetBoundaryConditionType() ==
211  {
212  for(k = 0; k < bndCondFaceExp->GetNedges(); k++)
213  {
214  meshEdgeId = bndCondFaceExp->as<LocalRegions::Expansion2D>()->GetGeom2D()->GetEid(k);
215  if(edgeDirMap.count(meshEdgeId) == 0)
216  {
217  edgeDirMap.insert(meshEdgeId);
218  }
219  }
220  meshFaceId = bndCondFaceExp->as<LocalRegions::Expansion2D>()->GetGeom2D()->GetGlobalID();
221  faceDirMap.insert(meshFaceId);
222  }
223  }
224  }
225 
226  int dof=0;
227  int maxFaceDof=0;
228  int maxEdgeDof=0;
229  int nlocalNonDirEdges=0;
230  int nlocalNonDirFaces=0;
231  int ntotalentries=0;
232 
233  map<int,int> EdgeSize;
234  map<int,int> FaceSize;
235  map<int,pair<int,int> >FaceModes;
236 
237  /// - Count edges, face and add up min edges and min face sizes
238  for(n = 0; n < n_exp; ++n)
239  {
240  locExpansion = expList->GetExp(n);
241 
242  nEdges = locExpansion->GetNedges();
243  for(j = 0; j < nEdges; ++j)
244  {
245  int nEdgeInteriorCoeffs = locExpansion->GetEdgeNcoeffs(j) - 2;
246  meshEdgeId = locExpansion->as<LocalRegions::Expansion3D>()
247  ->GetGeom3D()->GetEid(j);
248  if(EdgeSize.count(meshEdgeId) == 0)
249  {
250  EdgeSize[meshEdgeId] = nEdgeInteriorCoeffs;
251  }
252  else
253  {
254  EdgeSize[meshEdgeId] = min(EdgeSize[meshEdgeId],
255  nEdgeInteriorCoeffs);
256  }
257  }
258 
259  nFaces = locExpansion->GetNfaces();
260  for(j = 0; j < nFaces; ++j)
261  {
262  int nFaceInteriorCoeffs = locExpansion->GetFaceIntNcoeffs(j);
263  meshFaceId = locExpansion->as<LocalRegions::Expansion3D>()
264  ->GetGeom3D()->GetFid(j);
265  if(FaceSize.count(meshFaceId) == 0)
266  {
267  FaceSize[meshFaceId] = nFaceInteriorCoeffs;
268 
269  int m0,m1;
270  locExpansion->GetFaceNumModes(j,locExpansion->GetForient(j),m0,m1);
271  FaceModes[meshFaceId] = pair<int,int>(m0,m1);
272  }
273  else
274  {
275  if(nFaceInteriorCoeffs < FaceSize[meshFaceId])
276  {
277  FaceSize[meshFaceId] = nFaceInteriorCoeffs;
278  int m0,m1;
279  locExpansion->GetFaceNumModes(j,locExpansion->GetForient(j),m0,m1);
280  FaceModes[meshFaceId] = pair<int,int>(m0,m1);
281  }
282  }
283  }
284  }
285 
286  bool verbose =
287  expList->GetSession()->DefinesCmdLineArgument("verbose");
288 
289  // For parallel runs need to check have minimum of edges and faces over
290  // partition boundaries
291  if(m_comm->GetSize() > 1)
292  {
293  int EdgeSizeLen = EdgeSize.size();
294  int FaceSizeLen = FaceSize.size();
295  Array<OneD, long> FacetMap(EdgeSizeLen+FaceSizeLen,-1);
296  Array<OneD, NekDouble> FacetLen(EdgeSizeLen+FaceSizeLen,-1);
297 
298  map<int,int>::iterator it;
299 
300  cnt = 0;
301  int maxid = 0;
302  for(it = EdgeSize.begin(); it!=EdgeSize.end(); ++it,++cnt)
303  {
304  FacetMap[cnt] = it->first;
305  maxid = max(it->first,maxid);
306  FacetLen[cnt] = it->second;
307  }
308  maxid++;
309 
310  m_comm->AllReduce(maxid,ReduceMax);
311 
312  for(it = FaceSize.begin(); it!=FaceSize.end(); ++it,++cnt)
313  {
314  FacetMap[cnt] = it->first + maxid;
315  FacetLen[cnt] = it->second;
316  }
317 
318  //Exchange vertex data over different processes
319  Gs::gs_data *tmp = Gs::Init(FacetMap, m_comm, verbose);
320  Gs::Gather(FacetLen, Gs::gs_min, tmp);
321 
322  cnt = 0;
323  for(it = EdgeSize.begin(); it!=EdgeSize.end(); ++it,++cnt)
324  {
325  it->second = (int) FacetLen[cnt];
326  }
327 
328  for(it = FaceSize.begin(); it!=FaceSize.end(); ++it,++cnt)
329  {
330  it->second = (int)FacetLen[cnt];
331  }
332  }
333 
334  // Loop over all the elements in the domain and compute total edge
335  // DOF and set up unique ordering.
336  map<int,int> nblks;
337  int matrixlocation = 0;
338 
339  // First do periodic edges
340  for (auto &pIt : periodicEdges)
341  {
342  meshEdgeId = pIt.first;
343 
344  if(edgeDirMap.count(meshEdgeId)==0)
345  {
346  dof = EdgeSize[meshEdgeId];
347  if(uniqueEdgeMap.count(meshEdgeId)==0 && dof > 0)
348  {
349  bool SetUpNewEdge = true;
350 
351 
352  for (i = 0; i < pIt.second.size(); ++i)
353  {
354  if (!pIt.second[i].isLocal)
355  {
356  continue;
357  }
358 
359  int meshEdgeId2 = pIt.second[i].id;
360  if(edgeDirMap.count(meshEdgeId2)==0)
361  {
362  if(uniqueEdgeMap.count(meshEdgeId2)!=0)
363  {
364  // set unique map to same location
365  uniqueEdgeMap[meshEdgeId] =
366  uniqueEdgeMap[meshEdgeId2];
367  SetUpNewEdge = false;
368  }
369  }
370  else
371  {
372  edgeDirMap.insert(meshEdgeId);
373  SetUpNewEdge = false;
374  }
375  }
376 
377  if(SetUpNewEdge)
378  {
379  uniqueEdgeMap[meshEdgeId]=matrixlocation;
380  globaloffset[matrixlocation]+=ntotalentries;
381  modeoffset[matrixlocation]=dof*dof;
382  ntotalentries+=dof*dof;
383  nblks [matrixlocation++] = dof;
384  }
385  }
386  }
387  }
388 
389  for(cnt=n=0; n < n_exp; ++n)
390  {
391  locExpansion = expList->GetExp(n);
392 
393  for (j = 0; j < locExpansion->GetNedges(); ++j)
394  {
395  meshEdgeId = locExpansion->as<LocalRegions::Expansion3D>()->GetGeom3D()->GetEid(j);
396  dof = EdgeSize[meshEdgeId];
397  maxEdgeDof = (dof > maxEdgeDof ? dof : maxEdgeDof);
398 
399  if(edgeDirMap.count(meshEdgeId)==0)
400  {
401  if(uniqueEdgeMap.count(meshEdgeId)==0 && dof > 0)
402 
403  {
404  uniqueEdgeMap[meshEdgeId]=matrixlocation;
405 
406  globaloffset[matrixlocation]+=ntotalentries;
407  modeoffset[matrixlocation]=dof*dof;
408  ntotalentries+=dof*dof;
409  nblks[matrixlocation++] = dof;
410  }
411  nlocalNonDirEdges+=dof*dof;
412  }
413  }
414  }
415 
416  // Loop over all the elements in the domain and compute max face
417  // DOF. Reduce across all processes to get universal maximum.
418  // - Periodic faces
419  for (auto &pIt : periodicFaces)
420  {
421  meshFaceId = pIt.first;
422 
423  if(faceDirMap.count(meshFaceId)==0)
424  {
425  dof = FaceSize[meshFaceId];
426 
427  if(uniqueFaceMap.count(meshFaceId) == 0 && dof > 0)
428  {
429  bool SetUpNewFace = true;
430 
431  if(pIt.second[0].isLocal)
432  {
433  int meshFaceId2 = pIt.second[0].id;
434 
435  if(faceDirMap.count(meshFaceId2)==0)
436  {
437  if(uniqueFaceMap.count(meshFaceId2)!=0)
438  {
439  // set unique map to same location
440  uniqueFaceMap[meshFaceId] =
441  uniqueFaceMap[meshFaceId2];
442  SetUpNewFace = false;
443  }
444  }
445  else // set face to be a Dirichlet face
446  {
447  faceDirMap.insert(meshFaceId);
448  SetUpNewFace = false;
449  }
450  }
451 
452  if(SetUpNewFace)
453  {
454  uniqueFaceMap[meshFaceId]=matrixlocation;
455 
456  modeoffset[matrixlocation]=dof*dof;
457  globaloffset[matrixlocation]+=ntotalentries;
458  ntotalentries+=dof*dof;
459  nblks[matrixlocation++] = dof;
460  }
461  }
462  }
463  }
464 
465  for(cnt=n=0; n < n_exp; ++n)
466  {
467  locExpansion = expList->GetExp(n);
468 
469  for (j = 0; j < locExpansion->GetNfaces(); ++j)
470  {
471  meshFaceId = locExpansion->as<LocalRegions::Expansion3D>()->
472  GetGeom3D()->GetFid(j);
473 
474  dof = FaceSize[meshFaceId];
475  maxFaceDof = (dof > maxFaceDof ? dof : maxFaceDof);
476 
477  if(faceDirMap.count(meshFaceId)==0)
478  {
479  if(uniqueFaceMap.count(meshFaceId)==0 && dof > 0)
480  {
481  uniqueFaceMap[meshFaceId]=matrixlocation;
482  modeoffset[matrixlocation]=dof*dof;
483  globaloffset[matrixlocation]+=ntotalentries;
484  ntotalentries+=dof*dof;
485  nblks[matrixlocation++] = dof;
486  }
487  nlocalNonDirFaces+=dof*dof;
488  }
489  }
490  }
491 
492  m_comm->AllReduce(maxEdgeDof, ReduceMax);
493  m_comm->AllReduce(maxFaceDof, ReduceMax);
494 
495  //Allocate arrays for block to universal map (number of expansions * p^2)
496  Array<OneD, long> BlockToUniversalMap(ntotalentries,-1);
497  Array<OneD, int> localToGlobalMatrixMap(nlocalNonDirEdges +
498  nlocalNonDirFaces,-1);
499 
500  //Allocate arrays to store matrices (number of expansions * p^2)
501  Array<OneD, NekDouble> BlockArray(nlocalNonDirEdges +
502  nlocalNonDirFaces,0.0);
503 
504  int matrixoffset=0;
505  int vGlobal;
506  int uniEdgeOffset = 0;
507 
508  // Need to obtain a fixed offset for the universal number
509  // of the faces which come after the edge numbering
510  for(n=0; n < n_exp; ++n)
511  {
512  for(j = 0; j < locExpansion->GetNedges(); ++j)
513  {
514  meshEdgeId = locExpansion->as<LocalRegions::Expansion3D>()
515  ->GetGeom3D()->GetEid(j);
516 
517  uniEdgeOffset = max(meshEdgeId, uniEdgeOffset);
518  }
519  }
520  uniEdgeOffset++;
521 
522  m_comm->AllReduce(uniEdgeOffset,ReduceMax);
523  uniEdgeOffset = uniEdgeOffset*maxEdgeDof*maxEdgeDof;
524 
525  for(n=0; n < n_exp; ++n)
526  {
527  locExpansion = expList->GetExp(n);
528 
529  //loop over the edges of the expansion
530  for(j = 0; j < locExpansion->GetNedges(); ++j)
531  {
532  //get mesh edge id
533  meshEdgeId = locExpansion->as<LocalRegions::Expansion3D>()
534  ->GetGeom3D()->GetEid(j);
535 
536  nedgemodes = EdgeSize[meshEdgeId];
537 
538  if(edgeDirMap.count(meshEdgeId)==0 && nedgemodes > 0)
539  {
540  // Determine the Global edge offset
541  int edgeOffset = globaloffset[uniqueEdgeMap[meshEdgeId]];
542 
543  // Determine a universal map offset
544  int uniOffset = meshEdgeId;
545  auto pIt = periodicEdges.find(meshEdgeId);
546  if (pIt != periodicEdges.end())
547  {
548  for (int l = 0; l < pIt->second.size(); ++l)
549  {
550  uniOffset = min(uniOffset, pIt->second[l].id);
551  }
552  }
553  uniOffset = uniOffset*maxEdgeDof*maxEdgeDof;
554 
555  for(k=0; k<nedgemodes*nedgemodes; ++k)
556  {
557  vGlobal=edgeOffset+k;
558  localToGlobalMatrixMap[matrixoffset+k]=vGlobal;
559  BlockToUniversalMap[vGlobal] = uniOffset + k + 1;
560  }
561  matrixoffset+=nedgemodes*nedgemodes;
562  }
563  }
564 
565  Array<OneD, unsigned int> faceInteriorMap;
566  Array<OneD, int> faceInteriorSign;
567  //loop over the faces of the expansion
568  for(j = 0; j < locExpansion->GetNfaces(); ++j)
569  {
570  //get mesh face id
571  meshFaceId = locExpansion->as<LocalRegions::Expansion3D>()
572  ->GetGeom3D()->GetFid(j);
573 
574  nfacemodes = FaceSize[meshFaceId];
575 
576  //Check if face has dirichlet values
577  if(faceDirMap.count(meshFaceId)==0 && nfacemodes > 0)
578  {
579  // Determine the Global edge offset
580  int faceOffset = globaloffset[uniqueFaceMap[meshFaceId]];
581  // Determine a universal map offset
582  int uniOffset = meshFaceId;
583  // use minimum face edge when periodic
584  auto pIt = periodicFaces.find(meshFaceId);
585  if (pIt != periodicFaces.end())
586  {
587  uniOffset = min(uniOffset, pIt->second[0].id);
588  }
589  uniOffset = uniOffset * maxFaceDof * maxFaceDof;
590 
591  for(k=0; k<nfacemodes*nfacemodes; ++k)
592  {
593  vGlobal=faceOffset+k;
594 
595  localToGlobalMatrixMap[matrixoffset+k]
596  = vGlobal;
597 
598  BlockToUniversalMap[vGlobal] = uniOffset +
599  uniEdgeOffset + k + 1;
600  }
601  matrixoffset+=nfacemodes*nfacemodes;
602  }
603  }
604  }
605 
606  matrixoffset=0;
607 
608  map<int,int>::iterator it;
609  Array<OneD, unsigned int> n_blks(nblks.size()+1);
610  n_blks[0] = nNonDirVerts;
611  for(i =1, it = nblks.begin(); it != nblks.end(); ++it)
612  {
613  n_blks[i++] = it->second;
614  }
615 
617  ::AllocateSharedPtr(n_blks, n_blks, blkmatStorage);
618 
619  //Here we loop over the expansion and build the block low energy
620  //preconditioner as well as the block versions of the transformation
621  //matrices.
622  for(cnt=n=0; n < n_exp; ++n)
623  {
624  locExpansion = expList->GetExp(n);
625  nCoeffs=locExpansion->NumBndryCoeffs();
626 
627  //Get correct transformation matrix for element type
628  DNekMat &R = (*m_RBlk->GetBlock(n,n));
629 
631  (nCoeffs, nCoeffs, zero, storage);
632  RSRT = (*pRSRT);
633 
634  nVerts=locExpansion->GetGeom()->GetNumVerts();
635  nEdges=locExpansion->GetGeom()->GetNumEdges();
636  nFaces=locExpansion->GetGeom()->GetNumFaces();
637 
638  //Get statically condensed matrix
639  loc_mat = (m_linsys.lock())->GetStaticCondBlock(n);
640 
641  //Extract boundary block (elemental S1)
642  bnd_mat=loc_mat->GetBlock(0,0);
643 
644  //offset by number of rows
645  offset = bnd_mat->GetRows();
646 
647  DNekScalMat &S=(*bnd_mat);
648 
649  DNekMat Sloc(nCoeffs,nCoeffs);
650 
651  // For variable p we need to just use the active modes
652  NekDouble mask1 = 1.0;
653  NekDouble mask2 = 1.0;
654  NekDouble val;
655 
656  for(int i = 0; i < nCoeffs; ++i)
657  {
658  if(m_signChange)
659  {
660  mask1 = (m_locToGloSignMult[cnt+i] == 0.0)? 0.0:1.0;
661  }
662  for(int j = 0; j < nCoeffs; ++j)
663  {
664  if(m_signChange)
665  {
666  mask2 = (m_locToGloSignMult[cnt+j] == 0.0)? 0.0:1.0;
667  }
668  val = S(i,j)*mask1*mask2;
669  Sloc.SetValue(i,j,val);
670  }
671  }
672 
673  //Calculate R*S*trans(R)
674  RSRT = R*Sloc*Transpose(R);
675 
676  //loop over vertices of the element and return the vertex map
677  //for each vertex
678  for (v=0; v<nVerts; ++v)
679  {
680  vMap1=locExpansion->GetVertexMap(v);
681 
682  //Get vertex map
683  globalrow = asmMap->
684  GetLocalToGlobalBndMap(cnt+vMap1)-nDirBnd;
685 
686  if(globalrow >= 0)
687  {
688  for (m=0; m<nVerts; ++m)
689  {
690  vMap2=locExpansion->GetVertexMap(m);
691 
692  //global matrix location (without offset due to
693  //dirichlet values)
694  globalcol = asmMap->
695  GetLocalToGlobalBndMap(cnt+vMap2)-nDirBnd;
696 
697  //offset for dirichlet conditions
698  if (globalcol == globalrow)
699  {
700  //modal connectivity between elements
701  sign1 = asmMap->
702  GetLocalToGlobalBndSign(cnt + vMap1);
703  sign2 = asmMap->
704  GetLocalToGlobalBndSign(cnt + vMap2);
705 
706  vertArray[globalrow]
707  += sign1*sign2*RSRT(vMap1,vMap2);
708 
709 
710  meshVertId = locExpansion->as<LocalRegions::Expansion3D>()->GetGeom3D()->GetVid(v);
711 
712  auto pIt = periodicVerts.find(meshVertId);
713  if (pIt != periodicVerts.end())
714  {
715  for (k = 0; k < pIt->second.size(); ++k)
716  {
717  meshVertId = min(meshVertId, pIt->second[k].id);
718  }
719  }
720 
721  VertBlockToUniversalMap[globalrow]
722  = meshVertId + 1;
723  }
724  }
725  }
726  }
727 
728  //loop over edges of the element and return the edge map
729  for (eid=0; eid<nEdges; ++eid)
730  {
731 
732  meshEdgeId = locExpansion->as<LocalRegions::Expansion3D>()
733  ->GetGeom3D()->GetEid(eid);
734 
735 
736  nedgemodes = EdgeSize[meshEdgeId];
737  if(nedgemodes)
738  {
739  nedgemodesloc = locExpansion->GetEdgeNcoeffs(eid)-2;
740  DNekMatSharedPtr m_locMat =
742  (nedgemodes,nedgemodes,zero,storage);
743 
744  Array<OneD, unsigned int> edgemodearray = locExpansion->GetEdgeInverseBoundaryMap(eid);
745 
746  if(edgeDirMap.count(meshEdgeId)==0)
747  {
748  for (v=0; v<nedgemodesloc; ++v)
749  {
750  eMap1=edgemodearray[v];
751  sign1 = asmMap->
752  GetLocalToGlobalBndSign(cnt + eMap1);
753 
754  if(sign1 == 0)
755  {
756  continue;
757  }
758 
759  for (m=0; m<nedgemodesloc; ++m)
760  {
761  eMap2=edgemodearray[m];
762 
763  //modal connectivity between elements
764  sign2 = asmMap->
765  GetLocalToGlobalBndSign(cnt + eMap2);
766 
767  NekDouble globalEdgeValue = sign1*sign2*RSRT(eMap1,eMap2);
768 
769  if(sign2 != 0)
770  {
771  //if(eMap1 == eMap2)
772  BlockArray[matrixoffset+v*nedgemodes+m]=globalEdgeValue;
773  }
774  }
775  }
776  matrixoffset+=nedgemodes*nedgemodes;
777  }
778  }
779  }
780 
781  //loop over faces of the element and return the face map
782  for (fid=0; fid<nFaces; ++fid)
783  {
784  meshFaceId = locExpansion->as<LocalRegions::Expansion3D>()
785  ->GetGeom3D()->GetFid(fid);
786 
787  nfacemodes = FaceSize[meshFaceId];
788  if(nfacemodes > 0)
789  {
790  DNekMatSharedPtr m_locMat =
792  (nfacemodes,nfacemodes,zero,storage);
793 
794  if(faceDirMap.count(meshFaceId) == 0)
795  {
796  Array<OneD, unsigned int> facemodearray;
797  StdRegions::Orientation faceOrient =
798  locExpansion->GetForient(fid);
799 
800  auto pIt = periodicFaces.find(meshFaceId);
801  if (pIt != periodicFaces.end())
802  {
803  if(meshFaceId == min(meshFaceId, pIt->second[0].id))
804  {
805  faceOrient = DeterminePeriodicFaceOrient
806  (faceOrient,pIt->second[0].orient);
807  }
808  }
809 
810  facemodearray = locExpansion->GetFaceInverseBoundaryMap
811  (fid,faceOrient,FaceModes[meshFaceId].first,
812  FaceModes[meshFaceId].second);
813 
814  for (v=0; v<nfacemodes; ++v)
815  {
816  fMap1=facemodearray[v];
817 
818  sign1 = asmMap->
819  GetLocalToGlobalBndSign(cnt + fMap1);
820 
821  ASSERTL1(sign1 != 0,"Something is wrong since we "
822  "shoudl only be extracting modes for "
823  "lowest order expansion");
824 
825  for (m=0; m<nfacemodes; ++m)
826  {
827  fMap2=facemodearray[m];
828 
829  //modal connectivity between elements
830  sign2 = asmMap->
831  GetLocalToGlobalBndSign(cnt + fMap2);
832 
833  ASSERTL1(sign2 != 0,"Something is wrong since "
834  "we shoudl only be extracting modes for "
835  "lowest order expansion");
836 
837  // Get the face-face value from the
838  // low energy matrix (S2)
839  NekDouble globalFaceValue = sign1*sign2*
840  RSRT(fMap1,fMap2);
841 
842  //local face value to global face value
843  //if(fMap1 == fMap2)
844  BlockArray[matrixoffset+v*nfacemodes+m]=
845  globalFaceValue;
846  }
847  }
848  matrixoffset+=nfacemodes*nfacemodes;
849  }
850  }
851  }
852 
853  //offset for the expansion
854  cnt+=nCoeffs;
855  }
856 
857  if(nNonDirVerts!=0)
858  {
859  //Exchange vertex data over different processes
860  Gs::gs_data *tmp = Gs::Init(VertBlockToUniversalMap, m_comm, verbose);
861  Gs::Gather(vertArray, Gs::gs_add, tmp);
862 
863  }
864 
865  Array<OneD, NekDouble> GlobalBlock(ntotalentries,0.0);
866  if(ntotalentries)
867  {
868  //Assemble edge matrices of each process
869  Vmath::Assmb(BlockArray.num_elements(),
870  BlockArray,
871  localToGlobalMatrixMap,
872  GlobalBlock);
873  }
874 
875  //Exchange edge & face data over different processes
876  Gs::gs_data *tmp1 = Gs::Init(BlockToUniversalMap, m_comm, verbose);
877  Gs::Gather(GlobalBlock, Gs::gs_add, tmp1);
878 
879  // Populate vertex block
880  for (int i = 0; i < nNonDirVerts; ++i)
881  {
882  VertBlk->SetValue(i,i,1.0/vertArray[i]);
883  }
884 
885  //Set the first block to be the diagonal of the vertex space
886  m_BlkMat->SetBlock(0,0, VertBlk);
887 
888  //Build the edge and face matrices from the vector
889  DNekMatSharedPtr gmat;
890 
891  offset=0;
892  // -1 since we ignore vert block
893  for(int loc=0; loc<n_blks.num_elements()-1; ++loc)
894  {
895  nmodes = n_blks[1+loc];
897  (nmodes,nmodes,zero,storage);
898 
899  for (v=0; v<nmodes; ++v)
900  {
901  for (m=0; m<nmodes; ++m)
902  {
903  NekDouble Value = GlobalBlock[offset+v*nmodes+m];
904  gmat->SetValue(v,m,Value);
905 
906  }
907  }
908  m_BlkMat->SetBlock(1+loc,1+loc, gmat);
909  offset+=modeoffset[loc];
910  }
911 
912  // invert blocks.
913  int totblks=m_BlkMat->GetNumberOfBlockRows();
914  for (i=1; i< totblks; ++i)
915  {
916  unsigned int nmodes=m_BlkMat->GetNumberOfRowsInBlockRow(i);
917  if(nmodes)
918  {
919  DNekMatSharedPtr tmp_mat =
921  (nmodes,nmodes,zero,storage);
922 
923  tmp_mat=m_BlkMat->GetBlock(i,i);
924  tmp_mat->Invert();
925 
926  m_BlkMat->SetBlock(i,i,tmp_mat);
927  }
928  }
929  }
930 
931 
932  /**
933  * Apply the low energy preconditioner during the conjugate gradient
934  * routine
935  */
937  const Array<OneD, NekDouble>& pInput,
938  Array<OneD, NekDouble>& pOutput)
939  {
940  int nDir = m_locToGloMap.lock()->GetNumGlobalDirBndCoeffs();
941  int nGlobal = m_locToGloMap.lock()->GetNumGlobalBndCoeffs();
942  int nNonDir = nGlobal-nDir;
943  DNekBlkMat &M = (*m_BlkMat);
944 
945  NekVector<NekDouble> r(nNonDir,pInput,eWrapper);
946  NekVector<NekDouble> z(nNonDir,pOutput,eWrapper);
947 
948  z = M * r;
949  }
950 
951 
952  /**
953  * Set a block transformation matrices for each element type. These are
954  * needed in routines that transform the schur complement matrix to and
955  * from the low energy basis.
956  */
958  {
959  std::shared_ptr<MultiRegions::ExpList>
960  expList=((m_linsys.lock())->GetLocMat()).lock();
963  map<int,int> EdgeSize;
964 
965  int n;
966 
967  std::map<ShapeType, DNekScalMatSharedPtr> maxRmat;
968  map<ShapeType, LocalRegions::ExpansionSharedPtr > maxElmt;
969  map<ShapeType, Array<OneD, unsigned int> > vertMapMaxR;
970  map<ShapeType, Array<OneD, Array<OneD, unsigned int> > > edgeMapMaxR;
971 
972 
973  //Sets up reference element and builds transformation matrix for
974  // maximum polynomial order meshes
975  SetUpReferenceElements(maxRmat,maxElmt,vertMapMaxR,edgeMapMaxR);
976 
977  const Array<OneD,const unsigned int>& nbdry_size
978  = m_locToGloMap.lock()->GetNumLocalBndCoeffsPerPatch();
979 
980  int n_exp=expList->GetNumElmts();
981 
982  MatrixStorage blkmatStorage = eDIAGONAL;
983 
984  //Variants of R matrices required for low energy preconditioning
986  ::AllocateSharedPtr(nbdry_size, nbdry_size , blkmatStorage);
988  ::AllocateSharedPtr(nbdry_size, nbdry_size , blkmatStorage);
989 
990  DNekMatSharedPtr rmat, invrmat;
991 
992  int offset = 0;
993 
994  // Set up transformation matrices whilst checking to see if
995  // consecutive matrices are the same and if so reuse the
996  // matrices and store how many consecutive offsets there
997  // are
998  for(n=0; n < n_exp; ++n)
999  {
1000  locExp = expList->GetExp(n);
1001  ShapeType eltype = locExp->DetShapeType();
1002 
1003  int nbndcoeffs = locExp->NumBndryCoeffs();
1004 
1005  if(m_sameBlock.size() == 0)
1006  {
1007  rmat = ExtractLocMat(locExp,maxRmat[eltype],
1008  maxElmt[eltype],
1009  vertMapMaxR[eltype],
1010  edgeMapMaxR[eltype]);
1011  //Block R matrix
1012  m_RBlk->SetBlock(n, n, rmat);
1013 
1015  invrmat->Invert();
1016 
1017  //Block inverse R matrix
1018  m_InvRBlk->SetBlock(n, n, invrmat);
1019 
1020  m_sameBlock.push_back(pair<int,int>(1,nbndcoeffs));
1021  locExpSav = locExp;
1022  }
1023  else
1024  {
1025  bool reuse = true;
1026 
1027  // check to see if same as previous matrix and
1028  // reuse if we can
1029  for(int i = 0; i < 3; ++i)
1030  {
1031  if(locExpSav->GetBasis(i) != locExp->GetBasis(i))
1032  {
1033  reuse = false;
1034  break;
1035  }
1036  }
1037 
1038  if(reuse)
1039  {
1040  m_RBlk->SetBlock(n, n, rmat);
1041  m_InvRBlk->SetBlock(n, n, invrmat);
1042 
1043  m_sameBlock[offset] =
1044  (pair<int,int>(m_sameBlock[offset].first+1,nbndcoeffs));
1045  }
1046  else
1047  {
1048  rmat = ExtractLocMat(locExp,maxRmat[eltype],
1049  maxElmt[eltype],
1050  vertMapMaxR[eltype],
1051  edgeMapMaxR[eltype]);
1052 
1053  //Block R matrix
1054  m_RBlk->SetBlock(n, n, rmat);
1055 
1057  invrmat->Invert();
1058  //Block inverse R matrix
1059  m_InvRBlk->SetBlock(n, n, invrmat);
1060 
1061  m_sameBlock.push_back(pair<int,int>(1,nbndcoeffs));
1062  offset++;
1063  locExpSav = locExp;
1064  }
1065  }
1066  }
1067  }
1068 
1069  /**
1070  * \brief Transform the solution vector vector to low energy.
1071  *
1072  * As the conjugate gradient system is solved for the low energy basis,
1073  * the solution vector \f$\mathbf{x}\f$ must be transformed to the low
1074  * energy basis i.e. \f$\overline{\mathbf{x}}=\mathbf{R}\mathbf{x}\f$.
1075  */
1077  Array<OneD, NekDouble>& pInOut,
1078  int offset)
1079  {
1080  auto asmMap = m_locToGloMap.lock();
1081  int nGlobBndDofs = asmMap->GetNumGlobalBndCoeffs();
1082  int nDirBndDofs = asmMap->GetNumGlobalDirBndCoeffs();
1083  int nGlobHomBndDofs = nGlobBndDofs - nDirBndDofs;
1084  int nLocBndDofs = asmMap->GetNumLocalBndCoeffs();
1085 
1086  //Non-dirichlet boundary dofs
1087  Array<OneD, NekDouble> tmpOffset = pInOut + offset;
1088  NekVector<NekDouble> F_HomBnd(nGlobHomBndDofs, tmpOffset, eWrapper);
1089 
1090  //Block transformation matrix
1091  DNekBlkMat &R = *m_RBlk;
1092 
1093  Array<OneD, NekDouble> pLocal(nLocBndDofs, 0.0);
1094  NekVector<NekDouble> F_LocBnd(nLocBndDofs,pLocal,eWrapper);
1095  m_map = asmMap->GetLocalToGlobalBndMap();
1096  Array<OneD, NekDouble> pLocalIn(nLocBndDofs, 0.0);
1097 
1098  //Not actually needed but we should only work with the
1099  //Global boundary dofs
1100  Array<OneD,NekDouble> tmp(nGlobBndDofs,0.0);
1101  Vmath::Vcopy(nGlobBndDofs, pInOut.get(), 1, tmp.get(), 1);
1102 
1103  //Global boundary (with dirichlet values) to local
1104  //boundary with multiplicity
1105  Vmath::Gathr(m_map.num_elements(), m_locToGloSignMult.get(),
1106  tmp.get(), m_map.get(), pLocalIn.get());
1107 
1108  //Multiply by the block transformation matrix
1109  int cnt = 0;
1110  int cnt1 = 0;
1111  for(int i = 0; i < m_sameBlock.size(); ++i)
1112  {
1113  int nexp = m_sameBlock[i].first;
1114  int nbndcoeffs = m_sameBlock[i].second;
1115  Blas::Dgemm('N','N', nbndcoeffs, nexp, nbndcoeffs,
1116  1.0, &(R.GetBlock(cnt1,cnt1)->GetPtr()[0]),
1117  nbndcoeffs,pLocalIn.get() + cnt, nbndcoeffs,
1118  0.0, pLocal.get() + cnt, nbndcoeffs);
1119  cnt += nbndcoeffs*nexp;
1120  cnt1 += nexp;
1121  }
1122 
1123  //Assemble local boundary to global non-dirichlet Dofs
1124  asmMap->AssembleBnd(F_LocBnd,F_HomBnd, nDirBndDofs);
1125  }
1126 
1127  /**
1128  * \brief Transform the solution vector to low energy form.
1129  *
1130  * As the conjugate gradient system is solved for the low energy basis,
1131  * the solution vector \f$\mathbf{x}\f$ must be transformed to the low
1132  * energy basis i.e. \f$\overline{\mathbf{x}}=\mathbf{R}\mathbf{x}\f$.
1133  */
1135  const Array<OneD, NekDouble>& pInput,
1136  Array<OneD, NekDouble>& pOutput)
1137  {
1138  auto asmMap = m_locToGloMap.lock();
1139 
1140  int nGlobBndDofs = asmMap->GetNumGlobalBndCoeffs();
1141  int nDirBndDofs = asmMap->GetNumGlobalDirBndCoeffs();
1142  int nGlobHomBndDofs = nGlobBndDofs - nDirBndDofs;
1143  int nLocBndDofs = asmMap->GetNumLocalBndCoeffs();
1144 
1145  //Input/output vectors should be length nGlobHomBndDofs
1146  ASSERTL1(pInput.num_elements() >= nGlobHomBndDofs,
1147  "Input array is greater than the nGlobHomBndDofs");
1148  ASSERTL1(pOutput.num_elements() >= nGlobHomBndDofs,
1149  "Output array is greater than the nGlobHomBndDofs");
1150 
1151  //vectors of length number of non-dirichlet boundary dofs
1152  NekVector<NekDouble> F_HomBnd(nGlobHomBndDofs,pOutput,
1153  eWrapper);
1154  //Block transformation matrix
1155  DNekBlkMat &R = *m_RBlk;
1156 
1157  Array<OneD, NekDouble> pLocal(nLocBndDofs, 0.0);
1158  NekVector<NekDouble> F_LocBnd(nLocBndDofs,pLocal,eWrapper);
1159  m_map = asmMap->GetLocalToGlobalBndMap();
1160  Array<OneD, NekDouble> pLocalIn(nLocBndDofs, 0.0);
1161 
1162  // Allocated array of size number of global boundary dofs and copy
1163  // the input array to the tmp array offset by Dirichlet boundary
1164  // conditions.
1165  Array<OneD,NekDouble> tmp(nGlobBndDofs,0.0);
1166  Vmath::Vcopy(nGlobHomBndDofs, pInput.get(), 1, tmp.get()
1167  + nDirBndDofs, 1);
1168 
1169  //Global boundary dofs (with zeroed dirichlet values) to
1170  //local boundary dofs - This also divides by the mulplicity
1171  Vmath::Gathr(m_map.num_elements(), m_locToGloSignMult.get(),
1172  tmp.get(), m_map.get(), pLocalIn.get());
1173 
1174  //Multiply by the block transformation matrix
1175  int cnt = 0;
1176  int cnt1 = 0;
1177  for(int i = 0; i < m_sameBlock.size(); ++i)
1178  {
1179  int nexp = m_sameBlock[i].first;
1180  int nbndcoeffs = m_sameBlock[i].second;
1181  Blas::Dgemm('N','N', nbndcoeffs, nexp, nbndcoeffs,
1182  1.0, &(R.GetBlock(cnt1,cnt1)->GetPtr()[0]),
1183  nbndcoeffs,pLocalIn.get() + cnt, nbndcoeffs,
1184  0.0, pLocal.get() + cnt, nbndcoeffs);
1185  cnt += nbndcoeffs*nexp;
1186  cnt1 += nexp;
1187  }
1188 
1189  //Assemble local boundary to global non-dirichlet boundary
1190  asmMap->AssembleBnd(F_LocBnd,F_HomBnd,nDirBndDofs);
1191  }
1192 
1193  /**
1194  * \brief transform the solution vector from low energy back to the
1195  * original basis.
1196  *
1197  * After the conjugate gradient routine the output vector is in the low
1198  * energy basis and must be trasnformed back to the original basis in
1199  * order to get the correct solution out. the solution vector
1200  * i.e. \f$\mathbf{x}=\mathbf{R^{T}}\mathbf{\overline{x}}\f$.
1201  */
1203  Array<OneD, NekDouble>& pInOut)
1204  {
1205  auto asmMap = m_locToGloMap.lock();
1206 
1207  int nGlobBndDofs = asmMap->GetNumGlobalBndCoeffs();
1208  int nDirBndDofs = asmMap->GetNumGlobalDirBndCoeffs();
1209  int nGlobHomBndDofs = nGlobBndDofs - nDirBndDofs;
1210  int nLocBndDofs = asmMap->GetNumLocalBndCoeffs();
1211 
1212  ASSERTL1(pInOut.num_elements() >= nGlobBndDofs,
1213  "Output array is greater than the nGlobBndDofs");
1214 
1215  //Block transformation matrix
1216  DNekBlkMat &R = *m_RBlk;
1217 
1218  Array<OneD, NekDouble> tmpOffset = pInOut + nDirBndDofs;
1219  NekVector<NekDouble> V_GlobHomBnd(nGlobHomBndDofs, tmpOffset, eWrapper);
1220 
1221  Array<OneD, NekDouble> pLocalIn(nLocBndDofs, 0.0);
1222  NekVector<NekDouble> V_LocBnd(nLocBndDofs,pLocalIn,eWrapper);
1223  m_map = asmMap->GetLocalToGlobalBndMap();
1224  Array<OneD,NekDouble> tmp(nGlobBndDofs,0.0);
1225  Array<OneD, NekDouble> pLocal(nLocBndDofs, 0.0);
1226 
1227  //Global boundary (less dirichlet) to local boundary
1228  asmMap->GlobalToLocalBnd(V_GlobHomBnd,V_LocBnd, nDirBndDofs);
1229 
1230  //Multiply by the transpose of block transformation matrix
1231  int cnt = 0;
1232  int cnt1 = 0;
1233  for(int i = 0; i < m_sameBlock.size(); ++i)
1234  {
1235  int nexp = m_sameBlock[i].first;
1236  int nbndcoeffs = m_sameBlock[i].second;
1237  Blas::Dgemm('T','N', nbndcoeffs, nexp, nbndcoeffs,
1238  1.0, &(R.GetBlock(cnt1,cnt1)->GetPtr()[0]),
1239  nbndcoeffs,pLocalIn.get() + cnt, nbndcoeffs,
1240  0.0, pLocal.get() + cnt, nbndcoeffs);
1241  cnt += nbndcoeffs*nexp;
1242  cnt1 += nexp;
1243  }
1244 
1245  //Assemble local boundary to global boundary
1246  Vmath::Assmb(nLocBndDofs, m_locToGloSignMult.get(),pLocal.get(), m_map.get(), tmp.get());
1247 
1248  //Universal assemble across processors
1249  asmMap->UniversalAssembleBnd(tmp);
1250 
1251  //copy non-dirichlet boundary values
1252  Vmath::Vcopy(nGlobBndDofs-nDirBndDofs, tmp.get() + nDirBndDofs, 1, pInOut.get() + nDirBndDofs, 1);
1253  }
1254 
1255  /**
1256  * \brief Multiply by the block inverse transformation matrix
1257  */
1259  const Array<OneD, NekDouble>& pInput,
1260  Array<OneD, NekDouble>& pOutput)
1261  {
1262  auto asmMap = m_locToGloMap.lock();
1263 
1264  int nGlobBndDofs = asmMap->GetNumGlobalBndCoeffs();
1265  int nDirBndDofs = asmMap->GetNumGlobalDirBndCoeffs();
1266  int nGlobHomBndDofs = nGlobBndDofs - nDirBndDofs;
1267  int nLocBndDofs = asmMap->GetNumLocalBndCoeffs();
1268 
1269  ASSERTL1(pInput.num_elements() >= nGlobHomBndDofs,
1270  "Input array is greater than the nGlobHomBndDofs");
1271  ASSERTL1(pOutput.num_elements() >= nGlobHomBndDofs,
1272  "Output array is greater than the nGlobHomBndDofs");
1273 
1274  //vectors of length number of non-dirichlet boundary dofs
1275  NekVector<NekDouble> F_GlobBnd(nGlobHomBndDofs,pInput,eWrapper);
1276  NekVector<NekDouble> F_HomBnd(nGlobHomBndDofs,pOutput,
1277  eWrapper);
1278  //Block inverse transformation matrix
1279  DNekBlkMat &invR = *m_InvRBlk;
1280 
1281  Array<OneD, NekDouble> pLocal(nLocBndDofs, 0.0);
1282  NekVector<NekDouble> F_LocBnd(nLocBndDofs,pLocal,eWrapper);
1283  m_map = asmMap->GetLocalToGlobalBndMap();
1284  Array<OneD, NekDouble> pLocalIn(nLocBndDofs, 0.0);
1285 
1286  // Allocated array of size number of global boundary dofs and copy
1287  // the input array to the tmp array offset by Dirichlet boundary
1288  // conditions.
1289  Array<OneD,NekDouble> tmp(nGlobBndDofs,0.0);
1290  Vmath::Vcopy(nGlobHomBndDofs, pInput.get(), 1, tmp.get() + nDirBndDofs, 1);
1291 
1292  //Global boundary dofs (with zeroed dirichlet values) to
1293  //local boundary dofs
1294  Vmath::Gathr(m_map.num_elements(), m_locToGloSignMult.get(),
1295  tmp.get(), m_map.get(), pLocalIn.get());
1296 
1297  //Multiply by the inverse transformation matrix
1298  int cnt = 0;
1299  int cnt1 = 0;
1300  for(int i = 0; i < m_sameBlock.size(); ++i)
1301  {
1302  int nexp = m_sameBlock[i].first;
1303  int nbndcoeffs = m_sameBlock[i].second;
1304  Blas::Dgemm('N','N', nbndcoeffs, nexp, nbndcoeffs,
1305  1.0, &(invR.GetBlock(cnt1,cnt1)->GetPtr()[0]),
1306  nbndcoeffs,pLocalIn.get() + cnt, nbndcoeffs,
1307  0.0, pLocal.get() + cnt, nbndcoeffs);
1308  cnt += nbndcoeffs*nexp;
1309  cnt1 += nexp;
1310  }
1311 
1312 
1313  //Assemble local boundary to global non-dirichlet boundary
1314  asmMap->AssembleBnd(F_LocBnd,F_HomBnd,nDirBndDofs);
1315 
1316  }
1317 
1318  /**
1319  * \brief Multiply by the block tranposed inverse transformation matrix
1320  */
1322  const Array<OneD, NekDouble>& pInput,
1323  Array<OneD, NekDouble>& pOutput)
1324  {
1325  auto asmMap = m_locToGloMap.lock();
1326 
1327  int nGlobBndDofs = asmMap->GetNumGlobalBndCoeffs();
1328  int nDirBndDofs = asmMap->GetNumGlobalDirBndCoeffs();
1329  int nGlobHomBndDofs = nGlobBndDofs - nDirBndDofs;
1330  int nLocBndDofs = asmMap->GetNumLocalBndCoeffs();
1331 
1332  ASSERTL1(pInput.num_elements() >= nGlobHomBndDofs,
1333  "Input array is greater than the nGlobHomBndDofs");
1334  ASSERTL1(pOutput.num_elements() >= nGlobHomBndDofs,
1335  "Output array is greater than the nGlobHomBndDofs");
1336 
1337  //vectors of length number of non-dirichlet boundary dofs
1338  NekVector<NekDouble> F_GlobBnd(nGlobHomBndDofs,pInput,eWrapper);
1339  NekVector<NekDouble> F_HomBnd(nGlobHomBndDofs,pOutput,
1340  eWrapper);
1341  //Block inverse transformation matrix
1342  DNekBlkMat &invR = *m_InvRBlk;
1343 
1344  Array<OneD, NekDouble> pLocalIn(nLocBndDofs, 0.0);
1345  NekVector<NekDouble> F_LocBnd(nLocBndDofs,pLocalIn,eWrapper);
1346  m_map = asmMap->GetLocalToGlobalBndMap();
1347  Array<OneD, NekDouble> pLocal(nLocBndDofs, 0.0);
1348 
1349  asmMap->GlobalToLocalBnd(pInput,pLocalIn, nDirBndDofs);
1350 
1351 
1352  //Multiply by the transpose of block transformation matrix
1353  int cnt = 0;
1354  int cnt1 = 0;
1355  for(int i = 0; i < m_sameBlock.size(); ++i)
1356  {
1357  int nexp = m_sameBlock[i].first;
1358  int nbndcoeffs = m_sameBlock[i].second;
1359  Blas::Dgemm('T','N', nbndcoeffs, nexp, nbndcoeffs,
1360  1.0, &(invR.GetBlock(cnt1,cnt1)->GetPtr()[0]),
1361  nbndcoeffs,pLocalIn.get() + cnt, nbndcoeffs,
1362  0.0, pLocal.get() + cnt, nbndcoeffs);
1363  cnt += nbndcoeffs*nexp;
1364  cnt1 += nexp;
1365  }
1366 
1367 
1368  asmMap->AssembleBnd(pLocal,pOutput, nDirBndDofs);
1369 
1370  Vmath::Vmul(nGlobHomBndDofs,pOutput,1,m_multiplicity,1,pOutput,1);
1371  }
1372 
1373 
1374  /**
1375  * \brief Set up the transformed block matrix system
1376  *
1377  * Sets up a block elemental matrix in which each of the block matrix is
1378  * the low energy equivalent
1379  * i.e. \f$\mathbf{S}_{2}=\mathbf{R}\mathbf{S}_{1}\mathbf{R}^{T}\f$
1380  */
1383  int bndoffset,
1384  const std::shared_ptr<DNekScalMat > &loc_mat)
1385  {
1386  std::shared_ptr<MultiRegions::ExpList>
1387  expList=((m_linsys.lock())->GetLocMat()).lock();
1388 
1389  LocalRegions::ExpansionSharedPtr locExpansion;
1390  locExpansion = expList->GetExp(n);
1391  unsigned int nbnd=locExpansion->NumBndryCoeffs();
1392 
1393  MatrixStorage storage = eFULL;
1395  AllocateSharedPtr(nbnd,nbnd,0.0,storage);
1396 
1397  //transformation matrices
1398  DNekMat &R = (*m_RBlk->GetBlock(n,n));
1399 
1400  // Original Schur Complement
1401  DNekScalMat &S1 = (*loc_mat);
1402 
1403  DNekMat Sloc(nbnd,nbnd);
1404 
1405  // For variable p we need to just use the active modes
1406  NekDouble mask1 = 1.0;
1407  NekDouble mask2 = 1.0;
1408  NekDouble val;
1409 
1410  for(int i = 0; i < nbnd; ++i)
1411  {
1412  if(m_signChange)
1413  {
1414  mask1 = (m_locToGloSignMult[bndoffset+i] == 0.0)? 0.0:1.0;
1415  }
1416  for(int j = 0; j < nbnd; ++j)
1417  {
1418  if(m_signChange)
1419  {
1420  mask2 = (m_locToGloSignMult[bndoffset+j] == 0.0)? 0.0:1.0;
1421  }
1422  val = S1(i,j)*mask1*mask2;
1423  Sloc.SetValue(i,j,val);
1424  }
1425  }
1426 
1427  //create low energy matrix
1428  DNekMat &S2 = (*pS2);
1429 
1430  S2= R*Sloc*Transpose(R);
1431 
1432  DNekScalMatSharedPtr return_val;
1433  return_val = MemoryManager<DNekScalMat>::AllocateSharedPtr(1.0, pS2);
1434 
1435  return return_val;
1436  }
1437 
1438  /**
1439  * Create the inverse multiplicity map.
1440  */
1442  {
1443  auto asmMap = m_locToGloMap.lock();
1444 
1445  unsigned int nGlobalBnd = asmMap->GetNumGlobalBndCoeffs();
1446  unsigned int nEntries = asmMap->GetNumLocalBndCoeffs();
1447  unsigned int i;
1448 
1449  const Array<OneD, const int> &vMap
1450  = asmMap->GetLocalToGlobalBndMap();
1451 
1453  = asmMap->GetLocalToGlobalBndSign();
1454 
1455  m_signChange=asmMap->GetSignChange();
1456 
1457  // Count the multiplicity of each global DOF on this process
1458  Array<OneD, NekDouble> vCounts(nGlobalBnd, 0.0);
1459  if(m_signChange)
1460  {
1461  for (i = 0; i < nEntries; ++i)
1462  {
1463  if(fabs(sign[i]) > 0.0)
1464  {
1465  vCounts[vMap[i]] += 1.0;
1466  }
1467  else // set zero modes to 1 so inverse below does not cause problems
1468  {
1469  vCounts[vMap[i]] = 1.0;
1470  }
1471  }
1472  }
1473  else
1474  {
1475  for (i = 0; i < nEntries; ++i)
1476  {
1477  vCounts[vMap[i]] += 1.0;
1478  }
1479  }
1480 
1481  // Get universal multiplicity by globally assembling counts
1482  asmMap->UniversalAssembleBnd(vCounts);
1483 
1484  // Construct a map of 1/multiplicity
1486  for (i = 0; i < nEntries; ++i)
1487  {
1488  if(m_signChange)
1489  {
1490  m_locToGloSignMult[i] = sign[i]*1.0/vCounts[vMap[i]];
1491  }
1492  else
1493  {
1494  m_locToGloSignMult[i] = 1.0/vCounts[vMap[i]];
1495  }
1496  }
1497 
1498  int nDirBnd = asmMap->GetNumGlobalDirBndCoeffs();
1499  int nGlobHomBnd = nGlobalBnd - nDirBnd;
1500  int nLocBnd = asmMap->GetNumLocalBndCoeffs();
1501 
1502  //Set up multiplicity array for inverse transposed transformation matrix
1503  Array<OneD,NekDouble> tmp(nGlobHomBnd,1.0);
1504  m_multiplicity = Array<OneD,NekDouble>(nGlobHomBnd,1.0);
1505  Array<OneD,NekDouble> loc(nLocBnd,1.0);
1506 
1507  asmMap->GlobalToLocalBnd(tmp,loc, nDirBnd);
1508  asmMap->AssembleBnd(loc,m_multiplicity, nDirBnd);
1509  Vmath::Sdiv(nGlobHomBnd,1.0,m_multiplicity,1,m_multiplicity,1);
1510 
1511  }
1512 
1513  /**
1514  *\brief Sets up the reference prismatic element needed to construct
1515  *a low energy basis
1516  */
1518  {
1519  //////////////////////////
1520  // Set up Prism element //
1521  //////////////////////////
1522 
1523  const int three=3;
1524  const int nVerts = 6;
1525  const double point[][3] = {
1526  {-1,-1,0}, {1,-1,0}, {1,1,0},
1527  {-1,1,0}, {0,-1,sqrt(double(3))}, {0,1,sqrt(double(3))},
1528  };
1529 
1530  //std::shared_ptr<SpatialDomains::PointGeom> verts[6];
1532  for(int i=0; i < nVerts; ++i)
1533  {
1535  ( three, i, point[i][0], point[i][1], point[i][2] );
1536  }
1537  const int nEdges = 9;
1538  const int vertexConnectivity[][2] = {
1539  {0,1}, {1,2}, {3,2}, {0,3}, {0,4},
1540  {1,4}, {2,5}, {3,5}, {4,5}
1541  };
1542 
1543  // Populate the list of edges
1544  SpatialDomains::SegGeomSharedPtr edges[nEdges];
1545  for(int i=0; i < nEdges; ++i){
1546  SpatialDomains::PointGeomSharedPtr vertsArray[2];
1547  for(int j=0; j<2; ++j)
1548  {
1549  vertsArray[j] = verts[vertexConnectivity[i][j]];
1550  }
1551  edges[i] = MemoryManager<SpatialDomains::SegGeom>::AllocateSharedPtr(i, three, vertsArray);
1552  }
1553 
1554  ////////////////////////
1555  // Set up Prism faces //
1556  ////////////////////////
1557 
1558  const int nFaces = 5;
1559  //quad-edge connectivity base-face0, vertical-quadface2, vertical-quadface4
1560  const int quadEdgeConnectivity[][4] = { {0,1,2,3}, {1,6,8,5}, {3,7,8,4} };
1561  // QuadId ordered as 0, 1, 2, otherwise return false
1562  const int quadId[] = { 0,-1,1,-1,2 };
1563 
1564  //triangle-edge connectivity side-triface-1, side triface-3
1565  const int triEdgeConnectivity[][3] = { {0,5,4}, {2,6,7} };
1566  // TriId ordered as 0, 1, otherwise return false
1567  const int triId[] = { -1,0,-1,1,-1 };
1568 
1569  // Populate the list of faces
1570  SpatialDomains::Geometry2DSharedPtr faces[nFaces];
1571  for(int f = 0; f < nFaces; ++f){
1572  if(f == 1 || f == 3) {
1573  int i = triId[f];
1574  SpatialDomains::SegGeomSharedPtr edgeArray[3];
1575  for(int j = 0; j < 3; ++j){
1576  edgeArray[j] = edges[triEdgeConnectivity[i][j]];
1577  }
1579  }
1580  else {
1581  int i = quadId[f];
1582  SpatialDomains::SegGeomSharedPtr edgeArray[4];
1583  for(int j=0; j < 4; ++j){
1584  edgeArray[j] = edges[quadEdgeConnectivity[i][j]];
1585  }
1587  }
1588  }
1589 
1591 
1592  return geom;
1593  }
1594 
1595  /**
1596  *\brief Sets up the reference prismatic element needed to construct
1597  *a low energy basis mapping arrays
1598  */
1600  {
1601  //////////////////////////
1602  // Set up Pyramid element //
1603  //////////////////////////
1604 
1605  const int nVerts = 5;
1606  const double point[][3] = {
1607  {-1,-1,0}, {1,-1,0}, {1,1,0},
1608  {-1,1,0}, {0,0,sqrt(double(2))}
1609  };
1610 
1611  //boost::shared_ptr<SpatialDomains::PointGeom> verts[6];
1612  const int three=3;
1614  for(int i=0; i < nVerts; ++i)
1615  {
1617  ( three, i, point[i][0], point[i][1], point[i][2] );
1618  }
1619  const int nEdges = 8;
1620  const int vertexConnectivity[][2] = {
1621  {0,1}, {1,2}, {2,3}, {3,0},
1622  {0,4}, {1,4}, {2,4}, {3,4}
1623  };
1624 
1625  // Populate the list of edges
1626  SpatialDomains::SegGeomSharedPtr edges[nEdges];
1627  for(int i=0; i < nEdges; ++i)
1628  {
1629  SpatialDomains::PointGeomSharedPtr vertsArray[2];
1630  for(int j=0; j<2; ++j)
1631  {
1632  vertsArray[j] = verts[vertexConnectivity[i][j]];
1633  }
1634  edges[i] = MemoryManager<SpatialDomains::SegGeom>::AllocateSharedPtr(i, three, vertsArray);
1635  }
1636 
1637  ////////////////////////
1638  // Set up Pyramid faces //
1639  ////////////////////////
1640 
1641  const int nFaces = 5;
1642  //quad-edge connectivity base-face0,
1643  const int quadEdgeConnectivity[][4] = {{0,1,2,3}};
1644 
1645  //triangle-edge connectivity side-triface-1, side triface-2
1646  const int triEdgeConnectivity[][3] = { {0,5,4}, {1,6,5}, {2,7,6}, {3,4,7}};
1647 
1648  // Populate the list of faces
1649  SpatialDomains::Geometry2DSharedPtr faces[nFaces];
1650  for(int f = 0; f < nFaces; ++f)
1651  {
1652  if(f == 0)
1653  {
1654  SpatialDomains::SegGeomSharedPtr edgeArray[4];
1655  for(int j=0; j < 4; ++j)
1656  {
1657  edgeArray[j] = edges[quadEdgeConnectivity[f][j]];
1658  }
1659 
1661  }
1662  else {
1663  int i = f-1;
1664  SpatialDomains::SegGeomSharedPtr edgeArray[3];
1665  for(int j = 0; j < 3; ++j)
1666  {
1667  edgeArray[j] = edges[triEdgeConnectivity[i][j]];
1668  }
1670  }
1671  }
1672 
1675 
1676  return geom;
1677  }
1678 
1679  /**
1680  *\brief Sets up the reference tretrahedral element needed to construct
1681  *a low energy basis
1682  */
1684  {
1685  /////////////////////////////////
1686  // Set up Tetrahedron vertices //
1687  /////////////////////////////////
1688 
1689  int i,j;
1690  const int three=3;
1691  const int nVerts = 4;
1692  const double point[][3] = {
1693  {-1,-1/sqrt(double(3)),-1/sqrt(double(6))},
1694  {1,-1/sqrt(double(3)),-1/sqrt(double(6))},
1695  {0,2/sqrt(double(3)),-1/sqrt(double(6))},
1696  {0,0,3/sqrt(double(6))}};
1697 
1698  std::shared_ptr<SpatialDomains::PointGeom> verts[4];
1699  for(i=0; i < nVerts; ++i)
1700  {
1701  verts[i] =
1704  ( three, i, point[i][0], point[i][1], point[i][2] );
1705  }
1706 
1707  //////////////////////////////
1708  // Set up Tetrahedron Edges //
1709  //////////////////////////////
1710 
1711  // SegGeom (int id, const int coordim), EdgeComponent(id, coordim)
1712  const int nEdges = 6;
1713  const int vertexConnectivity[][2] = {
1714  {0,1},{1,2},{0,2},{0,3},{1,3},{2,3}
1715  };
1716 
1717  // Populate the list of edges
1718  SpatialDomains::SegGeomSharedPtr edges[nEdges];
1719  for(i=0; i < nEdges; ++i)
1720  {
1721  std::shared_ptr<SpatialDomains::PointGeom>
1722  vertsArray[2];
1723  for(j=0; j<2; ++j)
1724  {
1725  vertsArray[j] = verts[vertexConnectivity[i][j]];
1726  }
1727 
1729  ::AllocateSharedPtr(i, three, vertsArray);
1730  }
1731 
1732  //////////////////////////////
1733  // Set up Tetrahedron faces //
1734  //////////////////////////////
1735 
1736  const int nFaces = 4;
1737  const int edgeConnectivity[][3] = {
1738  {0,1,2}, {0,4,3}, {1,5,4}, {2,5,3}
1739  };
1740 
1741  // Populate the list of faces
1742  SpatialDomains::TriGeomSharedPtr faces[nFaces];
1743  for(i=0; i < nFaces; ++i)
1744  {
1745  SpatialDomains::SegGeomSharedPtr edgeArray[3];
1746  for(j=0; j < 3; ++j)
1747  {
1748  edgeArray[j] = edges[edgeConnectivity[i][j]];
1749  }
1750 
1751 
1753  ::AllocateSharedPtr(i, edgeArray);
1754  }
1755 
1758  (0, faces);
1759 
1760  return geom;
1761  }
1762 
1763  /**
1764  *\brief Sets up the reference hexahedral element needed to construct
1765  *a low energy basis
1766  */
1768  {
1769  ////////////////////////////////
1770  // Set up Hexahedron vertices //
1771  ////////////////////////////////
1772 
1773  const int three=3;
1774 
1775  const int nVerts = 8;
1776  const double point[][3] = {
1777  {0,0,0}, {1,0,0}, {1,1,0}, {0,1,0},
1778  {0,0,1}, {1,0,1}, {1,1,1}, {0,1,1}
1779  };
1780 
1781  // Populate the list of verts
1783  for( int i = 0; i < nVerts; ++i ) {
1785  ::AllocateSharedPtr(three, i, point[i][0],
1786  point[i][1], point[i][2]);
1787  }
1788 
1789  /////////////////////////////
1790  // Set up Hexahedron Edges //
1791  /////////////////////////////
1792 
1793  // SegGeom (int id, const int coordim), EdgeComponent(id, coordim)
1794  const int nEdges = 12;
1795  const int vertexConnectivity[][2] = {
1796  {0,1}, {1,2}, {2,3}, {0,3}, {0,4}, {1,5},
1797  {2,6}, {3,7}, {4,5}, {5,6}, {6,7}, {4,7}
1798  };
1799 
1800  // Populate the list of edges
1801  SpatialDomains::SegGeomSharedPtr edges[nEdges];
1802  for( int i = 0; i < nEdges; ++i ) {
1803  SpatialDomains::PointGeomSharedPtr vertsArray[2];
1804  for( int j = 0; j < 2; ++j ) {
1805  vertsArray[j] = verts[vertexConnectivity[i][j]];
1806  }
1808  AllocateSharedPtr( i, three, vertsArray);
1809  }
1810 
1811  /////////////////////////////
1812  // Set up Hexahedron faces //
1813  /////////////////////////////
1814 
1815  const int nFaces = 6;
1816  const int edgeConnectivity[][4] = {
1817  {0,1,2,3}, {0,5,8,4}, {1,6,9,5},
1818  {2,7,10,6}, {3,7,11,4}, {8,9,10,11}
1819  };
1820 
1821  // Populate the list of faces
1822  SpatialDomains::QuadGeomSharedPtr faces[nFaces];
1823  for( int i = 0; i < nFaces; ++i ) {
1824  SpatialDomains::SegGeomSharedPtr edgeArray[4];
1825  for( int j = 0; j < 4; ++j ) {
1826  edgeArray[j] = edges[edgeConnectivity[i][j]];
1827  }
1829  }
1830 
1833  (0, faces);
1834 
1835  return geom;
1836  }
1837 
1838 
1839  /**
1840  * \brief Loop expansion and determine different variants of the
1841  * transformation matrix
1842  *
1843  * Sets up multiple reference elements based on the element expansion.
1844  */
1846  std::map<ShapeType, DNekScalMatSharedPtr> &maxRmat,
1847  map<ShapeType, LocalRegions::ExpansionSharedPtr > &maxElmt,
1848  map<ShapeType, Array<OneD, unsigned int> > &vertMapMaxR,
1849  map<ShapeType, Array<OneD, Array<OneD, unsigned int> > > &edgeMapMaxR)
1850  {
1851 
1852  std::shared_ptr<MultiRegions::ExpList>
1853  expList=((m_linsys.lock())->GetLocMat()).lock();
1854  GlobalLinSysKey linSysKey=(m_linsys.lock())->GetKey();
1855 
1857 
1858  // face maps for pyramid and hybrid meshes - not need to return.
1859  map<ShapeType, Array<OneD, Array<OneD, unsigned int> > > faceMapMaxR;
1860 
1861  /* Determine the maximum expansion order for all elements */
1862  int nummodesmax = 0;
1864 
1865  for(int n = 0; n < expList->GetNumElmts(); ++n)
1866  {
1867  locExp = expList->GetExp(n);
1868 
1869  nummodesmax = max(nummodesmax, locExp->GetBasisNumModes(0));
1870  nummodesmax = max(nummodesmax, locExp->GetBasisNumModes(1));
1871  nummodesmax = max(nummodesmax, locExp->GetBasisNumModes(2));
1872 
1873  Shapes[locExp->DetShapeType()] = 1;
1874  }
1875 
1876 
1877  m_comm->AllReduce(nummodesmax, ReduceMax);
1878  m_comm->AllReduce(Shapes, ReduceMax);
1879 
1880  if(Shapes[ePyramid]) // if Pyramids used then need Tet and Hex expansion
1881  {
1882  Shapes[eTetrahedron] = 1;
1883  Shapes[eHexahedron] = 1;
1884  }
1885 
1886  StdRegions::MatrixType PreconR;
1887  if(linSysKey.GetMatrixType() == StdRegions::eMass)
1888  {
1889  PreconR = StdRegions::ePreconRMass;
1890  }
1891  else
1892  {
1893  PreconR = StdRegions::ePreconR;
1894  }
1895 
1899 
1900  /*
1901  * Set up a transformation matrices for equal max order
1902  * polynomial meshes
1903  */
1904 
1905  if(Shapes[eHexahedron])
1906  {
1908  //Bases for Hex element
1909  const BasisKey HexBa(eModified_A, nummodesmax,
1910  PointsKey(nummodesmax+1, eGaussLobattoLegendre));
1911  const BasisKey HexBb(eModified_A, nummodesmax,
1912  PointsKey(nummodesmax+1, eGaussLobattoLegendre));
1913  const BasisKey HexBc(eModified_A, nummodesmax,
1914  PointsKey(nummodesmax+1, eGaussLobattoLegendre));
1915 
1916  //Create reference Hexahdedral expansion
1918 
1920  ::AllocateSharedPtr(HexBa,HexBb,HexBc,
1921  hexgeom);
1922 
1923  maxElmt[eHexahedron] = HexExp;
1924 
1925  // Hex:
1926  HexExp->GetInverseBoundaryMaps(vmap,emap,fmap);
1927  vertMapMaxR[eHexahedron] = vmap;
1928  edgeMapMaxR[eHexahedron] = emap;
1929  faceMapMaxR[eHexahedron] = fmap;
1930 
1931  //Get hexahedral transformation matrix
1933  (PreconR, eHexahedron,
1934  *HexExp, linSysKey.GetConstFactors());
1935  maxRmat[eHexahedron] = HexExp->GetLocMatrix(HexR);
1936  }
1937 
1938  if(Shapes[eTetrahedron])
1939  {
1941  //Bases for Tetrahedral element
1942  const BasisKey TetBa(eModified_A, nummodesmax,
1943  PointsKey(nummodesmax+1, eGaussLobattoLegendre));
1944  const BasisKey TetBb(eModified_B, nummodesmax,
1945  PointsKey(nummodesmax, eGaussRadauMAlpha1Beta0));
1946  const BasisKey TetBc(eModified_C, nummodesmax,
1947  PointsKey(nummodesmax, eGaussRadauMAlpha2Beta0));
1948 
1949  //Create reference tetrahedral expansion
1951 
1953  ::AllocateSharedPtr(TetBa,TetBb,TetBc,tetgeom);
1954 
1955  maxElmt[eTetrahedron] = TetExp;
1956 
1957  TetExp->GetInverseBoundaryMaps(vmap,emap,fmap);
1958  vertMapMaxR[eTetrahedron] = vmap;
1959  edgeMapMaxR[eTetrahedron] = emap;
1960  faceMapMaxR[eTetrahedron] = fmap;
1961 
1962  //Get tetrahedral transformation matrix
1964  (PreconR, eTetrahedron,
1965  *TetExp, linSysKey.GetConstFactors());
1966  maxRmat[eTetrahedron] = TetExp->GetLocMatrix(TetR);
1967 
1968  if((Shapes[ePyramid])||(Shapes[eHexahedron]))
1969  {
1970  ReSetTetMaxRMat(nummodesmax, TetExp, maxRmat,
1971  vertMapMaxR, edgeMapMaxR, faceMapMaxR);
1972  }
1973  }
1974 
1975  if(Shapes[ePyramid])
1976  {
1978 
1979  //Bases for Pyramid element
1980  const BasisKey PyrBa(eModified_A, nummodesmax,
1981  PointsKey(nummodesmax+1, eGaussLobattoLegendre));
1982  const BasisKey PyrBb(eModified_A, nummodesmax,
1983  PointsKey(nummodesmax+1, eGaussLobattoLegendre));
1984  const BasisKey PyrBc(eModifiedPyr_C, nummodesmax,
1985  PointsKey(nummodesmax, eGaussRadauMAlpha2Beta0));
1986 
1987  //Create reference pyramid expansion
1989 
1991  ::AllocateSharedPtr(PyrBa,PyrBb,PyrBc,pyrgeom);
1992 
1993  maxElmt[ePyramid] = PyrExp;
1994 
1995  // Pyramid:
1996  PyrExp->GetInverseBoundaryMaps(vmap,emap,fmap);
1997  vertMapMaxR[ePyramid] = vmap;
1998  edgeMapMaxR[ePyramid] = emap;
1999  faceMapMaxR[ePyramid] = fmap;
2000 
2001  // Set up Pyramid Transformation Matrix based on Tet
2002  // and Hex expansion
2003  SetUpPyrMaxRMat(nummodesmax,PyrExp,maxRmat,vertMapMaxR,
2004  edgeMapMaxR,faceMapMaxR);
2005  }
2006 
2007  if(Shapes[ePrism])
2008  {
2010  //Bases for Prism element
2011  const BasisKey PrismBa(eModified_A, nummodesmax,
2012  PointsKey(nummodesmax+1, eGaussLobattoLegendre));
2013  const BasisKey PrismBb(eModified_A, nummodesmax,
2014  PointsKey(nummodesmax+1, eGaussLobattoLegendre));
2015  const BasisKey PrismBc(eModified_B, nummodesmax,
2016  PointsKey(nummodesmax, eGaussRadauMAlpha1Beta0));
2017 
2018  //Create reference prismatic expansion
2020 
2022  ::AllocateSharedPtr(PrismBa,PrismBb,PrismBc,prismgeom);
2023  maxElmt[ePrism] = PrismExp;
2024 
2025  // Prism:
2026  PrismExp->GetInverseBoundaryMaps(vmap,emap,fmap);
2027  vertMapMaxR[ePrism] = vmap;
2028  edgeMapMaxR[ePrism] = emap;
2029 
2030  faceMapMaxR[ePrism] = fmap;
2031 
2032  if((Shapes[ePyramid])||(Shapes[eHexahedron]))
2033  {
2034  ReSetPrismMaxRMat(nummodesmax, PrismExp, maxRmat,
2035  vertMapMaxR, edgeMapMaxR,
2036  faceMapMaxR, false);
2037  }
2038  else
2039  {
2040 
2041  //Get prismatic transformation matrix
2043  (PreconR, ePrism,
2044  *PrismExp, linSysKey.GetConstFactors());
2045  maxRmat[ePrism] =
2046  PrismExp->GetLocMatrix(PrismR);
2047 
2048  if(Shapes[eTetrahedron]) // reset triangular faces from Tet
2049  {
2050  ReSetPrismMaxRMat(nummodesmax, PrismExp, maxRmat,
2051  vertMapMaxR, edgeMapMaxR,
2052  faceMapMaxR, true);
2053  }
2054  }
2055  }
2056  }
2057 
2060  std::map<ShapeType, DNekScalMatSharedPtr> &maxRmat,
2061  std::map<ShapeType, Array<OneD, unsigned int> > &vertMapMaxR,
2062  std::map<ShapeType, Array<OneD, Array<OneD, unsigned int> > > &edgeMapMaxR,
2063  std::map<ShapeType, Array<OneD, Array<OneD, unsigned int> > > &faceMapMaxR)
2064  {
2065  int nRows = PyrExp->NumBndryCoeffs();
2066  NekDouble val;
2067  NekDouble zero = 0.0;
2069  AllocateSharedPtr(nRows,nRows,zero,eFULL);
2070 
2071  // set diagonal to 1
2072  for(int i = 0; i < nRows; ++i)
2073  {
2074  newmat->SetValue(i,i,1.0);
2075  }
2076 
2077  // The following lists specify the number of adjacent
2078  // edges to each vertex (nadj) then the Hex vert to
2079  // use for each pyramid ver in the vert-edge map (VEVert)
2080  // followed by the hex edge to use for each pyramid edge
2081  // in the vert-edge map (VEEdge)
2082  const int nadjedge[] = {3,3,3,3,4};
2083  const int VEHexVert[][4] = {{0,0,0,-1},{1,1,1,-1},{2,2,2,-1},{3,3,3,-1},{4,5,6,7}};
2084  const int VEHexEdge[][4] = {{0,3,4,-1},{0,1,5,-1},{1,2,6,-1},{2,3,7,-1},{4,5,6,7}};
2085  const int VEPyrEdge[][4] = {{0,3,4,-1},{0,1,5,-1},{1,2,6,-1},{2,3,7,-1},{4,5,6,7}};
2086 
2087  // fill vertex to edge coupling
2088  for(int v = 0; v < 5; ++v)
2089  {
2090  for(int e = 0; e < nadjedge[v]; ++e)
2091  {
2092  for(int i = 0; i < nummodesmax-2; ++i)
2093  {
2094  // Note this is using wrong shape but gives
2095  // answer that seems to have correct error!
2096  val = (*maxRmat[eHexahedron])(
2097  vertMapMaxR[eHexahedron][VEHexVert[v][e]],
2098  edgeMapMaxR[eHexahedron][VEHexEdge[v][e]][i]);
2099  newmat->SetValue(vertMapMaxR[ePyramid][v],
2100  edgeMapMaxR[ePyramid][VEPyrEdge[v][e]][i],val);
2101  }
2102  }
2103  }
2104 
2105  int nfacemodes;
2106  nfacemodes = (nummodesmax-2)*(nummodesmax-2);
2107  // First four verties are all adjacent to base face
2108  for(int v = 0; v < 4; ++v)
2109  {
2110  for(int i = 0; i < nfacemodes; ++i)
2111  {
2112  val = (*maxRmat[eHexahedron])(vertMapMaxR[eHexahedron][v],
2113  faceMapMaxR[eHexahedron][0][i]);
2114  newmat->SetValue(vertMapMaxR[ePyramid][v],
2115  faceMapMaxR[ePyramid][0][i],val);
2116  }
2117  }
2118 
2119 
2120  const int nadjface[] = {2,2,2,2,4};
2121  const int VFTetVert[][4] = {{0,0,-1,-1},{1,1,-1,-1},{2,2,-1,-1},{0,2,-1,-1},{3,3,3,3}};
2122  const int VFTetFace[][4] = {{1,3,-1,-1},{1,2,-1,-1},{2,3,-1,-1},{1,3,-1,-1},{1,2,1,2}};
2123  const int VFPyrFace[][4] = {{1,4,-1,-1},{1,2,-1,-1},{2,3,-1,-1},{3,4,-1,-1},{1,2,3,4}};
2124 
2125  // next handle all triangular faces from tetrahedron
2126  nfacemodes = (nummodesmax-3)*(nummodesmax-2)/2;
2127  for(int v = 0; v < 5; ++v)
2128  {
2129  for(int f = 0; f < nadjface[v]; ++f)
2130  {
2131  for(int i = 0; i < nfacemodes; ++i)
2132  {
2133  val = (*maxRmat[eTetrahedron])(vertMapMaxR[eTetrahedron][VFTetVert[v][f]],
2134  faceMapMaxR[eTetrahedron][VFTetFace[v][f]][i]);
2135  newmat->SetValue(vertMapMaxR[ePyramid][v],
2136  faceMapMaxR[ePyramid][VFPyrFace[v][f]][i],val);
2137  }
2138 
2139  }
2140  }
2141 
2142  // Edge to face coupling
2143  // all base edges are coupled to face 0
2144  nfacemodes = (nummodesmax-2)*(nummodesmax-2);
2145  for(int e = 0; e < 4; ++e)
2146  {
2147  for(int i = 0; i < nummodesmax-2; ++i)
2148  {
2149  for(int j = 0; j < nfacemodes; ++j)
2150  {
2151  int edgemapid = edgeMapMaxR[eHexahedron][e][i];
2152  int facemapid = faceMapMaxR[eHexahedron][0][j];
2153 
2154  val = (*maxRmat[eHexahedron])(edgemapid,facemapid);
2155  newmat->SetValue(edgeMapMaxR[ePyramid][e][i],
2156  faceMapMaxR[ePyramid][0][j],val);
2157  }
2158 
2159  }
2160  }
2161 
2162  const int nadjface1[] = {1,1,1,1,2,2,2,2};
2163  const int EFTetEdge[][2] = {{0,-1},{1,-1},{0,-1},{2,-1},{3,3},{4,4},{5,5},{3,5}};
2164  const int EFTetFace[][2] = {{1,-1},{2,-1},{1,-1},{3,-1},{1,3},{1,2},{2,3},{1,3}};
2165  const int EFPyrFace[][2] = {{1,-1},{2,-1},{3,-1},{4,-1},{1,4},{1,2},{2,3},{3,4}};
2166 
2167  // next handle all triangular faces from tetrahedron
2168  nfacemodes = (nummodesmax-3)*(nummodesmax-2)/2;
2169  for(int e = 0; e < 8; ++e)
2170  {
2171  for(int f = 0; f < nadjface1[e]; ++f)
2172  {
2173  for(int i = 0; i < nummodesmax-2; ++i)
2174  {
2175  for(int j = 0; j < nfacemodes; ++j)
2176  {
2177  int edgemapid = edgeMapMaxR[eTetrahedron][EFTetEdge[e][f]][i];
2178  int facemapid = faceMapMaxR[eTetrahedron][EFTetFace[e][f]][j];
2179 
2180  val = (*maxRmat[eTetrahedron])(edgemapid,facemapid);
2181  newmat->SetValue(edgeMapMaxR[ePyramid][e][i],
2182  faceMapMaxR[ePyramid][EFPyrFace[e][f]][j],val);
2183  }
2184  }
2185  }
2186  }
2187 
2188  DNekScalMatSharedPtr PyrR;
2190  maxRmat[ePyramid] =PyrR;
2191  }
2192 
2193 
2196  std::map<ShapeType, DNekScalMatSharedPtr> &maxRmat,
2197  std::map<ShapeType, Array<OneD, unsigned int> > &vertMapMaxR,
2198  std::map<ShapeType, Array<OneD, Array<OneD, unsigned int> > > &edgeMapMaxR,
2199  std::map<ShapeType, Array<OneD, Array<OneD, unsigned int> > > &faceMapMaxR)
2200  {
2201  boost::ignore_unused(faceMapMaxR);
2202 
2203  int nRows = TetExp->NumBndryCoeffs();
2204  NekDouble val;
2205  NekDouble zero = 0.0;
2207  AllocateSharedPtr(nRows,nRows,zero,eFULL);
2208 
2209  // copy existing system
2210  for(int i = 0; i < nRows; ++i)
2211  {
2212  for(int j = 0; j < nRows; ++j)
2213  {
2214  val = (*maxRmat[eTetrahedron])(i,j);
2215  newmat->SetValue(i,j,val);
2216  }
2217  }
2218 
2219  // The following lists specify the number of adjacent
2220  // edges to each vertex (nadj) then the Hex vert to
2221  // use for each pyramid ver in the vert-edge map (VEVert)
2222  // followed by the hex edge to use for each Tet edge
2223  // in the vert-edge map (VEEdge)
2224  const int VEHexVert[][4] = {{0,0,0},{1,1,1},{2,2,2},{4,5,6}};
2225  const int VEHexEdge[][4] = {{0,3,4},{0,1,5},{1,2,6},{4,5,6}};
2226  const int VETetEdge[][4] = {{0,2,3},{0,1,4},{1,2,5},{3,4,5}};
2227 
2228  // fill vertex to edge coupling
2229  for(int v = 0; v < 4; ++v)
2230  {
2231  for(int e = 0; e < 3; ++e)
2232  {
2233  for(int i = 0; i < nummodesmax-2; ++i)
2234  {
2235  // Note this is using wrong shape but gives
2236  // answer that seems to have correct error!
2237  val = (*maxRmat[eHexahedron])(
2238  vertMapMaxR[eHexahedron][VEHexVert[v][e]],
2239  edgeMapMaxR[eHexahedron][VEHexEdge[v][e]][i]);
2240  newmat->SetValue(vertMapMaxR[eTetrahedron][v],
2241  edgeMapMaxR[eTetrahedron][VETetEdge[v][e]][i],
2242  val);
2243  }
2244  }
2245  }
2246 
2247  DNekScalMatSharedPtr TetR =
2249 
2250  maxRmat[eTetrahedron] = TetR;
2251  }
2252 
2255  std::map<ShapeType, DNekScalMatSharedPtr> &maxRmat,
2256  std::map<ShapeType, Array<OneD, unsigned int> > &vertMapMaxR,
2257  std::map<ShapeType, Array<OneD, Array<OneD, unsigned int> > > &edgeMapMaxR,
2258  std::map<ShapeType, Array<OneD, Array<OneD, unsigned int> > > &faceMapMaxR,
2259  bool UseTetOnly)
2260  {
2261  int nRows = PrismExp->NumBndryCoeffs();
2262  NekDouble val;
2263  NekDouble zero = 0.0;
2265  AllocateSharedPtr(nRows,nRows,zero,eFULL);
2266 
2267 
2268  int nfacemodes;
2269 
2270  if(UseTetOnly)
2271  {
2272  // copy existing system
2273  for(int i = 0; i < nRows; ++i)
2274  {
2275  for(int j = 0; j < nRows; ++j)
2276  {
2277  val = (*maxRmat[ePrism])(i,j);
2278  newmat->SetValue(i,j,val);
2279  }
2280  }
2281 
2282  // Reset vertex to edge mapping from tet.
2283  const int VETetVert[][2] = {{0,0},{1,1},{1,1},{0,0},{3,3},{3,3}};
2284  const int VETetEdge[][2] = {{0,3},{0,4},{0,4},{0,3},{3,4},{4,3}};
2285  const int VEPrismEdge[][2] = {{0,4},{0,5},{2,6},{2,7},{4,5},{6,7}};
2286 
2287  // fill vertex to edge coupling
2288  for(int v = 0; v < 6; ++v)
2289  {
2290  for(int e = 0; e < 2; ++e)
2291  {
2292  for(int i = 0; i < nummodesmax-2; ++i)
2293  {
2294  // Note this is using wrong shape but gives
2295  // answer that seems to have correct error!
2296  val = (*maxRmat[eTetrahedron])(
2297  vertMapMaxR[eTetrahedron][VETetVert[v][e]],
2298  edgeMapMaxR[eTetrahedron][VETetEdge[v][e]][i]);
2299  newmat->
2300  SetValue(vertMapMaxR[ePrism][v],
2301  edgeMapMaxR[ePrism][VEPrismEdge[v][e]][i],
2302  val);
2303  }
2304  }
2305  }
2306  }
2307  else
2308  {
2309 
2310  // set diagonal to 1
2311  for(int i = 0; i < nRows; ++i)
2312  {
2313  newmat->SetValue(i,i,1.0);
2314  }
2315 
2316 
2317  // Set vertex to edge mapping from Hex.
2318 
2319  // The following lists specify the number of adjacent
2320  // edges to each vertex (nadj) then the Hex vert to
2321  // use for each prism ver in the vert-edge map (VEVert)
2322  // followed by the hex edge to use for each prism edge
2323  // in the vert-edge map (VEEdge)
2324  const int VEHexVert[][3] = {{0,0,0},{1,1,1},{2,2,2},{3,3,3},
2325  {4,5,5},{6,7,7}};
2326  const int VEHexEdge[][3] = {{0,3,4},{0,1,5},{1,2,6},{2,3,7},
2327  {4,5,9},{6,7,11}};
2328  const int VEPrismEdge[][3] = {{0,3,4},{0,1,5},{1,2,6},{2,3,7},
2329  {4,5,8},{6,7,8}};
2330 
2331  // fill vertex to edge coupling
2332  for(int v = 0; v < 6; ++v)
2333  {
2334  for(int e = 0; e < 3; ++e)
2335  {
2336  for(int i = 0; i < nummodesmax-2; ++i)
2337  {
2338  // Note this is using wrong shape but gives
2339  // answer that seems to have correct error!
2340  val = (*maxRmat[eHexahedron])(
2341  vertMapMaxR[eHexahedron][VEHexVert[v][e]],
2342  edgeMapMaxR[eHexahedron][VEHexEdge[v][e]][i]);
2343  newmat->SetValue(vertMapMaxR[ePrism][v],
2344  edgeMapMaxR[ePrism][VEPrismEdge[v][e]][i],
2345  val);
2346  }
2347  }
2348  }
2349 
2350 
2351  // Setup vertex to face mapping from Hex
2352  const int VFHexVert[][2] = {{0,0},{1,1},{4,5},{2,2},{3,3},{6,7}};
2353  const int VFHexFace[][2] = {{0,4},{0,2},{4,2},{0,2},{0,4},{2,4}};
2354 
2355  const int VQFPrismVert[][2] = {{0,0},{1,1},{4,4},{2,2},{3,3},{5,5}};
2356  const int VQFPrismFace[][2] = {{0,4},{0,2},{4,2},{0,2},{0,4},{2,4}};
2357 
2358  nfacemodes = (nummodesmax-2)*(nummodesmax-2);
2359  // Replace two Quad faces on every vertex
2360  for(int v = 0; v < 6; ++v)
2361  {
2362  for(int f = 0; f < 2; ++f)
2363  {
2364  for(int i = 0; i < nfacemodes; ++i)
2365  {
2366  val = (*maxRmat[eHexahedron])(
2367  vertMapMaxR[eHexahedron][VFHexVert[v][f]],
2368  faceMapMaxR[eHexahedron][VFHexFace[v][f]][i]);
2369  newmat->SetValue(vertMapMaxR[ePrism][VQFPrismVert[v][f]],
2370  faceMapMaxR[ePrism][VQFPrismFace[v][f]][i],val);
2371  }
2372  }
2373  }
2374 
2375  // Mapping of Hex Edge-Face mappings to Prism Edge-Face Mappings
2376  const int nadjface[] = {1,2,1,2,1,1,1,1,2};
2377  const int EFHexEdge[][2] = {{0,-1},{1,1},{2,-1},{3,3},{4,-1},{5,-1},{6,-1},{7,-1},{9,11}};
2378  const int EFHexFace[][2] = {{0,-1},{0,2},{0,-1},{0,4},{4,-1},{2,-1},{2,-1},{4,-1},{2,4}};
2379  const int EQFPrismEdge[][2] = {{0,-1},{1,1},{2,-1},{3,3},{4,-1},{5,-1},{6,-1},{7,-1},{8,8}};
2380  const int EQFPrismFace[][2] = {{0,-1},{0,2},{0,-1},{0,4},{4,-1},{2,-1},{2,-1},{4,-1},{2,4}};
2381 
2382  // all base edges are coupled to face 0
2383  nfacemodes = (nummodesmax-2)*(nummodesmax-2);
2384  for(int e = 0; e < 9; ++e)
2385  {
2386  for(int f = 0; f < nadjface[e]; ++f)
2387  {
2388  for(int i = 0; i < nummodesmax-2; ++i)
2389  {
2390  for(int j = 0; j < nfacemodes; ++j)
2391  {
2392  int edgemapid = edgeMapMaxR[eHexahedron][EFHexEdge[e][f]][i];
2393  int facemapid = faceMapMaxR[eHexahedron][EFHexFace[e][f]][j];
2394 
2395  val = (*maxRmat[eHexahedron])(edgemapid,facemapid);
2396 
2397  int edgemapid1 = edgeMapMaxR[ePrism][EQFPrismEdge[e][f]][i];
2398  int facemapid1 = faceMapMaxR[ePrism][EQFPrismFace[e][f]][j];
2399  newmat->SetValue(edgemapid1, facemapid1, val);
2400  }
2401  }
2402  }
2403  }
2404  }
2405 
2406  const int VFTetVert[] = {0,1,3,1,0,3};
2407  const int VFTetFace[] = {1,1,1,1,1,1};
2408  const int VTFPrismVert[] = {0,1,4,2,3,5};
2409  const int VTFPrismFace[] = {1,1,1,3,3,3};
2410 
2411  // Handle all triangular faces from tetrahedron
2412  nfacemodes = (nummodesmax-3)*(nummodesmax-2)/2;
2413  for(int v = 0; v < 6; ++v)
2414  {
2415  for(int i = 0; i < nfacemodes; ++i)
2416  {
2417  val = (*maxRmat[eTetrahedron])
2418  (vertMapMaxR[eTetrahedron][VFTetVert[v]],
2419  faceMapMaxR[eTetrahedron][VFTetFace[v]][i]);
2420 
2421  newmat->SetValue(vertMapMaxR[ePrism][VTFPrismVert[v]],
2422  faceMapMaxR[ePrism][VTFPrismFace[v]][i],val);
2423  }
2424  }
2425 
2426  // Mapping of Tet Edge-Face mappings to Prism Edge-Face Mappings
2427  const int EFTetEdge[] = {0,3,4,0,4,3};
2428  const int EFTetFace[] = {1,1,1,1,1,1};
2429  const int ETFPrismEdge[] = {0,4,5,2,6,7};
2430  const int ETFPrismFace[] = {1,1,1,3,3,3};
2431 
2432  // handle all edge to triangular faces from tetrahedron
2433  // (only 6 this time)
2434  nfacemodes = (nummodesmax-3)*(nummodesmax-2)/2;
2435  for(int e = 0; e < 6; ++e)
2436  {
2437  for(int i = 0; i < nummodesmax-2; ++i)
2438  {
2439  for(int j = 0; j < nfacemodes; ++j)
2440  {
2441  int edgemapid = edgeMapMaxR[eTetrahedron][EFTetEdge[e]][i];
2442  int facemapid = faceMapMaxR[eTetrahedron][EFTetFace[e]][j];
2443  val = (*maxRmat[eTetrahedron])(edgemapid,facemapid);
2444 
2445  newmat->SetValue(edgeMapMaxR[ePrism][ETFPrismEdge[e]][i],
2446  faceMapMaxR[ePrism][ETFPrismFace[e]][j],val);
2447  }
2448  }
2449  }
2450 
2451 
2452  DNekScalMatSharedPtr PrismR
2454  maxRmat[ePrism] = PrismR;
2455  }
2456 
2459  DNekScalMatSharedPtr &maxRmat,
2463  {
2464  NekDouble val;
2465  NekDouble zero = 0.0;
2466 
2467  int nRows = locExp->NumBndryCoeffs();
2469  AllocateSharedPtr(nRows,nRows,zero,eFULL);
2470 
2471  Array<OneD, unsigned int> vlocmap;
2474 
2475  locExp->GetInverseBoundaryMaps(vlocmap,elocmap,flocmap);
2476 
2477  // fill diagonal
2478  for(int i = 0; i < nRows; ++i)
2479  {
2480  val = 1.0;
2481  newmat->SetValue(i,i,val);
2482  }
2483 
2484  int nverts = locExp->GetNverts();
2485  int nedges = locExp->GetNedges();
2486  int nfaces = locExp->GetNfaces();
2487 
2488  // fill vertex to edge coupling
2489  for(int e = 0; e < nedges; ++e)
2490  {
2491  int nEdgeInteriorCoeffs = locExp->GetEdgeNcoeffs(e) -2;
2492 
2493  for(int v = 0; v < nverts; ++v)
2494  {
2495  for(int i = 0; i < nEdgeInteriorCoeffs; ++i)
2496  {
2497  val = (*maxRmat)(vmap[v],emap[e][i]);
2498  newmat->SetValue(vlocmap[v],elocmap[e][i],val);
2499  }
2500  }
2501  }
2502 
2503  for(int f = 0; f < nfaces; ++f)
2504  {
2505  // Get details to extrac this face from max reference matrix
2507  int m0,m1; //Local face expansion orders.
2508 
2509  int nFaceInteriorCoeffs = locExp->GetFaceIntNcoeffs(f);
2510 
2511  locExp->GetFaceNumModes(f,FwdOrient,m0,m1);
2512 
2513  Array<OneD, unsigned int> fmapRmat = maxExp->
2514  GetFaceInverseBoundaryMap(f,FwdOrient, m0,m1);
2515 
2516  // fill in vertex to face coupling
2517  for(int v = 0; v < nverts; ++v)
2518  {
2519  for(int i = 0; i < nFaceInteriorCoeffs; ++i)
2520  {
2521  val = (*maxRmat)(vmap[v],fmapRmat[i]);
2522  newmat->SetValue(vlocmap[v],flocmap[f][i],val);
2523  }
2524  }
2525 
2526  // fill in edges to face coupling
2527  for(int e = 0; e < nedges; ++e)
2528  {
2529  int nEdgeInteriorCoeffs = locExp->GetEdgeNcoeffs(e) -2;
2530 
2531  for(int j = 0; j < nEdgeInteriorCoeffs; ++j)
2532  {
2533 
2534  for(int i = 0; i < nFaceInteriorCoeffs; ++i)
2535  {
2536  val = (*maxRmat)(emap[e][j],fmapRmat[i]);
2537  newmat->SetValue(elocmap[e][j],flocmap[f][i],val);
2538  }
2539  }
2540  }
2541  }
2542 
2543  return newmat;
2544  }
2545  }
2546 }
2547 
LibUtilities::CommSharedPtr m_comm
virtual void v_DoPreconditioner(const Array< OneD, NekDouble > &pInput, Array< OneD, NekDouble > &pOutput)
std::vector< std::pair< int, int > > m_sameBlock
#define ASSERTL0(condition, msg)
Definition: ErrorUtil.hpp:216
virtual DNekScalMatSharedPtr v_TransformedSchurCompl(int n, int offset, const std::shared_ptr< DNekScalMat > &loc_mat)
Set up the transformed block matrix system.
Principle Modified Functions .
Definition: BasisType.h:50
std::shared_ptr< Geometry2D > Geometry2DSharedPtr
Definition: Geometry.h:65
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
virtual void v_DoMultiplybyInverseTransposedTransformationMatrix(const Array< OneD, NekDouble > &pInput, Array< OneD, NekDouble > &pOutput)
Multiply by the block tranposed inverse transformation matrix.
void Gathr(int n, const T *x, const int *y, T *z)
Gather vector z[i] = x[y[i]].
Definition: Vmath.cpp:647
#define sign(a, b)
return the sign(b)*a
Definition: Polylib.cpp:16
Principle Modified Functions .
Definition: BasisType.h:52
std::shared_ptr< DNekScalMat > DNekScalMatSharedPtr
std::shared_ptr< TetGeom > TetGeomSharedPtr
Definition: TetGeom.h:88
std::shared_ptr< DNekScalBlkMat > DNekScalBlkMatSharedPtr
Definition: NekTypeDefs.hpp:73
const std::weak_ptr< GlobalLinSys > m_linsys
DNekMatSharedPtr ExtractLocMat(StdRegions::StdExpansionSharedPtr &locExp, DNekScalMatSharedPtr &maxRmat, LocalRegions::ExpansionSharedPtr &expMax, Array< OneD, unsigned int > &vertMapMaxR, Array< OneD, Array< OneD, unsigned int > > &edgeMapMaxR)
std::shared_ptr< QuadGeom > QuadGeomSharedPtr
Definition: HexGeom.h:46
std::shared_ptr< HexExp > HexExpSharedPtr
Definition: HexExp.h:56
void ReSetTetMaxRMat(int nummodesmax, LocalRegions::TetExpSharedPtr &TetExp, ShapeToDNekMap &maxRmat, ShapeToIntArrayMap &vertMapMaxR, ShapeToIntArrayArrayMap &edgeMapMaxR, ShapeToIntArrayArrayMap &faceMapMaxR)
const StdRegions::ConstFactorMap & GetConstFactors() const
Returns all the constants.
Principle Modified Functions .
Definition: BasisType.h:48
PreconFactory & GetPreconFactory()
STL namespace.
std::shared_ptr< TetExp > TetExpSharedPtr
Definition: TetExp.h:226
void Sdiv(int n, const T alpha, const T *x, const int incx, T *y, const int incy)
Scalar multiply y = alpha/y.
Definition: Vmath.cpp:274
std::shared_ptr< DNekMat > DNekMatSharedPtr
Definition: NekTypeDefs.hpp:69
static void Dgemm(const char &transa, const char &transb, const int &m, const int &n, const int &k, const double &alpha, const double *a, const int &lda, const double *b, const int &ldb, const double &beta, double *c, const int &ldc)
BLAS level 3: Matrix-matrix multiply C = A x B where A[m x n], B[n x k], C[m x k].
Definition: Blas.hpp:213
Gauss Radau pinned at x=-1, .
Definition: PointsType.h:58
SpatialDomains::HexGeomSharedPtr CreateRefHexGeom(void)
Sets up the reference hexahedral element needed to construct a low energy basis.
std::shared_ptr< AssemblyMap > AssemblyMapSharedPtr
Definition: AssemblyMap.h:52
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< StdExpansion > StdExpansionSharedPtr
std::shared_ptr< TriGeom > TriGeomSharedPtr
Definition: TriGeom.h:58
virtual void v_BuildPreconditioner()
Construct the low energy preconditioner from .
StdRegions::MatrixType GetMatrixType() const
Return the matrix type.
Principle Modified Functions .
Definition: BasisType.h:49
StdRegions::Orientation DeterminePeriodicFaceOrient(StdRegions::Orientation faceOrient, StdRegions::Orientation perFaceOrient)
Determine relative orientation between two faces.
void SetUpPyrMaxRMat(int nummodesmax, LocalRegions::PyrExpSharedPtr &PyrExp, ShapeToDNekMap &maxRmat, ShapeToIntArrayMap &vertMapMaxR, ShapeToIntArrayArrayMap &edgeMapMaxR, ShapeToIntArrayArrayMap &faceMapMaxR)
static std::shared_ptr< DataType > AllocateSharedPtr(const Args &...args)
Allocate a shared pointer from the memory pool.
virtual void v_DoTransformToLowEnergy(Array< OneD, NekDouble > &pInOut, int offset)
Transform the solution vector vector to low energy.
SpatialDomains::PyrGeomSharedPtr CreateRefPyrGeom(void)
Sets up the reference prismatic element needed to construct a low energy basis mapping arrays...
NekMatrix< InnerMatrixType, BlockMatrixTag > Transpose(NekMatrix< InnerMatrixType, BlockMatrixTag > &rhs)
Defines a specification for a set of points.
Definition: Points.h:59
void SetUpReferenceElements(ShapeToDNekMap &maxRmat, ShapeToExpMap &maxElmt, ShapeToIntArrayMap &vertMapMaxR, ShapeToIntArrayArrayMap &edgeMapMaxR)
Loop expansion and determine different variants of the transformation matrix.
double NekDouble
void Assmb(int n, const T *x, const int *y, T *z)
Assemble z[y[i]] += x[i]; z should be zero&#39;d first.
Definition: Vmath.cpp:712
std::shared_ptr< PointGeom > PointGeomSharedPtr
Definition: Geometry.h:59
std::map< int, std::vector< PeriodicEntity > > PeriodicMap
virtual void v_DoMultiplybyInverseTransformationMatrix(const Array< OneD, NekDouble > &pInput, Array< OneD, NekDouble > &pOutput)
Multiply by the block inverse transformation matrix.
Describe a linear system.
virtual void v_DoTransformFromLowEnergy(Array< OneD, NekDouble > &pInOut)
transform the solution vector from low energy back to the original basis.
std::shared_ptr< PyrExp > PyrExpSharedPtr
Definition: PyrExp.h:191
bg::model::point< double, 3, bg::cs::cartesian > point
Definition: BLMesh.cpp:53
SpatialDomains::TetGeomSharedPtr CreateRefTetGeom(void)
Sets up the reference tretrahedral element needed to construct a low energy basis.
std::shared_ptr< Expansion > ExpansionSharedPtr
Definition: Expansion.h:65
std::shared_ptr< PyrGeom > PyrGeomSharedPtr
Definition: PyrGeom.h:80
std::shared_ptr< PrismExp > PrismExpSharedPtr
Definition: PrismExp.h:220
std::shared_ptr< Expansion2D > Expansion2DSharedPtr
Definition: Expansion1D.h:47
std::shared_ptr< T > as()
std::shared_ptr< HexGeom > HexGeomSharedPtr
Definition: HexGeom.h:90
Gauss Radau pinned at x=-1, .
Definition: PointsType.h:59
SpatialDomains::PrismGeomSharedPtr CreateRefPrismGeom(void)
Sets up the reference prismatic element needed to construct a low energy basis.
tKey RegisterCreatorFunction(tKey idKey, CreatorFunction classCreator, std::string pDesc="")
Register a class with the factory.
Definition: NekFactory.hpp:199
std::shared_ptr< PrismGeom > PrismGeomSharedPtr
Definition: PrismGeom.h:88
#define ASSERTL1(condition, msg)
Assert Level 1 – Debugging which is used whether in FULLDEBUG or DEBUG compilation mode...
Definition: ErrorUtil.hpp:250
std::shared_ptr< SegGeom > SegGeomSharedPtr
Definition: Geometry2D.h:62
void Vcopy(int n, const T *x, const int incx, T *y, const int incy)
Definition: Vmath.cpp:1064
std::weak_ptr< AssemblyMap > m_locToGloMap
void ReSetPrismMaxRMat(int nummodesmax, LocalRegions::PrismExpSharedPtr &PirsmExp, ShapeToDNekMap &maxRmat, ShapeToIntArrayMap &vertMapMaxR, ShapeToIntArrayArrayMap &edgeMapMaxR, ShapeToIntArrayArrayMap &faceMapMaxR, bool UseTetOnly)
Describes the specification for a Basis.
Definition: Basis.h:49
1D Gauss-Lobatto-Legendre quadrature points
Definition: PointsType.h:51
void Vmul(int n, const T *x, const int incx, const T *y, const int incy, T *z, const int incz)
Multiply vector z = x*y.
Definition: Vmath.cpp:186