Nektar++
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
DisContField3D.cpp
Go to the documentation of this file.
1  //////////////////////////////////////////////////////////////////////////////
2  //
3  // File DisContField3D.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  // License for the specific language governing rights and limitations under
14  // Permission is hereby granted, free of charge, to any person obtaining a
15  // copy of this software and associated documentation files (the "Software"),
16  // to deal in the Software without restriction, including without limitation
17  // the rights to use, copy, modify, merge, publish, distribute, sublicense,
18  // and/or sell copies of the Software, and to permit persons to whom the
19  // Software is furnished to do so, subject to the following conditions:
20  //
21  // The above copyright notice and this permission notice shall be included
22  // in all copies or substantial portions of the Software.
23  //
24  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
25  // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
27  // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
29  // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
30  // DEALINGS IN THE SOFTWARE.
31  //
32  // Description: Field definition for 3D domain with boundary
33  // conditions using LDG flux
34  //
35  ///////////////////////////////////////////////////////////////////////////////
36 
41  #include <LocalRegions/HexExp.h>
42  #include <LocalRegions/TetExp.h>
43  #include <LocalRegions/PrismExp.h>
44 
45  #include <boost/assign/std/vector.hpp>
46 
47  using namespace boost::assign;
48 
49  namespace Nektar
50  {
51  namespace MultiRegions
52  {
53  /**
54  * @class DisContField3D
55  * Abstraction of a global discontinuous three-dimensional spectral/hp
56  * element expansion which approximates the solution of a set of
57  * partial differential equations.
58  */
59 
60  /**
61  * @brief Default constructor.
62  */
63  DisContField3D::DisContField3D() :
64  ExpList3D (),
65  m_bndCondExpansions (),
66  m_bndConditions (),
67  m_trace(NullExpListSharedPtr)
68  {
69  }
70 
71  /**
72  * @brief Constructs a global discontinuous field based on an input mesh
73  * with boundary conditions.
74  */
78  const std::string &variable,
79  const bool SetUpJustDG)
80  : ExpList3D (pSession,graph3D,variable),
81  m_bndCondExpansions(),
82  m_bndConditions (),
83  m_trace(NullExpListSharedPtr)
84  {
85  if(variable.compare("DefaultVar") != 0) // do not set up BCs if default variable
86  {
88 
89  GenerateBoundaryConditionExpansion(graph3D,bcs,variable);
90  EvaluateBoundaryConditions(0.0, variable);
91 
92  // Find periodic edges for this variable.
93  FindPeriodicFaces(bcs, variable);
94  }
95 
96  if(SetUpJustDG)
97  {
98  SetUpDG();
99  }
100  else
101  {
102  // Set element edges to point to Robin BC edges if required.
103  int i,cnt,f;
104  Array<OneD, int> ElmtID, FaceID;
105  GetBoundaryToElmtMap(ElmtID, FaceID);
106 
107  for(cnt = i = 0; i < m_bndCondExpansions.num_elements(); ++i)
108  {
110  locExpList = m_bndCondExpansions[i];
111 
112  for(f = 0; f < locExpList->GetExpSize(); ++f)
113  {
115  = (*m_exp)[ElmtID[cnt+f]]->
116  as<LocalRegions::Expansion3D>();
118  = locExpList->GetExp(f)->
119  as<LocalRegions::Expansion2D>();
120 
121  exp3d->SetFaceExp(FaceID[cnt+f],exp2d);
122  exp2d->SetAdjacentElementExp(FaceID[cnt+f],exp3d);
123  }
124  cnt += m_bndCondExpansions[i]->GetExpSize();
125  }
126  }
127  }
128 
129  /*
130  * @brief Copy type constructor which declares new boundary conditions
131  * and re-uses mapping info and trace space if possible
132  */
134  const DisContField3D &In,
135  const SpatialDomains::MeshGraphSharedPtr &graph3D,
136  const std::string &variable,
137  const bool SetUpJustDG)
138  : ExpList3D(In),
139  m_trace(NullExpListSharedPtr)
140  {
142 
143  GenerateBoundaryConditionExpansion(graph3D,bcs,variable);
144  EvaluateBoundaryConditions(0.0, variable);
145  ApplyGeomInfo();
146 
148  {
149  // Find periodic edges for this variable.
150  FindPeriodicFaces(bcs, variable);
151 
152  if (SetUpJustDG)
153  {
154  SetUpDG(variable);
155  }
156  else
157  {
158  int i,cnt,f;
159  Array<OneD, int> ElmtID,FaceID;
160  GetBoundaryToElmtMap(ElmtID,FaceID);
161 
162  for(cnt = i = 0; i < m_bndCondExpansions.num_elements(); ++i)
163  {
165  locExpList = m_bndCondExpansions[i];
166 
167  for(f = 0; f < locExpList->GetExpSize(); ++f)
168  {
170  = (*m_exp)[ElmtID[cnt+f]]->
171  as<LocalRegions::Expansion3D>();
173  = locExpList->GetExp(f)->
174  as<LocalRegions::Expansion2D>();
175 
176  exp3d->SetFaceExp(FaceID[cnt+f],exp2d);
177  exp2d->SetAdjacentElementExp(FaceID[cnt+f],exp3d);
178  }
179 
180  cnt += m_bndCondExpansions[i]->GetExpSize();
181  }
183  }
184 
185  }
186  //else if we have the same boundary condition
187  else
188  {
189  if(SetUpJustDG)
190  {
192  m_trace = In.m_trace;
193  m_traceMap = In.m_traceMap;
197  }
198  else
199  {
201  m_trace = In.m_trace;
202  m_traceMap = In.m_traceMap;
206 
207  int i,cnt,f;
208  Array<OneD, int> ElmtID,FaceID;
209  GetBoundaryToElmtMap(ElmtID,FaceID);
210 
211  for(cnt = i = 0; i < m_bndCondExpansions.num_elements(); ++i)
212  {
214  locExpList = m_bndCondExpansions[i];
215 
216  for(f = 0; f < locExpList->GetExpSize(); ++f)
217  {
219  = (*m_exp)[ElmtID[cnt+f]]->
220  as<LocalRegions::Expansion3D>();
222  = locExpList->GetExp(f)->
223  as<LocalRegions::Expansion2D>();
224 
225  exp3d->SetFaceExp(FaceID[cnt+f],exp2d);
226  exp2d->SetAdjacentElementExp(FaceID[cnt+f],exp3d);
227  }
228 
229  cnt += m_bndCondExpansions[i]->GetExpSize();
230  }
231 
232  if(m_session->DefinesSolverInfo("PROJECTION"))
233  {
234  std::string ProjectStr =
235  m_session->GetSolverInfo("PROJECTION");
236  if (ProjectStr == "MixedCGDG" ||
237  ProjectStr == "Mixed_CG_Discontinuous")
238  {
239  SetUpDG(variable);
240  }
241  else
242  {
244  }
245  }
246  else
247  {
249  }
250  }
251  }
252  }
253 
254  /**
255  *
256  */
258  ExpList3D(In),
259  m_bndCondExpansions (In.m_bndCondExpansions),
260  m_bndConditions (In.m_bndConditions),
261  m_globalBndMat (In.m_globalBndMat),
262  m_trace (In.m_trace),
263  m_traceMap (In.m_traceMap),
264  m_periodicVerts (In.m_periodicVerts),
265  m_periodicEdges (In.m_periodicEdges),
266  m_periodicFaces (In.m_periodicFaces)
267  {
268  }
269 
270  /**
271  * @brief Destructor.
272  */
274  {
275  }
276 
278  const GlobalLinSysKey &mkey)
279  {
281  "Routine currently only tested for HybridDGHelmholtz");
282  ASSERTL1(mkey.GetGlobalSysSolnType() ==
283  m_traceMap->GetGlobalSysSolnType(),
284  "The local to global map is not set up for the requested "
285  "solution type");
286 
287  GlobalLinSysSharedPtr glo_matrix;
288  GlobalLinSysMap::iterator matrixIter = m_globalBndMat->find(mkey);
289 
290  if (matrixIter == m_globalBndMat->end())
291  {
292  glo_matrix = GenGlobalBndLinSys(mkey,m_traceMap);
293  (*m_globalBndMat)[mkey] = glo_matrix;
294  }
295  else
296  {
297  glo_matrix = matrixIter->second;
298  }
299 
300  return glo_matrix;
301  }
302 
303  /**
304  * @brief Set up all DG member variables and maps.
305  */
306  void DisContField3D::SetUpDG(const std::string variable)
307  {
309  {
310  return;
311  }
312 
313  ExpList2DSharedPtr trace;
314 
316  boost::dynamic_pointer_cast<SpatialDomains::MeshGraph3D>(
317  m_graph);
318 
319  // Set up matrix map
322 
323  // Set up Trace space
324  bool UseGenSegExp = true;
327  *m_exp,graph3D, m_periodicFaces, UseGenSegExp);
328 
329  m_trace = trace;
331  m_session,graph3D,trace,*this,m_bndCondExpansions,
332  m_bndConditions, m_periodicFaces,variable);
333 
334  Array<OneD, Array<OneD, StdRegions::StdExpansionSharedPtr> >
335  &elmtToTrace = m_traceMap->GetElmtToTrace();
336 
337  // Scatter trace segments to 3D elements. For each element, we find
338  // the trace segment associated to each edge. The element then
339  // retains a pointer to the trace space segments, to ensure
340  // uniqueness of normals when retrieving from two adjoining elements
341  // which do not lie in a plane.
342  for (int i = 0; i < m_exp->size(); ++i)
343  {
344  for (int j = 0; j < (*m_exp)[i]->GetNfaces(); ++j)
345  {
347  (*m_exp)[i]->as<LocalRegions::Expansion3D>();
349  elmtToTrace[i][j]->as<LocalRegions::Expansion2D>();
350  exp3d->SetFaceExp (j, exp2d);
351  exp2d->SetAdjacentElementExp(j, exp3d);
352  }
353  }
354 
355  // Set up physical normals
357 
358  // Set up information for parallel jobs.
359  for (int i = 0; i < m_trace->GetExpSize(); ++i)
360  {
362  m_trace->GetExp(i)->as<LocalRegions::Expansion2D>();
363 
364  int offset = m_trace->GetPhys_Offset(i);
365  int traceGeomId = traceEl->GetGeom2D()->GetGlobalID();
367  traceGeomId);
368 
369  if (pIt != m_periodicFaces.end() && !pIt->second[0].isLocal)
370  {
371  if (traceGeomId != min(pIt->second[0].id, traceGeomId))
372  {
373  traceEl->GetLeftAdjacentElementExp()->NegateFaceNormal(
374  traceEl->GetLeftAdjacentElementFace());
375  }
376  }
377  else if (m_traceMap->GetTraceToUniversalMapUnique(offset) < 0)
378  {
379  traceEl->GetLeftAdjacentElementExp()->NegateFaceNormal(
380  traceEl->GetLeftAdjacentElementFace());
381  }
382  }
383 
384  int cnt, n, e;
385 
386  // Identify boundary faces
387  for(cnt = 0, n = 0; n < m_bndCondExpansions.num_elements(); ++n)
388  {
389  if (m_bndConditions[n]->GetBoundaryConditionType() !=
391  {
392  for(e = 0; e < m_bndCondExpansions[n]->GetExpSize(); ++e)
393  {
394  m_boundaryFaces.insert(
395  m_traceMap->GetBndCondTraceToGlobalTraceMap(cnt+e));
396  }
397  }
398  cnt += m_bndCondExpansions[n]->GetExpSize();
399  }
400 
401  // Set up information for periodic boundary conditions.
402  boost::unordered_map<int,pair<int,int> > perFaceToExpMap;
403  boost::unordered_map<int,pair<int,int> >::iterator it2;
404  cnt = 0;
406  for (int n = 0; n < m_exp->size(); ++n)
407  {
408  exp3d = (*m_exp)[n]->as<LocalRegions::Expansion3D>();
409  for (int e = 0; e < exp3d->GetNfaces(); ++e, ++cnt)
410  {
412  exp3d->GetGeom3D()->GetFid(e));
413 
414  if (it != m_periodicFaces.end())
415  {
416  perFaceToExpMap[it->first] = make_pair(n, e);
417  }
418  }
419  }
420 
421  // Set up left-adjacent face list.
422  m_leftAdjacentFaces.resize(cnt);
423  cnt = 0;
424  for (int i = 0; i < m_exp->size(); ++i)
425  {
426  for (int j = 0; j < (*m_exp)[i]->GetNfaces(); ++j, ++cnt)
427  {
429  }
430  }
431 
432  // Set up mapping to copy Fwd of periodic bcs to Bwd of other edge.
433  cnt = 0;
434  for (int n = 0; n < m_exp->size(); ++n)
435  {
436  exp3d = (*m_exp)[n]->as<LocalRegions::Expansion3D>();
437  for (int e = 0; e < exp3d->GetNfaces(); ++e, ++cnt)
438  {
439  int faceGeomId = exp3d->GetGeom3D()->GetFid(e);
440  int offset = m_trace->GetPhys_Offset(
441  elmtToTrace[n][e]->GetElmtId());
442 
443  // Check to see if this face is periodic.
444  PeriodicMap::iterator it = m_periodicFaces.find(faceGeomId);
445 
446  if (it != m_periodicFaces.end())
447  {
448  const PeriodicEntity &ent = it->second[0];
449  it2 = perFaceToExpMap.find(ent.id);
450 
451  if (it2 == perFaceToExpMap.end())
452  {
453  if (m_session->GetComm()->GetSize() > 1 &&
454  !ent.isLocal)
455  {
456  continue;
457  }
458  else
459  {
460  ASSERTL1(false, "Periodic edge not found!");
461  }
462  }
463 
465  "Periodic face in non-forward space?");
466 
467  int offset2 = m_trace->GetPhys_Offset(
468  elmtToTrace[it2->second.first][it2->second.second]->
469  GetElmtId());
470 
471  // Calculate relative orientations between faces to
472  // calculate copying map.
473  int nquad1 = elmtToTrace[n][e]->GetNumPoints(0);
474  int nquad2 = elmtToTrace[n][e]->GetNumPoints(1);
475 
476  vector<int> tmpBwd(nquad1*nquad2);
477  vector<int> tmpFwd(nquad1*nquad2);
478 
483  {
484  for (int i = 0; i < nquad2; ++i)
485  {
486  for (int j = 0; j < nquad1; ++j)
487  {
488  tmpBwd[i*nquad1+j] = offset2 + i*nquad1+j;
489  tmpFwd[i*nquad1+j] = offset + j*nquad2+i;
490  }
491  }
492  }
493  else
494  {
495  for (int i = 0; i < nquad2; ++i)
496  {
497  for (int j = 0; j < nquad1; ++j)
498  {
499  tmpBwd[i*nquad1+j] = offset2 + i*nquad1+j;
500  tmpFwd[i*nquad1+j] = offset + i*nquad1+j;
501  }
502  }
503  }
504 
509  {
510  // Reverse x direction
511  for (int i = 0; i < nquad2; ++i)
512  {
513  for (int j = 0; j < nquad1/2; ++j)
514  {
515  swap(tmpFwd[i*nquad1 + j],
516  tmpFwd[i*nquad1 + nquad1-j-1]);
517  }
518  }
519  }
520 
525  {
526  // Reverse y direction
527  for (int j = 0; j < nquad1; ++j)
528  {
529  for (int i = 0; i < nquad2/2; ++i)
530  {
531  swap(tmpFwd[i*nquad1 + j],
532  tmpFwd[(nquad2-i-1)*nquad1 + j]);
533  }
534  }
535  }
536 
537  for (int i = 0; i < nquad1*nquad2; ++i)
538  {
539  m_periodicFwdCopy.push_back(tmpFwd[i]);
540  m_periodicBwdCopy.push_back(tmpBwd[i]);
541  }
542  }
543  }
544  }
545  }
546 
547  /**
548  * For each boundary region, checks that the types and number of
549  * boundary expansions in that region match.
550  *
551  * @param In ContField3D to compare with.
552  * @return true if boundary conditions match.
553  */
555  const DisContField3D &In)
556  {
557  int i;
558  bool returnval = true;
559 
560  for(i = 0; i < m_bndConditions.num_elements(); ++i)
561  {
562 
563  // check to see if boundary condition type is the same
564  // and there are the same number of boundary
565  // conditions in the boundary definition.
566  if((m_bndConditions[i]->GetBoundaryConditionType()
567  != In.m_bndConditions[i]->GetBoundaryConditionType())||
569  != In.m_bndCondExpansions[i]->GetExpSize()))
570  {
571  returnval = false;
572  break;
573  }
574  }
575 
576  // Compare with all other processes. Return true only if all
577  // processes report having the same boundary conditions.
578  int vSame = returnval ? 1 : 0;
579  m_comm->AllReduce(vSame, LibUtilities::ReduceMin);
580 
581  return (vSame == 1);
582  }
583 
584  /**
585  * According to their boundary region, the separate segmental boundary
586  * expansions are bundled together in an object of the class
587  * MultiRegions#ExpList2D.
588  *
589  * \param graph3D A mesh, containing information about the domain and
590  * the spectral/hp element expansion.
591  * \param bcs An entity containing information about the boundaries and
592  * boundary conditions.
593  * \param variable An optional parameter to indicate for which variable
594  * the boundary conditions should be discretised.
595  */
597  const SpatialDomains::MeshGraphSharedPtr &graph3D,
599  const std::string &variable)
600  {
601  int cnt = 0;
604 
606  bcs.GetBoundaryRegions();
607  const SpatialDomains::BoundaryConditionCollection &bconditions =
608  bcs.GetBoundaryConditions();
609  SpatialDomains::BoundaryRegionCollection::const_iterator it;
610 
611  // count the number of non-periodic boundary regions
612  for (it = bregions.begin(); it != bregions.end(); ++it)
613  {
614  SpatialDomains::BoundaryConditionShPtr boundaryCondition =
615  GetBoundaryCondition(bconditions, it->first, variable);
616  if (boundaryCondition->GetBoundaryConditionType() !=
618  {
619  cnt++;
620  }
621  }
622 
623  m_bndCondExpansions = Array<OneD,MultiRegions::ExpListSharedPtr>(cnt);
624  m_bndConditions = Array<OneD,SpatialDomains::BoundaryConditionShPtr>(cnt);
625 
626  cnt = 0;
627 
628  // list Dirichlet boundaries first
629  for (it = bregions.begin(); it != bregions.end(); ++it)
630  {
631  locBCond = GetBoundaryCondition(
632  bconditions, it->first, variable);
633 
634  if(locBCond->GetBoundaryConditionType()
636  {
638  ::AllocateSharedPtr(m_session, *(it->second),
639  graph3D, variable);
640 
641  // Set up normals on non-Dirichlet boundary conditions
642  if(locBCond->GetBoundaryConditionType() !=
644  {
646  }
647 
648  m_bndCondExpansions[cnt] = locExpList;
649  m_bndConditions[cnt++] = locBCond;
650  }
651  }
652  }
653 
654  /**
655  * @brief Determine the periodic faces, edges and vertices for the given
656  * graph.
657  *
658  * @param bcs Information about the boundary conditions.
659  * @param variable Specifies the field.
660  */
663  const std::string &variable)
664  {
666  = bcs.GetBoundaryRegions();
668  = bcs.GetBoundaryConditions();
670  = boost::dynamic_pointer_cast<
672  SpatialDomains::BoundaryRegionCollection::const_iterator it;
673 
675  m_session->GetComm()->GetRowComm();
677  m_session->GetCompositeOrdering();
678  LibUtilities::BndRegionOrdering bndRegOrder =
679  m_session->GetBndRegionOrdering();
681  m_graph->GetComposites();
682 
683  // perComps: Stores a unique collection of pairs of periodic
684  // composites (i.e. if composites 1 and 2 are periodic then this map
685  // will contain either the pair (1,2) or (2,1) but not both).
686  //
687  // The four maps allVerts, allCoord, allEdges and allOrient map a
688  // periodic face to a vector containing the vertex ids of the face;
689  // their coordinates; the edge ids of the face; and their
690  // orientation within that face respectively.
691  //
692  // Finally the three sets locVerts, locEdges and locFaces store any
693  // vertices, edges and faces that belong to a periodic composite and
694  // lie on this process.
695  map<int,int> perComps;
696  map<int,vector<int> > allVerts;
697  map<int,SpatialDomains::PointGeomVector> allCoord;
698  map<int,vector<int> > allEdges;
699  map<int,vector<StdRegions::Orientation> > allOrient;
700  set<int> locVerts;
701  set<int> locEdges;
702  set<int> locFaces;
703 
704  int region1ID, region2ID, i, j, k, cnt;
706 
707  // Set up a set of all local verts and edges.
708  for(i = 0; i < (*m_exp).size(); ++i)
709  {
710  for(j = 0; j < (*m_exp)[i]->GetNverts(); ++j)
711  {
712  int id = (*m_exp)[i]->GetGeom()->GetVid(j);
713  locVerts.insert(id);
714  }
715 
716  for(j = 0; j < (*m_exp)[i]->GetNedges(); ++j)
717  {
718  int id = (*m_exp)[i]->GetGeom()->GetEid(j);
719  locEdges.insert(id);
720  }
721  }
722 
723  // Begin by populating the perComps map. We loop over all periodic
724  // boundary conditions and determine the composite associated with
725  // it, then fill out the all* maps.
726  for (it = bregions.begin(); it != bregions.end(); ++it)
727  {
728  locBCond = GetBoundaryCondition(
729  bconditions, it->first, variable);
730 
731  if (locBCond->GetBoundaryConditionType()
733  {
734  continue;
735  }
736 
737  // Identify periodic boundary region IDs.
738  region1ID = it->first;
739  region2ID = boost::static_pointer_cast<
741  locBCond)->m_connectedBoundaryRegion;
742 
743  // Check the region only contains a single composite.
744  ASSERTL0(it->second->size() == 1,
745  "Boundary region "+boost::lexical_cast<string>(
746  region1ID)+" should only contain 1 composite.");
747 
748  // From this identify composites by looking at the original
749  // boundary region ordering. Note that in serial the mesh
750  // partitioner is not run, so this map will be empty and
751  // therefore needs to be populated by using the corresponding
752  // boundary region.
753  int cId1, cId2;
754  if (vComm->GetSize() == 1)
755  {
756  cId1 = it->second->begin()->first;
757  cId2 = bregions.find(region2ID)->second->begin()->first;
758  }
759  else
760  {
761  cId1 = bndRegOrder.find(region1ID)->second[0];
762  cId2 = bndRegOrder.find(region2ID)->second[0];
763  }
764 
765  SpatialDomains::Composite c = it->second->begin()->second;
766  vector<unsigned int> tmpOrder;
767 
768  // From the composite, we now construct the allVerts, allEdges
769  // and allCoord map so that they can be transferred across
770  // processors. We also populate the locFaces set to store a
771  // record of all faces local to this process.
772  for (i = 0; i < c->size(); ++i)
773  {
775  boost::dynamic_pointer_cast<
776  SpatialDomains::Geometry2D>((*c)[i]);
777  ASSERTL1(faceGeom, "Unable to cast to shared ptr");
778 
779  // Get geometry ID of this face and store in locFaces.
780  int faceId = (*c)[i]->GetGlobalID();
781  locFaces.insert(faceId);
782 
783  // In serial, mesh partitioning will not have occurred so
784  // need to fill composite ordering map manually.
785  if (vComm->GetSize() == 1)
786  {
787  tmpOrder.push_back((*c)[i]->GetGlobalID());
788  }
789 
790  // Loop over vertices and edges of the face to populate
791  // allVerts, allEdges and allCoord maps.
792  vector<int> vertList, edgeList;
794  vector<StdRegions::Orientation> orientVec;
795  for (j = 0; j < faceGeom->GetNumVerts(); ++j)
796  {
797  vertList .push_back(faceGeom->GetVid (j));
798  edgeList .push_back(faceGeom->GetEid (j));
799  coordVec .push_back(faceGeom->GetVertex(j));
800  orientVec.push_back(faceGeom->GetEorient(j));
801  }
802 
803  allVerts [faceId] = vertList;
804  allEdges [faceId] = edgeList;
805  allCoord [faceId] = coordVec;
806  allOrient[faceId] = orientVec;
807  }
808 
809  // In serial, record the composite ordering in compOrder for
810  // later in the routine.
811  if (vComm->GetSize() == 1)
812  {
813  compOrder[it->second->begin()->first] = tmpOrder;
814  }
815 
816  // See if we already have either region1 or region2 stored in
817  // perComps map already and do a sanity check to ensure regions
818  // are mutually periodic.
819  if (perComps.count(cId1) == 0)
820  {
821  if (perComps.count(cId2) == 0)
822  {
823  perComps[cId1] = cId2;
824  }
825  else
826  {
827  std::stringstream ss;
828  ss << "Boundary region " << cId2 << " should be "
829  << "periodic with " << perComps[cId2] << " but "
830  << "found " << cId1 << " instead!";
831  ASSERTL0(perComps[cId2] == cId1, ss.str());
832  }
833  }
834  else
835  {
836  std::stringstream ss;
837  ss << "Boundary region " << cId1 << " should be "
838  << "periodic with " << perComps[cId1] << " but "
839  << "found " << cId2 << " instead!";
840  ASSERTL0(perComps[cId1] == cId1, ss.str());
841  }
842  }
843 
844  // The next routines process local face lists to exchange vertices,
845  // edges and faces.
846  int n = vComm->GetSize();
847  int p = vComm->GetRank();
848  int totFaces;
849  Array<OneD, int> facecounts(n,0);
850  Array<OneD, int> vertcounts(n,0);
851  Array<OneD, int> faceoffset(n,0);
852  Array<OneD, int> vertoffset(n,0);
853 
854  // First exchange the number of faces on each process.
855  facecounts[p] = locFaces.size();
856  vComm->AllReduce(facecounts, LibUtilities::ReduceSum);
857 
858  // Set up an offset map to allow us to distribute face IDs to all
859  // processors.
860  faceoffset[0] = 0;
861  for (i = 1; i < n; ++i)
862  {
863  faceoffset[i] = faceoffset[i-1] + facecounts[i-1];
864  }
865 
866  // Calculate total number of faces.
867  totFaces = Vmath::Vsum(n, facecounts, 1);
868 
869  // faceIds holds face IDs for each periodic face. faceVerts holds
870  // the number of vertices in this face.
871  Array<OneD, int> faceIds (totFaces, 0);
872  Array<OneD, int> faceVerts(totFaces, 0);
873 
874  // Process p writes IDs of its faces into position faceoffset[p] of
875  // faceIds which allows us to perform an AllReduce to distribute
876  // information amongst processors.
877  set<int>::iterator sIt;
878  for (i = 0, sIt = locFaces.begin(); sIt != locFaces.end(); ++sIt)
879  {
880  faceIds [faceoffset[p] + i ] = *sIt;
881  faceVerts[faceoffset[p] + i++] = allVerts[*sIt].size();
882  }
883 
884  vComm->AllReduce(faceIds, LibUtilities::ReduceSum);
885  vComm->AllReduce(faceVerts, LibUtilities::ReduceSum);
886 
887  // procVerts holds number of vertices (and also edges since each
888  // face is 2D) on each process.
889  Array<OneD, int> procVerts(n,0);
890  int nTotVerts;
891 
892  // Note if there are no periodic faces at all calling Vsum will
893  // cause a segfault.
894  if (totFaces > 0)
895  {
896  // Calculate number of vertices on each processor.
897  nTotVerts = Vmath::Vsum(totFaces, faceVerts, 1);
898  }
899  else
900  {
901  nTotVerts = 0;
902  }
903 
904  for (i = 0; i < n; ++i)
905  {
906  if (facecounts[i] > 0)
907  {
908  procVerts[i] = Vmath::Vsum(
909  facecounts[i], faceVerts + faceoffset[i], 1);
910  }
911  else
912  {
913  procVerts[i] = 0;
914  }
915  }
916 
917  // vertoffset is defined in the same manner as edgeoffset
918  // beforehand.
919  vertoffset[0] = 0;
920  for (i = 1; i < n; ++i)
921  {
922  vertoffset[i] = vertoffset[i-1] + procVerts[i-1];
923  }
924 
925  // At this point we exchange all vertex IDs, edge IDs and vertex
926  // coordinates for each face. The coordinates are necessary because
927  // we need to calculate relative face orientations between periodic
928  // faces to determined edge and vertex connectivity.
929  Array<OneD, int> vertIds(nTotVerts, 0);
930  Array<OneD, int> edgeIds(nTotVerts, 0);
931  Array<OneD, int> edgeOrt(nTotVerts, 0);
932  Array<OneD, NekDouble> vertX (nTotVerts, 0.0);
933  Array<OneD, NekDouble> vertY (nTotVerts, 0.0);
934  Array<OneD, NekDouble> vertZ (nTotVerts, 0.0);
935 
936  for (cnt = 0, sIt = locFaces.begin();
937  sIt != locFaces.end(); ++sIt)
938  {
939  for (j = 0; j < allVerts[*sIt].size(); ++j)
940  {
941  int vertId = allVerts[*sIt][j];
942  vertIds[vertoffset[p] + cnt ] = vertId;
943  vertX [vertoffset[p] + cnt ] = (*allCoord[*sIt][j])(0);
944  vertY [vertoffset[p] + cnt ] = (*allCoord[*sIt][j])(1);
945  vertZ [vertoffset[p] + cnt ] = (*allCoord[*sIt][j])(2);
946  edgeIds[vertoffset[p] + cnt ] = allEdges [*sIt][j];
947  edgeOrt[vertoffset[p] + cnt++] = allOrient[*sIt][j];
948  }
949  }
950 
951  vComm->AllReduce(vertIds, LibUtilities::ReduceSum);
952  vComm->AllReduce(vertX, LibUtilities::ReduceSum);
953  vComm->AllReduce(vertY, LibUtilities::ReduceSum);
954  vComm->AllReduce(vertZ, LibUtilities::ReduceSum);
955  vComm->AllReduce(edgeIds, LibUtilities::ReduceSum);
956  vComm->AllReduce(edgeOrt, LibUtilities::ReduceSum);
957 
958  // Finally now we have all of this information, we construct maps
959  // which make accessing the information easier. These are
960  // conceptually the same as all* maps at the beginning of the
961  // routine, but now hold information for all periodic vertices.
962  map<int, vector<int> > vertMap;
963  map<int, vector<int> > edgeMap;
964  map<int, SpatialDomains::PointGeomVector> coordMap;
965 
966  // These final two maps are required for determining the relative
967  // orientation of periodic edges. vCoMap associates vertex IDs with
968  // their coordinates, and eIdMap maps an edge ID to the two vertices
969  // which construct it.
970  map<int, SpatialDomains::PointGeomSharedPtr> vCoMap;
971  map<int, pair<int, int> > eIdMap;
972 
973  for (cnt = i = 0; i < totFaces; ++i)
974  {
975  vector<int> edges(faceVerts[i]);
976  vector<int> verts(faceVerts[i]);
977  SpatialDomains::PointGeomVector coord(faceVerts[i]);
978 
979  // Keep track of cnt to enable correct edge vertices to be
980  // inserted into eIdMap.
981  int tmp = cnt;
982  for (j = 0; j < faceVerts[i]; ++j, ++cnt)
983  {
984  edges[j] = edgeIds[cnt];
985  verts[j] = vertIds[cnt];
988  3, verts[j], vertX[cnt], vertY[cnt], vertZ[cnt]);
989  vCoMap[vertIds[cnt]] = coord[j];
990 
991  // Try to insert edge into the eIdMap to avoid re-inserting.
992  pair<map<int, pair<int, int> >::iterator, bool> testIns =
993  eIdMap.insert(make_pair(
994  edgeIds[cnt],
995  make_pair(vertIds[tmp+j],
996  vertIds[tmp+((j+1) % faceVerts[i])])));
997 
998  if (testIns.second == false)
999  {
1000  continue;
1001  }
1002 
1003  // If the edge is reversed with respect to the face, then
1004  // swap the edges so that we have the original ordering of
1005  // the edge in the 3D element. This is necessary to properly
1006  // determine edge orientation.
1007  if ((StdRegions::Orientation)edgeOrt[cnt]
1009  {
1010  swap(testIns.first->second.first,
1011  testIns.first->second.second);
1012  }
1013  }
1014 
1015  vertMap [faceIds[i]] = verts;
1016  edgeMap [faceIds[i]] = edges;
1017  coordMap[faceIds[i]] = coord;
1018  }
1019 
1020  // Go through list of composites and figure out which edges are
1021  // parallel from original ordering in session file. This includes
1022  // composites which are not necessarily on this process.
1023  map<int,int>::iterator cIt, pIt;
1024  map<int,int>::const_iterator oIt;
1025 
1026  // Store temporary map of periodic vertices which will hold all
1027  // periodic vertices on the entire mesh so that doubly periodic
1028  // vertices/edges can be counted properly across partitions. Local
1029  // vertices/edges are copied into m_periodicVerts and
1030  // m_periodicEdges at the end of the function.
1031  PeriodicMap periodicVerts;
1032  PeriodicMap periodicEdges;
1033 
1034  // Construct two maps which determine how vertices and edges of
1035  // faces connect given a specific face orientation. The key of the
1036  // map is the number of vertices in the face, used to determine
1037  // difference between tris and quads.
1038  map<int, map<StdRegions::Orientation, vector<int> > > vmap;
1039  map<int, map<StdRegions::Orientation, vector<int> > > emap;
1040 
1041  map<StdRegions::Orientation, vector<int> > quadVertMap;
1042  quadVertMap[StdRegions::eDir1FwdDir1_Dir2FwdDir2] += 0,1,2,3;
1043  quadVertMap[StdRegions::eDir1FwdDir1_Dir2BwdDir2] += 3,2,1,0;
1044  quadVertMap[StdRegions::eDir1BwdDir1_Dir2FwdDir2] += 1,0,3,2;
1045  quadVertMap[StdRegions::eDir1BwdDir1_Dir2BwdDir2] += 2,3,0,1;
1046  quadVertMap[StdRegions::eDir1FwdDir2_Dir2FwdDir1] += 0,3,2,1;
1047  quadVertMap[StdRegions::eDir1FwdDir2_Dir2BwdDir1] += 1,2,3,0;
1048  quadVertMap[StdRegions::eDir1BwdDir2_Dir2FwdDir1] += 3,0,1,2;
1049  quadVertMap[StdRegions::eDir1BwdDir2_Dir2BwdDir1] += 2,1,0,3;
1050 
1051  map<StdRegions::Orientation, vector<int> > quadEdgeMap;
1052  quadEdgeMap[StdRegions::eDir1FwdDir1_Dir2FwdDir2] += 0,1,2,3;
1053  quadEdgeMap[StdRegions::eDir1FwdDir1_Dir2BwdDir2] += 2,1,0,3;
1054  quadEdgeMap[StdRegions::eDir1BwdDir1_Dir2FwdDir2] += 0,3,2,1;
1055  quadEdgeMap[StdRegions::eDir1BwdDir1_Dir2BwdDir2] += 2,3,0,1;
1056  quadEdgeMap[StdRegions::eDir1FwdDir2_Dir2FwdDir1] += 3,2,1,0;
1057  quadEdgeMap[StdRegions::eDir1FwdDir2_Dir2BwdDir1] += 1,2,3,0;
1058  quadEdgeMap[StdRegions::eDir1BwdDir2_Dir2FwdDir1] += 3,0,1,2;
1059  quadEdgeMap[StdRegions::eDir1BwdDir2_Dir2BwdDir1] += 1,0,3,2;
1060 
1061  map<StdRegions::Orientation, vector<int> > triVertMap;
1062  triVertMap[StdRegions::eDir1FwdDir1_Dir2FwdDir2] += 0,1,2;
1063  triVertMap[StdRegions::eDir1BwdDir1_Dir2FwdDir2] += 1,0,2;
1064 
1065  map<StdRegions::Orientation, vector<int> > triEdgeMap;
1066  triEdgeMap[StdRegions::eDir1FwdDir1_Dir2FwdDir2] += 0,1,2;
1067  triEdgeMap[StdRegions::eDir1BwdDir1_Dir2FwdDir2] += 0,2,1;
1068 
1069  vmap[3] = triVertMap;
1070  vmap[4] = quadVertMap;
1071  emap[3] = triEdgeMap;
1072  emap[4] = quadEdgeMap;
1073 
1074  map<int,int> allCompPairs;
1075 
1076  // Finally we have enough information to populate the periodic
1077  // vertex, edge and face maps. Begin by looping over all pairs of
1078  // periodic composites to determine pairs of periodic faces.
1079  for (cIt = perComps.begin(); cIt != perComps.end(); ++cIt)
1080  {
1082  const int id1 = cIt->first;
1083  const int id2 = cIt->second;
1084  std::string id1s = boost::lexical_cast<string>(id1);
1085  std::string id2s = boost::lexical_cast<string>(id2);
1086 
1087  if (compMap.count(id1) > 0)
1088  {
1089  c[0] = compMap[id1];
1090  }
1091 
1092  if (compMap.count(id2) > 0)
1093  {
1094  c[1] = compMap[id2];
1095  }
1096 
1097  ASSERTL0(c[0] || c[1],
1098  "Neither composite not found on this process!");
1099 
1100  // Loop over composite ordering to construct list of all
1101  // periodic faces, regardless of whether they are on this
1102  // process.
1103  map<int,int> compPairs;
1104 
1105  ASSERTL0(compOrder.count(id1) > 0,
1106  "Unable to find composite "+id1s+" in order map.");
1107  ASSERTL0(compOrder.count(id2) > 0,
1108  "Unable to find composite "+id2s+" in order map.");
1109  ASSERTL0(compOrder[id1].size() == compOrder[id2].size(),
1110  "Periodic composites "+id1s+" and "+id2s+
1111  " should have the same number of elements.");
1112  ASSERTL0(compOrder[id1].size() > 0,
1113  "Periodic composites "+id1s+" and "+id2s+
1114  " are empty!");
1115 
1116  // Look up composite ordering to determine pairs.
1117  for (i = 0; i < compOrder[id1].size(); ++i)
1118  {
1119  int eId1 = compOrder[id1][i];
1120  int eId2 = compOrder[id2][i];
1121 
1122  ASSERTL0(compPairs.count(eId1) == 0,
1123  "Already paired.");
1124 
1125  // Sanity check that the faces are mutually periodic.
1126  if (compPairs.count(eId2) != 0)
1127  {
1128  ASSERTL0(compPairs[eId2] == eId1, "Pairing incorrect");
1129  }
1130  compPairs[eId1] = eId2;
1131  }
1132 
1133  // Now that we have all pairs of periodic faces, loop over the
1134  // ones local to this process and populate face/edge/vertex
1135  // maps.
1136  for (pIt = compPairs.begin(); pIt != compPairs.end(); ++pIt)
1137  {
1138  int ids [2] = {pIt->first, pIt->second};
1139  bool local[2] = {locFaces.count(pIt->first) > 0,
1140  locFaces.count(pIt->second) > 0};
1141 
1142  ASSERTL0(coordMap.count(ids[0]) > 0 &&
1143  coordMap.count(ids[1]) > 0,
1144  "Unable to find face in coordinate map");
1145 
1146  allCompPairs[pIt->first ] = pIt->second;
1147  allCompPairs[pIt->second] = pIt->first;
1148 
1149  // Loop up coordinates of the faces, check they have the
1150  // same number of vertices.
1152  = { coordMap[ids[0]], coordMap[ids[1]] };
1153 
1154  ASSERTL0(tmpVec[0].size() == tmpVec[1].size(),
1155  "Two periodic faces have different number "
1156  "of vertices!");
1157 
1158  // o will store relative orientation of faces. Note that in
1159  // some transpose cases (Dir1FwdDir2_Dir2BwdDir1 and
1160  // Dir1BwdDir1_Dir2FwdDir1) it seems orientation will be
1161  // different going from face1->face2 instead of face2->face1
1162  // (check this).
1164 
1165  // Record periodic faces.
1166  for (i = 0; i < 2; ++i)
1167  {
1168  if (!local[i])
1169  {
1170  continue;
1171  }
1172 
1173  // Reference to the other face.
1174  int other = (i+1) % 2;
1175 
1176  // Calculate relative face orientation.
1177  if (tmpVec[0].size() == 3)
1178  {
1180  tmpVec[i], tmpVec[other]);
1181  }
1182  else
1183  {
1185  tmpVec[i], tmpVec[other]);
1186  }
1187 
1188  // Record face ID, orientation and whether other face is
1189  // local.
1190  PeriodicEntity ent(ids [other], o,
1191  local[other]);
1192  m_periodicFaces[ids[i]].push_back(ent);
1193  }
1194 
1195  int nFaceVerts = vertMap[ids[0]].size();
1196 
1197  // Determine periodic vertices.
1198  for (i = 0; i < 2; ++i)
1199  {
1200  int other = (i+1) % 2;
1201 
1202  // Calculate relative face orientation.
1203  if (tmpVec[0].size() == 3)
1204  {
1206  tmpVec[i], tmpVec[other]);
1207  }
1208  else
1209  {
1211  tmpVec[i], tmpVec[other]);
1212  }
1213 
1214  if (nFaceVerts == 3)
1215  {
1216  ASSERTL0(
1219  "Unsupported face orientation for face "+
1220  boost::lexical_cast<string>(ids[i]));
1221  }
1222 
1223  // Look up vertices for this face.
1224  vector<int> per1 = vertMap[ids[i]];
1225  vector<int> per2 = vertMap[ids[other]];
1226 
1227  // tmpMap will hold the pairs of vertices which are
1228  // periodic.
1229  map<int, pair<int, bool> > tmpMap;
1230  map<int, pair<int, bool> >::iterator mIt;
1231 
1232  // Use vmap to determine which vertices connect given
1233  // the orientation o.
1234  for (j = 0; j < nFaceVerts; ++j)
1235  {
1236  int v = vmap[nFaceVerts][o][j];
1237  tmpMap[per1[j]] = make_pair(
1238  per2[v], locVerts.count(per2[v]) > 0);
1239  }
1240 
1241  // Now loop over tmpMap to associate periodic vertices.
1242  for (mIt = tmpMap.begin(); mIt != tmpMap.end(); ++mIt)
1243  {
1244  PeriodicEntity ent2(mIt->second.first,
1246  mIt->second.second);
1247 
1248  // See if this vertex has been recorded already.
1249  PeriodicMap::iterator perIt = periodicVerts.find(
1250  mIt->first);
1251 
1252  if (perIt == periodicVerts.end())
1253  {
1254  // Vertex is new - just record this entity as
1255  // usual.
1256  periodicVerts[mIt->first].push_back(ent2);
1257  perIt = periodicVerts.find(mIt->first);
1258  }
1259  else
1260  {
1261  // Vertex is known - loop over the vertices
1262  // inside the record and potentially add vertex
1263  // mIt->second to the list.
1264  for (k = 0; k < perIt->second.size(); ++k)
1265  {
1266  if (perIt->second[k].id == mIt->second.first)
1267  {
1268  break;
1269  }
1270  }
1271 
1272  if (k == perIt->second.size())
1273  {
1274  perIt->second.push_back(ent2);
1275  }
1276  }
1277  }
1278  }
1279 
1280  // Determine periodic edges. Logic is the same as above, and
1281  // perhaps should be condensed to avoid replication.
1282  for (i = 0; i < 2; ++i)
1283  {
1284  int other = (i+1) % 2;
1285 
1286  if (tmpVec[0].size() == 3)
1287  {
1289  tmpVec[i], tmpVec[other]);
1290  }
1291  else
1292  {
1294  tmpVec[i], tmpVec[other]);
1295  }
1296 
1297  vector<int> per1 = edgeMap[ids[i]];
1298  vector<int> per2 = edgeMap[ids[other]];
1299 
1300  map<int, pair<int, bool> > tmpMap;
1301  map<int, pair<int, bool> >::iterator mIt;
1302 
1303  for (j = 0; j < nFaceVerts; ++j)
1304  {
1305  int e = emap[nFaceVerts][o][j];
1306  tmpMap[per1[j]] = make_pair(
1307  per2[e], locEdges.count(per2[e]) > 0);
1308  }
1309 
1310  for (mIt = tmpMap.begin(); mIt != tmpMap.end(); ++mIt)
1311  {
1312  // Note we assume orientation of edges is forwards -
1313  // this may be reversed later.
1314  PeriodicEntity ent2(mIt->second.first,
1316  mIt->second.second);
1317  PeriodicMap::iterator perIt = periodicEdges.find(
1318  mIt->first);
1319 
1320  if (perIt == periodicEdges.end())
1321  {
1322  periodicEdges[mIt->first].push_back(ent2);
1323  perIt = periodicEdges.find(mIt->first);
1324  }
1325  else
1326  {
1327  for (k = 0; k < perIt->second.size(); ++k)
1328  {
1329  if (perIt->second[k].id == mIt->second.first)
1330  {
1331  break;
1332  }
1333  }
1334 
1335  if (k == perIt->second.size())
1336  {
1337  perIt->second.push_back(ent2);
1338  }
1339  }
1340  }
1341  }
1342  }
1343  }
1344 
1345  Array<OneD, int> pairSizes(n, 0);
1346  pairSizes[p] = allCompPairs.size();
1347  vComm->AllReduce(pairSizes, LibUtilities::ReduceSum);
1348 
1349  int totPairSizes = Vmath::Vsum(n, pairSizes, 1);
1350 
1351  Array<OneD, int> pairOffsets(n, 0);
1352  pairOffsets[0] = 0;
1353 
1354  for (i = 1; i < n; ++i)
1355  {
1356  pairOffsets[i] = pairOffsets[i-1] + pairSizes[i-1];
1357  }
1358 
1359  Array<OneD, int> first (totPairSizes, 0);
1360  Array<OneD, int> second(totPairSizes, 0);
1361 
1362  cnt = pairOffsets[p];
1363 
1364  for (pIt = allCompPairs.begin(); pIt != allCompPairs.end(); ++pIt)
1365  {
1366  first [cnt ] = pIt->first;
1367  second[cnt++] = pIt->second;
1368  }
1369 
1370  vComm->AllReduce(first, LibUtilities::ReduceSum);
1371  vComm->AllReduce(second, LibUtilities::ReduceSum);
1372 
1373  allCompPairs.clear();
1374 
1375  for(cnt = 0; cnt < totPairSizes; ++cnt)
1376  {
1377  allCompPairs[first[cnt]] = second[cnt];
1378  }
1379 
1380  // Search for periodic vertices and edges which are not in
1381  // a periodic composite but lie in this process. First,
1382  // loop over all information we have from other
1383  // processors.
1384  for (cnt = i = 0; i < totFaces; ++i)
1385  {
1386  int faceId = faceIds[i];
1387 
1388  ASSERTL0(allCompPairs.count(faceId) > 0,
1389  "Unable to find matching periodic face.");
1390 
1391  int perFaceId = allCompPairs[faceId];
1392 
1393  for (j = 0; j < faceVerts[i]; ++j, ++cnt)
1394  {
1395  int vId = vertIds[cnt];
1396 
1397  PeriodicMap::iterator perId = periodicVerts.find(vId);
1398 
1399  if (perId == periodicVerts.end())
1400  {
1401 
1402  // This vertex is not included in the map. Figure out which
1403  // vertex it is supposed to be periodic with. perFaceId is
1404  // the face ID which is periodic with faceId. The logic is
1405  // much the same as the loop above.
1407  = { coordMap[faceId], coordMap[perFaceId] };
1408 
1409  int nFaceVerts = tmpVec[0].size();
1410  StdRegions::Orientation o = nFaceVerts == 3 ?
1412  tmpVec[0], tmpVec[1]) :
1414  tmpVec[0], tmpVec[1]);
1415 
1416  // Use vmap to determine which vertex of the other face
1417  // should be periodic with this one.
1418  int perVertexId = vertMap[perFaceId][vmap[nFaceVerts][o][j]];
1419 
1420 
1421  PeriodicEntity ent(perVertexId,
1423  locVerts.count(perVertexId) > 0);
1424 
1425  periodicVerts[vId].push_back(ent);
1426  }
1427 
1428  int eId = edgeIds[cnt];
1429 
1430  perId = periodicEdges.find(eId);
1431 
1432  if (perId == periodicEdges.end())
1433  {
1434  // This edge is not included in the map. Figure
1435  // out which edge it is supposed to be periodic
1436  // with. perFaceId is the face ID which is
1437  // periodic with faceId. The logic is much the
1438  // same as the loop above.
1440  = { coordMap[faceId], coordMap[perFaceId] };
1441 
1442  int nFaceEdges = tmpVec[0].size();
1443  StdRegions::Orientation o = nFaceEdges == 3 ?
1445  tmpVec[0], tmpVec[1]) :
1447  tmpVec[0], tmpVec[1]);
1448 
1449  // Use emap to determine which edge of the other
1450  // face should be periodic with this one.
1451  int perEdgeId = edgeMap[perFaceId][emap[nFaceEdges][o][j]];
1452 
1453 
1454  PeriodicEntity ent(perEdgeId,
1456  locEdges.count(perEdgeId) > 0);
1457 
1458  periodicEdges[eId].push_back(ent);
1459  }
1460  }
1461  }
1462 
1463  // Finally, we must loop over the periodicVerts and periodicEdges
1464  // map to complete connectivity information.
1465  PeriodicMap::iterator perIt, perIt2;
1466  for (perIt = periodicVerts.begin();
1467  perIt != periodicVerts.end(); ++perIt)
1468  {
1469  // For each vertex that is periodic with this one...
1470  for (i = 0; i < perIt->second.size(); ++i)
1471  {
1472  // Find the vertex in the periodicVerts map...
1473  perIt2 = periodicVerts.find(perIt->second[i].id);
1474  ASSERTL0(perIt2 != periodicVerts.end(),
1475  "Couldn't find periodic vertex.");
1476 
1477  // Now search through this vertex's list and make sure that
1478  // we have a record of any vertices which aren't in the
1479  // original list.
1480  for (j = 0; j < perIt2->second.size(); ++j)
1481  {
1482  if (perIt2->second[j].id == perIt->first)
1483  {
1484  continue;
1485  }
1486 
1487  for (k = 0; k < perIt->second.size(); ++k)
1488  {
1489  if (perIt2->second[j].id == perIt->second[k].id)
1490  {
1491  break;
1492  }
1493  }
1494 
1495  if (k == perIt->second.size())
1496  {
1497  perIt->second.push_back(perIt2->second[j]);
1498  }
1499  }
1500  }
1501  }
1502 
1503  for (perIt = periodicEdges.begin();
1504  perIt != periodicEdges.end(); ++perIt)
1505  {
1506  for (i = 0; i < perIt->second.size(); ++i)
1507  {
1508  perIt2 = periodicEdges.find(perIt->second[i].id);
1509  ASSERTL0(perIt2 != periodicEdges.end(),
1510  "Couldn't find periodic edge.");
1511 
1512  for (j = 0; j < perIt2->second.size(); ++j)
1513  {
1514  if (perIt2->second[j].id == perIt->first)
1515  {
1516  continue;
1517  }
1518 
1519  for (k = 0; k < perIt->second.size(); ++k)
1520  {
1521  if (perIt2->second[j].id == perIt->second[k].id)
1522  {
1523  break;
1524  }
1525  }
1526 
1527  if (k == perIt->second.size())
1528  {
1529  perIt->second.push_back(perIt2->second[j]);
1530  }
1531  }
1532  }
1533  }
1534 
1535  // Loop over periodic edges to determine relative edge orientations.
1536  for (perIt = periodicEdges.begin();
1537  perIt != periodicEdges.end(); perIt++)
1538  {
1539  // Find edge coordinates
1540  map<int, pair<int, int> >::iterator eIt
1541  = eIdMap.find(perIt->first);
1542  SpatialDomains::PointGeom v[2] = {
1543  *vCoMap[eIt->second.first],
1544  *vCoMap[eIt->second.second]
1545  };
1546 
1547  // Loop over each edge, and construct a vector that takes us
1548  // from one vertex to another. Use this to figure out which
1549  // vertex maps to which.
1550  for (i = 0; i < perIt->second.size(); ++i)
1551  {
1552  eIt = eIdMap.find(perIt->second[i].id);
1553 
1554  SpatialDomains::PointGeom w[2] = {
1555  *vCoMap[eIt->second.first],
1556  *vCoMap[eIt->second.second]
1557  };
1558 
1559  NekDouble cx = 0.5*(w[0](0)-v[0](0)+w[1](0)-v[1](0));
1560  NekDouble cy = 0.5*(w[0](1)-v[0](1)+w[1](1)-v[1](1));
1561  NekDouble cz = 0.5*(w[0](2)-v[0](2)+w[1](2)-v[1](2));
1562 
1563  int vMap[2] = {-1,-1};
1564  for (j = 0; j < 2; ++j)
1565  {
1566  NekDouble x = v[j](0);
1567  NekDouble y = v[j](1);
1568  NekDouble z = v[j](2);
1569  for (k = 0; k < 2; ++k)
1570  {
1571  NekDouble x1 = w[k](0)-cx;
1572  NekDouble y1 = w[k](1)-cy;
1573  NekDouble z1 = w[k](2)-cz;
1574 
1575  if (sqrt((x1-x)*(x1-x)+(y1-y)*(y1-y)+(z1-z)*(z1-z))
1576  < 1e-8)
1577  {
1578  vMap[k] = j;
1579  break;
1580  }
1581  }
1582  }
1583 
1584  // Sanity check the map.
1585  ASSERTL0(vMap[0] >= 0 && vMap[1] >= 0,
1586  "Unable to align periodic vertices.");
1587  ASSERTL0((vMap[0] == 0 || vMap[0] == 1) &&
1588  (vMap[1] == 0 || vMap[1] == 1) &&
1589  (vMap[0] != vMap[1]),
1590  "Unable to align periodic vertices.");
1591 
1592  // If 0 -> 0 then edges are aligned already; otherwise
1593  // reverse the orientation.
1594  if (vMap[0] != 0)
1595  {
1596  perIt->second[i].orient = StdRegions::eBackwards;
1597  }
1598  }
1599  }
1600 
1601  // Do one final loop over periodic vertices/edges to remove
1602  // non-local vertices/edges from map.
1603  for (perIt = periodicVerts.begin();
1604  perIt != periodicVerts.end(); ++perIt)
1605  {
1606  if (locVerts.count(perIt->first) > 0)
1607  {
1608  m_periodicVerts.insert(*perIt);
1609  }
1610  }
1611 
1612  for (perIt = periodicEdges.begin();
1613  perIt != periodicEdges.end(); ++perIt)
1614  {
1615  if (locEdges.count(perIt->first) > 0)
1616  {
1617  m_periodicEdges.insert(*perIt);
1618  }
1619  }
1620  }
1621 
1622  bool DisContField3D::IsLeftAdjacentFace(const int n, const int e)
1623  {
1624  set<int>::iterator it;
1626  m_traceMap->GetElmtToTrace()[n][e]->
1627  as<LocalRegions::Expansion2D>();
1628 
1629  int offset = m_trace->GetPhys_Offset(traceEl->GetElmtId());
1630 
1631  bool fwd = true;
1632  if (traceEl->GetLeftAdjacentElementFace () == -1 ||
1633  traceEl->GetRightAdjacentElementFace() == -1)
1634  {
1635  // Boundary edge (1 connected element). Do nothing in
1636  // serial.
1637  it = m_boundaryFaces.find(traceEl->GetElmtId());
1638 
1639  // If the edge does not have a boundary condition set on
1640  // it, then assume it is a partition edge.
1641  if (it == m_boundaryFaces.end())
1642  {
1643  int traceGeomId = traceEl->GetGeom2D()->GetGlobalID();
1645  traceGeomId);
1646 
1647  if (pIt != m_periodicFaces.end() && !pIt->second[0].isLocal)
1648  {
1649  fwd = traceGeomId == min(traceGeomId,pIt->second[0].id);
1650  }
1651  else
1652  {
1653  fwd = m_traceMap->
1654  GetTraceToUniversalMapUnique(offset) >= 0;
1655  }
1656  }
1657  }
1658  else if (traceEl->GetLeftAdjacentElementFace () != -1 &&
1659  traceEl->GetRightAdjacentElementFace() != -1)
1660  {
1661  // Non-boundary edge (2 connected elements).
1662  fwd = dynamic_cast<Nektar::StdRegions::StdExpansion*>
1663  (traceEl->GetLeftAdjacentElementExp().get()) ==
1664  (*m_exp)[n].get();
1665  }
1666  else
1667  {
1668  ASSERTL2(false, "Unconnected trace element!");
1669  }
1670 
1671  return fwd;
1672  }
1673 
1674  /**
1675  * \brief This method extracts the "forward" and "backward" trace
1676  * data from the array \a field and puts the data into output
1677  * vectors \a Fwd and \a Bwd.
1678  *
1679  * We first define the convention which defines "forwards" and
1680  * "backwards". First an association is made between the face of
1681  * each element and its corresponding face in the trace space
1682  * using the mapping #m_traceMap. The element can either be
1683  * left-adjacent or right-adjacent to this trace face (see
1684  * Expansion2D::GetLeftAdjacentElementExp). Boundary faces are
1685  * always left-adjacent since left-adjacency is populated first.
1686  *
1687  * If the element is left-adjacent we extract the face trace data
1688  * from \a field into the forward trace space \a Fwd; otherwise,
1689  * we place it in the backwards trace space \a Bwd. In this way,
1690  * we form a unique set of trace normals since these are always
1691  * extracted from left-adjacent elements.
1692  *
1693  * \param field is a NekDouble array which contains the 3D data
1694  * from which we wish to extract the backward and forward
1695  * orientated trace/face arrays.
1696  *
1697  * \return Updates a NekDouble array \a Fwd and \a Bwd
1698  */
1699  void DisContField3D::v_GetFwdBwdTracePhys(Array<OneD, NekDouble> &Fwd,
1700  Array<OneD, NekDouble> &Bwd)
1701  {
1702  v_GetFwdBwdTracePhys(m_phys, Fwd, Bwd);
1703  }
1704 
1705 
1707  const Array<OneD, const NekDouble> &field,
1708  Array<OneD, NekDouble> &Fwd,
1709  Array<OneD, NekDouble> &Bwd)
1710  {
1711  // Loop over elements and collect forward and backward expansions.
1712  int nexp = GetExpSize();
1713  int cnt, n, e, npts, offset, phys_offset;
1714  Array<OneD,NekDouble> e_tmp;
1715 
1716  Array<OneD, Array<OneD, StdRegions::StdExpansionSharedPtr> >
1717  &elmtToTrace = m_traceMap->GetElmtToTrace();
1718 
1719  set<int>::iterator it;
1721  boost::unordered_map<int,pair<int,int> >::iterator it3;
1722 
1723  // Zero vectors.
1724  Vmath::Zero(Fwd.num_elements(), Fwd, 1);
1725  Vmath::Zero(Bwd.num_elements(), Bwd, 1);
1726 
1728  bool fwd;
1729 
1730  for(cnt = n = 0; n < nexp; ++n)
1731  {
1732  exp3d = (*m_exp)[n]->as<LocalRegions::Expansion3D>();
1733  phys_offset = GetPhys_Offset(n);
1734  for(e = 0; e < exp3d->GetNfaces(); ++e, ++cnt)
1735  {
1736  offset = m_trace->GetPhys_Offset(
1737  elmtToTrace[n][e]->GetElmtId());
1738 
1739  fwd = m_leftAdjacentFaces[cnt];
1740  if (fwd)
1741  {
1742  exp3d->GetFacePhysVals(e, elmtToTrace[n][e],
1743  field + phys_offset,
1744  e_tmp = Fwd + offset);
1745  }
1746  else
1747  {
1748  exp3d->GetFacePhysVals(e, elmtToTrace[n][e],
1749  field + phys_offset,
1750  e_tmp = Bwd + offset);
1751  }
1752  }
1753  }
1754 
1755  // fill boundary conditions into missing elements
1756  int id1,id2 = 0;
1757  cnt = 0;
1758 
1759  for(n = 0; n < m_bndCondExpansions.num_elements(); ++n)
1760  {
1761  if(m_bndConditions[n]->GetBoundaryConditionType() ==
1763  {
1764  for(e = 0; e < m_bndCondExpansions[n]->GetExpSize(); ++e)
1765  {
1766  npts = m_bndCondExpansions[n]->GetExp(e)->GetTotPoints();
1767  id1 = m_bndCondExpansions[n]->GetPhys_Offset(e);
1768  id2 = m_trace->GetPhys_Offset(
1769  m_traceMap->GetBndCondTraceToGlobalTraceMap(cnt+e));
1770  Vmath::Vcopy(npts,
1771  &(m_bndCondExpansions[n]->GetPhys())[id1], 1,
1772  &Bwd[id2], 1);
1773  }
1774 
1775  cnt += e;
1776  }
1777  else if (m_bndConditions[n]->GetBoundaryConditionType() ==
1779  m_bndConditions[n]->GetBoundaryConditionType() ==
1781  {
1782  for(e = 0; e < m_bndCondExpansions[n]->GetExpSize(); ++e)
1783  {
1784  npts = m_bndCondExpansions[n]->GetExp(e)->GetTotPoints();
1785  id1 = m_bndCondExpansions[n]->GetPhys_Offset(e);
1786  id2 = m_trace->GetPhys_Offset(
1787  m_traceMap->GetBndCondTraceToGlobalTraceMap(cnt+e));
1788 
1789  ASSERTL0((m_bndCondExpansions[n]->GetPhys())[id1] == 0.0,
1790  "method not set up for non-zero Neumann "
1791  "boundary condition");
1792 
1793  Vmath::Vcopy(npts,&Fwd[id2],1,&Bwd[id2],1);
1794  }
1795 
1796  cnt += e;
1797  }
1798  else
1799  {
1800  ASSERTL0(false, "Method only set up for Dirichlet, Neumann "
1801  "and Robin conditions.");
1802  }
1803  }
1804 
1805  // Copy any periodic boundary conditions.
1806  for (n = 0; n < m_periodicFwdCopy.size(); ++n)
1807  {
1808  Bwd[m_periodicBwdCopy[n]] = Fwd[m_periodicFwdCopy[n]];
1809  }
1810 
1811  // Do parallel exchange for forwards/backwards spaces.
1812  m_traceMap->UniversalTraceAssemble(Fwd);
1813  m_traceMap->UniversalTraceAssemble(Bwd);
1814  }
1815 
1817  Array<OneD, NekDouble> &outarray)
1818  {
1819  ASSERTL1(m_physState == true,
1820  "Field is not in physical space.");
1821 
1822  v_ExtractTracePhys(m_phys, outarray);
1823  }
1824 
1826  const Array<OneD, const NekDouble> &inarray,
1827  Array<OneD, NekDouble> &outarray)
1828  {
1829  // Loop over elemente and collect forward expansion
1830  int nexp = GetExpSize();
1831  int n,e,offset,phys_offset;
1832  Array<OneD,NekDouble> e_tmp;
1833  Array<OneD, Array<OneD, StdRegions::StdExpansionSharedPtr> >
1834  &elmtToTrace = m_traceMap->GetElmtToTrace();
1835 
1836  ASSERTL1(outarray.num_elements() >= m_trace->GetNpoints(),
1837  "input array is of insufficient length");
1838 
1839  // use m_trace tmp space in element to fill values
1840  for(n = 0; n < nexp; ++n)
1841  {
1842  phys_offset = GetPhys_Offset(n);
1843 
1844  for(e = 0; e < (*m_exp)[n]->GetNfaces(); ++e)
1845  {
1846  offset = m_trace->GetPhys_Offset(elmtToTrace[n][e]->GetElmtId());
1847  (*m_exp)[n]->GetFacePhysVals(e, elmtToTrace[n][e],
1848  inarray + phys_offset,
1849  e_tmp = outarray + offset);
1850  }
1851  }
1852  }
1853 
1854  /**
1855  * @brief Add trace contributions into elemental coefficient spaces.
1856  *
1857  * Given some quantity \f$ \vec{Fn} \f$, which conatins this
1858  * routine calculates the integral
1859  *
1860  * \f[
1861  * \int_{\Omega^e} \vec{Fn}, \mathrm{d}S
1862  * \f]
1863  *
1864  * and adds this to the coefficient space provided by outarray.
1865  *
1866  * @see Expansion3D::AddFaceNormBoundaryInt
1867  *
1868  * @param Fn The trace quantities.
1869  * @param outarray Resulting 3D coefficient space.
1870  *
1871  */
1873  const Array<OneD, const NekDouble> &Fn,
1874  Array<OneD, NekDouble> &outarray)
1875  {
1876  int e,n,offset, t_offset;
1877  Array<OneD, NekDouble> e_outarray;
1878  Array<OneD, Array<OneD, StdRegions::StdExpansionSharedPtr> >
1879  &elmtToTrace = m_traceMap->GetElmtToTrace();
1880 
1881  for(n = 0; n < GetExpSize(); ++n)
1882  {
1883  offset = GetCoeff_Offset(n);
1884  e_outarray = outarray+offset;
1885  for(e = 0; e < (*m_exp)[n]->GetNfaces(); ++e)
1886  {
1887  t_offset = m_trace->GetPhys_Offset(
1888  elmtToTrace[n][e]->GetElmtId());
1889  (*m_exp)[n]->AddFaceNormBoundaryInt(e,
1890  elmtToTrace[n][e],
1891  Fn + t_offset,
1892  e_outarray);
1893  }
1894  }
1895  }
1896  /**
1897  * @brief Add trace contributions into elemental coefficient spaces.
1898  *
1899  * Given some quantity \f$ \vec{Fn} \f$, which conatins this
1900  * routine calculates the integral
1901  *
1902  * \f[
1903  * \int_{\Omega^e} \vec{Fn}, \mathrm{d}S
1904  * \f]
1905  *
1906  * and adds this to the coefficient space provided by
1907  * outarray. The value of q is determined from the routine
1908  * IsLeftAdjacentFace() which if true we use Fwd else we use
1909  * Bwd
1910  *
1911  * @see Expansion3D::AddFaceNormBoundaryInt
1912  *
1913  * @param Fwd The trace quantities associated with left (fwd)
1914  * adjancent elmt.
1915  * @param Bwd The trace quantities associated with right (bwd)
1916  * adjacent elet.
1917  * @param outarray Resulting 3D coefficient space.
1918  */
1920  const Array<OneD, const NekDouble> &Fwd,
1921  const Array<OneD, const NekDouble> &Bwd,
1922  Array<OneD, NekDouble> &outarray)
1923  {
1924  int e,n,offset, t_offset;
1925  Array<OneD, NekDouble> e_outarray;
1926  Array<OneD, Array<OneD, StdRegions::StdExpansionSharedPtr> >
1927  &elmtToTrace = m_traceMap->GetElmtToTrace();
1928 
1929  for(n = 0; n < GetExpSize(); ++n)
1930  {
1931  offset = GetCoeff_Offset(n);
1932  for(e = 0; e < (*m_exp)[n]->GetNfaces(); ++e)
1933  {
1934  t_offset = m_trace->GetPhys_Offset(elmtToTrace[n][e]->GetElmtId());
1935 
1936  // Evaluate upwind flux less local edge
1937  if(IsLeftAdjacentFace(n,e))
1938  {
1939  (*m_exp)[n]->AddFaceNormBoundaryInt(
1940  e, elmtToTrace[n][e], Fwd + t_offset,
1941  e_outarray = outarray+offset);
1942  }
1943  else
1944  {
1945  (*m_exp)[n]->AddFaceNormBoundaryInt(
1946  e, elmtToTrace[n][e], Bwd + t_offset,
1947  e_outarray = outarray+offset);
1948  }
1949  }
1950  }
1951  }
1952 
1953  /**
1954  * @brief Set up a list of elemeent IDs and edge IDs that link to the
1955  * boundary conditions.
1956  */
1958  Array<OneD, int> &ElmtID,
1959  Array<OneD, int> &FaceID)
1960  {
1961  map<int,int> globalIdMap;
1962  int i, n;
1963  int cnt;
1964  int nbcs = 0;
1965 
1967  boost::dynamic_pointer_cast<SpatialDomains::MeshGraph3D>(
1968  m_graph);
1969 
1970  // Populate global ID map (takes global geometry ID to local
1971  // expansion list ID).
1973  for (i = 0; i < GetExpSize(); ++i)
1974  {
1975  exp3d = (*m_exp)[i]->as<LocalRegions::Expansion3D>();
1976  globalIdMap[exp3d->GetGeom3D()->GetGlobalID()] = i;
1977  }
1978 
1979  // Determine number of boundary condition expansions.
1980  for(i = 0; i < m_bndConditions.num_elements(); ++i)
1981  {
1982  nbcs += m_bndCondExpansions[i]->GetExpSize();
1983  }
1984 
1985  // make sure arrays are of sufficient length
1986  if(ElmtID.num_elements() != nbcs)
1987  {
1988  ElmtID = Array<OneD, int>(nbcs);
1989  }
1990 
1991  if(FaceID.num_elements() != nbcs)
1992  {
1993  FaceID = Array<OneD, int>(nbcs);
1994  }
1995 
1997  for(cnt = n = 0; n < m_bndCondExpansions.num_elements(); ++n)
1998  {
1999  for(i = 0; i < m_bndCondExpansions[n]->GetExpSize(); ++i, ++cnt)
2000  {
2001  exp2d = m_bndCondExpansions[n]->GetExp(i)->
2002  as<LocalRegions::Expansion2D>();
2003  // Use face to element map from MeshGraph3D.
2005  graph3D->GetElementsFromFace(exp2d->GetGeom2D());
2006 
2007  ElmtID[cnt] = globalIdMap[(*tmp)[0]->
2008  m_Element->GetGlobalID()];
2009  FaceID[cnt] = (*tmp)[0]->m_FaceIndx;
2010  }
2011  }
2012  }
2013 
2014  /**
2015  * Solving Helmholtz Equation in 3D
2016  */
2018  const Array<OneD, const NekDouble> &inarray,
2019  Array<OneD, NekDouble> &outarray,
2020  const FlagList &flags,
2021  const StdRegions::ConstFactorMap &factors,
2022  const StdRegions::VarCoeffMap &varcoeff,
2023  const Array<OneD, const NekDouble> &dirForcing)
2024  {
2025  int i,j,n,cnt,cnt1,nbndry;
2026  int nexp = GetExpSize();
2028 
2029  Array<OneD,NekDouble> f(m_ncoeffs);
2030  DNekVec F(m_ncoeffs,f,eWrapper);
2031  Array<OneD,NekDouble> e_f, e_l;
2032 
2033  //----------------------------------
2034  // Setup RHS Inner product
2035  //----------------------------------
2036  IProductWRTBase(inarray,f);
2037  Vmath::Neg(m_ncoeffs,f,1);
2038 
2039  //----------------------------------
2040  // Solve continuous flux System
2041  //----------------------------------
2042  int GloBndDofs = m_traceMap->GetNumGlobalBndCoeffs();
2043  int NumDirichlet = m_traceMap->GetNumLocalDirBndCoeffs();
2044  int e_ncoeffs,id;
2045 
2046  // Retrieve block matrix of U^e
2048  const DNekScalBlkMatSharedPtr &HDGLamToU = GetBlockMatrix(HDGLamToUKey);
2049 
2050  // Retrieve global trace space storage, \Lambda, from trace expansion
2051  Array<OneD,NekDouble> BndSol = m_trace->UpdateCoeffs();
2052 
2053  // Create trace space forcing, F
2054  Array<OneD,NekDouble> BndRhs(GloBndDofs,0.0);
2055 
2056  // Zero \Lambda
2057  Vmath::Zero(GloBndDofs,BndSol,1);
2058 
2059  // Retrieve number of local trace space coefficients N_{\lambda},
2060  // and set up local elemental trace solution \lambda^e.
2061  int LocBndCoeffs = m_traceMap->GetNumLocalBndCoeffs();
2062  Array<OneD, NekDouble> loc_lambda(LocBndCoeffs);
2063  DNekVec LocLambda(LocBndCoeffs,loc_lambda,eWrapper);
2064 
2065  //----------------------------------
2066  // Evaluate Trace Forcing vector F
2067  // Kirby et al, 2010, P23, Step 5.
2068  //----------------------------------
2069  // Loop over all expansions in the domain
2070  for(cnt = cnt1 = n = 0; n < nexp; ++n)
2071  {
2072  nbndry = (*m_exp)[m_offset_elmt_id[n]]->NumDGBndryCoeffs();
2073 
2074  e_ncoeffs = (*m_exp)[m_offset_elmt_id[n]]->GetNcoeffs();
2075  e_f = f + cnt;
2076  e_l = loc_lambda + cnt1;
2077 
2078  // Local trace space \lambda^e
2079  DNekVec Floc (nbndry, e_l, eWrapper);
2080  // Local forcing f^e
2081  DNekVec ElmtFce (e_ncoeffs, e_f, eWrapper);
2082  // Compute local (U^e)^{\top} f^e
2083  Floc = Transpose(*(HDGLamToU->GetBlock(n,n)))*ElmtFce;
2084 
2085  cnt += e_ncoeffs;
2086  cnt1 += nbndry;
2087  }
2088 
2089  // Assemble local \lambda_e into global \Lambda
2090  m_traceMap->AssembleBnd(loc_lambda,BndRhs);
2091 
2092  // Copy Dirichlet boundary conditions and weak forcing into trace
2093  // space
2094  cnt = 0;
2095  for(i = 0; i < m_bndCondExpansions.num_elements(); ++i)
2096  {
2097  if(m_bndConditions[i]->GetBoundaryConditionType() == SpatialDomains::eDirichlet)
2098  {
2099  for(j = 0; j < (m_bndCondExpansions[i])->GetNcoeffs(); ++j)
2100  {
2101  id = m_traceMap->GetBndCondCoeffsToGlobalCoeffsMap(cnt++);
2102  BndSol[id] = m_bndCondExpansions[i]->GetCoeffs()[j];
2103  }
2104  }
2105  else
2106  {
2107  //Add weak boundary condition to trace forcing
2108  for(j = 0; j < (m_bndCondExpansions[i])->GetNcoeffs(); ++j)
2109  {
2110  id = m_traceMap->GetBndCondCoeffsToGlobalCoeffsMap(cnt++);
2111  BndRhs[id] += m_bndCondExpansions[i]->GetCoeffs()[j];
2112  }
2113  }
2114  }
2115 
2116  //----------------------------------
2117  // Solve trace problem: \Lambda = K^{-1} F
2118  // K is the HybridDGHelmBndLam matrix.
2119  //----------------------------------
2120  if(GloBndDofs - NumDirichlet > 0)
2121  {
2123  m_traceMap,factors,varcoeff);
2125  LinSys->Solve(BndRhs,BndSol,m_traceMap);
2126  }
2127 
2128  //----------------------------------
2129  // Internal element solves
2130  //----------------------------------
2132  const DNekScalBlkMatSharedPtr& InvHDGHelm = GetBlockMatrix(invHDGhelmkey);
2133  DNekVec out(m_ncoeffs,outarray,eWrapper);
2134  Vmath::Zero(m_ncoeffs,outarray,1);
2135 
2136  // get local trace solution from BndSol
2137  m_traceMap->GlobalToLocalBnd(BndSol,loc_lambda);
2138 
2139  // out = u_f + u_lam = (*InvHDGHelm)*f + (LamtoU)*Lam
2140  out = (*InvHDGHelm)*F + (*HDGLamToU)*LocLambda;
2141  }
2142 
2143  /**
2144  * @brief Calculates the result of the multiplication of a global matrix
2145  * of type specified by @a mkey with a vector given by @a inarray.
2146  *
2147  * @param mkey Key representing desired matrix multiplication.
2148  * @param inarray Input vector.
2149  * @param outarray Resulting multiplication.
2150  */
2152  const GlobalMatrixKey &gkey,
2153  const Array<OneD,const NekDouble> &inarray,
2154  Array<OneD, NekDouble> &outarray,
2155  CoeffState coeffstate)
2156  {
2157  int LocBndCoeffs = m_traceMap->GetNumLocalBndCoeffs();
2158  Array<OneD, NekDouble> loc_lambda(LocBndCoeffs);
2159  DNekVec LocLambda(LocBndCoeffs,loc_lambda,eWrapper);
2160  const DNekScalBlkMatSharedPtr& HDGHelm = GetBlockMatrix(gkey);
2161 
2162  m_traceMap->GlobalToLocalBnd(inarray, loc_lambda);
2163  LocLambda = (*HDGHelm) * LocLambda;
2164  m_traceMap->AssembleBnd(loc_lambda,outarray);
2165  }
2166 
2167  /**
2168  * Search through the edge expansions and identify which ones have
2169  * Robin/Mixed type boundary conditions. If find a Robin boundary then
2170  * store the edge id of the boundary condition and the array of points
2171  * of the physical space boundary condition which are hold the boundary
2172  * condition primitive variable coefficient at the quatrature points
2173  *
2174  * \return std map containing the robin boundary condition
2175  * info using a key of the element id
2176  *
2177  * There is a next member to allow for more than one Robin
2178  * boundary condition per element
2179  */
2180  map<int, RobinBCInfoSharedPtr> DisContField3D::v_GetRobinBCInfo(void)
2181  {
2182  int i,cnt;
2183  map<int, RobinBCInfoSharedPtr> returnval;
2184  Array<OneD, int> ElmtID,FaceID;
2185  GetBoundaryToElmtMap(ElmtID,FaceID);
2186 
2187  for(cnt = i = 0; i < m_bndCondExpansions.num_elements(); ++i)
2188  {
2189  MultiRegions::ExpListSharedPtr locExpList;
2190 
2191  if(m_bndConditions[i]->GetBoundaryConditionType() == SpatialDomains::eRobin)
2192  {
2193  int e,elmtid;
2194  Array<OneD, NekDouble> Array_tmp;
2195 
2196  locExpList = m_bndCondExpansions[i];
2197 
2198  for(e = 0; e < locExpList->GetExpSize(); ++e)
2199  {
2200  RobinBCInfoSharedPtr rInfo = MemoryManager<RobinBCInfo>::AllocateSharedPtr(FaceID[cnt+e],Array_tmp = locExpList->GetPhys() + locExpList->GetPhys_Offset(e));
2201  elmtid = ElmtID[cnt+e];
2202  // make link list if necessary
2203  if(returnval.count(elmtid) != 0)
2204  {
2205  rInfo->next = returnval.find(elmtid)->second;
2206  }
2207  returnval[elmtid] = rInfo;
2208  }
2209  }
2210  cnt += m_bndCondExpansions[i]->GetExpSize();
2211  }
2212 
2213  return returnval;
2214  }
2215 
2216  /**
2217  * @brief Evaluate HDG post-processing to increase polynomial order of
2218  * solution.
2219  *
2220  * This function takes the solution (assumed to be one order lower) in
2221  * physical space, and postprocesses at the current polynomial order by
2222  * solving the system:
2223  *
2224  * \f[
2225  * \begin{aligned}
2226  * (\nabla w, \nabla u^*) &= (\nabla w, u), \\
2227  * \langle \nabla u^*, 1 \rangle &= \langle \nabla u, 1 \rangle
2228  * \end{aligned}
2229  * \f]
2230  *
2231  * where \f$ u \f$ corresponds with the current solution as stored
2232  * inside #m_coeffs.
2233  *
2234  * @param outarray The resulting field \f$ u^* \f$.
2235  */
2237  Array<OneD, NekDouble> &outarray)
2238  {
2239  int i,cnt,f,ncoeff_face;
2240  Array<OneD, NekDouble> force, out_tmp,qrhs,qrhs1;
2241  Array<OneD, Array< OneD, StdRegions::StdExpansionSharedPtr> >
2242  &elmtToTrace = m_traceMap->GetElmtToTrace();
2243 
2244  int eid,nq_elmt, nm_elmt;
2245  int LocBndCoeffs = m_traceMap->GetNumLocalBndCoeffs();
2246  Array<OneD, NekDouble> loc_lambda(LocBndCoeffs), face_lambda;
2247  Array<OneD, NekDouble> tmp_coeffs;
2248  m_traceMap->GlobalToLocalBnd(m_trace->GetCoeffs(),loc_lambda);
2249 
2250  face_lambda = loc_lambda;
2251 
2252  // Calculate Q using standard DG formulation.
2253  for(i = cnt = 0; i < GetExpSize(); ++i)
2254  {
2256  (*m_exp)[i]->as<LocalRegions::Expansion3D>();
2257 
2258  eid = m_offset_elmt_id[i];
2259  nq_elmt = (*m_exp)[eid]->GetTotPoints();
2260  nm_elmt = (*m_exp)[eid]->GetNcoeffs();
2261  qrhs = Array<OneD, NekDouble>(nq_elmt);
2262  qrhs1 = Array<OneD, NekDouble>(nq_elmt);
2263  force = Array<OneD, NekDouble>(2*nm_elmt);
2264  out_tmp = force + nm_elmt;
2266 
2267  int num_points0 = (*m_exp)[eid]->GetBasis(0)->GetNumPoints();
2268  int num_points1 = (*m_exp)[eid]->GetBasis(1)->GetNumPoints();
2269  int num_points2 = (*m_exp)[eid]->GetBasis(2)->GetNumPoints();
2270  int num_modes0 = (*m_exp)[eid]->GetBasis(0)->GetNumModes();
2271  int num_modes1 = (*m_exp)[eid]->GetBasis(1)->GetNumModes();
2272  int num_modes2 = (*m_exp)[eid]->GetBasis(2)->GetNumModes();
2273 
2274  // Probably a better way of setting up lambda than this. Note
2275  // cannot use PutCoeffsInToElmts since lambda space is mapped
2276  // during the solve.
2277  int nFaces = (*m_exp)[eid]->GetNfaces();
2278  Array<OneD, Array<OneD, NekDouble> > faceCoeffs(nFaces);
2279  for(f = 0; f < nFaces; ++f)
2280  {
2281  ncoeff_face = elmtToTrace[eid][f]->GetNcoeffs();
2282  faceCoeffs[f] = Array<OneD, NekDouble>(ncoeff_face);
2283  Vmath::Vcopy(ncoeff_face, face_lambda, 1, faceCoeffs[f], 1);
2284  exp->SetFaceToGeomOrientation(f, faceCoeffs[f]);
2285  face_lambda = face_lambda + ncoeff_face;
2286  }
2287 
2288  //creating orthogonal expansion (checking if we have quads or triangles)
2289  LibUtilities::ShapeType shape = (*m_exp)[eid]->DetShapeType();
2290  switch(shape)
2291  {
2293  {
2297  LibUtilities::BasisKey BkeyH1(LibUtilities::eOrtho_A, num_modes0, PkeyH1);
2298  LibUtilities::BasisKey BkeyH2(LibUtilities::eOrtho_A, num_modes1, PkeyH2);
2299  LibUtilities::BasisKey BkeyH3(LibUtilities::eOrtho_A, num_modes2, PkeyH3);
2300  SpatialDomains::HexGeomSharedPtr hGeom = boost::dynamic_pointer_cast<SpatialDomains::HexGeom>((*m_exp)[eid]->GetGeom());
2301  ppExp = MemoryManager<LocalRegions::HexExp>::AllocateSharedPtr(BkeyH1, BkeyH2, BkeyH3, hGeom);
2302  }
2303  break;
2305  {
2309  LibUtilities::BasisKey BkeyT1(LibUtilities::eOrtho_A, num_modes0, PkeyT1);
2310  LibUtilities::BasisKey BkeyT2(LibUtilities::eOrtho_B, num_modes1, PkeyT2);
2311  LibUtilities::BasisKey BkeyT3(LibUtilities::eOrtho_C, num_modes2, PkeyT3);
2312  SpatialDomains::TetGeomSharedPtr tGeom = boost::dynamic_pointer_cast<SpatialDomains::TetGeom>((*m_exp)[eid]->GetGeom());
2313  ppExp = MemoryManager<LocalRegions::TetExp>::AllocateSharedPtr(BkeyT1, BkeyT2, BkeyT3, tGeom);
2314  }
2315  break;
2316  case LibUtilities::ePrism:
2317  {
2321  LibUtilities::BasisKey BkeyP1(LibUtilities::eOrtho_A, num_modes0, PkeyP1);
2322  LibUtilities::BasisKey BkeyP2(LibUtilities::eOrtho_A, num_modes1, PkeyP2);
2323  LibUtilities::BasisKey BkeyP3(LibUtilities::eOrtho_B, num_modes2, PkeyP3);
2324  SpatialDomains::PrismGeomSharedPtr pGeom = boost::dynamic_pointer_cast<SpatialDomains::PrismGeom>((*m_exp)[eid]->GetGeom());
2325  ppExp = MemoryManager<LocalRegions::PrismExp>::AllocateSharedPtr(BkeyP1, BkeyP2, BkeyP3, pGeom);
2326  }
2327  break;
2328  default:
2329  ASSERTL0(false, "Wrong shape type, HDG postprocessing is not implemented");
2330  };
2331 
2332 
2333  //DGDeriv
2334  // (d/dx w, q_0)
2335  (*m_exp)[eid]->DGDeriv(
2336  0,tmp_coeffs = m_coeffs + m_coeff_offset[eid],
2337  elmtToTrace[eid], faceCoeffs, out_tmp);
2338  (*m_exp)[eid]->BwdTrans(out_tmp,qrhs);
2339  ppExp->IProductWRTDerivBase(0,qrhs,force);
2340 
2341 
2342  // + (d/dy w, q_1)
2343  (*m_exp)[eid]->DGDeriv(
2344  1,tmp_coeffs = m_coeffs + m_coeff_offset[eid],
2345  elmtToTrace[eid], faceCoeffs, out_tmp);
2346  (*m_exp)[eid]->BwdTrans(out_tmp,qrhs);
2347  ppExp->IProductWRTDerivBase(1,qrhs,out_tmp);
2348 
2349  Vmath::Vadd(nm_elmt,force,1,out_tmp,1,force,1);
2350 
2351  // + (d/dz w, q_2)
2352  (*m_exp)[eid]->DGDeriv(
2353  2,tmp_coeffs = m_coeffs + m_coeff_offset[eid],
2354  elmtToTrace[eid], faceCoeffs, out_tmp);
2355  (*m_exp)[eid]->BwdTrans(out_tmp,qrhs);
2356  ppExp->IProductWRTDerivBase(2,qrhs,out_tmp);
2357 
2358  Vmath::Vadd(nm_elmt,force,1,out_tmp,1,force,1);
2359  // determine force[0] = (1,u)
2360  (*m_exp)[eid]->BwdTrans(
2361  tmp_coeffs = m_coeffs + m_coeff_offset[eid],qrhs);
2362  force[0] = (*m_exp)[eid]->Integral(qrhs);
2363 
2364  // multiply by inverse Laplacian matrix
2365  // get matrix inverse
2366  LocalRegions::MatrixKey lapkey(StdRegions::eInvLaplacianWithUnityMean, ppExp->DetShapeType(), *ppExp);
2367  DNekScalMatSharedPtr lapsys = ppExp->GetLocMatrix(lapkey);
2368 
2369  NekVector<NekDouble> in (nm_elmt, force, eWrapper);
2370  NekVector<NekDouble> out(nm_elmt);
2371 
2372  out = (*lapsys)*in;
2373 
2374  // Transforming back to modified basis
2375  Array<OneD, NekDouble> work(nq_elmt);
2376  ppExp->BwdTrans(out.GetPtr(), work);
2377  (*m_exp)[eid]->FwdTrans(work,
2378  tmp_coeffs = outarray + m_coeff_offset[eid]);
2379  }
2380  }
2381 
2382  /**
2383  * \brief This function evaluates the boundary conditions at a certain
2384  * time-level.
2385  *
2386  * Based on the boundary condition \f$g(\boldsymbol{x},t)\f$ evaluated
2387  * at a given time-level \a t, this function transforms the boundary
2388  * conditions onto the coefficients of the (one-dimensional) boundary
2389  * expansion. Depending on the type of boundary conditions, these
2390  * expansion coefficients are calculated in different ways:
2391  * - <b>Dirichlet boundary conditions</b><BR>
2392  * In order to ensure global \f$C^0\f$ continuity of the spectral/hp
2393  * approximation, the Dirichlet boundary conditions are projected onto
2394  * the boundary expansion by means of a modified \f$C^0\f$ continuous
2395  * Galerkin projection. This projection can be viewed as a collocation
2396  * projection at the vertices, followed by an \f$L^2\f$ projection on
2397  * the interior modes of the edges. The resulting coefficients
2398  * \f$\boldsymbol{\hat{u}}^{\mathcal{D}}\f$ will be stored for the
2399  * boundary expansion.
2400  * - <b>Neumann boundary conditions</b>
2401  * In the discrete Galerkin formulation of the problem to be solved,
2402  * the Neumann boundary conditions appear as the set of surface
2403  * integrals: \f[\boldsymbol{\hat{g}}=\int_{\Gamma}
2404  * \phi^e_n(\boldsymbol{x})g(\boldsymbol{x})d(\boldsymbol{x})\quad
2405  * \forall n \f]
2406  * As a result, it are the coefficients \f$\boldsymbol{\hat{g}}\f$
2407  * that will be stored in the boundary expansion
2408  *
2409  * @param time The time at which the boundary conditions
2410  * should be evaluated.
2411  * @param bndCondExpansions List of boundary conditions.
2412  * @param bndConditions Information about the boundary conditions.
2413  */
2415  const NekDouble time,
2416  const std::string varName,
2417  const NekDouble x2_in,
2418  const NekDouble x3_in)
2419  {
2420  int i;
2421  int npoints;
2422  int nbnd = m_bndCondExpansions.num_elements();
2423  MultiRegions::ExpListSharedPtr locExpList;
2424 
2425  for (i = 0; i < nbnd; ++i)
2426  {
2427  if (time == 0.0 || m_bndConditions[i]->GetUserDefined() ==
2429  {
2430  locExpList = m_bndCondExpansions[i];
2431  npoints = locExpList->GetNpoints();
2432 
2433  Array<OneD, NekDouble> x0(npoints, 0.0);
2434  Array<OneD, NekDouble> x1(npoints, 0.0);
2435  Array<OneD, NekDouble> x2(npoints, 0.0);
2436 
2437  locExpList->GetCoords(x0, x1, x2);
2438 
2439  if (m_bndConditions[i]->GetBoundaryConditionType()
2441  {
2442  string filebcs = boost::static_pointer_cast<
2444  m_bndConditions[i])->m_filename;
2445 
2446  if (filebcs != "")
2447  {
2448  ExtractFileBCs(filebcs, varName, locExpList);
2449  }
2450  else
2451  {
2452  LibUtilities::Equation condition = boost::static_pointer_cast<SpatialDomains::
2454  m_bndConditions[i])->m_dirichletCondition;
2455 
2456  condition.Evaluate(x0, x1, x2, time,
2457  locExpList->UpdatePhys());
2458 
2459  locExpList->FwdTrans_BndConstrained(
2460  locExpList->GetPhys(),
2461  locExpList->UpdateCoeffs());
2462  }
2463  }
2464  else if (m_bndConditions[i]->GetBoundaryConditionType()
2466  {
2467  LibUtilities::Equation condition = boost::
2468  static_pointer_cast<SpatialDomains::
2470  m_bndConditions[i])->m_neumannCondition;
2471 
2472  condition.Evaluate(x0, x1, x2, time,
2473  locExpList->UpdatePhys());
2474 
2475  locExpList->IProductWRTBase(locExpList->GetPhys(),
2476  locExpList->UpdateCoeffs());
2477  }
2478  else if (m_bndConditions[i]->GetBoundaryConditionType()
2480  {
2481  LibUtilities::Equation condition = boost::
2482  static_pointer_cast<SpatialDomains::
2484  m_bndConditions[i])->m_robinFunction;
2485 
2486  LibUtilities::Equation coeff = boost::
2487  static_pointer_cast<SpatialDomains::
2489  m_bndConditions[i])->m_robinPrimitiveCoeff;
2490 
2491  condition.Evaluate(x0, x1, x2, time,
2492  locExpList->UpdatePhys());
2493 
2494  locExpList->IProductWRTBase(locExpList->GetPhys(),
2495  locExpList->UpdateCoeffs());
2496 
2497  // Put primitive coefficient into the physical
2498  // space storage
2499  coeff.Evaluate(x0, x1, x2, time,
2500  locExpList->UpdatePhys());
2501 
2502  }
2503  else
2504  {
2505  ASSERTL0(false, "This type of BC not implemented yet");
2506  }
2507  }
2508  }
2509  }
2510  } // end of namespace
2511 } // end of namespace