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 <cmath>
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 variable p mask
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.size(); ++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.size(); 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->GetNtraces(); k++)
213  {
214  meshEdgeId = bndCondFaceExp->GetGeom()->GetEid(k);
215  if(edgeDirMap.count(meshEdgeId) == 0)
216  {
217  edgeDirMap.insert(meshEdgeId);
218  }
219  }
220  meshFaceId = bndCondFaceExp->GetGeom()->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)->as<LocalRegions::Expansion3D>();
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->GetNtraces();
260  for(j = 0; j < nFaces; ++j)
261  {
262  int nFaceInteriorCoeffs = locExpansion->GetTraceIntNcoeffs(j);
263  meshFaceId = locExpansion->GetGeom3D()->GetFid(j);
264 
265  if(FaceSize.count(meshFaceId) == 0)
266  {
267  FaceSize[meshFaceId] = nFaceInteriorCoeffs;
268 
269  int m0,m1;
270  locExpansion->GetTraceNumModes(j,m0,m1,locExpansion->
271  GetTraceOrient(j));
272  FaceModes[meshFaceId] = pair<int,int>(m0,m1);
273  }
274  else
275  {
276  if(nFaceInteriorCoeffs < FaceSize[meshFaceId])
277  {
278  FaceSize[meshFaceId] = nFaceInteriorCoeffs;
279  int m0,m1;
280  locExpansion->GetTraceNumModes(j,m0,m1,locExpansion->
281  GetTraceOrient(j));
282  FaceModes[meshFaceId] = pair<int,int>(m0,m1);
283  }
284  }
285  }
286  }
287 
288  bool verbose =
289  expList->GetSession()->DefinesCmdLineArgument("verbose");
290 
291  // For parallel runs need to check have minimum of edges and faces over
292  // partition boundaries
293  if(m_comm->GetSize() > 1)
294  {
295  int EdgeSizeLen = EdgeSize.size();
296  int FaceSizeLen = FaceSize.size();
297  Array<OneD, long> FacetMap(EdgeSizeLen+FaceSizeLen,-1);
298  Array<OneD, NekDouble> FacetLen(EdgeSizeLen+FaceSizeLen,-1);
299 
300  map<int,int>::iterator it;
301 
302  cnt = 0;
303  int maxid = 0;
304  for(it = EdgeSize.begin(); it!=EdgeSize.end(); ++it,++cnt)
305  {
306  FacetMap[cnt] = it->first;
307  maxid = max(it->first,maxid);
308  FacetLen[cnt] = it->second;
309  }
310  maxid++;
311 
312  m_comm->AllReduce(maxid,ReduceMax);
313 
314  for(it = FaceSize.begin(); it!=FaceSize.end(); ++it,++cnt)
315  {
316  FacetMap[cnt] = it->first + maxid;
317  FacetLen[cnt] = it->second;
318  }
319 
320  //Exchange vertex data over different processes
321  Gs::gs_data *tmp = Gs::Init(FacetMap, m_comm, verbose);
322  Gs::Gather(FacetLen, Gs::gs_min, tmp);
323 
324  cnt = 0;
325  for(it = EdgeSize.begin(); it!=EdgeSize.end(); ++it,++cnt)
326  {
327  it->second = (int) FacetLen[cnt];
328  }
329 
330  for(it = FaceSize.begin(); it!=FaceSize.end(); ++it,++cnt)
331  {
332  it->second = (int)FacetLen[cnt];
333  }
334  }
335 
336  // Loop over all the elements in the domain and compute total edge
337  // DOF and set up unique ordering.
338  map<int,int> nblks;
339  int matrixlocation = 0;
340 
341  // First do periodic edges
342  for (auto &pIt : periodicEdges)
343  {
344  meshEdgeId = pIt.first;
345 
346  if(edgeDirMap.count(meshEdgeId)==0)
347  {
348  dof = EdgeSize[meshEdgeId];
349  if(uniqueEdgeMap.count(meshEdgeId)==0 && dof > 0)
350  {
351  bool SetUpNewEdge = true;
352 
353 
354  for (i = 0; i < pIt.second.size(); ++i)
355  {
356  if (!pIt.second[i].isLocal)
357  {
358  continue;
359  }
360 
361  int meshEdgeId2 = pIt.second[i].id;
362  if(edgeDirMap.count(meshEdgeId2)==0)
363  {
364  if(uniqueEdgeMap.count(meshEdgeId2)!=0)
365  {
366  // set unique map to same location
367  uniqueEdgeMap[meshEdgeId] =
368  uniqueEdgeMap[meshEdgeId2];
369  SetUpNewEdge = false;
370  }
371  }
372  else
373  {
374  edgeDirMap.insert(meshEdgeId);
375  SetUpNewEdge = false;
376  }
377  }
378 
379  if(SetUpNewEdge)
380  {
381  uniqueEdgeMap[meshEdgeId]=matrixlocation;
382  globaloffset[matrixlocation]+=ntotalentries;
383  modeoffset[matrixlocation]=dof*dof;
384  ntotalentries+=dof*dof;
385  nblks [matrixlocation++] = dof;
386  }
387  }
388  }
389  }
390 
391  for(cnt=n=0; n < n_exp; ++n)
392  {
393  locExpansion = expList->GetExp(n)->as<LocalRegions::Expansion3D>();
394 
395  for (j = 0; j < locExpansion->GetNedges(); ++j)
396  {
397  meshEdgeId = locExpansion->GetGeom()->GetEid(j);
398  dof = EdgeSize[meshEdgeId];
399  maxEdgeDof = (dof > maxEdgeDof ? dof : maxEdgeDof);
400 
401  if(edgeDirMap.count(meshEdgeId)==0)
402  {
403  if(uniqueEdgeMap.count(meshEdgeId)==0 && dof > 0)
404 
405  {
406  uniqueEdgeMap[meshEdgeId]=matrixlocation;
407 
408  globaloffset[matrixlocation]+=ntotalentries;
409  modeoffset[matrixlocation]=dof*dof;
410  ntotalentries+=dof*dof;
411  nblks[matrixlocation++] = dof;
412  }
413  nlocalNonDirEdges+=dof*dof;
414  }
415  }
416  }
417 
418  // Loop over all the elements in the domain and compute max face
419  // DOF. Reduce across all processes to get universal maximum.
420  // - Periodic faces
421  for (auto &pIt : periodicFaces)
422  {
423  meshFaceId = pIt.first;
424 
425  if(faceDirMap.count(meshFaceId)==0)
426  {
427  dof = FaceSize[meshFaceId];
428 
429  if(uniqueFaceMap.count(meshFaceId) == 0 && dof > 0)
430  {
431  bool SetUpNewFace = true;
432 
433  if(pIt.second[0].isLocal)
434  {
435  int meshFaceId2 = pIt.second[0].id;
436 
437  if(faceDirMap.count(meshFaceId2)==0)
438  {
439  if(uniqueFaceMap.count(meshFaceId2)!=0)
440  {
441  // set unique map to same location
442  uniqueFaceMap[meshFaceId] =
443  uniqueFaceMap[meshFaceId2];
444  SetUpNewFace = false;
445  }
446  }
447  else // set face to be a Dirichlet face
448  {
449  faceDirMap.insert(meshFaceId);
450  SetUpNewFace = false;
451  }
452  }
453 
454  if(SetUpNewFace)
455  {
456  uniqueFaceMap[meshFaceId]=matrixlocation;
457 
458  modeoffset[matrixlocation]=dof*dof;
459  globaloffset[matrixlocation]+=ntotalentries;
460  ntotalentries+=dof*dof;
461  nblks[matrixlocation++] = dof;
462  }
463  }
464  }
465  }
466 
467  for(cnt=n=0; n < n_exp; ++n)
468  {
469  locExpansion = expList->GetExp(n)->as<LocalRegions::Expansion3D>();
470 
471  for (j = 0; j < locExpansion->GetNtraces(); ++j)
472  {
473  meshFaceId = locExpansion->GetGeom()->GetFid(j);
474 
475  dof = FaceSize[meshFaceId];
476  maxFaceDof = (dof > maxFaceDof ? dof : maxFaceDof);
477 
478  if(faceDirMap.count(meshFaceId)==0)
479  {
480  if(uniqueFaceMap.count(meshFaceId)==0 && dof > 0)
481  {
482  uniqueFaceMap[meshFaceId]=matrixlocation;
483  modeoffset[matrixlocation]=dof*dof;
484  globaloffset[matrixlocation]+=ntotalentries;
485  ntotalentries+=dof*dof;
486  nblks[matrixlocation++] = dof;
487  }
488  nlocalNonDirFaces+=dof*dof;
489  }
490  }
491  }
492 
493  m_comm->AllReduce(maxEdgeDof, ReduceMax);
494  m_comm->AllReduce(maxFaceDof, ReduceMax);
495 
496  //Allocate arrays for block to universal map (number of expansions * p^2)
497  Array<OneD, long> BlockToUniversalMap(ntotalentries,-1);
498  Array<OneD, int> localToGlobalMatrixMap(nlocalNonDirEdges +
499  nlocalNonDirFaces,-1);
500 
501  //Allocate arrays to store matrices (number of expansions * p^2)
502  Array<OneD, NekDouble> BlockArray(nlocalNonDirEdges +
503  nlocalNonDirFaces,0.0);
504 
505  int matrixoffset=0;
506  int vGlobal;
507  int uniEdgeOffset = 0;
508 
509  // Need to obtain a fixed offset for the universal number
510  // of the faces which come after the edge numbering
511  for(n=0; n < n_exp; ++n)
512  {
513  for(j = 0; j < locExpansion->GetNedges(); ++j)
514  {
515  meshEdgeId = locExpansion->as<LocalRegions::Expansion3D>()
516  ->GetGeom3D()->GetEid(j);
517 
518  uniEdgeOffset = max(meshEdgeId, uniEdgeOffset);
519  }
520  }
521  uniEdgeOffset++;
522 
523  m_comm->AllReduce(uniEdgeOffset,ReduceMax);
524  uniEdgeOffset = uniEdgeOffset*maxEdgeDof*maxEdgeDof;
525 
526  for(n=0; n < n_exp; ++n)
527  {
528  locExpansion = expList->GetExp(n)->as<LocalRegions::Expansion3D>();
529 
530  //loop over the edges of the expansion
531  for(j = 0; j < locExpansion->GetNedges(); ++j)
532  {
533  //get mesh edge id
534  meshEdgeId = locExpansion->GetGeom()->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->GetNtraces(); ++j)
569  {
570  //get mesh face id
571  meshFaceId = locExpansion->GetGeom()->GetFid(j);
572 
573  nfacemodes = FaceSize[meshFaceId];
574 
575  //Check if face has dirichlet values
576  if(faceDirMap.count(meshFaceId)==0 && nfacemodes > 0)
577  {
578  // Determine the Global edge offset
579  int faceOffset = globaloffset[uniqueFaceMap[meshFaceId]];
580  // Determine a universal map offset
581  int uniOffset = meshFaceId;
582  // use minimum face edge when periodic
583  auto pIt = periodicFaces.find(meshFaceId);
584  if (pIt != periodicFaces.end())
585  {
586  uniOffset = min(uniOffset, pIt->second[0].id);
587  }
588  uniOffset = uniOffset * maxFaceDof * maxFaceDof;
589 
590  for(k=0; k<nfacemodes*nfacemodes; ++k)
591  {
592  vGlobal=faceOffset+k;
593 
594  localToGlobalMatrixMap[matrixoffset+k]
595  = vGlobal;
596 
597  BlockToUniversalMap[vGlobal] = uniOffset +
598  uniEdgeOffset + k + 1;
599  }
600  matrixoffset+=nfacemodes*nfacemodes;
601  }
602  }
603  }
604 
605  matrixoffset=0;
606 
607  map<int,int>::iterator it;
608  Array<OneD, unsigned int> n_blks(nblks.size()+1);
609  n_blks[0] = nNonDirVerts;
610  for(i =1, it = nblks.begin(); it != nblks.end(); ++it)
611  {
612  n_blks[i++] = it->second;
613  }
614 
616  ::AllocateSharedPtr(n_blks, n_blks, blkmatStorage);
617 
618  //Here we loop over the expansion and build the block low energy
619  //preconditioner as well as the block versions of the transformation
620  //matrices.
621  for(cnt=n=0; n < n_exp; ++n)
622  {
623  locExpansion = expList->GetExp(n)->as<LocalRegions::Expansion3D>();
624  nCoeffs=locExpansion->NumBndryCoeffs();
625 
626  //Get correct transformation matrix for element type
627  DNekMat &R = (*m_RBlk->GetBlock(n,n));
628 
630  (nCoeffs, nCoeffs, zero, storage);
631  RSRT = (*pRSRT);
632 
633  nVerts=locExpansion->GetGeom()->GetNumVerts();
634  nEdges=locExpansion->GetGeom()->GetNumEdges();
635  nFaces=locExpansion->GetGeom()->GetNumFaces();
636 
637  //Get statically condensed matrix
638  loc_mat = (m_linsys.lock())->GetStaticCondBlock(n);
639 
640  //Extract boundary block (elemental S1)
641  bnd_mat=loc_mat->GetBlock(0,0);
642 
643  //offset by number of rows
644  offset = bnd_mat->GetRows();
645 
646  DNekScalMat &S=(*bnd_mat);
647 
648  DNekMat Sloc(nCoeffs,nCoeffs);
649 
650  // For variable p we need to just use the active modes
651  NekDouble val;
652 
653  for(int i = 0; i < nCoeffs; ++i)
654  {
655  for(int j = 0; j < nCoeffs; ++j)
656  {
657  val = S(i,j)*m_variablePmask[cnt+i]*m_variablePmask[cnt+j];
658  Sloc.SetValue(i,j,val);
659  }
660  }
661 
662  //Calculate R*S*trans(R)
663  RSRT = R*Sloc*Transpose(R);
664 
665  //loop over vertices of the element and return the vertex map
666  //for each vertex
667  for (v=0; v<nVerts; ++v)
668  {
669  vMap1=locExpansion->GetVertexMap(v);
670 
671  //Get vertex map
672  globalrow = asmMap->
673  GetLocalToGlobalBndMap(cnt+vMap1)-nDirBnd;
674 
675  if(globalrow >= 0)
676  {
677  for (m=0; m<nVerts; ++m)
678  {
679  vMap2=locExpansion->GetVertexMap(m);
680 
681  //global matrix location (without offset due to
682  //dirichlet values)
683  globalcol = asmMap->
684  GetLocalToGlobalBndMap(cnt+vMap2)-nDirBnd;
685 
686  //offset for dirichlet conditions
687  if (globalcol == globalrow)
688  {
689  //modal connectivity between elements
690  sign1 = asmMap->
691  GetLocalToGlobalBndSign(cnt + vMap1);
692  sign2 = asmMap->
693  GetLocalToGlobalBndSign(cnt + vMap2);
694 
695  vertArray[globalrow]
696  += sign1*sign2*RSRT(vMap1,vMap2);
697 
698 
699  meshVertId = locExpansion->GetGeom()->GetVid(v);
700 
701  auto pIt = periodicVerts.find(meshVertId);
702  if (pIt != periodicVerts.end())
703  {
704  for (k = 0; k < pIt->second.size(); ++k)
705  {
706  meshVertId = min(meshVertId, pIt->second[k].id);
707  }
708  }
709 
710  VertBlockToUniversalMap[globalrow]
711  = meshVertId + 1;
712  }
713  }
714  }
715  }
716 
717  //loop over edges of the element and return the edge map
718  for (eid=0; eid<nEdges; ++eid)
719  {
720 
721  meshEdgeId = locExpansion->as<LocalRegions::Expansion3D>()
722  ->GetGeom3D()->GetEid(eid);
723 
724 
725  nedgemodes = EdgeSize[meshEdgeId];
726  if(nedgemodes)
727  {
728  nedgemodesloc = locExpansion->GetEdgeNcoeffs(eid)-2;
729  DNekMatSharedPtr m_locMat =
731  (nedgemodes,nedgemodes,zero,storage);
732 
733  Array<OneD, unsigned int> edgemodearray = locExpansion->GetEdgeInverseBoundaryMap(eid);
734 
735  if(edgeDirMap.count(meshEdgeId)==0)
736  {
737  for (v=0; v<nedgemodesloc; ++v)
738  {
739  eMap1=edgemodearray[v];
740  sign1 = asmMap->
741  GetLocalToGlobalBndSign(cnt + eMap1);
742 
743  if(sign1 == 0)
744  {
745  continue;
746  }
747 
748  for (m=0; m<nedgemodesloc; ++m)
749  {
750  eMap2=edgemodearray[m];
751 
752  //modal connectivity between elements
753  sign2 = asmMap->
754  GetLocalToGlobalBndSign(cnt + eMap2);
755 
756  NekDouble globalEdgeValue = sign1*sign2*RSRT(eMap1,eMap2);
757 
758  if(sign2 != 0)
759  {
760  //if(eMap1 == eMap2)
761  BlockArray[matrixoffset+v*nedgemodes+m]=globalEdgeValue;
762  }
763  }
764  }
765  matrixoffset+=nedgemodes*nedgemodes;
766  }
767  }
768  }
769 
770  //loop over faces of the element and return the face map
771  for (fid=0; fid<nFaces; ++fid)
772  {
773  meshFaceId = locExpansion->as<LocalRegions::Expansion3D>()
774  ->GetGeom3D()->GetFid(fid);
775 
776  nfacemodes = FaceSize[meshFaceId];
777  if(nfacemodes > 0)
778  {
779  DNekMatSharedPtr m_locMat =
781  (nfacemodes,nfacemodes,zero,storage);
782 
783  if(faceDirMap.count(meshFaceId) == 0)
784  {
785  Array<OneD, unsigned int> facemodearray;
786  StdRegions::Orientation faceOrient =
787  locExpansion->GetTraceOrient(fid);
788 
789  auto pIt = periodicFaces.find(meshFaceId);
790  if (pIt != periodicFaces.end())
791  {
792  if(meshFaceId == min(meshFaceId, pIt->second[0].id))
793  {
794  faceOrient = DeterminePeriodicFaceOrient
795  (faceOrient,pIt->second[0].orient);
796  }
797  }
798 
799  facemodearray = locExpansion->GetTraceInverseBoundaryMap
800  (fid,faceOrient,FaceModes[meshFaceId].first,
801  FaceModes[meshFaceId].second);
802 
803  for (v=0; v<nfacemodes; ++v)
804  {
805  fMap1=facemodearray[v];
806 
807  sign1 = asmMap->
808  GetLocalToGlobalBndSign(cnt + fMap1);
809 
810  ASSERTL1(sign1 != 0,"Something is wrong since we "
811  "shoudl only be extracting modes for "
812  "lowest order expansion");
813 
814  for (m=0; m<nfacemodes; ++m)
815  {
816  fMap2=facemodearray[m];
817 
818  //modal connectivity between elements
819  sign2 = asmMap->
820  GetLocalToGlobalBndSign(cnt + fMap2);
821 
822  ASSERTL1(sign2 != 0,"Something is wrong since "
823  "we shoudl only be extracting modes for "
824  "lowest order expansion");
825 
826  // Get the face-face value from the
827  // low energy matrix (S2)
828  NekDouble globalFaceValue = sign1*sign2*
829  RSRT(fMap1,fMap2);
830 
831  //local face value to global face value
832  //if(fMap1 == fMap2)
833  BlockArray[matrixoffset+v*nfacemodes+m]=
834  globalFaceValue;
835  }
836  }
837  matrixoffset+=nfacemodes*nfacemodes;
838  }
839  }
840  }
841 
842  //offset for the expansion
843  cnt+=nCoeffs;
844  }
845 
846  if(nNonDirVerts!=0)
847  {
848  //Exchange vertex data over different processes
849  Gs::gs_data *tmp = Gs::Init(VertBlockToUniversalMap, m_comm, verbose);
850  Gs::Gather(vertArray, Gs::gs_add, tmp);
851 
852  }
853 
854  Array<OneD, NekDouble> GlobalBlock(ntotalentries,0.0);
855  if(ntotalentries)
856  {
857  //Assemble edge matrices of each process
858  Vmath::Assmb(BlockArray.size(),
859  BlockArray,
860  localToGlobalMatrixMap,
861  GlobalBlock);
862  }
863 
864  //Exchange edge & face data over different processes
865  Gs::gs_data *tmp1 = Gs::Init(BlockToUniversalMap, m_comm, verbose);
866  Gs::Gather(GlobalBlock, Gs::gs_add, tmp1);
867 
868  // Populate vertex block
869  for (int i = 0; i < nNonDirVerts; ++i)
870  {
871  VertBlk->SetValue(i,i,1.0/vertArray[i]);
872  }
873 
874  //Set the first block to be the diagonal of the vertex space
875  m_BlkMat->SetBlock(0,0, VertBlk);
876 
877  //Build the edge and face matrices from the vector
878  DNekMatSharedPtr gmat;
879 
880  offset=0;
881  // -1 since we ignore vert block
882  for(int loc=0; loc<n_blks.size()-1; ++loc)
883  {
884  nmodes = n_blks[1+loc];
886  (nmodes,nmodes,zero,storage);
887 
888  for (v=0; v<nmodes; ++v)
889  {
890  for (m=0; m<nmodes; ++m)
891  {
892  NekDouble Value = GlobalBlock[offset+v*nmodes+m];
893  gmat->SetValue(v,m,Value);
894 
895  }
896  }
897  m_BlkMat->SetBlock(1+loc,1+loc, gmat);
898  offset+=modeoffset[loc];
899  }
900 
901  // invert blocks.
902  int totblks=m_BlkMat->GetNumberOfBlockRows();
903  for (i=1; i< totblks; ++i)
904  {
905  unsigned int nmodes=m_BlkMat->GetNumberOfRowsInBlockRow(i);
906  if(nmodes)
907  {
908  DNekMatSharedPtr tmp_mat =
910  (nmodes,nmodes,zero,storage);
911 
912  tmp_mat=m_BlkMat->GetBlock(i,i);
913  tmp_mat->Invert();
914 
915  m_BlkMat->SetBlock(i,i,tmp_mat);
916  }
917  }
918  }
919 
920 
921  /**
922  * Apply the low energy preconditioner during the conjugate gradient
923  * routine
924  */
926  const Array<OneD, NekDouble>& pInput,
927  Array<OneD, NekDouble>& pOutput)
928  {
929  int nDir = m_locToGloMap.lock()->GetNumGlobalDirBndCoeffs();
930  int nGlobal = m_locToGloMap.lock()->GetNumGlobalBndCoeffs();
931  int nNonDir = nGlobal-nDir;
932  DNekBlkMat &M = (*m_BlkMat);
933 
934  NekVector<NekDouble> r(nNonDir,pInput,eWrapper);
935  NekVector<NekDouble> z(nNonDir,pOutput,eWrapper);
936 
937  z = M * r;
938  }
939 
940 
941  /**
942  * Set a block transformation matrices for each element type. These are
943  * needed in routines that transform the schur complement matrix to and
944  * from the low energy basis.
945  */
947  {
948  std::shared_ptr<MultiRegions::ExpList>
949  expList=((m_linsys.lock())->GetLocMat()).lock();
952  map<int,int> EdgeSize;
953 
954  int n;
955 
956  std::map<ShapeType, DNekScalMatSharedPtr> maxRmat;
957  map<ShapeType, LocalRegions::Expansion3DSharedPtr > maxElmt;
958  map<ShapeType, Array<OneD, unsigned int> > vertMapMaxR;
959  map<ShapeType, Array<OneD, Array<OneD, unsigned int> > > edgeMapMaxR;
960 
961 
962  //Sets up reference element and builds transformation matrix for
963  // maximum polynomial order meshes
964  SetUpReferenceElements(maxRmat,maxElmt,vertMapMaxR,edgeMapMaxR);
965 
966  const Array<OneD,const unsigned int>& nbdry_size
967  = m_locToGloMap.lock()->GetNumLocalBndCoeffsPerPatch();
968 
969  int n_exp=expList->GetNumElmts();
970 
971  MatrixStorage blkmatStorage = eDIAGONAL;
972 
973  //Variants of R matrices required for low energy preconditioning
975  ::AllocateSharedPtr(nbdry_size, nbdry_size , blkmatStorage);
977  ::AllocateSharedPtr(nbdry_size, nbdry_size , blkmatStorage);
978 
979  DNekMatSharedPtr rmat, invrmat;
980 
981  int offset = 0;
982 
983  // Set up transformation matrices whilst checking to see if
984  // consecutive matrices are the same and if so reuse the
985  // matrices and store how many consecutive offsets there
986  // are
987  for(n=0; n < n_exp; ++n)
988  {
989  locExp = expList->GetExp(n)->as<LocalRegions::Expansion3D>();
990 
991  ShapeType eltype = locExp->DetShapeType();
992 
993  int nbndcoeffs = locExp->NumBndryCoeffs();
994 
995  if(m_sameBlock.size() == 0)
996  {
997  rmat = ExtractLocMat(locExp,maxRmat[eltype],
998  maxElmt[eltype],
999  vertMapMaxR[eltype],
1000  edgeMapMaxR[eltype]);
1001  //Block R matrix
1002  m_RBlk->SetBlock(n, n, rmat);
1003 
1005  invrmat->Invert();
1006 
1007  //Block inverse R matrix
1008  m_InvRBlk->SetBlock(n, n, invrmat);
1009 
1010  m_sameBlock.push_back(pair<int,int>(1,nbndcoeffs));
1011  locExpSav = locExp;
1012  }
1013  else
1014  {
1015  bool reuse = true;
1016 
1017  // check to see if same as previous matrix and
1018  // reuse if we can
1019  for(int i = 0; i < 3; ++i)
1020  {
1021  if(locExpSav->GetBasis(i) != locExp->GetBasis(i))
1022  {
1023  reuse = false;
1024  break;
1025  }
1026  }
1027 
1028  if(reuse)
1029  {
1030  m_RBlk->SetBlock(n, n, rmat);
1031  m_InvRBlk->SetBlock(n, n, invrmat);
1032 
1033  m_sameBlock[offset] =
1034  (pair<int,int>(m_sameBlock[offset].first+1,nbndcoeffs));
1035  }
1036  else
1037  {
1038  rmat = ExtractLocMat(locExp,maxRmat[eltype],
1039  maxElmt[eltype],
1040  vertMapMaxR[eltype],
1041  edgeMapMaxR[eltype]);
1042 
1043  //Block R matrix
1044  m_RBlk->SetBlock(n, n, rmat);
1045 
1047  invrmat->Invert();
1048  //Block inverse R matrix
1049  m_InvRBlk->SetBlock(n, n, invrmat);
1050 
1051  m_sameBlock.push_back(pair<int,int>(1,nbndcoeffs));
1052  offset++;
1053  locExpSav = locExp;
1054  }
1055  }
1056  }
1057  }
1058 
1059  /**
1060  * \brief Transform the basis vector to low energy space
1061  *
1062  * As the conjugate gradient system is solved for the low energy basis,
1063  * the solution vector \f$\mathbf{x}\f$ must be transformed to the low
1064  * energy basis i.e. \f$\overline{\mathbf{x}}=\mathbf{R}\mathbf{x}\f$.
1065  */
1067  (Array<OneD, NekDouble>& pInOut)
1068  {
1069  int nLocBndDofs = m_locToGloMap.lock()->GetNumLocalBndCoeffs();
1070 
1071  //Block transformation matrix
1072  DNekBlkMat &R = *m_RBlk;
1073 
1074  Array<OneD, NekDouble> pLocalIn(nLocBndDofs,pInOut.get());
1075 
1076  //Apply mask in case of variable P
1077  Vmath::Vmul(nLocBndDofs,pLocalIn, 1, m_variablePmask,1,
1078  pLocalIn,1);
1079 
1080  //Multiply by the block transformation matrix
1081  int cnt = 0;
1082  int cnt1 = 0;
1083  for(int i = 0; i < m_sameBlock.size(); ++i)
1084  {
1085  int nexp = m_sameBlock[i].first;
1086  int nbndcoeffs = m_sameBlock[i].second;
1087  Blas::Dgemm('N','N', nbndcoeffs, nexp, nbndcoeffs,
1088  1.0, &(R.GetBlock(cnt1,cnt1)->GetPtr()[0]),
1089  nbndcoeffs,pLocalIn.get() + cnt, nbndcoeffs,
1090  0.0, pInOut.get() + cnt, nbndcoeffs);
1091  cnt += nbndcoeffs*nexp;
1092  cnt1 += nexp;
1093  }
1094  }
1095 
1096  /**
1097  * \brief transform the solution coeffiicents from low energy
1098  * back to the original coefficient space.
1099  *
1100  * After the conjugate gradient routine the output vector is in the low
1101  * energy basis and must be trasnformed back to the original basis in
1102  * order to get the correct solution out. the solution vector
1103  * i.e. \f$\mathbf{x}=\mathbf{R^{T}}\mathbf{\overline{x}}\f$.
1104  */
1106  Array<OneD, NekDouble>& pInOut)
1107  {
1108  int nLocBndDofs = m_locToGloMap.lock()->GetNumLocalBndCoeffs();
1109 
1110  ASSERTL1(pInOut.size() >= nLocBndDofs,
1111  "Output array is not greater than the nLocBndDofs");
1112 
1113  //Block transposed transformation matrix
1114  DNekBlkMat &R = *m_RBlk;
1115 
1116  Array<OneD, NekDouble> pLocalIn(nLocBndDofs, pInOut.get());
1117 
1118  //Multiply by the transpose of block transformation matrix
1119  int cnt = 0;
1120  int cnt1 = 0;
1121  for(int i = 0; i < m_sameBlock.size(); ++i)
1122  {
1123  int nexp = m_sameBlock[i].first;
1124  int nbndcoeffs = m_sameBlock[i].second;
1125  Blas::Dgemm('T','N', nbndcoeffs, nexp, nbndcoeffs,
1126  1.0, &(R.GetBlock(cnt1,cnt1)->GetPtr()[0]),
1127  nbndcoeffs,pLocalIn.get() + cnt, nbndcoeffs,
1128  0.0, pInOut.get() + cnt, nbndcoeffs);
1129  cnt += nbndcoeffs*nexp;
1130  cnt1 += nexp;
1131 
1132  }
1133 
1134  Vmath::Vmul(nLocBndDofs,pInOut, 1, m_variablePmask,1,
1135  pInOut,1);
1136  }
1137 
1138  /**
1139  * \brief Multiply by the block inverse transformation matrix
1140  * This transforms the bassi from Low Energy to original basis
1141  *
1142  * Note; not typically required
1143  */
1144 
1146  const Array<OneD, NekDouble>& pInput,
1147  Array<OneD, NekDouble>& pOutput)
1148  {
1149  int nLocBndDofs = m_locToGloMap.lock()->GetNumLocalBndCoeffs();
1150 
1151  ASSERTL1(pInput.size() >= nLocBndDofs,
1152  "Input array is smaller than nLocBndDofs");
1153  ASSERTL1(pOutput.size() >= nLocBndDofs,
1154  "Output array is smaller than nLocBndDofs");
1155 
1156  //Block inverse transformation matrix
1157  DNekBlkMat &invR = *m_InvRBlk;
1158 
1159  Array<OneD, NekDouble> pLocalIn(nLocBndDofs, pInput.get());
1160 
1161  //Multiply by the inverse transformation matrix
1162  int cnt = 0;
1163  int cnt1 = 0;
1164  for(int i = 0; i < m_sameBlock.size(); ++i)
1165  {
1166  int nexp = m_sameBlock[i].first;
1167  int nbndcoeffs = m_sameBlock[i].second;
1168  Blas::Dgemm('N','N', nbndcoeffs, nexp, nbndcoeffs,
1169  1.0, &(invR.GetBlock(cnt1,cnt1)->GetPtr()[0]),
1170  nbndcoeffs,pLocalIn.get() + cnt, nbndcoeffs,
1171  0.0, pOutput.get() + cnt, nbndcoeffs);
1172  cnt += nbndcoeffs*nexp;
1173  cnt1 += nexp;
1174  }
1175  }
1176 
1177  /**
1178  * \brief Multiply by the block tranposed inverse
1179  * transformation matrix (R^T)^{-1} which is equivlaent to
1180  * transforming coefficients to LowEnergy space
1181  *
1182  * In JCP 2001 paper on low energy this is seen as (C^T)^{-1}
1183  */
1185  const Array<OneD, NekDouble>& pInput,
1186  Array<OneD, NekDouble>& pOutput)
1187  {
1188  int nLocBndDofs = m_locToGloMap.lock()->GetNumLocalBndCoeffs();
1189 
1190  ASSERTL1(pInput.size() >= nLocBndDofs,
1191  "Input array is less than nLocBndDofs");
1192  ASSERTL1(pOutput.size() >= nLocBndDofs,
1193  "Output array is less than nLocBndDofs");
1194 
1195  //Block inverse transformation matrix
1196  DNekBlkMat &invR = *m_InvRBlk;
1197 
1198  Array<OneD, NekDouble> pLocalIn(nLocBndDofs, pInput.get());
1199 
1200  //Multiply by the transpose of block transformation matrix
1201  int cnt = 0;
1202  int cnt1 = 0;
1203  for(int i = 0; i < m_sameBlock.size(); ++i)
1204  {
1205  int nexp = m_sameBlock[i].first;
1206  int nbndcoeffs = m_sameBlock[i].second;
1207  Blas::Dgemm('T','N', nbndcoeffs, nexp, nbndcoeffs,
1208  1.0, &(invR.GetBlock(cnt1,cnt1)->GetPtr()[0]),
1209  nbndcoeffs,pLocalIn.get() + cnt, nbndcoeffs,
1210  0.0, pOutput.get() + cnt, nbndcoeffs);
1211  cnt += nbndcoeffs*nexp;
1212  cnt1 += nexp;
1213  }
1214  }
1215 
1216  /**
1217  * \brief Set up the transformed block matrix system
1218  *
1219  * Sets up a block elemental matrix in which each of the block matrix is
1220  * the low energy equivalent
1221  * i.e. \f$\mathbf{S}_{2}=\mathbf{R}\mathbf{S}_{1}\mathbf{R}^{T}\f$
1222  */
1225  int bndoffset,
1226  const std::shared_ptr<DNekScalMat > &loc_mat)
1227  {
1228  std::shared_ptr<MultiRegions::ExpList>
1229  expList=((m_linsys.lock())->GetLocMat()).lock();
1230 
1231  LocalRegions::ExpansionSharedPtr locExpansion;
1232  locExpansion = expList->GetExp(n);
1233  unsigned int nbnd=locExpansion->NumBndryCoeffs();
1234 
1235  MatrixStorage storage = eFULL;
1237  AllocateSharedPtr(nbnd,nbnd,0.0,storage);
1238 
1239  //transformation matrices
1240  DNekMat &R = (*m_RBlk->GetBlock(n,n));
1241 
1242  // Original Schur Complement
1243  DNekScalMat &S1 = (*loc_mat);
1244 
1245  DNekMat Sloc(nbnd,nbnd);
1246 
1247  // For variable p we need to just use the active modes
1248  NekDouble val;
1249 
1250  for(int i = 0; i < nbnd; ++i)
1251  {
1252  for(int j = 0; j < nbnd; ++j)
1253  {
1254  val = S1(i,j)*m_variablePmask[bndoffset+i]*
1255  m_variablePmask[bndoffset+j];
1256  Sloc.SetValue(i,j,val);
1257  }
1258  }
1259 
1260  //create low energy matrix
1261  DNekMat &S2 = (*pS2);
1262 
1263  S2= R*Sloc*Transpose(R);
1264 
1265  DNekScalMatSharedPtr return_val;
1266  return_val = MemoryManager<DNekScalMat>::AllocateSharedPtr(1.0, pS2);
1267 
1268  return return_val;
1269  }
1270 
1271  /**
1272  * Create the inverse multiplicity map.
1273  */
1275  {
1276  unsigned int nLocBnd = m_locToGloMap.lock()->GetNumLocalBndCoeffs();
1277  unsigned int i;
1278  auto asmMap = m_locToGloMap.lock();
1279 
1281  = asmMap->GetLocalToGlobalBndSign();
1282 
1283  m_signChange=asmMap->GetSignChange();
1284 
1285  // Construct a map of 1/multiplicity
1287  for (i = 0; i < nLocBnd; ++i)
1288  {
1289  if(m_signChange)
1290  {
1291  m_variablePmask[i] = fabs(sign[i]);
1292  }
1293  else
1294  {
1295  m_variablePmask[i] = 1.0;
1296  }
1297  }
1298  }
1299 
1300  /**
1301  *\brief Sets up the reference prismatic element needed to construct
1302  *a low energy basis
1303  */
1305  {
1306  //////////////////////////
1307  // Set up Prism element //
1308  //////////////////////////
1309 
1310  const int three=3;
1311  const int nVerts = 6;
1312  const double point[][3] = {
1313  {-1,-1,0}, {1,-1,0}, {1,1,0},
1314  {-1,1,0}, {0,-1,sqrt(double(3))}, {0,1,sqrt(double(3))},
1315  };
1316 
1317  //std::shared_ptr<SpatialDomains::PointGeom> verts[6];
1319  for(int i=0; i < nVerts; ++i)
1320  {
1322  ( three, i, point[i][0], point[i][1], point[i][2] );
1323  }
1324  const int nEdges = 9;
1325  const int vertexConnectivity[][2] = {
1326  {0,1}, {1,2}, {3,2}, {0,3}, {0,4},
1327  {1,4}, {2,5}, {3,5}, {4,5}
1328  };
1329 
1330  // Populate the list of edges
1331  SpatialDomains::SegGeomSharedPtr edges[nEdges];
1332  for(int i=0; i < nEdges; ++i){
1333  SpatialDomains::PointGeomSharedPtr vertsArray[2];
1334  for(int j=0; j<2; ++j)
1335  {
1336  vertsArray[j] = verts[vertexConnectivity[i][j]];
1337  }
1338  edges[i] = MemoryManager<SpatialDomains::SegGeom>::AllocateSharedPtr(i, three, vertsArray);
1339  }
1340 
1341  ////////////////////////
1342  // Set up Prism faces //
1343  ////////////////////////
1344 
1345  const int nFaces = 5;
1346  //quad-edge connectivity base-face0, vertical-quadface2, vertical-quadface4
1347  const int quadEdgeConnectivity[][4] = { {0,1,2,3}, {1,6,8,5}, {3,7,8,4} };
1348  // QuadId ordered as 0, 1, 2, otherwise return false
1349  const int quadId[] = { 0,-1,1,-1,2 };
1350 
1351  //triangle-edge connectivity side-triface-1, side triface-3
1352  const int triEdgeConnectivity[][3] = { {0,5,4}, {2,6,7} };
1353  // TriId ordered as 0, 1, otherwise return false
1354  const int triId[] = { -1,0,-1,1,-1 };
1355 
1356  // Populate the list of faces
1358  for(int f = 0; f < nFaces; ++f){
1359  if(f == 1 || f == 3) {
1360  int i = triId[f];
1361  SpatialDomains::SegGeomSharedPtr edgeArray[3];
1362  for(int j = 0; j < 3; ++j){
1363  edgeArray[j] = edges[triEdgeConnectivity[i][j]];
1364  }
1366  }
1367  else {
1368  int i = quadId[f];
1369  SpatialDomains::SegGeomSharedPtr edgeArray[4];
1370  for(int j=0; j < 4; ++j){
1371  edgeArray[j] = edges[quadEdgeConnectivity[i][j]];
1372  }
1374  }
1375  }
1376 
1378 
1379  return geom;
1380  }
1381 
1382  /**
1383  *\brief Sets up the reference prismatic element needed to construct
1384  *a low energy basis mapping arrays
1385  */
1387  {
1388  //////////////////////////
1389  // Set up Pyramid element //
1390  //////////////////////////
1391 
1392  const int nVerts = 5;
1393  const double point[][3] = {
1394  {-1,-1,0}, {1,-1,0}, {1,1,0},
1395  {-1,1,0}, {0,0,sqrt(double(2))}
1396  };
1397 
1398  //boost::shared_ptr<SpatialDomains::PointGeom> verts[6];
1399  const int three=3;
1401  for(int i=0; i < nVerts; ++i)
1402  {
1404  ( three, i, point[i][0], point[i][1], point[i][2] );
1405  }
1406  const int nEdges = 8;
1407  const int vertexConnectivity[][2] = {
1408  {0,1}, {1,2}, {2,3}, {3,0},
1409  {0,4}, {1,4}, {2,4}, {3,4}
1410  };
1411 
1412  // Populate the list of edges
1413  SpatialDomains::SegGeomSharedPtr edges[nEdges];
1414  for(int i=0; i < nEdges; ++i)
1415  {
1416  SpatialDomains::PointGeomSharedPtr vertsArray[2];
1417  for(int j=0; j<2; ++j)
1418  {
1419  vertsArray[j] = verts[vertexConnectivity[i][j]];
1420  }
1421  edges[i] = MemoryManager<SpatialDomains::SegGeom>::AllocateSharedPtr(i, three, vertsArray);
1422  }
1423 
1424  ////////////////////////
1425  // Set up Pyramid faces //
1426  ////////////////////////
1427 
1428  const int nFaces = 5;
1429  //quad-edge connectivity base-face0,
1430  const int quadEdgeConnectivity[][4] = {{0,1,2,3}};
1431 
1432  //triangle-edge connectivity side-triface-1, side triface-2
1433  const int triEdgeConnectivity[][3] = { {0,5,4}, {1,6,5}, {2,7,6}, {3,4,7}};
1434 
1435  // Populate the list of faces
1437  for(int f = 0; f < nFaces; ++f)
1438  {
1439  if(f == 0)
1440  {
1441  SpatialDomains::SegGeomSharedPtr edgeArray[4];
1442  for(int j=0; j < 4; ++j)
1443  {
1444  edgeArray[j] = edges[quadEdgeConnectivity[f][j]];
1445  }
1446 
1448  }
1449  else {
1450  int i = f-1;
1451  SpatialDomains::SegGeomSharedPtr edgeArray[3];
1452  for(int j = 0; j < 3; ++j)
1453  {
1454  edgeArray[j] = edges[triEdgeConnectivity[i][j]];
1455  }
1457  }
1458  }
1459 
1462 
1463  return geom;
1464  }
1465 
1466  /**
1467  *\brief Sets up the reference tretrahedral element needed to construct
1468  *a low energy basis
1469  */
1471  {
1472  /////////////////////////////////
1473  // Set up Tetrahedron vertices //
1474  /////////////////////////////////
1475 
1476  int i,j;
1477  const int three=3;
1478  const int nVerts = 4;
1479  const double point[][3] = {
1480  {-1,-1/sqrt(double(3)),-1/sqrt(double(6))},
1481  {1,-1/sqrt(double(3)),-1/sqrt(double(6))},
1482  {0,2/sqrt(double(3)),-1/sqrt(double(6))},
1483  {0,0,3/sqrt(double(6))}};
1484 
1485  std::shared_ptr<SpatialDomains::PointGeom> verts[4];
1486  for(i=0; i < nVerts; ++i)
1487  {
1488  verts[i] =
1491  ( three, i, point[i][0], point[i][1], point[i][2] );
1492  }
1493 
1494  //////////////////////////////
1495  // Set up Tetrahedron Edges //
1496  //////////////////////////////
1497 
1498  // SegGeom (int id, const int coordim), EdgeComponent(id, coordim)
1499  const int nEdges = 6;
1500  const int vertexConnectivity[][2] = {
1501  {0,1},{1,2},{0,2},{0,3},{1,3},{2,3}
1502  };
1503 
1504  // Populate the list of edges
1505  SpatialDomains::SegGeomSharedPtr edges[nEdges];
1506  for(i=0; i < nEdges; ++i)
1507  {
1508  std::shared_ptr<SpatialDomains::PointGeom>
1509  vertsArray[2];
1510  for(j=0; j<2; ++j)
1511  {
1512  vertsArray[j] = verts[vertexConnectivity[i][j]];
1513  }
1514 
1516  ::AllocateSharedPtr(i, three, vertsArray);
1517  }
1518 
1519  //////////////////////////////
1520  // Set up Tetrahedron faces //
1521  //////////////////////////////
1522 
1523  const int nFaces = 4;
1524  const int edgeConnectivity[][3] = {
1525  {0,1,2}, {0,4,3}, {1,5,4}, {2,5,3}
1526  };
1527 
1528  // Populate the list of faces
1529  SpatialDomains::TriGeomSharedPtr faces[nFaces];
1530  for(i=0; i < nFaces; ++i)
1531  {
1532  SpatialDomains::SegGeomSharedPtr edgeArray[3];
1533  for(j=0; j < 3; ++j)
1534  {
1535  edgeArray[j] = edges[edgeConnectivity[i][j]];
1536  }
1537 
1538 
1540  ::AllocateSharedPtr(i, edgeArray);
1541  }
1542 
1545  (0, faces);
1546 
1547  return geom;
1548  }
1549 
1550  /**
1551  *\brief Sets up the reference hexahedral element needed to construct
1552  *a low energy basis
1553  */
1555  {
1556  ////////////////////////////////
1557  // Set up Hexahedron vertices //
1558  ////////////////////////////////
1559 
1560  const int three=3;
1561 
1562  const int nVerts = 8;
1563  const double point[][3] = {
1564  {0,0,0}, {1,0,0}, {1,1,0}, {0,1,0},
1565  {0,0,1}, {1,0,1}, {1,1,1}, {0,1,1}
1566  };
1567 
1568  // Populate the list of verts
1570  for( int i = 0; i < nVerts; ++i ) {
1572  ::AllocateSharedPtr(three, i, point[i][0],
1573  point[i][1], point[i][2]);
1574  }
1575 
1576  /////////////////////////////
1577  // Set up Hexahedron Edges //
1578  /////////////////////////////
1579 
1580  // SegGeom (int id, const int coordim), EdgeComponent(id, coordim)
1581  const int nEdges = 12;
1582  const int vertexConnectivity[][2] = {
1583  {0,1}, {1,2}, {2,3}, {0,3}, {0,4}, {1,5},
1584  {2,6}, {3,7}, {4,5}, {5,6}, {6,7}, {4,7}
1585  };
1586 
1587  // Populate the list of edges
1588  SpatialDomains::SegGeomSharedPtr edges[nEdges];
1589  for( int i = 0; i < nEdges; ++i ) {
1590  SpatialDomains::PointGeomSharedPtr vertsArray[2];
1591  for( int j = 0; j < 2; ++j ) {
1592  vertsArray[j] = verts[vertexConnectivity[i][j]];
1593  }
1595  AllocateSharedPtr( i, three, vertsArray);
1596  }
1597 
1598  /////////////////////////////
1599  // Set up Hexahedron faces //
1600  /////////////////////////////
1601 
1602  const int nFaces = 6;
1603  const int edgeConnectivity[][4] = {
1604  {0,1,2,3}, {0,5,8,4}, {1,6,9,5},
1605  {2,7,10,6}, {3,7,11,4}, {8,9,10,11}
1606  };
1607 
1608  // Populate the list of faces
1609  SpatialDomains::QuadGeomSharedPtr faces[nFaces];
1610  for( int i = 0; i < nFaces; ++i ) {
1611  SpatialDomains::SegGeomSharedPtr edgeArray[4];
1612  for( int j = 0; j < 4; ++j ) {
1613  edgeArray[j] = edges[edgeConnectivity[i][j]];
1614  }
1616  }
1617 
1620  (0, faces);
1621 
1622  return geom;
1623  }
1624 
1625 
1626  /**
1627  * \brief Loop expansion and determine different variants of the
1628  * transformation matrix
1629  *
1630  * Sets up multiple reference elements based on the element expansion.
1631  */
1633  (std::map<ShapeType, DNekScalMatSharedPtr> &maxRmat,
1634  map<ShapeType, LocalRegions::Expansion3DSharedPtr > &maxElmt,
1635  map<ShapeType, Array<OneD, unsigned int> > &vertMapMaxR,
1636  map<ShapeType, Array<OneD, Array<OneD, unsigned int> > > &edgeMapMaxR)
1637  {
1638 
1639  std::shared_ptr<MultiRegions::ExpList>
1640  expList=((m_linsys.lock())->GetLocMat()).lock();
1641  GlobalLinSysKey linSysKey=(m_linsys.lock())->GetKey();
1642 
1644 
1645  // face maps for pyramid and hybrid meshes - not need to return.
1646  map<ShapeType, Array<OneD, Array<OneD, unsigned int> > > faceMapMaxR;
1647 
1648  /* Determine the maximum expansion order for all elements */
1649  int nummodesmax = 0;
1651 
1652  for(int n = 0; n < expList->GetNumElmts(); ++n)
1653  {
1654  locExp = expList->GetExp(n);
1655 
1656  nummodesmax = max(nummodesmax, locExp->GetBasisNumModes(0));
1657  nummodesmax = max(nummodesmax, locExp->GetBasisNumModes(1));
1658  nummodesmax = max(nummodesmax, locExp->GetBasisNumModes(2));
1659 
1660  Shapes[locExp->DetShapeType()] = 1;
1661  }
1662 
1663 
1664  m_comm->AllReduce(nummodesmax, ReduceMax);
1665  m_comm->AllReduce(Shapes, ReduceMax);
1666 
1667  if(Shapes[ePyramid] || Shapes[ePrism]) // if Pyramids or Prisms used then need Tet and Hex expansion
1668  {
1669  Shapes[eTetrahedron] = 1;
1670  Shapes[eHexahedron] = 1;
1671  }
1672 
1673  StdRegions::MatrixType PreconR;
1674  if(linSysKey.GetMatrixType() == StdRegions::eMass)
1675  {
1676  PreconR = StdRegions::ePreconRMass;
1677  }
1678  else
1679  {
1680  PreconR = StdRegions::ePreconR;
1681  }
1682 
1686 
1687  /*
1688  * Set up a transformation matrices for equal max order
1689  * polynomial meshes
1690  */
1691 
1692  if(Shapes[eHexahedron])
1693  {
1695  //Bases for Hex element
1696  const BasisKey HexBa(eModified_A, nummodesmax,
1697  PointsKey(nummodesmax+1, eGaussLobattoLegendre));
1698  const BasisKey HexBb(eModified_A, nummodesmax,
1699  PointsKey(nummodesmax+1, eGaussLobattoLegendre));
1700  const BasisKey HexBc(eModified_A, nummodesmax,
1701  PointsKey(nummodesmax+1, eGaussLobattoLegendre));
1702 
1703  //Create reference Hexahdedral expansion
1705 
1707  ::AllocateSharedPtr(HexBa,HexBb,HexBc,
1708  hexgeom);
1709 
1710  maxElmt[eHexahedron] = HexExp;
1711 
1712  // Hex:
1713  HexExp->GetInverseBoundaryMaps(vmap,emap,fmap);
1714  vertMapMaxR[eHexahedron] = vmap;
1715  edgeMapMaxR[eHexahedron] = emap;
1716  faceMapMaxR[eHexahedron] = fmap;
1717 
1718  //Get hexahedral transformation matrix
1720  (PreconR, eHexahedron,
1721  *HexExp, linSysKey.GetConstFactors());
1722  maxRmat[eHexahedron] = HexExp->GetLocMatrix(HexR);
1723  }
1724 
1725  if(Shapes[eTetrahedron])
1726  {
1728  //Bases for Tetrahedral element
1729  const BasisKey TetBa(eModified_A, nummodesmax,
1730  PointsKey(nummodesmax+1, eGaussLobattoLegendre));
1731  const BasisKey TetBb(eModified_B, nummodesmax,
1732  PointsKey(nummodesmax, eGaussRadauMAlpha1Beta0));
1733  const BasisKey TetBc(eModified_C, nummodesmax,
1734  PointsKey(nummodesmax, eGaussRadauMAlpha2Beta0));
1735 
1736  //Create reference tetrahedral expansion
1738 
1740  ::AllocateSharedPtr(TetBa,TetBb,TetBc,tetgeom);
1741 
1742  maxElmt[eTetrahedron] = TetExp;
1743 
1744  TetExp->GetInverseBoundaryMaps(vmap,emap,fmap);
1745  vertMapMaxR[eTetrahedron] = vmap;
1746  edgeMapMaxR[eTetrahedron] = emap;
1747  faceMapMaxR[eTetrahedron] = fmap;
1748 
1749  //Get tetrahedral transformation matrix
1751  (PreconR, eTetrahedron,
1752  *TetExp, linSysKey.GetConstFactors());
1753  maxRmat[eTetrahedron] = TetExp->GetLocMatrix(TetR);
1754 
1755  if((Shapes[ePyramid])||(Shapes[eHexahedron]))
1756  {
1757  ReSetTetMaxRMat(nummodesmax, TetExp, maxRmat,
1758  vertMapMaxR, edgeMapMaxR, faceMapMaxR);
1759  }
1760  }
1761 
1762  if(Shapes[ePyramid])
1763  {
1765 
1766  //Bases for Pyramid element
1767  const BasisKey PyrBa(eModified_A, nummodesmax,
1768  PointsKey(nummodesmax+1, eGaussLobattoLegendre));
1769  const BasisKey PyrBb(eModified_A, nummodesmax,
1770  PointsKey(nummodesmax+1, eGaussLobattoLegendre));
1771  const BasisKey PyrBc(eModifiedPyr_C, nummodesmax,
1772  PointsKey(nummodesmax, eGaussRadauMAlpha2Beta0));
1773 
1774  //Create reference pyramid expansion
1776 
1778  ::AllocateSharedPtr(PyrBa,PyrBb,PyrBc,pyrgeom);
1779 
1780  maxElmt[ePyramid] = PyrExp;
1781 
1782  // Pyramid:
1783  PyrExp->GetInverseBoundaryMaps(vmap,emap,fmap);
1784  vertMapMaxR[ePyramid] = vmap;
1785  edgeMapMaxR[ePyramid] = emap;
1786  faceMapMaxR[ePyramid] = fmap;
1787 
1788  // Set up Pyramid Transformation Matrix based on Tet
1789  // and Hex expansion
1790  SetUpPyrMaxRMat(nummodesmax,PyrExp,maxRmat,vertMapMaxR,
1791  edgeMapMaxR,faceMapMaxR);
1792  }
1793 
1794  if(Shapes[ePrism])
1795  {
1797  //Bases for Prism element
1798  const BasisKey PrismBa(eModified_A, nummodesmax,
1799  PointsKey(nummodesmax+1, eGaussLobattoLegendre));
1800  const BasisKey PrismBb(eModified_A, nummodesmax,
1801  PointsKey(nummodesmax+1, eGaussLobattoLegendre));
1802  const BasisKey PrismBc(eModified_B, nummodesmax,
1803  PointsKey(nummodesmax, eGaussRadauMAlpha1Beta0));
1804 
1805  //Create reference prismatic expansion
1807 
1809  ::AllocateSharedPtr(PrismBa,PrismBb,PrismBc,prismgeom);
1810  maxElmt[ePrism] = PrismExp;
1811 
1812  // Prism:
1813  PrismExp->GetInverseBoundaryMaps(vmap,emap,fmap);
1814  vertMapMaxR[ePrism] = vmap;
1815  edgeMapMaxR[ePrism] = emap;
1816 
1817  faceMapMaxR[ePrism] = fmap;
1818 
1819  if((Shapes[ePyramid])||(Shapes[eHexahedron]))
1820  {
1821  ReSetPrismMaxRMat(nummodesmax, PrismExp, maxRmat,
1822  vertMapMaxR, edgeMapMaxR,
1823  faceMapMaxR, false);
1824  }
1825  else
1826  {
1827 
1828  //Get prismatic transformation matrix
1830  (PreconR, ePrism,
1831  *PrismExp, linSysKey.GetConstFactors());
1832  maxRmat[ePrism] =
1833  PrismExp->GetLocMatrix(PrismR);
1834 
1835  if(Shapes[eTetrahedron]) // reset triangular faces from Tet
1836  {
1837  ReSetPrismMaxRMat(nummodesmax, PrismExp, maxRmat,
1838  vertMapMaxR, edgeMapMaxR,
1839  faceMapMaxR, true);
1840  }
1841  }
1842  }
1843  }
1844 
1847  std::map<ShapeType, DNekScalMatSharedPtr> &maxRmat,
1848  std::map<ShapeType, Array<OneD, unsigned int> > &vertMapMaxR,
1849  std::map<ShapeType, Array<OneD, Array<OneD, unsigned int> > > &edgeMapMaxR,
1850  std::map<ShapeType, Array<OneD, Array<OneD, unsigned int> > > &faceMapMaxR)
1851  {
1852  int nRows = PyrExp->NumBndryCoeffs();
1853  NekDouble val;
1854  NekDouble zero = 0.0;
1856  AllocateSharedPtr(nRows,nRows,zero,eFULL);
1857 
1858  // set diagonal to 1
1859  for(int i = 0; i < nRows; ++i)
1860  {
1861  newmat->SetValue(i,i,1.0);
1862  }
1863 
1864  // The following lists specify the number of adjacent
1865  // edges to each vertex (nadj) then the Hex vert to
1866  // use for each pyramid ver in the vert-edge map (VEVert)
1867  // followed by the hex edge to use for each pyramid edge
1868  // in the vert-edge map (VEEdge)
1869  const int nadjedge[] = {3,3,3,3,4};
1870  const int VEHexVert[][4] = {{0,0,0,-1},{1,1,1,-1},{2,2,2,-1},{3,3,3,-1},{4,5,6,7}};
1871  const int VEHexEdge[][4] = {{0,3,4,-1},{0,1,5,-1},{1,2,6,-1},{2,3,7,-1},{4,5,6,7}};
1872  const int VEPyrEdge[][4] = {{0,3,4,-1},{0,1,5,-1},{1,2,6,-1},{2,3,7,-1},{4,5,6,7}};
1873 
1874  // fill vertex to edge coupling
1875  for(int v = 0; v < 5; ++v)
1876  {
1877  for(int e = 0; e < nadjedge[v]; ++e)
1878  {
1879  for(int i = 0; i < nummodesmax-2; ++i)
1880  {
1881  // Note this is using wrong shape but gives
1882  // answer that seems to have correct error!
1883  val = (*maxRmat[eHexahedron])(
1884  vertMapMaxR[eHexahedron][VEHexVert[v][e]],
1885  edgeMapMaxR[eHexahedron][VEHexEdge[v][e]][i]);
1886  newmat->SetValue(vertMapMaxR[ePyramid][v],
1887  edgeMapMaxR[ePyramid][VEPyrEdge[v][e]][i],val);
1888  }
1889  }
1890  }
1891 
1892  int nfacemodes;
1893  nfacemodes = (nummodesmax-2)*(nummodesmax-2);
1894  // First four verties are all adjacent to base face
1895  for(int v = 0; v < 4; ++v)
1896  {
1897  for(int i = 0; i < nfacemodes; ++i)
1898  {
1899  val = (*maxRmat[eHexahedron])(vertMapMaxR[eHexahedron][v],
1900  faceMapMaxR[eHexahedron][0][i]);
1901  newmat->SetValue(vertMapMaxR[ePyramid][v],
1902  faceMapMaxR[ePyramid][0][i],val);
1903  }
1904  }
1905 
1906 
1907  const int nadjface[] = {2,2,2,2,4};
1908  const int VFTetVert[][4] = {{0,0,-1,-1},{1,1,-1,-1},{2,2,-1,-1},{0,2,-1,-1},{3,3,3,3}};
1909  const int VFTetFace[][4] = {{1,3,-1,-1},{1,2,-1,-1},{2,3,-1,-1},{1,3,-1,-1},{1,2,1,2}};
1910  const int VFPyrFace[][4] = {{1,4,-1,-1},{1,2,-1,-1},{2,3,-1,-1},{3,4,-1,-1},{1,2,3,4}};
1911 
1912  // next handle all triangular faces from tetrahedron
1913  nfacemodes = (nummodesmax-3)*(nummodesmax-2)/2;
1914  for(int v = 0; v < 5; ++v)
1915  {
1916  for(int f = 0; f < nadjface[v]; ++f)
1917  {
1918  for(int i = 0; i < nfacemodes; ++i)
1919  {
1920  val = (*maxRmat[eTetrahedron])(vertMapMaxR[eTetrahedron][VFTetVert[v][f]],
1921  faceMapMaxR[eTetrahedron][VFTetFace[v][f]][i]);
1922  newmat->SetValue(vertMapMaxR[ePyramid][v],
1923  faceMapMaxR[ePyramid][VFPyrFace[v][f]][i],val);
1924  }
1925 
1926  }
1927  }
1928 
1929  // Edge to face coupling
1930  // all base edges are coupled to face 0
1931  nfacemodes = (nummodesmax-2)*(nummodesmax-2);
1932  for(int e = 0; e < 4; ++e)
1933  {
1934  for(int i = 0; i < nummodesmax-2; ++i)
1935  {
1936  for(int j = 0; j < nfacemodes; ++j)
1937  {
1938  int edgemapid = edgeMapMaxR[eHexahedron][e][i];
1939  int facemapid = faceMapMaxR[eHexahedron][0][j];
1940 
1941  val = (*maxRmat[eHexahedron])(edgemapid,facemapid);
1942  newmat->SetValue(edgeMapMaxR[ePyramid][e][i],
1943  faceMapMaxR[ePyramid][0][j],val);
1944  }
1945 
1946  }
1947  }
1948 
1949  const int nadjface1[] = {1,1,1,1,2,2,2,2};
1950  const int EFTetEdge[][2] = {{0,-1},{1,-1},{0,-1},{2,-1},{3,3},{4,4},{5,5},{3,5}};
1951  const int EFTetFace[][2] = {{1,-1},{2,-1},{1,-1},{3,-1},{1,3},{1,2},{2,3},{1,3}};
1952  const int EFPyrFace[][2] = {{1,-1},{2,-1},{3,-1},{4,-1},{1,4},{1,2},{2,3},{3,4}};
1953 
1954  // next handle all triangular faces from tetrahedron
1955  nfacemodes = (nummodesmax-3)*(nummodesmax-2)/2;
1956  for(int e = 0; e < 8; ++e)
1957  {
1958  for(int f = 0; f < nadjface1[e]; ++f)
1959  {
1960  for(int i = 0; i < nummodesmax-2; ++i)
1961  {
1962  for(int j = 0; j < nfacemodes; ++j)
1963  {
1964  int edgemapid = edgeMapMaxR[eTetrahedron][EFTetEdge[e][f]][i];
1965  int facemapid = faceMapMaxR[eTetrahedron][EFTetFace[e][f]][j];
1966 
1967  val = (*maxRmat[eTetrahedron])(edgemapid,facemapid);
1968  newmat->SetValue(edgeMapMaxR[ePyramid][e][i],
1969  faceMapMaxR[ePyramid][EFPyrFace[e][f]][j],val);
1970  }
1971  }
1972  }
1973  }
1974 
1975  DNekScalMatSharedPtr PyrR;
1977  maxRmat[ePyramid] =PyrR;
1978  }
1979 
1980 
1983  std::map<ShapeType, DNekScalMatSharedPtr> &maxRmat,
1984  std::map<ShapeType, Array<OneD, unsigned int> > &vertMapMaxR,
1985  std::map<ShapeType, Array<OneD, Array<OneD, unsigned int> > > &edgeMapMaxR,
1986  std::map<ShapeType, Array<OneD, Array<OneD, unsigned int> > > &faceMapMaxR)
1987  {
1988  boost::ignore_unused(faceMapMaxR);
1989 
1990  int nRows = TetExp->NumBndryCoeffs();
1991  NekDouble val;
1992  NekDouble zero = 0.0;
1994  AllocateSharedPtr(nRows,nRows,zero,eFULL);
1995 
1996  // copy existing system
1997  for(int i = 0; i < nRows; ++i)
1998  {
1999  for(int j = 0; j < nRows; ++j)
2000  {
2001  val = (*maxRmat[eTetrahedron])(i,j);
2002  newmat->SetValue(i,j,val);
2003  }
2004  }
2005 
2006  // The following lists specify the number of adjacent
2007  // edges to each vertex (nadj) then the Hex vert to
2008  // use for each pyramid ver in the vert-edge map (VEVert)
2009  // followed by the hex edge to use for each Tet edge
2010  // in the vert-edge map (VEEdge)
2011  const int VEHexVert[][4] = {{0,0,0},{1,1,1},{2,2,2},{4,5,6}};
2012  const int VEHexEdge[][4] = {{0,3,4},{0,1,5},{1,2,6},{4,5,6}};
2013  const int VETetEdge[][4] = {{0,2,3},{0,1,4},{1,2,5},{3,4,5}};
2014 
2015  // fill vertex to edge coupling
2016  for(int v = 0; v < 4; ++v)
2017  {
2018  for(int e = 0; e < 3; ++e)
2019  {
2020  for(int i = 0; i < nummodesmax-2; ++i)
2021  {
2022  // Note this is using wrong shape but gives
2023  // answer that seems to have correct error!
2024  val = (*maxRmat[eHexahedron])(
2025  vertMapMaxR[eHexahedron][VEHexVert[v][e]],
2026  edgeMapMaxR[eHexahedron][VEHexEdge[v][e]][i]);
2027  newmat->SetValue(vertMapMaxR[eTetrahedron][v],
2028  edgeMapMaxR[eTetrahedron][VETetEdge[v][e]][i],
2029  val);
2030  }
2031  }
2032  }
2033 
2034  DNekScalMatSharedPtr TetR =
2036 
2037  maxRmat[eTetrahedron] = TetR;
2038  }
2039 
2042  std::map<ShapeType, DNekScalMatSharedPtr> &maxRmat,
2043  std::map<ShapeType, Array<OneD, unsigned int> > &vertMapMaxR,
2044  std::map<ShapeType, Array<OneD, Array<OneD, unsigned int> > > &edgeMapMaxR,
2045  std::map<ShapeType, Array<OneD, Array<OneD, unsigned int> > > &faceMapMaxR,
2046  bool UseTetOnly)
2047  {
2048  int nRows = PrismExp->NumBndryCoeffs();
2049  NekDouble val;
2050  NekDouble zero = 0.0;
2052  AllocateSharedPtr(nRows,nRows,zero,eFULL);
2053 
2054 
2055  int nfacemodes;
2056 
2057  if(UseTetOnly)
2058  {
2059  // copy existing system
2060  for(int i = 0; i < nRows; ++i)
2061  {
2062  for(int j = 0; j < nRows; ++j)
2063  {
2064  val = (*maxRmat[ePrism])(i,j);
2065  newmat->SetValue(i,j,val);
2066  }
2067  }
2068 
2069  // Reset vertex to edge mapping from tet.
2070  const int VETetVert[][2] = {{0,0},{1,1},{1,1},{0,0},{3,3},{3,3}};
2071  const int VETetEdge[][2] = {{0,3},{0,4},{0,4},{0,3},{3,4},{4,3}};
2072  const int VEPrismEdge[][2] = {{0,4},{0,5},{2,6},{2,7},{4,5},{6,7}};
2073 
2074  // fill vertex to edge coupling
2075  for(int v = 0; v < 6; ++v)
2076  {
2077  for(int e = 0; e < 2; ++e)
2078  {
2079  for(int i = 0; i < nummodesmax-2; ++i)
2080  {
2081  // Note this is using wrong shape but gives
2082  // answer that seems to have correct error!
2083  val = (*maxRmat[eTetrahedron])(
2084  vertMapMaxR[eTetrahedron][VETetVert[v][e]],
2085  edgeMapMaxR[eTetrahedron][VETetEdge[v][e]][i]);
2086  newmat->
2087  SetValue(vertMapMaxR[ePrism][v],
2088  edgeMapMaxR[ePrism][VEPrismEdge[v][e]][i],
2089  val);
2090  }
2091  }
2092  }
2093  }
2094  else
2095  {
2096 
2097  // set diagonal to 1
2098  for(int i = 0; i < nRows; ++i)
2099  {
2100  newmat->SetValue(i,i,1.0);
2101  }
2102 
2103 
2104  // Set vertex to edge mapping from Hex.
2105 
2106  // The following lists specify the number of adjacent
2107  // edges to each vertex (nadj) then the Hex vert to
2108  // use for each prism ver in the vert-edge map (VEVert)
2109  // followed by the hex edge to use for each prism edge
2110  // in the vert-edge map (VEEdge)
2111  const int VEHexVert[][3] = {{0,0,0},{1,1,1},{2,2,2},{3,3,3},
2112  {4,5,5},{6,7,7}};
2113  const int VEHexEdge[][3] = {{0,3,4},{0,1,5},{1,2,6},{2,3,7},
2114  {4,5,9},{6,7,11}};
2115  const int VEPrismEdge[][3] = {{0,3,4},{0,1,5},{1,2,6},{2,3,7},
2116  {4,5,8},{6,7,8}};
2117 
2118  // fill vertex to edge coupling
2119  for(int v = 0; v < 6; ++v)
2120  {
2121  for(int e = 0; e < 3; ++e)
2122  {
2123  for(int i = 0; i < nummodesmax-2; ++i)
2124  {
2125  // Note this is using wrong shape but gives
2126  // answer that seems to have correct error!
2127  val = (*maxRmat[eHexahedron])(
2128  vertMapMaxR[eHexahedron][VEHexVert[v][e]],
2129  edgeMapMaxR[eHexahedron][VEHexEdge[v][e]][i]);
2130  newmat->SetValue(vertMapMaxR[ePrism][v],
2131  edgeMapMaxR[ePrism][VEPrismEdge[v][e]][i],
2132  val);
2133  }
2134  }
2135  }
2136 
2137 
2138  // Setup vertex to face mapping from Hex
2139  const int VFHexVert[][2] = {{0,0},{1,1},{4,5},{2,2},{3,3},{6,7}};
2140  const int VFHexFace[][2] = {{0,4},{0,2},{4,2},{0,2},{0,4},{2,4}};
2141 
2142  const int VQFPrismVert[][2] = {{0,0},{1,1},{4,4},{2,2},{3,3},{5,5}};
2143  const int VQFPrismFace[][2] = {{0,4},{0,2},{4,2},{0,2},{0,4},{2,4}};
2144 
2145  nfacemodes = (nummodesmax-2)*(nummodesmax-2);
2146  // Replace two Quad faces on every vertex
2147  for(int v = 0; v < 6; ++v)
2148  {
2149  for(int f = 0; f < 2; ++f)
2150  {
2151  for(int i = 0; i < nfacemodes; ++i)
2152  {
2153  val = (*maxRmat[eHexahedron])(
2154  vertMapMaxR[eHexahedron][VFHexVert[v][f]],
2155  faceMapMaxR[eHexahedron][VFHexFace[v][f]][i]);
2156  newmat->SetValue(vertMapMaxR[ePrism][VQFPrismVert[v][f]],
2157  faceMapMaxR[ePrism][VQFPrismFace[v][f]][i],val);
2158  }
2159  }
2160  }
2161 
2162  // Mapping of Hex Edge-Face mappings to Prism Edge-Face Mappings
2163  const int nadjface[] = {1,2,1,2,1,1,1,1,2};
2164  const int EFHexEdge[][2] = {{0,-1},{1,1},{2,-1},{3,3},{4,-1},{5,-1},{6,-1},{7,-1},{9,11}};
2165  const int EFHexFace[][2] = {{0,-1},{0,2},{0,-1},{0,4},{4,-1},{2,-1},{2,-1},{4,-1},{2,4}};
2166  const int EQFPrismEdge[][2] = {{0,-1},{1,1},{2,-1},{3,3},{4,-1},{5,-1},{6,-1},{7,-1},{8,8}};
2167  const int EQFPrismFace[][2] = {{0,-1},{0,2},{0,-1},{0,4},{4,-1},{2,-1},{2,-1},{4,-1},{2,4}};
2168 
2169  // all base edges are coupled to face 0
2170  nfacemodes = (nummodesmax-2)*(nummodesmax-2);
2171  for(int e = 0; e < 9; ++e)
2172  {
2173  for(int f = 0; f < nadjface[e]; ++f)
2174  {
2175  for(int i = 0; i < nummodesmax-2; ++i)
2176  {
2177  for(int j = 0; j < nfacemodes; ++j)
2178  {
2179  int edgemapid = edgeMapMaxR[eHexahedron][EFHexEdge[e][f]][i];
2180  int facemapid = faceMapMaxR[eHexahedron][EFHexFace[e][f]][j];
2181 
2182  val = (*maxRmat[eHexahedron])(edgemapid,facemapid);
2183 
2184  int edgemapid1 = edgeMapMaxR[ePrism][EQFPrismEdge[e][f]][i];
2185  int facemapid1 = faceMapMaxR[ePrism][EQFPrismFace[e][f]][j];
2186  newmat->SetValue(edgemapid1, facemapid1, val);
2187  }
2188  }
2189  }
2190  }
2191  }
2192 
2193  const int VFTetVert[] = {0,1,3,1,0,3};
2194  const int VFTetFace[] = {1,1,1,1,1,1};
2195  const int VTFPrismVert[] = {0,1,4,2,3,5};
2196  const int VTFPrismFace[] = {1,1,1,3,3,3};
2197 
2198  // Handle all triangular faces from tetrahedron
2199  nfacemodes = (nummodesmax-3)*(nummodesmax-2)/2;
2200  for(int v = 0; v < 6; ++v)
2201  {
2202  for(int i = 0; i < nfacemodes; ++i)
2203  {
2204  val = (*maxRmat[eTetrahedron])
2205  (vertMapMaxR[eTetrahedron][VFTetVert[v]],
2206  faceMapMaxR[eTetrahedron][VFTetFace[v]][i]);
2207 
2208  newmat->SetValue(vertMapMaxR[ePrism][VTFPrismVert[v]],
2209  faceMapMaxR[ePrism][VTFPrismFace[v]][i],val);
2210  }
2211  }
2212 
2213  // Mapping of Tet Edge-Face mappings to Prism Edge-Face Mappings
2214  const int EFTetEdge[] = {0,3,4,0,4,3};
2215  const int EFTetFace[] = {1,1,1,1,1,1};
2216  const int ETFPrismEdge[] = {0,4,5,2,6,7};
2217  const int ETFPrismFace[] = {1,1,1,3,3,3};
2218 
2219  // handle all edge to triangular faces from tetrahedron
2220  // (only 6 this time)
2221  nfacemodes = (nummodesmax-3)*(nummodesmax-2)/2;
2222  for(int e = 0; e < 6; ++e)
2223  {
2224  for(int i = 0; i < nummodesmax-2; ++i)
2225  {
2226  for(int j = 0; j < nfacemodes; ++j)
2227  {
2228  int edgemapid = edgeMapMaxR[eTetrahedron][EFTetEdge[e]][i];
2229  int facemapid = faceMapMaxR[eTetrahedron][EFTetFace[e]][j];
2230  val = (*maxRmat[eTetrahedron])(edgemapid,facemapid);
2231 
2232  newmat->SetValue(edgeMapMaxR[ePrism][ETFPrismEdge[e]][i],
2233  faceMapMaxR[ePrism][ETFPrismFace[e]][j],val);
2234  }
2235  }
2236  }
2237 
2238 
2239  DNekScalMatSharedPtr PrismR
2241  maxRmat[ePrism] = PrismR;
2242  }
2243 
2246  DNekScalMatSharedPtr &maxRmat,
2250  {
2251  NekDouble val;
2252  NekDouble zero = 0.0;
2253 
2254  int nRows = locExp->NumBndryCoeffs();
2256  AllocateSharedPtr(nRows,nRows,zero,eFULL);
2257 
2258  Array<OneD, unsigned int> vlocmap;
2261 
2262  locExp->GetInverseBoundaryMaps(vlocmap,elocmap,flocmap);
2263 
2264  // fill diagonal
2265  for(int i = 0; i < nRows; ++i)
2266  {
2267  val = 1.0;
2268  newmat->SetValue(i,i,val);
2269  }
2270 
2271  int nverts = locExp->GetNverts();
2272  int nedges = locExp->GetNedges();
2273  int nfaces = locExp->GetNtraces();
2274 
2275  // fill vertex to edge coupling
2276  for(int e = 0; e < nedges; ++e)
2277  {
2278  int nEdgeInteriorCoeffs = locExp->GetEdgeNcoeffs(e) -2;
2279 
2280  for(int v = 0; v < nverts; ++v)
2281  {
2282  for(int i = 0; i < nEdgeInteriorCoeffs; ++i)
2283  {
2284  val = (*maxRmat)(vmap[v],emap[e][i]);
2285  newmat->SetValue(vlocmap[v],elocmap[e][i],val);
2286  }
2287  }
2288  }
2289 
2290  for(int f = 0; f < nfaces; ++f)
2291  {
2292  // Get details to extrac this face from max reference matrix
2294  int m0,m1; //Local face expansion orders.
2295 
2296  int nFaceInteriorCoeffs = locExp->GetTraceIntNcoeffs(f);
2297 
2298  locExp->GetTraceNumModes(f,m0,m1,FwdOrient);
2299 
2300  Array<OneD, unsigned int> fmapRmat = maxExp->
2301  GetTraceInverseBoundaryMap(f,FwdOrient, m0,m1);
2302 
2303  // fill in vertex to face coupling
2304  for(int v = 0; v < nverts; ++v)
2305  {
2306  for(int i = 0; i < nFaceInteriorCoeffs; ++i)
2307  {
2308  val = (*maxRmat)(vmap[v],fmapRmat[i]);
2309  newmat->SetValue(vlocmap[v],flocmap[f][i],val);
2310  }
2311  }
2312 
2313  // fill in edges to face coupling
2314  for(int e = 0; e < nedges; ++e)
2315  {
2316  int nEdgeInteriorCoeffs = locExp->GetEdgeNcoeffs(e) -2;
2317 
2318  for(int j = 0; j < nEdgeInteriorCoeffs; ++j)
2319  {
2320 
2321  for(int i = 0; i < nFaceInteriorCoeffs; ++i)
2322  {
2323  val = (*maxRmat)(emap[e][j],fmapRmat[i]);
2324  newmat->SetValue(elocmap[e][j],flocmap[f][i],val);
2325  }
2326  }
2327  }
2328  }
2329 
2330  return newmat;
2331  }
2332  }
2333 }
2334 
#define ASSERTL0(condition, msg)
Definition: ErrorUtil.hpp:216
#define ASSERTL1(condition, msg)
Assert Level 1 – Debugging which is used whether in FULLDEBUG or DEBUG compilation mode....
Definition: ErrorUtil.hpp:250
#define sign(a, b)
return the sign(b)*a
Definition: Polylib.cpp:15
Describes the specification for a Basis.
Definition: Basis.h:50
tKey RegisterCreatorFunction(tKey idKey, CreatorFunction classCreator, std::string pDesc="")
Register a class with the factory.
Definition: NekFactory.hpp:200
Defines a specification for a set of points.
Definition: Points.h:60
SpatialDomains::GeometrySharedPtr GetGeom() const
Definition: Expansion.cpp:172
static std::shared_ptr< DataType > AllocateSharedPtr(const Args &...args)
Allocate a shared pointer from the memory pool.
const StdRegions::ConstFactorMap & GetConstFactors() const
Returns all the constants.
StdRegions::MatrixType GetMatrixType() const
Return the matrix type.
LibUtilities::CommSharedPtr m_comm
const std::weak_ptr< GlobalLinSys > m_linsys
std::weak_ptr< AssemblyMap > m_locToGloMap
SpatialDomains::HexGeomSharedPtr CreateRefHexGeom(void)
Sets up the reference hexahedral element needed to construct a low energy basis.
void ReSetPrismMaxRMat(int nummodesmax, LocalRegions::PrismExpSharedPtr &PirsmExp, ShapeToDNekMap &maxRmat, ShapeToIntArrayMap &vertMapMaxR, ShapeToIntArrayArrayMap &edgeMapMaxR, ShapeToIntArrayArrayMap &faceMapMaxR, bool UseTetOnly)
virtual void v_DoTransformBasisFromLowEnergy(const Array< OneD, NekDouble > &pInput, Array< OneD, NekDouble > &pOutput)
Multiply by the block inverse transformation matrix This transforms the bassi from Low Energy to orig...
virtual void v_DoPreconditioner(const Array< OneD, NekDouble > &pInput, Array< OneD, NekDouble > &pOutput)
virtual void v_DoTransformCoeffsFromLowEnergy(Array< OneD, NekDouble > &pInOut)
transform the solution coeffiicents from low energy back to the original coefficient space.
virtual void v_DoTransformBasisToLowEnergy(Array< OneD, NekDouble > &pInOut)
Transform the basis vector to low energy space.
DNekMatSharedPtr ExtractLocMat(LocalRegions::Expansion3DSharedPtr &locExp, DNekScalMatSharedPtr &maxRmat, LocalRegions::Expansion3DSharedPtr &expMax, Array< OneD, unsigned int > &vertMapMaxR, Array< OneD, Array< OneD, unsigned int > > &edgeMapMaxR)
void ReSetTetMaxRMat(int nummodesmax, LocalRegions::TetExpSharedPtr &TetExp, ShapeToDNekMap &maxRmat, ShapeToIntArrayMap &vertMapMaxR, ShapeToIntArrayArrayMap &edgeMapMaxR, ShapeToIntArrayArrayMap &faceMapMaxR)
SpatialDomains::TetGeomSharedPtr CreateRefTetGeom(void)
Sets up the reference tretrahedral element needed to construct a low energy basis.
std::vector< std::pair< int, int > > m_sameBlock
virtual void v_BuildPreconditioner()
Construct the low energy preconditioner from .
SpatialDomains::PrismGeomSharedPtr CreateRefPrismGeom(void)
Sets up the reference prismatic element needed to construct a low energy basis.
void SetUpReferenceElements(ShapeToDNekMap &maxRmat, ShapeToExpMap &maxElmt, ShapeToIntArrayMap &vertMapMaxR, ShapeToIntArrayArrayMap &edgeMapMaxR)
Loop expansion and determine different variants of the transformation matrix.
void SetUpPyrMaxRMat(int nummodesmax, LocalRegions::PyrExpSharedPtr &PyrExp, ShapeToDNekMap &maxRmat, ShapeToIntArrayMap &vertMapMaxR, ShapeToIntArrayArrayMap &edgeMapMaxR, ShapeToIntArrayArrayMap &faceMapMaxR)
virtual DNekScalMatSharedPtr v_TransformedSchurCompl(int n, int offset, const std::shared_ptr< DNekScalMat > &loc_mat)
Set up the transformed block matrix system.
virtual void v_DoTransformCoeffsToLowEnergy(const Array< OneD, NekDouble > &pInput, Array< OneD, NekDouble > &pOutput)
Multiply by the block tranposed inverse transformation matrix (R^T)^{-1} which is equivlaent to trans...
SpatialDomains::PyrGeomSharedPtr CreateRefPyrGeom(void)
Sets up the reference prismatic element needed to construct a low energy basis mapping arrays.
int GetEdgeNcoeffs(const int i) const
This function returns the number of expansion coefficients belonging to the i-th edge.
int GetNedges() const
return the number of edges in 3D expansion
LibUtilities::ShapeType DetShapeType() const
This function returns the shape of the expansion domain.
Definition: StdExpansion.h:376
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 op(A)[m x k], op(B)[k x n], C[m x n] DGEMM perfo...
Definition: Blas.hpp:394
static void Gather(Nektar::Array< OneD, NekDouble > pU, gs_op pOp, gs_data *pGsh, Nektar::Array< OneD, NekDouble > pBuffer=NullNekDouble1DArray)
Performs a gather-scatter operation of the provided values.
Definition: GsLib.hpp:245
@ gs_add
Definition: GsLib.hpp:53
@ gs_min
Definition: GsLib.hpp:53
static gs_data * Init(const Nektar::Array< OneD, long > pId, const LibUtilities::CommSharedPtr &pComm, bool verbose=true)
Initialise Gather-Scatter map.
Definition: GsLib.hpp:167
@ eGaussRadauMAlpha2Beta0
Gauss Radau pinned at x=-1, .
Definition: PointsType.h:59
@ eGaussLobattoLegendre
1D Gauss-Lobatto-Legendre quadrature points
Definition: PointsType.h:51
@ eGaussRadauMAlpha1Beta0
Gauss Radau pinned at x=-1, .
Definition: PointsType.h:58
@ eModified_B
Principle Modified Functions .
Definition: BasisType.h:49
@ eModified_C
Principle Modified Functions .
Definition: BasisType.h:50
@ eModifiedPyr_C
Principle Modified Functions .
Definition: BasisType.h:52
@ eModified_A
Principle Modified Functions .
Definition: BasisType.h:48
std::shared_ptr< PrismExp > PrismExpSharedPtr
Definition: PrismExp.h:223
std::shared_ptr< Expansion > ExpansionSharedPtr
Definition: Expansion.h:68
std::shared_ptr< HexExp > HexExpSharedPtr
Definition: HexExp.h:56
std::shared_ptr< Expansion2D > Expansion2DSharedPtr
Definition: Expansion1D.h:47
std::shared_ptr< PyrExp > PyrExpSharedPtr
Definition: PyrExp.h:195
std::shared_ptr< Expansion3D > Expansion3DSharedPtr
Definition: Expansion2D.h:49
std::shared_ptr< TetExp > TetExpSharedPtr
Definition: TetExp.h:230
PreconFactory & GetPreconFactory()
std::shared_ptr< AssemblyMap > AssemblyMapSharedPtr
Definition: AssemblyMap.h:52
std::map< int, std::vector< PeriodicEntity > > PeriodicMap
StdRegions::Orientation DeterminePeriodicFaceOrient(StdRegions::Orientation faceOrient, StdRegions::Orientation perFaceOrient)
Determine relative orientation between two faces.
std::shared_ptr< QuadGeom > QuadGeomSharedPtr
Definition: HexGeom.h:46
std::shared_ptr< PrismGeom > PrismGeomSharedPtr
Definition: PrismGeom.h:82
std::shared_ptr< SegGeom > SegGeomSharedPtr
Definition: Geometry2D.h:62
std::shared_ptr< PyrGeom > PyrGeomSharedPtr
Definition: PyrGeom.h:74
std::shared_ptr< TetGeom > TetGeomSharedPtr
Definition: TetGeom.h:82
std::shared_ptr< HexGeom > HexGeomSharedPtr
Definition: HexGeom.h:84
std::shared_ptr< PointGeom > PointGeomSharedPtr
Definition: Geometry.h:59
std::shared_ptr< Geometry2D > Geometry2DSharedPtr
Definition: Geometry.h:65
std::shared_ptr< TriGeom > TriGeomSharedPtr
Definition: TriGeom.h:58
The above copyright notice and this permission notice shall be included.
Definition: CoupledSolver.h:1
std::shared_ptr< DNekScalMat > DNekScalMatSharedPtr
NekMatrix< InnerMatrixType, BlockMatrixTag > Transpose(NekMatrix< InnerMatrixType, BlockMatrixTag > &rhs)
std::shared_ptr< DNekScalBlkMat > DNekScalBlkMatSharedPtr
Definition: NekTypeDefs.hpp:73
std::shared_ptr< DNekMat > DNekMatSharedPtr
Definition: NekTypeDefs.hpp:69
double NekDouble
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:192
void Assmb(int n, const T *x, const int *y, T *z)
Assemble z[y[i]] += x[i]; z should be zero'd first.
Definition: Vmath.cpp:813
scalarT< T > sqrt(scalarT< T > in)
Definition: scalar.hpp:267