Nektar++
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  // 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: Field definition for 3D domain with boundary
32  // conditions using LDG flux
33  //
34  ///////////////////////////////////////////////////////////////////////////////
35 
36 #include <boost/core/ignore_unused.hpp>
37 
42 #include <LocalRegions/HexExp.h>
43 #include <LocalRegions/TetExp.h>
44 #include <LocalRegions/PrismExp.h>
47 #include <tuple>
48 
49 #include <boost/algorithm/string/classification.hpp>
50 #include <boost/algorithm/string/split.hpp>
51 #include <boost/algorithm/string/predicate.hpp>
52 
53 using namespace std;
54 
55  namespace Nektar
56  {
57  namespace MultiRegions
58  {
59  /**
60  * @class DisContField3D
61  * Abstraction of a global discontinuous three-dimensional spectral/hp
62  * element expansion which approximates the solution of a set of
63  * partial differential equations.
64  */
65 
66  /**
67  * @brief Default constructor.
68  */
69  DisContField3D::DisContField3D() :
70  ExpList3D (),
71  m_bndCondExpansions (),
72  m_bndConditions (),
73  m_trace(NullExpListSharedPtr)
74  {
75  }
76 
77  /**
78  * @brief Constructs a global discontinuous field based on an input
79  * mesh with boundary conditions.
80  */
84  const std::string &variable,
85  const bool SetUpJustDG,
86  const Collections::ImplementationType ImpType):
87  ExpList3D (pSession, graph3D, variable, ImpType),
89  m_bndConditions (),
91  {
92  // do not set up BCs if default variable
93  if (variable.compare("DefaultVar") != 0)
94  {
96 
97  GenerateBoundaryConditionExpansion(graph3D,bcs,variable);
98  EvaluateBoundaryConditions(0.0, variable);
99 
100  // Find periodic edges for this variable.
101  FindPeriodicFaces(bcs, variable);
102  }
103 
104  if(SetUpJustDG)
105  {
106  SetUpDG();
107  }
108  else
109  {
110  // Set element edges to point to Robin BC edges if required.
111  int i, cnt, f;
112  Array<OneD, int> ElmtID, FaceID;
113  GetBoundaryToElmtMap(ElmtID, FaceID);
114 
115  for(cnt = i = 0; i < m_bndCondExpansions.num_elements(); ++i)
116  {
118  locExpList = m_bndCondExpansions[i];
119 
120  for(f = 0; f < locExpList->GetExpSize(); ++f)
121  {
123  = (*m_exp)[ElmtID[cnt+f]]->
124  as<LocalRegions::Expansion3D>();
126  = locExpList->GetExp(f)->
127  as<LocalRegions::Expansion2D>();
128 
129  exp3d->SetFaceExp(FaceID[cnt+f],exp2d);
130  exp2d->SetAdjacentElementExp(FaceID[cnt+f],exp3d);
131  }
132  cnt += m_bndCondExpansions[i]->GetExpSize();
133  }
134  }
135  }
136 
137  /*
138  * @brief Copy type constructor which declares new boundary conditions
139  * and re-uses mapping info and trace space if possible
140  */
142  const DisContField3D &In,
143  const SpatialDomains::MeshGraphSharedPtr &graph3D,
144  const std::string &variable,
145  const bool SetUpJustDG)
146  : ExpList3D(In),
148  {
150 
151  GenerateBoundaryConditionExpansion(graph3D,bcs,variable);
152  EvaluateBoundaryConditions(0.0, variable);
153  ApplyGeomInfo();
154 
156  {
157  // Find periodic edges for this variable.
158  FindPeriodicFaces(bcs, variable);
159 
160  if (SetUpJustDG)
161  {
162  SetUpDG(variable);
163  }
164  else
165  {
166  int i,cnt,f;
167  Array<OneD, int> ElmtID,FaceID;
168  GetBoundaryToElmtMap(ElmtID,FaceID);
169 
170  for(cnt = i = 0; i < m_bndCondExpansions.num_elements(); ++i)
171  {
173  locExpList = m_bndCondExpansions[i];
174 
175  for(f = 0; f < locExpList->GetExpSize(); ++f)
176  {
178  = (*m_exp)[ElmtID[cnt+f]]->
179  as<LocalRegions::Expansion3D>();
181  = locExpList->GetExp(f)->
182  as<LocalRegions::Expansion2D>();
183 
184  exp3d->SetFaceExp(FaceID[cnt+f],exp2d);
185  exp2d->SetAdjacentElementExp(FaceID[cnt+f],exp3d);
186  }
187 
188  cnt += m_bndCondExpansions[i]->GetExpSize();
189  }
191  }
192 
193  }
194  //else if we have the same boundary condition
195  else
196  {
198  m_trace = In.m_trace;
199  m_traceMap = In.m_traceMap;
204 
205  if(SetUpJustDG)
206  {
207  }
208  else
209  {
210  int i,cnt,f;
211  Array<OneD, int> ElmtID,FaceID;
212  GetBoundaryToElmtMap(ElmtID,FaceID);
213 
214  for (cnt = i = 0;
215  i < m_bndCondExpansions.num_elements(); ++i)
216  {
218  locExpList = m_bndCondExpansions[i];
219 
220  for(f = 0; f < locExpList->GetExpSize(); ++f)
221  {
223  = (*m_exp)[ElmtID[cnt+f]]->
224  as<LocalRegions::Expansion3D>();
226  = locExpList->GetExp(f)->
227  as<LocalRegions::Expansion2D>();
228 
229  exp3d->SetFaceExp(FaceID[cnt+f], exp2d);
230  exp2d->SetAdjacentElementExp(FaceID[cnt+f], exp3d);
231  }
232 
233  cnt += m_bndCondExpansions[i]->GetExpSize();
234  }
235 
236  if (m_session->DefinesSolverInfo("PROJECTION"))
237  {
238  std::string ProjectStr =
239  m_session->GetSolverInfo("PROJECTION");
240  if (ProjectStr == "MixedCGDG" ||
241  ProjectStr == "Mixed_CG_Discontinuous")
242  {
243  SetUpDG(variable);
244  }
245  else
246  {
248  }
249  }
250  else
251  {
253  }
254  }
255  }
256  }
257 
258  /**
259  *
260  */
262  ExpList3D(In),
266  m_trace (In.m_trace),
267  m_traceMap (In.m_traceMap),
272  {
273  }
274 
275  /**
276  * @brief Destructor.
277  */
279  {
280  }
281 
283  const GlobalLinSysKey &mkey)
284  {
286  "Routine currently only tested for HybridDGHelmholtz");
287  ASSERTL1(mkey.GetGlobalSysSolnType() ==
288  m_traceMap->GetGlobalSysSolnType(),
289  "The local to global map is not set up for the requested "
290  "solution type");
291 
292  GlobalLinSysSharedPtr glo_matrix;
293  auto matrixIter = m_globalBndMat->find(mkey);
294 
295  if (matrixIter == m_globalBndMat->end())
296  {
297  glo_matrix = GenGlobalBndLinSys(mkey, m_traceMap);
298  (*m_globalBndMat)[mkey] = glo_matrix;
299  }
300  else
301  {
302  glo_matrix = matrixIter->second;
303  }
304 
305  return glo_matrix;
306  }
307 
308  /**
309  * @brief Set up all DG member variables and maps.
310  */
311  void DisContField3D::SetUpDG(const std::string variable)
312  {
314  {
315  return;
316  }
317 
318  ExpList2DSharedPtr trace;
319 
320  // Set up matrix map
323 
324  // Set up Trace space
325  bool UseGenSegExp = true;
328  *m_exp, m_graph, m_periodicFaces, UseGenSegExp);
329 
330  m_trace = trace;
331 
333  m_session, m_graph, trace, *this, m_bndCondExpansions,
334  m_bndConditions, m_periodicFaces,variable);
335 
336  if (m_session->DefinesCmdLineArgument("verbose"))
337  {
338  m_traceMap->PrintStats(std::cout, variable);
339  }
340 
342  &elmtToTrace = m_traceMap->GetElmtToTrace();
343 
344  // Scatter trace segments to 3D elements. For each element, we
345  // find the trace segment associated to each edge. The element
346  // then retains a pointer to the trace space segments, to ensure
347  // uniqueness of normals when retrieving from two adjoining
348  // elements which do not lie in a plane.
349  for (int i = 0; i < m_exp->size(); ++i)
350  {
351  for (int j = 0; j < (*m_exp)[i]->GetNfaces(); ++j)
352  {
354  (*m_exp)[i]->as<LocalRegions::Expansion3D>();
356  elmtToTrace[i][j]->as<LocalRegions::Expansion2D>();
357  exp3d->SetFaceExp (j, exp2d);
358  exp2d->SetAdjacentElementExp(j, exp3d);
359  }
360  }
361 
362  // Set up physical normals
364 
365  // Set up information for parallel jobs.
366  for (int i = 0; i < m_trace->GetExpSize(); ++i)
367  {
369  m_trace->GetExp(i)->as<LocalRegions::Expansion2D>();
370 
371  int offset = m_trace->GetPhys_Offset(i);
372  int traceGeomId = traceEl->GetGeom2D()->GetGlobalID();
373  auto pIt = m_periodicFaces.find(traceGeomId);
374 
375  if (pIt != m_periodicFaces.end() && !pIt->second[0].isLocal)
376  {
377  if (traceGeomId != min(pIt->second[0].id, traceGeomId))
378  {
379  traceEl->GetLeftAdjacentElementExp()->NegateFaceNormal(
380  traceEl->GetLeftAdjacentElementFace());
381  }
382  }
383  else if (m_traceMap->GetTraceToUniversalMapUnique(offset) < 0)
384  {
385  traceEl->GetLeftAdjacentElementExp()->NegateFaceNormal(
386  traceEl->GetLeftAdjacentElementFace());
387  }
388  }
389 
390  int cnt, n, e;
391 
392  // Identify boundary faces
393  for(cnt = 0, n = 0; n < m_bndCondExpansions.num_elements(); ++n)
394  {
395  if (m_bndConditions[n]->GetBoundaryConditionType() !=
397  {
398  for(e = 0; e < m_bndCondExpansions[n]->GetExpSize(); ++e)
399  {
400  m_boundaryFaces.insert(
401  m_traceMap->GetBndCondTraceToGlobalTraceMap(cnt+e));
402  }
403  cnt += m_bndCondExpansions[n]->GetExpSize();
404  }
405  }
406 
407  // Set up information for periodic boundary conditions.
408  std::unordered_map<int,pair<int,int> > perFaceToExpMap;
409  cnt = 0;
411  for (int n = 0; n < m_exp->size(); ++n)
412  {
413  exp3d = (*m_exp)[n]->as<LocalRegions::Expansion3D>();
414  for (int e = 0; e < exp3d->GetNfaces(); ++e, ++cnt)
415  {
416  auto it = m_periodicFaces.find(
417  exp3d->GetGeom3D()->GetFid(e));
418 
419  if (it != m_periodicFaces.end())
420  {
421  perFaceToExpMap[it->first] = make_pair(n, e);
422  }
423  }
424  }
425 
426  // Set up left-adjacent face list.
427  m_leftAdjacentFaces.resize(cnt);
428  cnt = 0;
429  for (int i = 0; i < m_exp->size(); ++i)
430  {
431  for (int j = 0; j < (*m_exp)[i]->GetNfaces(); ++j, ++cnt)
432  {
434  }
435  }
436 
437  // Set up mapping to copy Fwd of periodic bcs to Bwd of other edge.
438  cnt = 0;
439  for (int n = 0; n < m_exp->size(); ++n)
440  {
441  exp3d = (*m_exp)[n]->as<LocalRegions::Expansion3D>();
442  for (int e = 0; e < exp3d->GetNfaces(); ++e, ++cnt)
443  {
444  int faceGeomId = exp3d->GetGeom3D()->GetFid(e);
445  int offset = m_trace->GetPhys_Offset(
446  elmtToTrace[n][e]->GetElmtId());
447 
448  // Check to see if this face is periodic.
449  auto it = m_periodicFaces.find(faceGeomId);
450 
451  if (it != m_periodicFaces.end())
452  {
453  const PeriodicEntity &ent = it->second[0];
454  auto it2 = perFaceToExpMap.find(ent.id);
455 
456  if (it2 == perFaceToExpMap.end())
457  {
458  if (m_session->GetComm()->GetSize() > 1 &&
459  !ent.isLocal)
460  {
461  continue;
462  }
463  else
464  {
465  ASSERTL1(false, "Periodic edge not found!");
466  }
467  }
468 
470  "Periodic face in non-forward space?");
471 
472  int offset2 = m_trace->GetPhys_Offset(
473  elmtToTrace[it2->second.first][it2->second.second]->
474  GetElmtId());
475 
476  // Calculate relative orientations between faces to
477  // calculate copying map.
478  int nquad1 = elmtToTrace[n][e]->GetNumPoints(0);
479  int nquad2 = elmtToTrace[n][e]->GetNumPoints(1);
480 
481  vector<int> tmpBwd(nquad1*nquad2);
482  vector<int> tmpFwd(nquad1*nquad2);
483 
488  {
489  for (int i = 0; i < nquad2; ++i)
490  {
491  for (int j = 0; j < nquad1; ++j)
492  {
493  tmpBwd[i*nquad1+j] = offset2 + i*nquad1+j;
494  tmpFwd[i*nquad1+j] = offset + j*nquad2+i;
495  }
496  }
497  }
498  else
499  {
500  for (int i = 0; i < nquad2; ++i)
501  {
502  for (int j = 0; j < nquad1; ++j)
503  {
504  tmpBwd[i*nquad1+j] = offset2 + i*nquad1+j;
505  tmpFwd[i*nquad1+j] = offset + i*nquad1+j;
506  }
507  }
508  }
509 
514  {
515  // Reverse x direction
516  for (int i = 0; i < nquad2; ++i)
517  {
518  for (int j = 0; j < nquad1/2; ++j)
519  {
520  swap(tmpFwd[i*nquad1 + j],
521  tmpFwd[i*nquad1 + nquad1-j-1]);
522  }
523  }
524  }
525 
530  {
531  // Reverse y direction
532  for (int j = 0; j < nquad1; ++j)
533  {
534  for (int i = 0; i < nquad2/2; ++i)
535  {
536  swap(tmpFwd[i*nquad1 + j],
537  tmpFwd[(nquad2-i-1)*nquad1 + j]);
538  }
539  }
540  }
541 
542  for (int i = 0; i < nquad1*nquad2; ++i)
543  {
544  m_periodicFwdCopy.push_back(tmpFwd[i]);
545  m_periodicBwdCopy.push_back(tmpBwd[i]);
546  }
547  }
548  }
549  }
550 
552  AllocateSharedPtr(*this, m_trace, elmtToTrace,
554 
555  }
556 
557  /**
558  * For each boundary region, checks that the types and number of
559  * boundary expansions in that region match.
560  *
561  * @param In ContField3D to compare with.
562  * @return true if boundary conditions match.
563  */
565  const DisContField3D &In)
566  {
567  int i;
568  bool returnval = true;
569 
570  for(i = 0; i < m_bndConditions.num_elements(); ++i)
571  {
572 
573  // check to see if boundary condition type is the same
574  // and there are the same number of boundary
575  // conditions in the boundary definition.
576  if((m_bndConditions[i]->GetBoundaryConditionType()
577  != In.m_bndConditions[i]->GetBoundaryConditionType())||
579  != In.m_bndCondExpansions[i]->GetExpSize()))
580  {
581  returnval = false;
582  break;
583  }
584  }
585 
586  // Compare with all other processes. Return true only if all
587  // processes report having the same boundary conditions.
588  int vSame = returnval ? 1 : 0;
589  m_comm->AllReduce(vSame, LibUtilities::ReduceMin);
590 
591  return (vSame == 1);
592  }
593 
594  /**
595  * According to their boundary region, the separate segmental boundary
596  * expansions are bundled together in an object of the class
597  * MultiRegions#ExpList2D.
598  *
599  * \param graph3D A mesh, containing information about the domain and
600  * the spectral/hp element expansion.
601  * \param bcs An entity containing information about the boundaries and
602  * boundary conditions.
603  * \param variable An optional parameter to indicate for which variable
604  * the boundary conditions should be discretised.
605  */
607  const SpatialDomains::MeshGraphSharedPtr &graph3D,
609  const std::string &variable)
610  {
611  int cnt = 0;
614 
616  bcs.GetBoundaryRegions();
618  bcs.GetBoundaryConditions();
619 
624 
625  // list Dirichlet boundaries first
626  for (auto &it : bregions)
627  {
628  locBCond = GetBoundaryCondition(
629  bconditions, it.first, variable);
631  ::AllocateSharedPtr(m_session, *(it.second),
632  graph3D, variable, locBCond->GetComm());
633 
634  // Set up normals on non-Dirichlet boundary conditions
635  if(locBCond->GetBoundaryConditionType() !=
637  {
639  }
640 
641  m_bndCondExpansions[cnt] = locExpList;
642  m_bndConditions[cnt++] = locBCond;
643  }
644  }
645 
646  /**
647  * @brief Determine the periodic faces, edges and vertices for the given
648  * graph.
649  *
650  * @param bcs Information about the boundary conditions.
651  * @param variable Specifies the field.
652  */
655  const std::string &variable)
656  {
658  = bcs.GetBoundaryRegions();
660  = bcs.GetBoundaryConditions();
661 
663  m_session->GetComm()->GetRowComm();
665  m_graph->GetCompositeOrdering();
667  m_graph->GetBndRegionOrdering();
669  m_graph->GetComposites();
670 
671  // perComps: Stores a unique collection of pairs of periodic
672  // composites (i.e. if composites 1 and 2 are periodic then this map
673  // will contain either the pair (1,2) or (2,1) but not both).
674  //
675  // The four maps allVerts, allCoord, allEdges and allOrient map a
676  // periodic face to a vector containing the vertex ids of the face;
677  // their coordinates; the edge ids of the face; and their
678  // orientation within that face respectively.
679  //
680  // Finally the three sets locVerts, locEdges and locFaces store any
681  // vertices, edges and faces that belong to a periodic composite and
682  // lie on this process.
683  map<int,RotPeriodicInfo> rotComp;
684  map<int,int> perComps;
685  map<int,vector<int> > allVerts;
686  map<int,SpatialDomains::PointGeomVector> allCoord;
687  map<int,vector<int> > allEdges;
688  map<int,vector<StdRegions::Orientation> > allOrient;
689  set<int> locVerts;
690  set<int> locEdges;
691  set<int> locFaces;
692 
693  int region1ID, region2ID, i, j, k, cnt;
695 
696  // Set up a set of all local verts and edges.
697  for(i = 0; i < (*m_exp).size(); ++i)
698  {
699  for(j = 0; j < (*m_exp)[i]->GetNverts(); ++j)
700  {
701  int id = (*m_exp)[i]->GetGeom()->GetVid(j);
702  locVerts.insert(id);
703  }
704 
705  for(j = 0; j < (*m_exp)[i]->GetNedges(); ++j)
706  {
707  int id = (*m_exp)[i]->GetGeom()->GetEid(j);
708  locEdges.insert(id);
709  }
710  }
711 
712  // Begin by populating the perComps map. We loop over all periodic
713  // boundary conditions and determine the composite associated with
714  // it, then fill out the all* maps.
715  for (auto &it : bregions)
716  {
717 
718  locBCond = GetBoundaryCondition(
719  bconditions, it.first, variable);
720 
721  if (locBCond->GetBoundaryConditionType()
723  {
724  continue;
725  }
726 
727  // Identify periodic boundary region IDs.
728  region1ID = it.first;
729  region2ID = std::static_pointer_cast<
731  locBCond)->m_connectedBoundaryRegion;
732 
733  // Check the region only contains a single composite.
734  ASSERTL0(it.second->size() == 1,
735  "Boundary region "+boost::lexical_cast<string>(
736  region1ID)+" should only contain 1 composite.");
737 
738  // From this identify composites by looking at the original
739  // boundary region ordering. Note that in serial the mesh
740  // partitioner is not run, so this map will be empty and
741  // therefore needs to be populated by using the corresponding
742  // boundary region.
743  int cId1, cId2;
744  if (vComm->GetSize() == 1)
745  {
746  cId1 = it.second->begin()->first;
747  cId2 = bregions.find(region2ID)->second->begin()->first;
748  }
749  else
750  {
751  cId1 = bndRegOrder.find(region1ID)->second[0];
752  cId2 = bndRegOrder.find(region2ID)->second[0];
753  }
754 
755  // check to see if boundary is rotationally aligned
756  if(boost::icontains(locBCond->GetUserDefined(),"Rotated"))
757  {
758  vector<string> tmpstr;
759 
760  boost::split(tmpstr,locBCond->GetUserDefined(),
761  boost::is_any_of(":"));
762 
763  if(boost::iequals(tmpstr[0],"Rotated"))
764  {
765  ASSERTL1(tmpstr.size() > 2,
766  "Expected Rotated user defined string to "
767  "contain direction and rotation angle "
768  "and optionally a tolerance, "
769  "i.e. Rotated:dir:PI/2:1e-6");
770 
771 
772  ASSERTL1((tmpstr[1] == "x")||(tmpstr[1] == "y")
773  ||(tmpstr[1] == "z"), "Rotated Dir is "
774  "not specified as x,y or z");
775 
776  RotPeriodicInfo RotInfo;
777  RotInfo.m_dir = (tmpstr[1] == "x")? 0:
778  (tmpstr[1] == "y")? 1:2;
779 
781  int ExprId = strEval.DefineFunction("", tmpstr[2]);
782  RotInfo.m_angle = strEval.Evaluate(ExprId);
783 
784  if(tmpstr.size() == 4)
785  {
786  try {
787  RotInfo.m_tol = boost::lexical_cast
788  <NekDouble>(tmpstr[3]);
789  }
790  catch (...) {
792  "failed to cast tolerance input "
793  "to a double value in Rotated"
794  "boundary information");
795  }
796  }
797  else
798  {
799  RotInfo.m_tol = 1e-8;
800  }
801  rotComp[cId1] = RotInfo;
802  }
803  }
804 
805  SpatialDomains::CompositeSharedPtr c = it.second->begin()->second;
806 
807  vector<unsigned int> tmpOrder;
808 
809  // store the rotation info of this
810 
811  // From the composite, we now construct the allVerts, allEdges
812  // and allCoord map so that they can be transferred across
813  // processors. We also populate the locFaces set to store a
814  // record of all faces local to this process.
815  for (i = 0; i < c->m_geomVec.size(); ++i)
816  {
818  std::dynamic_pointer_cast<
819  SpatialDomains::Geometry2D>(c->m_geomVec[i]);
820  ASSERTL1(faceGeom, "Unable to cast to shared ptr");
821 
822  // Get geometry ID of this face and store in locFaces.
823  int faceId = c->m_geomVec[i]->GetGlobalID();
824  locFaces.insert(faceId);
825 
826  // In serial, mesh partitioning will not have occurred so
827  // need to fill composite ordering map manually.
828  if (vComm->GetSize() == 1)
829  {
830  tmpOrder.push_back(c->m_geomVec[i]->GetGlobalID());
831  }
832 
833  // Loop over vertices and edges of the face to populate
834  // allVerts, allEdges and allCoord maps.
835  vector<int> vertList, edgeList;
837  vector<StdRegions::Orientation> orientVec;
838  for (j = 0; j < faceGeom->GetNumVerts(); ++j)
839  {
840  vertList .push_back(faceGeom->GetVid (j));
841  edgeList .push_back(faceGeom->GetEid (j));
842  coordVec .push_back(faceGeom->GetVertex(j));
843  orientVec.push_back(faceGeom->GetEorient(j));
844  }
845 
846  allVerts [faceId] = vertList;
847  allEdges [faceId] = edgeList;
848  allCoord [faceId] = coordVec;
849  allOrient[faceId] = orientVec;
850  }
851 
852  // In serial, record the composite ordering in compOrder for
853  // later in the routine.
854  if (vComm->GetSize() == 1)
855  {
856  compOrder[it.second->begin()->first] = tmpOrder;
857  }
858 
859  // See if we already have either region1 or region2 stored in
860  // perComps map already and do a sanity check to ensure regions
861  // are mutually periodic.
862  if (perComps.count(cId1) == 0)
863  {
864  if (perComps.count(cId2) == 0)
865  {
866  perComps[cId1] = cId2;
867  }
868  else
869  {
870  std::stringstream ss;
871  ss << "Boundary region " << cId2 << " should be "
872  << "periodic with " << perComps[cId2] << " but "
873  << "found " << cId1 << " instead!";
874  ASSERTL0(perComps[cId2] == cId1, ss.str());
875  }
876  }
877  else
878  {
879  std::stringstream ss;
880  ss << "Boundary region " << cId1 << " should be "
881  << "periodic with " << perComps[cId1] << " but "
882  << "found " << cId2 << " instead!";
883  ASSERTL0(perComps[cId1] == cId1, ss.str());
884  }
885  }
886 
887  // The next routines process local face lists to exchange vertices,
888  // edges and faces.
889  int n = vComm->GetSize();
890  int p = vComm->GetRank();
891  int totFaces;
892  Array<OneD, int> facecounts(n,0);
893  Array<OneD, int> vertcounts(n,0);
894  Array<OneD, int> faceoffset(n,0);
895  Array<OneD, int> vertoffset(n,0);
896 
897  Array<OneD, int> rotcounts(n,0);
898  Array<OneD, int> rotoffset(n,0);
899 
900  rotcounts[p] = rotComp.size();
901  vComm->AllReduce(rotcounts, LibUtilities::ReduceSum);
902  int totrot = Vmath::Vsum(n,rotcounts,1);
903 
904  if(totrot)
905  {
906  for (i = 1; i < n ; ++i)
907  {
908  rotoffset[i] = rotoffset[i-1] + rotcounts[i-1];
909  }
910 
911  Array<OneD, int> compid(totrot,0);
912  Array<OneD, int> rotdir(totrot,0);
913  Array<OneD, NekDouble> rotangle(totrot,0.0);
914  Array<OneD, NekDouble> rottol(totrot,0.0);
915 
916  // fill in rotational informaiton
917  auto rIt = rotComp.begin();
918 
919  for(i = 0; rIt != rotComp.end(); ++rIt)
920  {
921  compid [rotoffset[p] + i ] = rIt->first;
922  rotdir [rotoffset[p] + i ] = rIt->second.m_dir;
923  rotangle[rotoffset[p] + i ] = rIt->second.m_angle;
924  rottol [rotoffset[p] + i++] = rIt->second.m_tol;
925  }
926 
927  vComm->AllReduce(compid, LibUtilities::ReduceSum);
928  vComm->AllReduce(rotdir, LibUtilities::ReduceSum);
929  vComm->AllReduce(rotangle, LibUtilities::ReduceSum);
930  vComm->AllReduce(rottol, LibUtilities::ReduceSum);
931 
932  // Fill in full rotational composite list
933  for(i =0; i < totrot; ++i)
934  {
935  RotPeriodicInfo rinfo(rotdir[i],rotangle[i], rottol[i]);
936 
937  rotComp[compid[i]] = rinfo;
938  }
939  }
940 
941  // First exchange the number of faces on each process.
942  facecounts[p] = locFaces.size();
943  vComm->AllReduce(facecounts, LibUtilities::ReduceSum);
944 
945  // Set up an offset map to allow us to distribute face IDs to all
946  // processors.
947  faceoffset[0] = 0;
948  for (i = 1; i < n; ++i)
949  {
950  faceoffset[i] = faceoffset[i-1] + facecounts[i-1];
951  }
952 
953  // Calculate total number of faces.
954  totFaces = Vmath::Vsum(n, facecounts, 1);
955 
956  // faceIds holds face IDs for each periodic face. faceVerts holds
957  // the number of vertices in this face.
958  Array<OneD, int> faceIds (totFaces, 0);
959  Array<OneD, int> faceVerts(totFaces, 0);
960 
961  // Process p writes IDs of its faces into position faceoffset[p] of
962  // faceIds which allows us to perform an AllReduce to distribute
963  // information amongst processors.
964  auto sIt = locFaces.begin();
965  for (i = 0; sIt != locFaces.end(); ++sIt)
966  {
967  faceIds [faceoffset[p] + i ] = *sIt;
968  faceVerts[faceoffset[p] + i++] = allVerts[*sIt].size();
969  }
970 
971  vComm->AllReduce(faceIds, LibUtilities::ReduceSum);
972  vComm->AllReduce(faceVerts, LibUtilities::ReduceSum);
973 
974  // procVerts holds number of vertices (and also edges since each
975  // face is 2D) on each process.
976  Array<OneD, int> procVerts(n,0);
977  int nTotVerts;
978 
979  // Note if there are no periodic faces at all calling Vsum will
980  // cause a segfault.
981  if (totFaces > 0)
982  {
983  // Calculate number of vertices on each processor.
984  nTotVerts = Vmath::Vsum(totFaces, faceVerts, 1);
985  }
986  else
987  {
988  nTotVerts = 0;
989  }
990 
991  for (i = 0; i < n; ++i)
992  {
993  if (facecounts[i] > 0)
994  {
995  procVerts[i] = Vmath::Vsum(
996  facecounts[i], faceVerts + faceoffset[i], 1);
997  }
998  else
999  {
1000  procVerts[i] = 0;
1001  }
1002  }
1003 
1004  // vertoffset is defined in the same manner as edgeoffset
1005  // beforehand.
1006  vertoffset[0] = 0;
1007  for (i = 1; i < n; ++i)
1008  {
1009  vertoffset[i] = vertoffset[i-1] + procVerts[i-1];
1010  }
1011 
1012  // At this point we exchange all vertex IDs, edge IDs and vertex
1013  // coordinates for each face. The coordinates are necessary because
1014  // we need to calculate relative face orientations between periodic
1015  // faces to determined edge and vertex connectivity.
1016  Array<OneD, int> vertIds(nTotVerts, 0);
1017  Array<OneD, int> edgeIds(nTotVerts, 0);
1018  Array<OneD, int> edgeOrt(nTotVerts, 0);
1019  Array<OneD, NekDouble> vertX (nTotVerts, 0.0);
1020  Array<OneD, NekDouble> vertY (nTotVerts, 0.0);
1021  Array<OneD, NekDouble> vertZ (nTotVerts, 0.0);
1022 
1023  for (cnt = 0, sIt = locFaces.begin();
1024  sIt != locFaces.end(); ++sIt)
1025  {
1026  for (j = 0; j < allVerts[*sIt].size(); ++j)
1027  {
1028  int vertId = allVerts[*sIt][j];
1029  vertIds[vertoffset[p] + cnt ] = vertId;
1030  vertX [vertoffset[p] + cnt ] = (*allCoord[*sIt][j])(0);
1031  vertY [vertoffset[p] + cnt ] = (*allCoord[*sIt][j])(1);
1032  vertZ [vertoffset[p] + cnt ] = (*allCoord[*sIt][j])(2);
1033  edgeIds[vertoffset[p] + cnt ] = allEdges [*sIt][j];
1034  edgeOrt[vertoffset[p] + cnt++] = allOrient[*sIt][j];
1035  }
1036  }
1037 
1038  vComm->AllReduce(vertIds, LibUtilities::ReduceSum);
1039  vComm->AllReduce(vertX, LibUtilities::ReduceSum);
1040  vComm->AllReduce(vertY, LibUtilities::ReduceSum);
1041  vComm->AllReduce(vertZ, LibUtilities::ReduceSum);
1042  vComm->AllReduce(edgeIds, LibUtilities::ReduceSum);
1043  vComm->AllReduce(edgeOrt, LibUtilities::ReduceSum);
1044 
1045  // Finally now we have all of this information, we construct maps
1046  // which make accessing the information easier. These are
1047  // conceptually the same as all* maps at the beginning of the
1048  // routine, but now hold information for all periodic vertices.
1049  map<int, vector<int> > vertMap;
1050  map<int, vector<int> > edgeMap;
1051  map<int, SpatialDomains::PointGeomVector> coordMap;
1052 
1053  // These final two maps are required for determining the relative
1054  // orientation of periodic edges. vCoMap associates vertex IDs with
1055  // their coordinates, and eIdMap maps an edge ID to the two vertices
1056  // which construct it.
1057  map<int, SpatialDomains::PointGeomSharedPtr> vCoMap;
1058  map<int, pair<int, int> > eIdMap;
1059 
1060  for (cnt = i = 0; i < totFaces; ++i)
1061  {
1062  vector<int> edges(faceVerts[i]);
1063  vector<int> verts(faceVerts[i]);
1064  SpatialDomains::PointGeomVector coord(faceVerts[i]);
1065 
1066  // Keep track of cnt to enable correct edge vertices to be
1067  // inserted into eIdMap.
1068  int tmp = cnt;
1069  for (j = 0; j < faceVerts[i]; ++j, ++cnt)
1070  {
1071  edges[j] = edgeIds[cnt];
1072  verts[j] = vertIds[cnt];
1075  3, verts[j], vertX[cnt], vertY[cnt], vertZ[cnt]);
1076  vCoMap[vertIds[cnt]] = coord[j];
1077 
1078  // Try to insert edge into the eIdMap to avoid re-inserting.
1079  auto testIns = eIdMap.insert(
1080  make_pair(
1081  edgeIds[cnt],
1082  make_pair(vertIds[tmp+j],
1083  vertIds[tmp+((j+1) % faceVerts[i])])));
1084 
1085  if (testIns.second == false)
1086  {
1087  continue;
1088  }
1089 
1090  // If the edge is reversed with respect to the face, then
1091  // swap the edges so that we have the original ordering of
1092  // the edge in the 3D element. This is necessary to properly
1093  // determine edge orientation. Note that the logic relies on
1094  // the fact that all edge forward directions are CCW
1095  // orientated: we use a tensor product ordering for 2D
1096  // elements so need to reverse this for edge IDs 2 and 3.
1097  StdRegions::Orientation edgeOrient =
1098  static_cast<StdRegions::Orientation>(edgeOrt[cnt]);
1099  if (j > 1)
1100  {
1101  edgeOrient = edgeOrient == StdRegions::eForwards ?
1103  }
1104 
1105  if (edgeOrient == StdRegions::eBackwards)
1106  {
1107  swap(testIns.first->second.first,
1108  testIns.first->second.second);
1109  }
1110  }
1111 
1112  vertMap [faceIds[i]] = verts;
1113  edgeMap [faceIds[i]] = edges;
1114  coordMap[faceIds[i]] = coord;
1115  }
1116 
1117  // Go through list of composites and figure out which edges are
1118  // parallel from original ordering in session file. This includes
1119  // composites which are not necessarily on this process.
1120 
1121  // Store temporary map of periodic vertices which will hold all
1122  // periodic vertices on the entire mesh so that doubly periodic
1123  // vertices/edges can be counted properly across partitions. Local
1124  // vertices/edges are copied into m_periodicVerts and
1125  // m_periodicEdges at the end of the function.
1126  PeriodicMap periodicVerts, periodicEdges;
1127 
1128  // Construct two maps which determine how vertices and edges of
1129  // faces connect given a specific face orientation. The key of the
1130  // map is the number of vertices in the face, used to determine
1131  // difference between tris and quads.
1132  map<int, map<StdRegions::Orientation, vector<int> > > vmap;
1133  map<int, map<StdRegions::Orientation, vector<int> > > emap;
1134 
1135  map<StdRegions::Orientation, vector<int> > quadVertMap;
1136  quadVertMap[StdRegions::eDir1FwdDir1_Dir2FwdDir2] = {0,1,2,3};
1137  quadVertMap[StdRegions::eDir1FwdDir1_Dir2BwdDir2] = {3,2,1,0};
1138  quadVertMap[StdRegions::eDir1BwdDir1_Dir2FwdDir2] = {1,0,3,2};
1139  quadVertMap[StdRegions::eDir1BwdDir1_Dir2BwdDir2] = {2,3,0,1};
1140  quadVertMap[StdRegions::eDir1FwdDir2_Dir2FwdDir1] = {0,3,2,1};
1141  quadVertMap[StdRegions::eDir1FwdDir2_Dir2BwdDir1] = {1,2,3,0};
1142  quadVertMap[StdRegions::eDir1BwdDir2_Dir2FwdDir1] = {3,0,1,2};
1143  quadVertMap[StdRegions::eDir1BwdDir2_Dir2BwdDir1] = {2,1,0,3};
1144 
1145  map<StdRegions::Orientation, vector<int> > quadEdgeMap;
1146  quadEdgeMap[StdRegions::eDir1FwdDir1_Dir2FwdDir2] = {0,1,2,3};
1147  quadEdgeMap[StdRegions::eDir1FwdDir1_Dir2BwdDir2] = {2,1,0,3};
1148  quadEdgeMap[StdRegions::eDir1BwdDir1_Dir2FwdDir2] = {0,3,2,1};
1149  quadEdgeMap[StdRegions::eDir1BwdDir1_Dir2BwdDir2] = {2,3,0,1};
1150  quadEdgeMap[StdRegions::eDir1FwdDir2_Dir2FwdDir1] = {3,2,1,0};
1151  quadEdgeMap[StdRegions::eDir1FwdDir2_Dir2BwdDir1] = {1,2,3,0};
1152  quadEdgeMap[StdRegions::eDir1BwdDir2_Dir2FwdDir1] = {3,0,1,2};
1153  quadEdgeMap[StdRegions::eDir1BwdDir2_Dir2BwdDir1] = {1,0,3,2};
1154 
1155  map<StdRegions::Orientation, vector<int> > triVertMap;
1156  triVertMap[StdRegions::eDir1FwdDir1_Dir2FwdDir2] = {0,1,2};
1157  triVertMap[StdRegions::eDir1BwdDir1_Dir2FwdDir2] = {1,0,2};
1158 
1159  map<StdRegions::Orientation, vector<int> > triEdgeMap;
1160  triEdgeMap[StdRegions::eDir1FwdDir1_Dir2FwdDir2] = {0,1,2};
1161  triEdgeMap[StdRegions::eDir1BwdDir1_Dir2FwdDir2] = {0,2,1};
1162 
1163  vmap[3] = triVertMap;
1164  vmap[4] = quadVertMap;
1165  emap[3] = triEdgeMap;
1166  emap[4] = quadEdgeMap;
1167 
1168  map<int,int> allCompPairs;
1169 
1170  // Collect composite ides of each periodic face for use if rotation is required
1171  map<int,int> fIdToCompId;
1172 
1173  // Finally we have enough information to populate the periodic
1174  // vertex, edge and face maps. Begin by looping over all pairs of
1175  // periodic composites to determine pairs of periodic faces.
1176  for (auto &cIt : perComps)
1177  {
1179  const int id1 = cIt.first;
1180  const int id2 = cIt.second;
1181  std::string id1s = boost::lexical_cast<string>(id1);
1182  std::string id2s = boost::lexical_cast<string>(id2);
1183 
1184  if (compMap.count(id1) > 0)
1185  {
1186  c[0] = compMap[id1];
1187  }
1188 
1189  if (compMap.count(id2) > 0)
1190  {
1191  c[1] = compMap[id2];
1192  }
1193 
1194  ASSERTL0(c[0] || c[1],
1195  "Neither composite not found on this process!");
1196 
1197  // Loop over composite ordering to construct list of all
1198  // periodic faces, regardless of whether they are on this
1199  // process.
1200  map<int,int> compPairs;
1201 
1202 
1203  ASSERTL0(compOrder.count(id1) > 0,
1204  "Unable to find composite "+id1s+" in order map.");
1205  ASSERTL0(compOrder.count(id2) > 0,
1206  "Unable to find composite "+id2s+" in order map.");
1207  ASSERTL0(compOrder[id1].size() == compOrder[id2].size(),
1208  "Periodic composites "+id1s+" and "+id2s+
1209  " should have the same number of elements.");
1210  ASSERTL0(compOrder[id1].size() > 0,
1211  "Periodic composites "+id1s+" and "+id2s+
1212  " are empty!");
1213 
1214  // Look up composite ordering to determine pairs.
1215  for (i = 0; i < compOrder[id1].size(); ++i)
1216  {
1217  int eId1 = compOrder[id1][i];
1218  int eId2 = compOrder[id2][i];
1219 
1220  ASSERTL0(compPairs.count(eId1) == 0,
1221  "Already paired.");
1222 
1223  // Sanity check that the faces are mutually periodic.
1224  if (compPairs.count(eId2) != 0)
1225  {
1226  ASSERTL0(compPairs[eId2] == eId1, "Pairing incorrect");
1227  }
1228  compPairs[eId1] = eId2;
1229 
1230  // store a map of face ids to composite ids
1231  fIdToCompId[eId1] = id1;
1232  fIdToCompId[eId2] = id2;
1233  }
1234 
1235  // Now that we have all pairs of periodic faces, loop over the
1236  // ones local on this process and populate face/edge/vertex
1237  // maps.
1238  for (auto &pIt : compPairs)
1239  {
1240  int ids [2] = {pIt.first, pIt.second};
1241  bool local[2] = {locFaces.count(pIt.first) > 0,
1242  locFaces.count(pIt.second) > 0};
1243 
1244  ASSERTL0(coordMap.count(ids[0]) > 0 &&
1245  coordMap.count(ids[1]) > 0,
1246  "Unable to find face in coordinate map");
1247 
1248  allCompPairs[pIt.first ] = pIt.second;
1249  allCompPairs[pIt.second] = pIt.first;
1250 
1251  // Loop up coordinates of the faces, check they have the
1252  // same number of vertices.
1254  = { coordMap[ids[0]], coordMap[ids[1]] };
1255 
1256  ASSERTL0(tmpVec[0].size() == tmpVec[1].size(),
1257  "Two periodic faces have different number "
1258  "of vertices!");
1259 
1260  // o will store relative orientation of faces. Note that in
1261  // some transpose cases (Dir1FwdDir2_Dir2BwdDir1 and
1262  // Dir1BwdDir1_Dir2FwdDir1) it seems orientation will be
1263  // different going from face1->face2 instead of face2->face1
1264  // (check this).
1266  bool rotbnd = false;
1267  int dir = 0;
1268  NekDouble angle = 0.0;
1269  NekDouble sign = 1.0;
1270  NekDouble tol = 1e-8;
1271 
1272  // check to see if perioid boundary is rotated
1273  if(rotComp.count(fIdToCompId[pIt.first]))
1274  {
1275  rotbnd = true;
1276  dir = rotComp[fIdToCompId[pIt.first]].m_dir;
1277  angle = rotComp[fIdToCompId[pIt.first]].m_angle;
1278  tol = rotComp[fIdToCompId[pIt.first]].m_tol;
1279  }
1280 
1281  // Record periodic faces.
1282  for (i = 0; i < 2; ++i)
1283  {
1284  if (!local[i])
1285  {
1286  continue;
1287  }
1288 
1289  // Reference to the other face.
1290  int other = (i+1) % 2;
1291 
1292  // angle is set up for i = 0 to i = 1
1293  sign = (i == 0)? 1.0:-1.0;
1294 
1295  // Calculate relative face orientation.
1296  if (tmpVec[0].size() == 3)
1297  {
1299  tmpVec[i], tmpVec[other],
1300  rotbnd, dir, sign*angle, tol);
1301  }
1302  else
1303  {
1305  tmpVec[i], tmpVec[other],
1306  rotbnd,dir,sign*angle,tol);
1307  }
1308 
1309  // Record face ID, orientation and whether other face is
1310  // local.
1311  PeriodicEntity ent(ids [other], o,
1312  local[other]);
1313  m_periodicFaces[ids[i]].push_back(ent);
1314  }
1315 
1316  int nFaceVerts = vertMap[ids[0]].size();
1317 
1318  // Determine periodic vertices.
1319  for (i = 0; i < 2; ++i)
1320  {
1321  int other = (i+1) % 2;
1322 
1323  // angle is set up for i = 0 to i = 1
1324  sign = (i == 0)? 1.0:-1.0;
1325 
1326  // Calculate relative face orientation.
1327  if (tmpVec[0].size() == 3)
1328  {
1330  tmpVec[i], tmpVec[other], rotbnd, dir,
1331  sign*angle, tol);
1332  }
1333  else
1334  {
1336  tmpVec[i], tmpVec[other], rotbnd, dir,
1337  sign*angle, tol);
1338  }
1339 
1340  if (nFaceVerts == 3)
1341  {
1342  ASSERTL0(
1345  "Unsupported face orientation for face "+
1346  boost::lexical_cast<string>(ids[i]));
1347  }
1348 
1349  // Look up vertices for this face.
1350  vector<int> per1 = vertMap[ids[i]];
1351  vector<int> per2 = vertMap[ids[other]];
1352 
1353  // tmpMap will hold the pairs of vertices which are
1354  // periodic.
1355  map<int, pair<int, bool> > tmpMap;
1356 
1357  // Use vmap to determine which vertices connect given
1358  // the orientation o.
1359  for (j = 0; j < nFaceVerts; ++j)
1360  {
1361  int v = vmap[nFaceVerts][o][j];
1362  tmpMap[per1[j]] = make_pair(
1363  per2[v], locVerts.count(per2[v]) > 0);
1364  }
1365 
1366  // Now loop over tmpMap to associate periodic vertices.
1367  for (auto &mIt : tmpMap)
1368  {
1369  PeriodicEntity ent2(mIt.second.first,
1371  mIt.second.second);
1372 
1373  // See if this vertex has been recorded already.
1374  auto perIt = periodicVerts.find(mIt.first);
1375 
1376  if (perIt == periodicVerts.end())
1377  {
1378  // Vertex is new - just record this entity as
1379  // usual.
1380  periodicVerts[mIt.first].push_back(ent2);
1381  perIt = periodicVerts.find(mIt.first);
1382  }
1383  else
1384  {
1385  // Vertex is known - loop over the vertices
1386  // inside the record and potentially add vertex
1387  // mIt.second to the list.
1388  for (k = 0; k < perIt->second.size(); ++k)
1389  {
1390  if (perIt->second[k].id == mIt.second.first)
1391  {
1392  break;
1393  }
1394  }
1395 
1396  if (k == perIt->second.size())
1397  {
1398  perIt->second.push_back(ent2);
1399  }
1400  }
1401  }
1402  }
1403 
1404  // Determine periodic edges. Logic is the same as above,
1405  // and perhaps should be condensed to avoid replication.
1406  for (i = 0; i < 2; ++i)
1407  {
1408  int other = (i+1) % 2;
1409 
1410  // angle is set up for i = 0 to i = 1
1411  sign = (i == 0)? 1.0:-1.0;
1412 
1413  if (tmpVec[0].size() == 3)
1414  {
1416  tmpVec[i], tmpVec[other], rotbnd, dir,
1417  sign*angle, tol);
1418  }
1419  else
1420  {
1422  tmpVec[i], tmpVec[other], rotbnd, dir,
1423  sign*angle, tol);
1424  }
1425 
1426  vector<int> per1 = edgeMap[ids[i]];
1427  vector<int> per2 = edgeMap[ids[other]];
1428 
1429  map<int, pair<int, bool> > tmpMap;
1430 
1431  for (j = 0; j < nFaceVerts; ++j)
1432  {
1433  int e = emap[nFaceVerts][o][j];
1434  tmpMap[per1[j]] = make_pair(
1435  per2[e], locEdges.count(per2[e]) > 0);
1436  }
1437 
1438  for (auto &mIt : tmpMap)
1439  {
1440  // Note we assume orientation of edges is forwards -
1441  // this may be reversed later.
1442  PeriodicEntity ent2(mIt.second.first,
1444  mIt.second.second);
1445  auto perIt = periodicEdges.find(mIt.first);
1446 
1447  if (perIt == periodicEdges.end())
1448  {
1449  periodicEdges[mIt.first].push_back(ent2);
1450  perIt = periodicEdges.find(mIt.first);
1451  }
1452  else
1453  {
1454  for (k = 0; k < perIt->second.size(); ++k)
1455  {
1456  if (perIt->second[k].id == mIt.second.first)
1457  {
1458  break;
1459  }
1460  }
1461 
1462  if (k == perIt->second.size())
1463  {
1464  perIt->second.push_back(ent2);
1465  }
1466  }
1467  }
1468  }
1469  }
1470  }
1471 
1472  Array<OneD, int> pairSizes(n, 0);
1473  pairSizes[p] = allCompPairs.size();
1474  vComm->AllReduce(pairSizes, LibUtilities::ReduceSum);
1475 
1476  int totPairSizes = Vmath::Vsum(n, pairSizes, 1);
1477 
1478  Array<OneD, int> pairOffsets(n, 0);
1479  pairOffsets[0] = 0;
1480 
1481  for (i = 1; i < n; ++i)
1482  {
1483  pairOffsets[i] = pairOffsets[i-1] + pairSizes[i-1];
1484  }
1485 
1486 
1487  ASSERTL1(allCompPairs.size() == fIdToCompId.size(),
1488  "At this point the size of allCompPairs "
1489  "should have been the same as fIdToCompId");
1490 
1491  Array<OneD, int> first (totPairSizes, 0);
1492  Array<OneD, int> second(totPairSizes, 0);
1493 
1494  cnt = pairOffsets[p];
1495 
1496  for (auto &pIt : allCompPairs)
1497  {
1498  first [cnt ] = pIt.first;
1499  second[cnt++] = pIt.second;
1500  }
1501 
1502  vComm->AllReduce(first, LibUtilities::ReduceSum);
1503  vComm->AllReduce(second, LibUtilities::ReduceSum);
1504 
1505  allCompPairs.clear();
1506 
1507  for(cnt = 0; cnt < totPairSizes; ++cnt)
1508  {
1509  allCompPairs[first[cnt]] = second[cnt];
1510  }
1511 
1512  // make global list of faces to composite ids if rotComp is non-zero
1513 
1514  if(rotComp.size())
1515  {
1516  Vmath::Zero(totPairSizes,first,1);
1517  Vmath::Zero(totPairSizes,second,1);
1518 
1519  cnt = pairOffsets[p];
1520 
1521  for (auto &pIt : fIdToCompId)
1522  {
1523  first [cnt ] = pIt.first;
1524  second[cnt++] = pIt.second;
1525  }
1526 
1527  vComm->AllReduce(first, LibUtilities::ReduceSum);
1528  vComm->AllReduce(second, LibUtilities::ReduceSum);
1529 
1530  fIdToCompId.clear();
1531 
1532  for(cnt = 0; cnt < totPairSizes; ++cnt)
1533  {
1534  fIdToCompId[first[cnt]] = second[cnt];
1535  }
1536  }
1537 
1538  // also will need an edge id to composite id at end of routine
1539  map<int,int> eIdToCompId;
1540 
1541  // Search for periodic vertices and edges which are not
1542  // in a periodic composite but lie in this process. First,
1543  // loop over all information we have from other
1544  // processors.
1545  for (cnt = i = 0; i < totFaces; ++i)
1546  {
1547  bool rotbnd = false;
1548  int dir = 0;
1549  NekDouble angle = 0.0;
1550  NekDouble tol = 1e-8;
1551 
1552  int faceId = faceIds[i];
1553 
1554  ASSERTL0(allCompPairs.count(faceId) > 0,
1555  "Unable to find matching periodic face.");
1556 
1557  int perFaceId = allCompPairs[faceId];
1558 
1559  // check to see if periodic boundary is rotated
1560  ASSERTL1(fIdToCompId.count(faceId) > 0,"Face " +
1561  boost::lexical_cast<string>(faceId) +
1562  " not found in fIdtoCompId map");
1563  if(rotComp.count(fIdToCompId[faceId]))
1564  {
1565  rotbnd = true;
1566  dir = rotComp[fIdToCompId[faceId]].m_dir;
1567  angle = rotComp[fIdToCompId[faceId]].m_angle;
1568  tol = rotComp[fIdToCompId[faceId]].m_tol;
1569  }
1570 
1571  for (j = 0; j < faceVerts[i]; ++j, ++cnt)
1572  {
1573  int vId = vertIds[cnt];
1574 
1575  auto perId = periodicVerts.find(vId);
1576 
1577  if (perId == periodicVerts.end())
1578  {
1579 
1580  // This vertex is not included in the
1581  // map. Figure out which vertex it is supposed
1582  // to be periodic with. perFaceId is the face
1583  // ID which is periodic with faceId. The logic
1584  // is much the same as the loop above.
1586  = { coordMap[faceId], coordMap[perFaceId] };
1587 
1588  int nFaceVerts = tmpVec[0].size();
1589  StdRegions::Orientation o = nFaceVerts == 3 ?
1591  tmpVec[0], tmpVec[1], rotbnd, dir, angle, tol):
1593  tmpVec[0], tmpVec[1], rotbnd, dir, angle, tol);
1594 
1595  // Use vmap to determine which vertex of the other face
1596  // should be periodic with this one.
1597  int perVertexId = vertMap[perFaceId][vmap[nFaceVerts][o][j]];
1598 
1599 
1600  PeriodicEntity ent(perVertexId,
1602  locVerts.count(perVertexId) > 0);
1603 
1604  periodicVerts[vId].push_back(ent);
1605  }
1606 
1607  int eId = edgeIds[cnt];
1608 
1609  perId = periodicEdges.find(eId);
1610 
1611  // this map is required at very end to determine rotation of edges.
1612  if(rotbnd)
1613  {
1614  eIdToCompId[eId] = fIdToCompId[faceId];
1615  }
1616 
1617  if (perId == periodicEdges.end())
1618  {
1619  // This edge is not included in the map. Figure
1620  // out which edge it is supposed to be periodic
1621  // with. perFaceId is the face ID which is
1622  // periodic with faceId. The logic is much the
1623  // same as the loop above.
1625  = { coordMap[faceId], coordMap[perFaceId] };
1626 
1627  int nFaceEdges = tmpVec[0].size();
1628  StdRegions::Orientation o = nFaceEdges == 3 ?
1630  tmpVec[0], tmpVec[1], rotbnd, dir, angle, tol):
1632  tmpVec[0], tmpVec[1], rotbnd, dir, angle, tol);
1633 
1634  // Use emap to determine which edge of the other
1635  // face should be periodic with this one.
1636  int perEdgeId = edgeMap[perFaceId][emap[nFaceEdges][o][j]];
1637 
1638  PeriodicEntity ent(perEdgeId,
1640  locEdges.count(perEdgeId) > 0);
1641 
1642  periodicEdges[eId].push_back(ent);
1643 
1644 
1645  // this map is required at very end to
1646  // determine rotation of edges.
1647  if(rotbnd)
1648  {
1649  eIdToCompId[perEdgeId] = fIdToCompId[perFaceId];
1650  }
1651  }
1652  }
1653  }
1654 
1655  // Finally, we must loop over the periodicVerts and periodicEdges
1656  // map to complete connectivity information.
1657  for (auto &perIt : periodicVerts)
1658  {
1659  // For each vertex that is periodic with this one...
1660  for (i = 0; i < perIt.second.size(); ++i)
1661  {
1662  // Find the vertex in the periodicVerts map...
1663  auto perIt2 = periodicVerts.find(perIt.second[i].id);
1664  ASSERTL0(perIt2 != periodicVerts.end(),
1665  "Couldn't find periodic vertex.");
1666 
1667  // Now search through this vertex's list and make sure that
1668  // we have a record of any vertices which aren't in the
1669  // original list.
1670  for (j = 0; j < perIt2->second.size(); ++j)
1671  {
1672  if (perIt2->second[j].id == perIt.first)
1673  {
1674  continue;
1675  }
1676 
1677  for (k = 0; k < perIt.second.size(); ++k)
1678  {
1679  if (perIt2->second[j].id == perIt.second[k].id)
1680  {
1681  break;
1682  }
1683  }
1684 
1685  if (k == perIt.second.size())
1686  {
1687  perIt.second.push_back(perIt2->second[j]);
1688  }
1689  }
1690  }
1691  }
1692 
1693  for (auto &perIt : periodicEdges)
1694  {
1695  for (i = 0; i < perIt.second.size(); ++i)
1696  {
1697  auto perIt2 = periodicEdges.find(perIt.second[i].id);
1698  ASSERTL0(perIt2 != periodicEdges.end(),
1699  "Couldn't find periodic edge.");
1700 
1701  for (j = 0; j < perIt2->second.size(); ++j)
1702  {
1703  if (perIt2->second[j].id == perIt.first)
1704  {
1705  continue;
1706  }
1707 
1708  for (k = 0; k < perIt.second.size(); ++k)
1709  {
1710  if (perIt2->second[j].id == perIt.second[k].id)
1711  {
1712  break;
1713  }
1714  }
1715 
1716  if (k == perIt.second.size())
1717  {
1718  perIt.second.push_back(perIt2->second[j]);
1719  }
1720  }
1721  }
1722  }
1723 
1724  // Loop over periodic edges to determine relative edge orientations.
1725  for (auto &perIt : periodicEdges)
1726  {
1727  bool rotbnd = false;
1728  int dir = 0;
1729  NekDouble angle = 0.0;
1730  NekDouble tol = 1e-8;
1731 
1732 
1733  // Find edge coordinates
1734  auto eIt = eIdMap.find(perIt.first);
1735  SpatialDomains::PointGeom v[2] = {
1736  *vCoMap[eIt->second.first],
1737  *vCoMap[eIt->second.second]
1738  };
1739 
1740  // check to see if perioid boundary is rotated
1741  if(rotComp.count(eIdToCompId[perIt.first]))
1742  {
1743  rotbnd = true;
1744  dir = rotComp[eIdToCompId[perIt.first]].m_dir;
1745  angle = rotComp[eIdToCompId[perIt.first]].m_angle;
1746  tol = rotComp[eIdToCompId[perIt.first]].m_tol;
1747  }
1748 
1749  // Loop over each edge, and construct a vector that takes us
1750  // from one vertex to another. Use this to figure out which
1751  // vertex maps to which.
1752  for (i = 0; i < perIt.second.size(); ++i)
1753  {
1754  eIt = eIdMap.find(perIt.second[i].id);
1755 
1756  SpatialDomains::PointGeom w[2] = {
1757  *vCoMap[eIt->second.first],
1758  *vCoMap[eIt->second.second]
1759  };
1760 
1761  int vMap[2] = {-1,-1};
1762  if(rotbnd)
1763  {
1764 
1766 
1767  r.Rotate(v[0],dir,angle);
1768 
1769  if(r.dist(w[0])< tol)
1770  {
1771  vMap[0] = 0;
1772  }
1773  else
1774  {
1775  r.Rotate(v[1],dir,angle);
1776  if(r.dist(w[0]) < tol)
1777  {
1778  vMap[0] = 1;
1779  }
1780  else
1781  {
1782  ASSERTL0(false,"Unable to align rotationally periodic edge vertex");
1783  }
1784  }
1785  }
1786  else // translation test
1787  {
1788  NekDouble cx = 0.5*(w[0](0)-v[0](0)+w[1](0)-v[1](0));
1789  NekDouble cy = 0.5*(w[0](1)-v[0](1)+w[1](1)-v[1](1));
1790  NekDouble cz = 0.5*(w[0](2)-v[0](2)+w[1](2)-v[1](2));
1791 
1792  for (j = 0; j < 2; ++j)
1793  {
1794  NekDouble x = v[j](0);
1795  NekDouble y = v[j](1);
1796  NekDouble z = v[j](2);
1797  for (k = 0; k < 2; ++k)
1798  {
1799  NekDouble x1 = w[k](0)-cx;
1800  NekDouble y1 = w[k](1)-cy;
1801  NekDouble z1 = w[k](2)-cz;
1802 
1803  if (sqrt((x1-x)*(x1-x)+(y1-y)*(y1-y)+(z1-z)*(z1-z))
1804  < 1e-8)
1805  {
1806  vMap[k] = j;
1807  break;
1808  }
1809  }
1810  }
1811 
1812  // Sanity check the map.
1813  ASSERTL0(vMap[0] >= 0 && vMap[1] >= 0,
1814  "Unable to align periodic edge vertex.");
1815  ASSERTL0((vMap[0] == 0 || vMap[0] == 1) &&
1816  (vMap[1] == 0 || vMap[1] == 1) &&
1817  (vMap[0] != vMap[1]),
1818  "Unable to align periodic edge vertex.");
1819  }
1820 
1821  // If 0 -> 0 then edges are aligned already; otherwise
1822  // reverse the orientation.
1823  if (vMap[0] != 0)
1824  {
1825  perIt.second[i].orient = StdRegions::eBackwards;
1826  }
1827  }
1828  }
1829 
1830  // Do one final loop over periodic vertices/edges to remove
1831  // non-local vertices/edges from map.
1832  for (auto &perIt : periodicVerts)
1833  {
1834  if (locVerts.count(perIt.first) > 0)
1835  {
1836  m_periodicVerts.insert(perIt);
1837  }
1838  }
1839 
1840  for (auto &perIt : periodicEdges)
1841  {
1842  if (locEdges.count(perIt.first) > 0)
1843  {
1844  m_periodicEdges.insert(perIt);
1845  }
1846  }
1847  }
1848 
1849  bool DisContField3D::IsLeftAdjacentFace(const int n, const int e)
1850  {
1852  m_traceMap->GetElmtToTrace()[n][e]->
1853  as<LocalRegions::Expansion2D>();
1854 
1855  int offset = m_trace->GetPhys_Offset(traceEl->GetElmtId());
1856 
1857  bool fwd = true;
1858  if (traceEl->GetLeftAdjacentElementFace () == -1 ||
1859  traceEl->GetRightAdjacentElementFace() == -1)
1860  {
1861  // Boundary edge (1 connected element). Do nothing in
1862  // serial.
1863  auto it = m_boundaryFaces.find(traceEl->GetElmtId());
1864 
1865  // If the edge does not have a boundary condition set on
1866  // it, then assume it is a partition edge.
1867  if (it == m_boundaryFaces.end())
1868  {
1869  int traceGeomId = traceEl->GetGeom2D()->GetGlobalID();
1870  auto pIt = m_periodicFaces.find(traceGeomId);
1871 
1872  if (pIt != m_periodicFaces.end() && !pIt->second[0].isLocal)
1873  {
1874  fwd = traceGeomId == min(traceGeomId,pIt->second[0].id);
1875  }
1876  else
1877  {
1878  fwd = m_traceMap->
1879  GetTraceToUniversalMapUnique(offset) >= 0;
1880  }
1881  }
1882  }
1883  else if (traceEl->GetLeftAdjacentElementFace () != -1 &&
1884  traceEl->GetRightAdjacentElementFace() != -1)
1885  {
1886  // Non-boundary edge (2 connected elements).
1887  fwd = (traceEl->GetLeftAdjacentElementExp().get() == (*m_exp)[n].get() );
1888  }
1889  else
1890  {
1891  ASSERTL2(false, "Unconnected trace element!");
1892  }
1893 
1894  return fwd;
1895  }
1896 
1897  /**
1898  * \brief This method extracts the "forward" and "backward" trace
1899  * data from the array \a field and puts the data into output
1900  * vectors \a Fwd and \a Bwd.
1901  *
1902  * We first define the convention which defines "forwards" and
1903  * "backwards". First an association is made between the face of
1904  * each element and its corresponding face in the trace space
1905  * using the mapping #m_traceMap. The element can either be
1906  * left-adjacent or right-adjacent to this trace face (see
1907  * Expansion2D::GetLeftAdjacentElementExp). Boundary faces are
1908  * always left-adjacent since left-adjacency is populated first.
1909  *
1910  * If the element is left-adjacent we extract the face trace data
1911  * from \a field into the forward trace space \a Fwd; otherwise,
1912  * we place it in the backwards trace space \a Bwd. In this way,
1913  * we form a unique set of trace normals since these are always
1914  * extracted from left-adjacent elements.
1915  *
1916  * \param field is a NekDouble array which contains the 3D data
1917  * from which we wish to extract the backward and forward
1918  * orientated trace/face arrays.
1919  *
1920  * \return Updates a NekDouble array \a Fwd and \a Bwd
1921  */
1924  {
1925  v_GetFwdBwdTracePhys(m_phys, Fwd, Bwd);
1926  }
1927 
1929  const Array<OneD, const NekDouble> &field,
1932  {
1933  int n, cnt, npts, e;
1934 
1935  // Zero vectors.
1936  Vmath::Zero(Fwd.num_elements(), Fwd, 1);
1937  Vmath::Zero(Bwd.num_elements(), Bwd, 1);
1938 
1940  GetNLocTracePts());
1941  m_locTraceToTraceMap->LocTracesFromField(field,facevals);
1942  m_locTraceToTraceMap->InterpLocFacesToTrace(0, facevals, Fwd);
1943 
1944  Array<OneD, NekDouble> invals = facevals + m_locTraceToTraceMap->
1945  GetNFwdLocTracePts();
1946  m_locTraceToTraceMap->InterpLocFacesToTrace(1, invals, Bwd);
1947 
1948  // Fill boundary conditions into missing elements
1949  int id1, id2 = 0;
1950  cnt = 0;
1951 
1952  for(n = 0; n < m_bndCondExpansions.num_elements(); ++n)
1953  {
1954  if(m_bndConditions[n]->GetBoundaryConditionType() ==
1956  {
1957  for(e = 0; e < m_bndCondExpansions[n]->GetExpSize(); ++e)
1958  {
1959  npts = m_bndCondExpansions[n]->GetExp(e)->GetTotPoints();
1960  id1 = m_bndCondExpansions[n]->GetPhys_Offset(e);
1961  id2 = m_trace->GetPhys_Offset(
1962  m_traceMap->GetBndCondTraceToGlobalTraceMap(cnt+e));
1963  Vmath::Vcopy(npts,
1964  &(m_bndCondExpansions[n]->GetPhys())[id1], 1,
1965  &Bwd[id2], 1);
1966  }
1967 
1968  cnt += e;
1969  }
1970  else if (m_bndConditions[n]->GetBoundaryConditionType() ==
1972  m_bndConditions[n]->GetBoundaryConditionType() ==
1974  {
1975  for(e = 0; e < m_bndCondExpansions[n]->GetExpSize(); ++e)
1976  {
1977  npts = m_bndCondExpansions[n]->GetExp(e)->GetTotPoints();
1978  id1 = m_bndCondExpansions[n]->GetPhys_Offset(e);
1979  id2 = m_trace->GetPhys_Offset(
1980  m_traceMap->GetBndCondTraceToGlobalTraceMap(cnt+e));
1981 
1982  // Turning this off since we can have non-zero
1983  //Neumann in mixed CG-DG method
1984  //ASSERTL1((m_bndCondExpansions[n]->GetPhys())[id1]
1985  //== 0.0, "method not set up for non-zero
1986  //Neumann " "boundary condition");
1987 
1988  Vmath::Vcopy(npts,&Fwd[id2],1,&Bwd[id2],1);
1989  }
1990 
1991  cnt += e;
1992  }
1993  else if (m_bndConditions[n]->GetBoundaryConditionType() ==
1995  {
1996  continue;
1997  }
1998  else
1999  {
2000  ASSERTL0(false, "Method only set up for Dirichlet, Neumann "
2001  "and Robin conditions.");
2002  }
2003  }
2004 
2005  // Copy any periodic boundary conditions.
2006  for (n = 0; n < m_periodicFwdCopy.size(); ++n)
2007  {
2008  Bwd[m_periodicBwdCopy[n]] = Fwd[m_periodicFwdCopy[n]];
2009  }
2010 
2011  // Do parallel exchange for forwards/backwards spaces.
2012  m_traceMap->UniversalTraceAssemble(Fwd);
2013  m_traceMap->UniversalTraceAssemble(Bwd);
2014  }
2015 
2016  const vector<bool> &DisContField3D::v_GetLeftAdjacentFaces(void) const
2017  {
2018  return m_leftAdjacentFaces;
2019  }
2020 
2022  Array<OneD, NekDouble> &outarray)
2023  {
2024  ASSERTL1(m_physState == true,
2025  "Field is not in physical space.");
2026 
2027  v_ExtractTracePhys(m_phys, outarray);
2028  }
2029 
2031  const Array<OneD, const NekDouble> &inarray,
2032  Array<OneD, NekDouble> &outarray)
2033  {
2034 
2035  Vmath::Zero(outarray.num_elements(), outarray, 1);
2036 
2037  Array<OneD, NekDouble> facevals(m_locTraceToTraceMap->GetNFwdLocTracePts());
2038  m_locTraceToTraceMap->FwdLocTracesFromField(inarray,facevals);
2039  m_locTraceToTraceMap->InterpLocFacesToTrace(0,facevals,outarray);
2040 
2041  // gather entries along parallel partitions which have
2042  // only filled in Fwd part on their own partition
2043  m_traceMap->UniversalTraceAssemble(outarray);
2044 
2045  }
2046 
2047  /**
2048  * @brief Add trace contributions into elemental coefficient spaces.
2049  *
2050  * Given some quantity \f$ \vec{Fn} \f$, which conatins this
2051  * routine calculates the integral
2052  *
2053  * \f[
2054  * \int_{\Omega^e} \vec{Fn}, \mathrm{d}S
2055  * \f]
2056  *
2057  * and adds this to the coefficient space provided by outarray.
2058  *
2059  * @see Expansion3D::AddFaceNormBoundaryInt
2060  *
2061  * @param Fn The trace quantities.
2062  * @param outarray Resulting 3D coefficient space.
2063  *
2064  */
2066  const Array<OneD, const NekDouble> &Fn,
2067  Array<OneD, NekDouble> &outarray)
2068  {
2069 
2070  Array<OneD, NekDouble> Fcoeffs(m_trace->GetNcoeffs());
2071  m_trace->IProductWRTBase(Fn, Fcoeffs);
2072 
2073  m_locTraceToTraceMap->AddTraceCoeffsToFieldCoeffs(Fcoeffs,
2074  outarray);
2075  }
2076  /**
2077  * @brief Add trace contributions into elemental coefficient spaces.
2078  *
2079  * Given some quantity \f$ \vec{Fn} \f$, which conatins this
2080  * routine calculates the integral
2081  *
2082  * \f[
2083  * \int_{\Omega^e} \vec{Fn}, \mathrm{d}S
2084  * \f]
2085  *
2086  * and adds this to the coefficient space provided by
2087  * outarray. The value of q is determined from the routine
2088  * IsLeftAdjacentFace() which if true we use Fwd else we use
2089  * Bwd
2090  *
2091  * @see Expansion3D::AddFaceNormBoundaryInt
2092  *
2093  * @param Fwd The trace quantities associated with left (fwd)
2094  * adjancent elmt.
2095  * @param Bwd The trace quantities associated with right (bwd)
2096  * adjacent elet.
2097  * @param outarray Resulting 3D coefficient space.
2098  */
2100  const Array<OneD, const NekDouble> &Fwd,
2101  const Array<OneD, const NekDouble> &Bwd,
2102  Array<OneD, NekDouble> &outarray)
2103  {
2104  Array<OneD, NekDouble> Coeffs(m_trace->GetNcoeffs());
2105 
2106  m_trace->IProductWRTBase(Fwd,Coeffs);
2107  m_locTraceToTraceMap->AddTraceCoeffsToFieldCoeffs(0,Coeffs,outarray);
2108  m_trace->IProductWRTBase(Bwd,Coeffs);
2109  m_locTraceToTraceMap->AddTraceCoeffsToFieldCoeffs(1,Coeffs,outarray);
2110  }
2111 
2112  /**
2113  * @brief Set up a list of elemeent IDs and edge IDs that link to the
2114  * boundary conditions.
2115  */
2117  Array<OneD, int> &ElmtID,
2118  Array<OneD, int> &FaceID)
2119  {
2120  if (m_BCtoElmMap.num_elements() == 0)
2121  {
2122  map<int,int> globalIdMap;
2123  int i, n;
2124  int cnt;
2125  int nbcs = 0;
2126 
2127  // Populate global ID map (takes global geometry ID to local
2128  // expansion list ID).
2130  for (i = 0; i < GetExpSize(); ++i)
2131  {
2132  exp3d = (*m_exp)[i]->as<LocalRegions::Expansion3D>();
2133  globalIdMap[exp3d->GetGeom3D()->GetGlobalID()] = i;
2134  }
2135 
2136  // Determine number of boundary condition expansions.
2137  for(i = 0; i < m_bndConditions.num_elements(); ++i)
2138  {
2139  nbcs += m_bndCondExpansions[i]->GetExpSize();
2140  }
2141 
2142  // Initialize arrays
2145 
2147  for(cnt = n = 0; n < m_bndCondExpansions.num_elements(); ++n)
2148  {
2149  for(i = 0; i < m_bndCondExpansions[n]->GetExpSize(); ++i, ++cnt)
2150  {
2151  exp2d = m_bndCondExpansions[n]->GetExp(i)->
2152  as<LocalRegions::Expansion2D>();
2153 
2155  m_graph->GetElementsFromFace(exp2d->GetGeom2D());
2156  m_BCtoElmMap[cnt] = globalIdMap[
2157  tmp->at(0).first->GetGlobalID()];
2158  m_BCtoFaceMap[cnt] = tmp->at(0).second;
2159  }
2160  }
2161  }
2162  ElmtID = m_BCtoElmMap;
2163  FaceID = m_BCtoFaceMap;
2164  }
2165 
2167  std::shared_ptr<ExpList> &result,
2168  const bool DeclareCoeffPhysArrays)
2169  {
2170  int n, cnt, nq;
2171  int offsetOld, offsetNew;
2172  std::vector<unsigned int> eIDs;
2173 
2174  Array<OneD, int> ElmtID,EdgeID;
2175  GetBoundaryToElmtMap(ElmtID,EdgeID);
2176 
2177  // Skip other boundary regions
2178  for (cnt = n = 0; n < i; ++n)
2179  {
2180  cnt += m_bndCondExpansions[n]->GetExpSize();
2181  }
2182 
2183  // Populate eIDs with information from BoundaryToElmtMap
2184  for (n = 0; n < m_bndCondExpansions[i]->GetExpSize(); ++n)
2185  {
2186  eIDs.push_back(ElmtID[cnt+n]);
2187  }
2188 
2189  // Create expansion list
2190  result =
2192  (*this, eIDs, DeclareCoeffPhysArrays);
2193 
2194  // Copy phys and coeffs to new explist
2195  if (DeclareCoeffPhysArrays)
2196  {
2197  Array<OneD, NekDouble> tmp1, tmp2;
2198  for (n = 0; n < result->GetExpSize(); ++n)
2199  {
2200  nq = GetExp(ElmtID[cnt+n])->GetTotPoints();
2201  offsetOld = GetPhys_Offset(ElmtID[cnt+n]);
2202  offsetNew = result->GetPhys_Offset(n);
2203  Vmath::Vcopy(nq, tmp1 = GetPhys()+ offsetOld, 1,
2204  tmp2 = result->UpdatePhys()+ offsetNew, 1);
2205 
2206  nq = GetExp(ElmtID[cnt+n])->GetNcoeffs();
2207  offsetOld = GetCoeff_Offset(ElmtID[cnt+n]);
2208  offsetNew = result->GetCoeff_Offset(n);
2209  Vmath::Vcopy(nq, tmp1 = GetCoeffs()+ offsetOld, 1,
2210  tmp2 = result->UpdateCoeffs()+ offsetNew, 1);
2211  }
2212  }
2213  }
2214 
2215  /**
2216  * @brief Reset this field, so that geometry information can be updated.
2217  */
2219  {
2220  ExpList::v_Reset();
2221 
2222  // Reset boundary condition expansions.
2223  for (int n = 0; n < m_bndCondExpansions.num_elements(); ++n)
2224  {
2225  m_bndCondExpansions[n]->Reset();
2226  }
2227  }
2228 
2229  /**
2230  * Solving Helmholtz Equation in 3D
2231  */
2233  const Array<OneD, const NekDouble> &inarray,
2234  Array<OneD, NekDouble> &outarray,
2235  const FlagList &flags,
2236  const StdRegions::ConstFactorMap &factors,
2237  const StdRegions::VarCoeffMap &varcoeff,
2238  const MultiRegions::VarFactorsMap &varfactors,
2239  const Array<OneD, const NekDouble> &dirForcing,
2240  const bool PhysSpaceForcing)
2241  {
2242  boost::ignore_unused(flags, varfactors, dirForcing);
2243 
2244  int i,j,n,cnt,cnt1,nbndry;
2245  int nexp = GetExpSize();
2247 
2249  DNekVec F(m_ncoeffs,f,eWrapper);
2250  Array<OneD,NekDouble> e_f, e_l;
2251 
2252  //----------------------------------
2253  // Setup RHS Inner product
2254  //----------------------------------
2255  if(PhysSpaceForcing)
2256  {
2257  IProductWRTBase(inarray,f);
2258  Vmath::Neg(m_ncoeffs,f,1);
2259  }
2260  else
2261  {
2262  Vmath::Smul(m_ncoeffs,-1.0,inarray,1,f,1);
2263  }
2264 
2265  //----------------------------------
2266  // Solve continuous flux System
2267  //----------------------------------
2268  int GloBndDofs = m_traceMap->GetNumGlobalBndCoeffs();
2269  int NumDirichlet = m_traceMap->GetNumLocalDirBndCoeffs();
2270  int e_ncoeffs,id;
2271 
2272  // Retrieve block matrix of U^e
2274  const DNekScalBlkMatSharedPtr &HDGLamToU = GetBlockMatrix(HDGLamToUKey);
2275 
2276  // Retrieve global trace space storage, \Lambda, from trace expansion
2277  Array<OneD,NekDouble> BndSol = m_trace->UpdateCoeffs();
2278 
2279  // Create trace space forcing, F
2280  Array<OneD,NekDouble> BndRhs(GloBndDofs,0.0);
2281 
2282  // Zero \Lambda
2283  Vmath::Zero(GloBndDofs,BndSol,1);
2284 
2285  // Retrieve number of local trace space coefficients N_{\lambda},
2286  // and set up local elemental trace solution \lambda^e.
2287  int LocBndCoeffs = m_traceMap->GetNumLocalBndCoeffs();
2288  Array<OneD, NekDouble> loc_lambda(LocBndCoeffs);
2289  DNekVec LocLambda(LocBndCoeffs,loc_lambda,eWrapper);
2290 
2291  //----------------------------------
2292  // Evaluate Trace Forcing vector F
2293  // Kirby et al, 2010, P23, Step 5.
2294  //----------------------------------
2295  // Loop over all expansions in the domain
2296  for(cnt = cnt1 = n = 0; n < nexp; ++n)
2297  {
2298  nbndry = (*m_exp)[n]->NumDGBndryCoeffs();
2299 
2300  e_ncoeffs = (*m_exp)[n]->GetNcoeffs();
2301  e_f = f + cnt;
2302  e_l = loc_lambda + cnt1;
2303 
2304  // Local trace space \lambda^e
2305  DNekVec Floc (nbndry, e_l, eWrapper);
2306  // Local forcing f^e
2307  DNekVec ElmtFce (e_ncoeffs, e_f, eWrapper);
2308  // Compute local (U^e)^{\top} f^e
2309  Floc = Transpose(*(HDGLamToU->GetBlock(n,n)))*ElmtFce;
2310 
2311  cnt += e_ncoeffs;
2312  cnt1 += nbndry;
2313  }
2314 
2315  // Assemble local \lambda_e into global \Lambda
2316  m_traceMap->AssembleBnd(loc_lambda,BndRhs);
2317 
2318  // Copy Dirichlet boundary conditions and weak forcing into trace
2319  // space
2320  cnt = 0;
2321  for(i = 0; i < m_bndCondExpansions.num_elements(); ++i)
2322  {
2323  if(m_bndConditions[i]->GetBoundaryConditionType() ==
2325  {
2326  for(j = 0; j < (m_bndCondExpansions[i])->GetNcoeffs(); ++j)
2327  {
2328  id = m_traceMap->GetBndCondCoeffsToGlobalCoeffsMap(cnt++);
2329  BndSol[id] = m_bndCondExpansions[i]->GetCoeffs()[j];
2330  }
2331  }
2332  else if (m_bndConditions[i]->GetBoundaryConditionType() ==
2334  m_bndConditions[i]->GetBoundaryConditionType() ==
2336  {
2337  //Add weak boundary condition to trace forcing
2338  for(j = 0; j < (m_bndCondExpansions[i])->GetNcoeffs(); ++j)
2339  {
2340  id = m_traceMap->GetBndCondCoeffsToGlobalCoeffsMap(cnt++);
2341  BndRhs[id] += m_bndCondExpansions[i]->GetCoeffs()[j];
2342  }
2343  }
2344  else if (m_bndConditions[i]->GetBoundaryConditionType() ==
2346  {
2347  ASSERTL0(false, "HDG implementation does not support "
2348  "periodic boundary conditions at present.");
2349  }
2350  }
2351 
2352  //----------------------------------
2353  // Solve trace problem: \Lambda = K^{-1} F
2354  // K is the HybridDGHelmBndLam matrix.
2355  //----------------------------------
2356  if(GloBndDofs - NumDirichlet > 0)
2357  {
2359  m_traceMap,factors,varcoeff);
2361  LinSys->Solve(BndRhs,BndSol,m_traceMap);
2362  }
2363 
2364  //----------------------------------
2365  // Internal element solves
2366  //----------------------------------
2368  const DNekScalBlkMatSharedPtr& InvHDGHelm = GetBlockMatrix(invHDGhelmkey);
2369  DNekVec out(m_ncoeffs,outarray,eWrapper);
2370  Vmath::Zero(m_ncoeffs,outarray,1);
2371 
2372  // get local trace solution from BndSol
2373  m_traceMap->GlobalToLocalBnd(BndSol,loc_lambda);
2374 
2375  // out = u_f + u_lam = (*InvHDGHelm)*f + (LamtoU)*Lam
2376  out = (*InvHDGHelm)*F + (*HDGLamToU)*LocLambda;
2377  }
2378 
2379  /**
2380  * @brief Calculates the result of the multiplication of a global matrix
2381  * of type specified by @a mkey with a vector given by @a inarray.
2382  *
2383  * @param mkey Key representing desired matrix multiplication.
2384  * @param inarray Input vector.
2385  * @param outarray Resulting multiplication.
2386  */
2388  const GlobalMatrixKey &gkey,
2389  const Array<OneD,const NekDouble> &inarray,
2390  Array<OneD, NekDouble> &outarray,
2391  CoeffState coeffstate)
2392  {
2393  boost::ignore_unused(coeffstate);
2394 
2395  int LocBndCoeffs = m_traceMap->GetNumLocalBndCoeffs();
2396  Array<OneD, NekDouble> loc_lambda(LocBndCoeffs);
2397  DNekVec LocLambda(LocBndCoeffs,loc_lambda,eWrapper);
2398  const DNekScalBlkMatSharedPtr& HDGHelm = GetBlockMatrix(gkey);
2399 
2400  m_traceMap->GlobalToLocalBnd(inarray, loc_lambda);
2401  LocLambda = (*HDGHelm) * LocLambda;
2402  m_traceMap->AssembleBnd(loc_lambda,outarray);
2403  }
2404 
2405  /**
2406  * Search through the edge expansions and identify which ones have
2407  * Robin/Mixed type boundary conditions. If find a Robin boundary then
2408  * store the edge id of the boundary condition and the array of points
2409  * of the physical space boundary condition which are hold the boundary
2410  * condition primitive variable coefficient at the quatrature points
2411  *
2412  * \return std map containing the robin boundary condition
2413  * info using a key of the element id
2414  *
2415  * There is a next member to allow for more than one Robin
2416  * boundary condition per element
2417  */
2418  map<int, RobinBCInfoSharedPtr> DisContField3D::v_GetRobinBCInfo(void)
2419  {
2420  int i,cnt;
2421  map<int, RobinBCInfoSharedPtr> returnval;
2422  Array<OneD, int> ElmtID,FaceID;
2423  GetBoundaryToElmtMap(ElmtID,FaceID);
2424 
2425  for(cnt = i = 0; i < m_bndCondExpansions.num_elements(); ++i)
2426  {
2427  MultiRegions::ExpListSharedPtr locExpList;
2428 
2429  if(m_bndConditions[i]->GetBoundaryConditionType() == SpatialDomains::eRobin)
2430  {
2431  int e,elmtid;
2432  Array<OneD, NekDouble> Array_tmp;
2433 
2434  locExpList = m_bndCondExpansions[i];
2435 
2436  int npoints = locExpList->GetNpoints();
2437  Array<OneD, NekDouble> x0(npoints, 0.0);
2438  Array<OneD, NekDouble> x1(npoints, 0.0);
2439  Array<OneD, NekDouble> x2(npoints, 0.0);
2440  Array<OneD, NekDouble> coeffphys(npoints);
2441 
2442  locExpList->GetCoords(x0, x1, x2);
2443 
2444  LibUtilities::Equation coeffeqn =
2445  std::static_pointer_cast<
2447  (m_bndConditions[i])->m_robinPrimitiveCoeff;
2448 
2449  // evalaute coefficient
2450  coeffeqn.Evaluate(x0, x1, x2, 0.0, coeffphys);
2451 
2452  for(e = 0; e < locExpList->GetExpSize(); ++e)
2453  {
2454  RobinBCInfoSharedPtr rInfo =
2456  ::AllocateSharedPtr(FaceID[cnt+e],
2457  Array_tmp = coeffphys +
2458  locExpList->GetPhys_Offset(e));
2459 
2460  elmtid = ElmtID[cnt+e];
2461  // make link list if necessary
2462  if(returnval.count(elmtid) != 0)
2463  {
2464  rInfo->next = returnval.find(elmtid)->second;
2465  }
2466  returnval[elmtid] = rInfo;
2467  }
2468  }
2469  cnt += m_bndCondExpansions[i]->GetExpSize();
2470  }
2471 
2472  return returnval;
2473  }
2474 
2475  /**
2476  * @brief Evaluate HDG post-processing to increase polynomial order of
2477  * solution.
2478  *
2479  * This function takes the solution (assumed to be one order lower) in
2480  * physical space, and postprocesses at the current polynomial order by
2481  * solving the system:
2482  *
2483  * \f[
2484  * \begin{aligned}
2485  * (\nabla w, \nabla u^*) &= (\nabla w, u), \\
2486  * \langle \nabla u^*, 1 \rangle &= \langle \nabla u, 1 \rangle
2487  * \end{aligned}
2488  * \f]
2489  *
2490  * where \f$ u \f$ corresponds with the current solution as stored
2491  * inside #m_coeffs.
2492  *
2493  * @param outarray The resulting field \f$ u^* \f$.
2494  */
2496  Array<OneD, NekDouble> &outarray)
2497  {
2498  int i,cnt,f,ncoeff_face;
2499  Array<OneD, NekDouble> force, out_tmp,qrhs,qrhs1;
2501  &elmtToTrace = m_traceMap->GetElmtToTrace();
2502 
2503  int nq_elmt, nm_elmt;
2504  int LocBndCoeffs = m_traceMap->GetNumLocalBndCoeffs();
2505  Array<OneD, NekDouble> loc_lambda(LocBndCoeffs), face_lambda;
2506  Array<OneD, NekDouble> tmp_coeffs;
2507  m_traceMap->GlobalToLocalBnd(m_trace->GetCoeffs(),loc_lambda);
2508 
2509  face_lambda = loc_lambda;
2510 
2511  // Calculate Q using standard DG formulation.
2512  for(i = cnt = 0; i < GetExpSize(); ++i)
2513  {
2515  (*m_exp)[i]->as<LocalRegions::Expansion3D>();
2516 
2517  nq_elmt = (*m_exp)[i]->GetTotPoints();
2518  nm_elmt = (*m_exp)[i]->GetNcoeffs();
2519  qrhs = Array<OneD, NekDouble>(nq_elmt);
2520  qrhs1 = Array<OneD, NekDouble>(nq_elmt);
2521  force = Array<OneD, NekDouble>(2*nm_elmt);
2522  out_tmp = force + nm_elmt;
2524 
2525  int num_points0 = (*m_exp)[i]->GetBasis(0)->GetNumPoints();
2526  int num_points1 = (*m_exp)[i]->GetBasis(1)->GetNumPoints();
2527  int num_points2 = (*m_exp)[i]->GetBasis(2)->GetNumPoints();
2528  int num_modes0 = (*m_exp)[i]->GetBasis(0)->GetNumModes();
2529  int num_modes1 = (*m_exp)[i]->GetBasis(1)->GetNumModes();
2530  int num_modes2 = (*m_exp)[i]->GetBasis(2)->GetNumModes();
2531 
2532  // Probably a better way of setting up lambda than this. Note
2533  // cannot use PutCoeffsInToElmts since lambda space is mapped
2534  // during the solve.
2535  int nFaces = (*m_exp)[i]->GetNfaces();
2536  Array<OneD, Array<OneD, NekDouble> > faceCoeffs(nFaces);
2537  for(f = 0; f < nFaces; ++f)
2538  {
2539  ncoeff_face = elmtToTrace[i][f]->GetNcoeffs();
2540  faceCoeffs[f] = Array<OneD, NekDouble>(ncoeff_face);
2541  Vmath::Vcopy(ncoeff_face, face_lambda, 1, faceCoeffs[f], 1);
2542  exp->SetFaceToGeomOrientation(f, faceCoeffs[f]);
2543  face_lambda = face_lambda + ncoeff_face;
2544  }
2545 
2546  //creating orthogonal expansion (checking if we have quads or triangles)
2547  LibUtilities::ShapeType shape = (*m_exp)[i]->DetShapeType();
2548  switch(shape)
2549  {
2551  {
2555  LibUtilities::BasisKey BkeyH1(LibUtilities::eOrtho_A, num_modes0, PkeyH1);
2556  LibUtilities::BasisKey BkeyH2(LibUtilities::eOrtho_A, num_modes1, PkeyH2);
2557  LibUtilities::BasisKey BkeyH3(LibUtilities::eOrtho_A, num_modes2, PkeyH3);
2558  SpatialDomains::HexGeomSharedPtr hGeom = std::dynamic_pointer_cast<SpatialDomains::HexGeom>((*m_exp)[i]->GetGeom());
2559  ppExp = MemoryManager<LocalRegions::HexExp>::AllocateSharedPtr(BkeyH1, BkeyH2, BkeyH3, hGeom);
2560  }
2561  break;
2563  {
2567  LibUtilities::BasisKey BkeyT1(LibUtilities::eOrtho_A, num_modes0, PkeyT1);
2568  LibUtilities::BasisKey BkeyT2(LibUtilities::eOrtho_B, num_modes1, PkeyT2);
2569  LibUtilities::BasisKey BkeyT3(LibUtilities::eOrtho_C, num_modes2, PkeyT3);
2570  SpatialDomains::TetGeomSharedPtr tGeom = std::dynamic_pointer_cast<SpatialDomains::TetGeom>((*m_exp)[i]->GetGeom());
2571  ppExp = MemoryManager<LocalRegions::TetExp>::AllocateSharedPtr(BkeyT1, BkeyT2, BkeyT3, tGeom);
2572  }
2573  break;
2574  case LibUtilities::ePrism:
2575  {
2579  LibUtilities::BasisKey BkeyP1(LibUtilities::eOrtho_A, num_modes0, PkeyP1);
2580  LibUtilities::BasisKey BkeyP2(LibUtilities::eOrtho_A, num_modes1, PkeyP2);
2581  LibUtilities::BasisKey BkeyP3(LibUtilities::eOrtho_B, num_modes2, PkeyP3);
2582  SpatialDomains::PrismGeomSharedPtr pGeom = std::dynamic_pointer_cast<SpatialDomains::PrismGeom>((*m_exp)[i]->GetGeom());
2583  ppExp = MemoryManager<LocalRegions::PrismExp>::AllocateSharedPtr(BkeyP1, BkeyP2, BkeyP3, pGeom);
2584  }
2585  break;
2586  default:
2587  ASSERTL0(false, "Wrong shape type, HDG postprocessing is not implemented");
2588  };
2589 
2590 
2591  //DGDeriv
2592  // (d/dx w, q_0)
2593  (*m_exp)[i]->DGDeriv(
2594  0,tmp_coeffs = m_coeffs + m_coeff_offset[i],
2595  elmtToTrace[i], faceCoeffs, out_tmp);
2596  (*m_exp)[i]->BwdTrans(out_tmp,qrhs);
2597  ppExp->IProductWRTDerivBase(0,qrhs,force);
2598 
2599 
2600  // + (d/dy w, q_1)
2601  (*m_exp)[i]->DGDeriv(
2602  1,tmp_coeffs = m_coeffs + m_coeff_offset[i],
2603  elmtToTrace[i], faceCoeffs, out_tmp);
2604  (*m_exp)[i]->BwdTrans(out_tmp,qrhs);
2605  ppExp->IProductWRTDerivBase(1,qrhs,out_tmp);
2606 
2607  Vmath::Vadd(nm_elmt,force,1,out_tmp,1,force,1);
2608 
2609  // + (d/dz w, q_2)
2610  (*m_exp)[i]->DGDeriv(
2611  2,tmp_coeffs = m_coeffs + m_coeff_offset[i],
2612  elmtToTrace[i], faceCoeffs, out_tmp);
2613  (*m_exp)[i]->BwdTrans(out_tmp,qrhs);
2614  ppExp->IProductWRTDerivBase(2,qrhs,out_tmp);
2615 
2616  Vmath::Vadd(nm_elmt,force,1,out_tmp,1,force,1);
2617  // determine force[0] = (1,u)
2618  (*m_exp)[i]->BwdTrans(
2619  tmp_coeffs = m_coeffs + m_coeff_offset[i],qrhs);
2620  force[0] = (*m_exp)[i]->Integral(qrhs);
2621 
2622  // multiply by inverse Laplacian matrix
2623  // get matrix inverse
2624  LocalRegions::MatrixKey lapkey(StdRegions::eInvLaplacianWithUnityMean, ppExp->DetShapeType(), *ppExp);
2625  DNekScalMatSharedPtr lapsys = ppExp->GetLocMatrix(lapkey);
2626 
2627  NekVector<NekDouble> in (nm_elmt, force, eWrapper);
2628  NekVector<NekDouble> out(nm_elmt);
2629 
2630  out = (*lapsys)*in;
2631 
2632  // Transforming back to modified basis
2633  Array<OneD, NekDouble> work(nq_elmt);
2634  ppExp->BwdTrans(out.GetPtr(), work);
2635  (*m_exp)[i]->FwdTrans(work,
2636  tmp_coeffs = outarray + m_coeff_offset[i]);
2637  }
2638  }
2639 
2640  /**
2641  * \brief This function evaluates the boundary conditions at a certain
2642  * time-level.
2643  *
2644  * Based on the boundary condition \f$g(\boldsymbol{x},t)\f$ evaluated
2645  * at a given time-level \a t, this function transforms the boundary
2646  * conditions onto the coefficients of the (one-dimensional) boundary
2647  * expansion. Depending on the type of boundary conditions, these
2648  * expansion coefficients are calculated in different ways:
2649  * - <b>Dirichlet boundary conditions</b><BR>
2650  * In order to ensure global \f$C^0\f$ continuity of the spectral/hp
2651  * approximation, the Dirichlet boundary conditions are projected onto
2652  * the boundary expansion by means of a modified \f$C^0\f$ continuous
2653  * Galerkin projection. This projection can be viewed as a collocation
2654  * projection at the vertices, followed by an \f$L^2\f$ projection on
2655  * the interior modes of the edges. The resulting coefficients
2656  * \f$\boldsymbol{\hat{u}}^{\mathcal{D}}\f$ will be stored for the
2657  * boundary expansion.
2658  * - <b>Neumann boundary conditions</b>
2659  * In the discrete Galerkin formulation of the problem to be solved,
2660  * the Neumann boundary conditions appear as the set of surface
2661  * integrals: \f[\boldsymbol{\hat{g}}=\int_{\Gamma}
2662  * \phi^e_n(\boldsymbol{x})g(\boldsymbol{x})d(\boldsymbol{x})\quad
2663  * \forall n \f]
2664  * As a result, it are the coefficients \f$\boldsymbol{\hat{g}}\f$
2665  * that will be stored in the boundary expansion
2666  *
2667  * @param time The time at which the boundary conditions
2668  * should be evaluated.
2669  * @param bndCondExpansions List of boundary conditions.
2670  * @param bndConditions Information about the boundary conditions.
2671  */
2673  const NekDouble time,
2674  const std::string varName,
2675  const NekDouble x2_in,
2676  const NekDouble x3_in)
2677  {
2678  boost::ignore_unused(x2_in, x3_in);
2679 
2680  int i;
2681  int npoints;
2682  int nbnd = m_bndCondExpansions.num_elements();
2683  MultiRegions::ExpListSharedPtr locExpList;
2684 
2685  for (i = 0; i < nbnd; ++i)
2686  {
2687  if (time == 0.0 || m_bndConditions[i]->IsTimeDependent())
2688  {
2689  locExpList = m_bndCondExpansions[i];
2690  npoints = locExpList->GetNpoints();
2691 
2692  Array<OneD, NekDouble> x0(npoints, 0.0);
2693  Array<OneD, NekDouble> x1(npoints, 0.0);
2694  Array<OneD, NekDouble> x2(npoints, 0.0);
2695  Array<OneD, NekDouble> valuesFile(npoints, 1.0), valuesExp(npoints, 1.0);
2696 
2697  locExpList->GetCoords(x0, x1, x2);
2698 
2699  if (m_bndConditions[i]->GetBoundaryConditionType()
2701  {
2702  SpatialDomains::DirichletBCShPtr bcPtr = std::static_pointer_cast<
2704  m_bndConditions[i]);
2705  string filebcs = bcPtr->m_filename;
2706  string exprbcs = bcPtr->m_expr;
2707 
2708  if (filebcs != "")
2709  {
2710  ExtractFileBCs(filebcs, bcPtr->GetComm(), varName, locExpList);
2711  valuesFile = locExpList->GetPhys();
2712  }
2713 
2714  if (exprbcs != "")
2715  {
2716  LibUtilities::Equation condition = std::static_pointer_cast<SpatialDomains::
2717  DirichletBoundaryCondition >(
2718  m_bndConditions[i])->m_dirichletCondition;
2719 
2720  condition.Evaluate(x0, x1, x2, time, valuesExp);
2721  }
2722 
2723  Vmath::Vmul(npoints, valuesExp, 1, valuesFile, 1, locExpList->UpdatePhys(), 1);
2724 
2725  locExpList->FwdTrans_BndConstrained(
2726  locExpList->GetPhys(),
2727  locExpList->UpdateCoeffs());
2728  }
2729  else if (m_bndConditions[i]->GetBoundaryConditionType()
2731  {
2732  SpatialDomains::NeumannBCShPtr bcPtr = std::static_pointer_cast<
2734  m_bndConditions[i]);
2735  string filebcs = bcPtr->m_filename;
2736 
2737  if (filebcs != "")
2738  {
2739  ExtractFileBCs(filebcs, bcPtr->GetComm(), varName, locExpList);
2740  }
2741  else
2742  {
2743 
2744  LibUtilities::Equation condition = std::
2745  static_pointer_cast<SpatialDomains::
2746  NeumannBoundaryCondition>(
2747  m_bndConditions[i])->m_neumannCondition;
2748 
2749  condition.Evaluate(x0, x1, x2, time,
2750  locExpList->UpdatePhys());
2751 
2752  locExpList->IProductWRTBase(locExpList->GetPhys(),
2753  locExpList->UpdateCoeffs());
2754  }
2755  }
2756  else if (m_bndConditions[i]->GetBoundaryConditionType()
2758  {
2759  SpatialDomains::RobinBCShPtr bcPtr = std::static_pointer_cast<
2761  m_bndConditions[i]);
2762  string filebcs = bcPtr->m_filename;
2763 
2764  if (filebcs != "")
2765  {
2766  ExtractFileBCs(filebcs, bcPtr->GetComm(), varName, locExpList);
2767  }
2768  else
2769  {
2770  LibUtilities::Equation condition = std::
2771  static_pointer_cast<SpatialDomains::
2772  RobinBoundaryCondition>(
2773  m_bndConditions[i])->m_robinFunction;
2774 
2775  condition.Evaluate(x0, x1, x2, time,
2776  locExpList->UpdatePhys());
2777 
2778  }
2779 
2780  locExpList->IProductWRTBase(locExpList->GetPhys(),
2781  locExpList->UpdateCoeffs());
2782 
2783  }
2784  else if (m_bndConditions[i]->GetBoundaryConditionType()
2786  {
2787  continue;
2788  }
2789  else
2790  {
2791  ASSERTL0(false, "This type of BC not implemented yet");
2792  }
2793  }
2794  }
2795  }
2796  } // end of namespace
2797 } // end of namespace
const Array< OneD, const NekDouble > & GetPhys() const
This function returns (a reference to) the array (implemented as m_phys) containing the function ev...
Definition: ExpList.h:2161
const DNekScalBlkMatSharedPtr & GetBlockMatrix(const GlobalMatrixKey &gkey)
Definition: ExpList.cpp:1004
#define ASSERTL0(condition, msg)
Definition: ErrorUtil.hpp:216
virtual void v_Reset()
Reset geometry information, metrics, matrix managers and geometry information.
Definition: ExpList.cpp:1583
std::shared_ptr< RobinBCInfo > RobinBCInfoSharedPtr
std::shared_ptr< MeshGraph > MeshGraphSharedPtr
Definition: MeshGraph.h:163
GlobalLinSysSharedPtr GetGlobalBndLinSys(const GlobalLinSysKey &mkey)
#define NEKERROR(type, msg)
Assert Level 0 – Fundamental assert which is used whether in FULLDEBUG, DEBUG or OPT compilation mod...
Definition: ErrorUtil.hpp:209
virtual void v_ExtractTracePhys(Array< OneD, NekDouble > &outarray)
Array< OneD, MultiRegions::ExpListSharedPtr > m_bndCondExpansions
An object which contains the discretised boundary conditions.
int id
Geometry ID of entity.
int DefineFunction(const std::string &vlist, const std::string &expr)
Defines a function for the purposes of evaluation.
std::shared_ptr< Geometry2D > Geometry2DSharedPtr
Definition: Geometry.h:65
static ExpListSharedPtr NullExpListSharedPtr
Definition: ExpList.h:1550
std::shared_ptr< LocalRegions::ExpansionVector > m_exp
The list of local expansions.
Definition: ExpList.h:1090
void EvaluateBoundaryConditions(const NekDouble time=0.0, const std::string varName="", const NekDouble=NekConstants::kNekUnsetDouble, const NekDouble=NekConstants::kNekUnsetDouble)
Definition: ExpList.h:2356
#define sign(a, b)
return the sign(b)*a
Definition: Polylib.cpp:16
std::shared_ptr< ExpList > ExpListSharedPtr
Shared pointer to an ExpList object.
std::vector< PointGeomSharedPtr > PointGeomVector
Definition: Geometry2D.h:64
void SetAdjacentElementExp(int face, Expansion3DSharedPtr &f)
Definition: Expansion2D.h:271
std::shared_ptr< DNekScalMat > DNekScalMatSharedPtr
std::shared_ptr< TetGeom > TetGeomSharedPtr
Definition: TetGeom.h:88
std::shared_ptr< std::vector< std::pair< GeometrySharedPtr, int > > > GeometryLinkSharedPtr
Definition: MeshGraph.h:159
bool IsLeftAdjacentFace(const int n, const int e)
std::shared_ptr< DNekScalBlkMat > DNekScalBlkMatSharedPtr
Definition: NekTypeDefs.hpp:73
NekDouble Evaluate(const int id)
Evaluate a function which depends only on constants and/or parameters.
const BoundaryConditionCollection & GetBoundaryConditions(void) const
Definition: Conditions.h:248
const Array< OneD, const NekDouble > & GetCoeffs() const
This function returns (a reference to) the array (implemented as m_coeffs) containing all local expa...
Definition: ExpList.h:2062
virtual const std::vector< bool > & v_GetLeftAdjacentFaces(void) const
std::shared_ptr< Comm > CommSharedPtr
Pointer to a Communicator object.
Definition: Comm.h:53
std::set< int > m_boundaryFaces
A set storing the global IDs of any boundary faces.
std::shared_ptr< GlobalLinSys > GlobalLinSysSharedPtr
Pointer to a GlobalLinSys object.
Definition: GlobalLinSys.h:50
STL namespace.
std::shared_ptr< NeumannBoundaryCondition > NeumannBCShPtr
Definition: Conditions.h:221
std::map< int, CompositeSharedPtr > CompositeMap
Definition: MeshGraph.h:137
PeriodicMap m_periodicFaces
A map which identifies pairs of periodic faces.
virtual void v_GetFwdBwdTracePhys(Array< OneD, NekDouble > &Fwd, Array< OneD, NekDouble > &Bwd)
This method extracts the "forward" and "backward" trace data from the array field and puts the data i...
std::map< ConstFactorType, NekDouble > ConstFactorMap
Definition: StdRegions.hpp:294
void GetBoundaryToElmtMap(Array< OneD, int > &ElmtID, Array< OneD, int > &EdgeID)
Definition: ExpList.h:2409
Array< OneD, NekDouble > m_phys
The global expansion evaluated at the quadrature points.
Definition: ExpList.h:1069
void EvaluateHDGPostProcessing(Array< OneD, NekDouble > &outarray)
Evaluate HDG post-processing to increase polynomial order of solution.
std::shared_ptr< Composite > CompositeSharedPtr
Definition: MeshGraph.h:136
virtual void v_AddFwdBwdTraceIntegral(const Array< OneD, const NekDouble > &Fwd, const Array< OneD, const NekDouble > &Bwd, Array< OneD, NekDouble > &outarray)
Add trace contributions into elemental coefficient spaces.
void ApplyGeomInfo()
Apply geometry information to each expansion.
Definition: ExpList.cpp:1570
std::shared_ptr< ExpList2D > ExpList2DSharedPtr
Shared pointer to an ExpList2D object.
Definition: ExpList2D.h:48
Array< OneD, NekDouble > m_coeffs
Concatenation of all local expansion coefficients.
Definition: ExpList.h:1052
int GetExpSize(void)
This function returns the number of elements in the expansion.
Definition: ExpList.h:2170
virtual void v_EvaluateBoundaryConditions(const NekDouble time=0.0, const std::string varName="", const NekDouble x2_in=NekConstants::kNekUnsetDouble, const NekDouble x3_in=NekConstants::kNekUnsetDouble)
This function evaluates the boundary conditions at a certain time-level.
const std::shared_ptr< LocalRegions::ExpansionVector > GetExp() const
This function returns the vector of elements in the expansion.
Definition: ExpList.h:2191
std::shared_ptr< GlobalLinSys > GenGlobalBndLinSys(const GlobalLinSysKey &mkey, const AssemblyMapSharedPtr &locToGloMap)
Generate a GlobalLinSys from information provided by the key "mkey" and the mapping provided in LocTo...
Definition: ExpList.cpp:1381
void Rotate(PointGeom &a, int dir, NekDouble angle)
_this = rotation of a by angle &#39;angle&#39; around axis dir
Definition: PointGeom.cpp:143
Gauss Radau pinned at x=-1, .
Definition: PointsType.h:58
std::shared_ptr< RobinBoundaryCondition > RobinBCShPtr
Definition: Conditions.h:222
NekDouble m_tol
Tolerance to rotation is considered identical.
Principle Orthogonal Functions .
Definition: BasisType.h:46
void FindPeriodicFaces(const SpatialDomains::BoundaryConditions &bcs, const std::string &variable)
Determine the periodic faces, edges and vertices for the given graph.
std::shared_ptr< StdExpansion > StdExpansionSharedPtr
std::shared_ptr< Expansion3D > Expansion3DSharedPtr
Definition: Expansion2D.h:49
Array< OneD, int > m_coeff_offset
Offset of elemental data into the array m_coeffs.
Definition: ExpList.h:1101
virtual std::map< int, RobinBCInfoSharedPtr > v_GetRobinBCInfo()
std::vector< int > m_periodicFwdCopy
A vector indicating degress of freedom which need to be copied from forwards to backwards space in ca...
void Smul(int n, const T alpha, const T *x, const int incx, T *y, const int incy)
Scalar multiply y = alpha*y.
Definition: Vmath.cpp:216
StdRegions::MatrixType GetMatrixType() const
Return the matrix type.
DisContField3D()
Default constructor.
std::map< StdRegions::VarCoeffType, Array< OneD, NekDouble > > VarCoeffMap
Definition: StdRegions.hpp:264
int GetNcoeffs(void) const
Returns the total number of local degrees of freedom .
Definition: ExpList.h:1558
LocTraceToTraceMapSharedPtr m_locTraceToTraceMap
Map of local trace (the points at the face of the element) to the trace space discretisation.
std::map< int, std::vector< unsigned int > > CompositeOrdering
Definition: MeshGraph.h:108
bool m_physState
The state of the array m_phys.
Definition: ExpList.h:1078
int m_ncoeffs
The total number of local degrees of freedom. m_ncoeffs .
Definition: ExpList.h:1030
std::map< int, BoundaryRegionShPtr > BoundaryRegionCollection
Definition: Conditions.h:217
bool isLocal
Flag specifying if this entity is local to this partition.
static std::shared_ptr< DataType > AllocateSharedPtr(const Args &...args)
Allocate a shared pointer from the memory pool.
Principle Orthogonal Functions .
Definition: BasisType.h:47
StdRegions::Orientation orient
Orientation of entity within higher dimensional entity.
Principle Orthogonal Functions .
Definition: BasisType.h:45
NekMatrix< InnerMatrixType, BlockMatrixTag > Transpose(NekMatrix< InnerMatrixType, BlockMatrixTag > &rhs)
SpatialDomains::MeshGraphSharedPtr m_graph
Mesh associated with this expansion list.
Definition: ExpList.h:1026
void GenerateBoundaryConditionExpansion(const SpatialDomains::MeshGraphSharedPtr &graph3D, const SpatialDomains::BoundaryConditions &bcs, const std::string &variable)
virtual void v_GeneralMatrixOp(const GlobalMatrixKey &gkey, const Array< OneD, const NekDouble > &inarray, Array< OneD, NekDouble > &outarray, CoeffState coeffstate=eLocal)
Calculates the result of the multiplication of a global matrix of type specified by mkey with a vecto...
LibUtilities::SessionReaderSharedPtr m_session
Session.
Definition: ExpList.h:1023
Defines a specification for a set of points.
Definition: Points.h:59
void Neg(int n, T *x, const int incx)
Negate x = -x.
Definition: Vmath.cpp:399
NekDouble Evaluate() const
Definition: Equation.cpp:95
double NekDouble
void SetUpDG(const std::string="DefaultVar")
Set up all DG member variables and maps.
bool SameTypeOfBoundaryConditions(const DisContField3D &In)
Defines a list of flags.
std::map< int, BoundaryConditionMapShPtr > BoundaryConditionCollection
Definition: Conditions.h:226
static StdRegions::Orientation GetFaceOrientation(const QuadGeom &face1, const QuadGeom &face2, bool doRot=false, int dir=0, NekDouble angle=0.0, NekDouble tol=1e-8)
Get the orientation of face1.
Definition: QuadGeom.cpp:138
static StdRegions::Orientation GetFaceOrientation(const TriGeom &face1, const TriGeom &face2, bool doRot, int dir, NekDouble angle, NekDouble tol)
Definition: TriGeom.cpp:118
void ExtractFileBCs(const std::string &fileName, LibUtilities::CommSharedPtr comm, const std::string &varName, const std::shared_ptr< ExpList > locExpList)
Definition: ExpList.cpp:2095
virtual void v_GetBndElmtExpansion(int i, std::shared_ptr< ExpList > &result, const bool DeclareCoeffPhysArrays)
std::map< int, std::vector< PeriodicEntity > > PeriodicMap
Describe a linear system.
std::shared_ptr< BoundaryConditionBase > BoundaryConditionShPtr
Definition: Conditions.h:219
Describes a matrix with ordering defined by a local to global map.
Interpreter class for the evaluation of mathematical expressions.
Definition: Interpreter.h:77
std::shared_ptr< Expansion > ExpansionSharedPtr
Definition: Expansion.h:65
LibUtilities::CommSharedPtr m_comm
Communicator.
Definition: ExpList.h:1020
static AssemblyMapSharedPtr NullAssemblyMapSharedPtr
Definition: AssemblyMap.h:54
SpatialDomains::Geometry3DSharedPtr GetGeom3D() const
Definition: Expansion3D.h:196
PeriodicMap m_periodicVerts
A map which identifies groups of periodic vertices.
2D geometry information
Definition: Geometry2D.h:68
std::shared_ptr< Expansion2D > Expansion2DSharedPtr
Definition: Expansion1D.h:47
virtual void v_GetBoundaryToElmtMap(Array< OneD, int > &ElmtID, Array< OneD, int > &FaceID)
Set up a list of elemeent IDs and edge IDs that link to the boundary conditions.
#define ASSERTL2(condition, msg)
Assert Level 2 – Debugging which is used FULLDEBUG compilation mode. This level assert is designed t...
Definition: ErrorUtil.hpp:274
std::shared_ptr< HexGeom > HexGeomSharedPtr
Definition: HexGeom.h:90
virtual void v_Reset()
Reset this field, so that geometry information can be updated.
void IProductWRTBase(const Array< OneD, const NekDouble > &inarray, Array< OneD, NekDouble > &outarray, CoeffState coeffstate=eLocal)
Definition: ExpList.h:1714
std::shared_ptr< DirichletBoundaryCondition > DirichletBCShPtr
Definition: Conditions.h:220
Gauss Radau pinned at x=-1, .
Definition: PointsType.h:59
Array< OneD, SpatialDomains::BoundaryConditionShPtr > m_bndConditions
An array which contains the information about the boundary condition on the different boundary region...
Abstraction of a three-dimensional multi-elemental expansion which is merely a collection of local ex...
Definition: ExpList3D.h:48
static SpatialDomains::BoundaryConditionShPtr GetBoundaryCondition(const SpatialDomains::BoundaryConditionCollection &collection, unsigned int index, const std::string &variable)
Definition: ExpList.cpp:3261
int m_dir
Axis of rotation. 0 = &#39;x&#39;, 1 = &#39;y&#39;, 2 = &#39;z&#39;.
GlobalSysSolnType GetGlobalSysSolnType() const
Return the associated solution type.
NekDouble dist(PointGeom &a)
return distance between this and input a
Definition: PointGeom.cpp:181
NekDouble m_angle
Angle of rotation in radians.
int GetTotPoints() const
This function returns the total number of quadrature points used in the element.
Definition: StdExpansion.h:140
std::shared_ptr< PrismGeom > PrismGeomSharedPtr
Definition: PrismGeom.h:88
std::map< int, std::vector< unsigned int > > BndRegionOrdering
Definition: MeshGraph.h:109
int GetCoeff_Offset(int n) const
Get the start offset position for a global list of m_coeffs correspoinding to element n...
Definition: ExpList.h:2200
T Vsum(int n, const T *x, const int incx)
Subtract return sum(x)
Definition: Vmath.cpp:740
void Zero(int n, T *x, const int incx)
Zero vector.
Definition: Vmath.cpp:376
#define ASSERTL1(condition, msg)
Assert Level 1 – Debugging which is used whether in FULLDEBUG or DEBUG compilation mode...
Definition: ErrorUtil.hpp:250
int GetPhys_Offset(int n) const
Get the start offset position for a global list of m_phys correspoinding to element n...
Definition: ExpList.h:2208
void Vcopy(int n, const T *x, const int incx, T *y, const int incy)
Definition: Vmath.cpp:1064
Describes the specification for a Basis.
Definition: Basis.h:49
std::shared_ptr< SessionReader > SessionReaderSharedPtr
const BoundaryRegionCollection & GetBoundaryRegions(void) const
Definition: Conditions.h:238
Array< OneD, DataType > & GetPtr()
Definition: NekVector.cpp:227
1D Gauss-Lobatto-Legendre quadrature points
Definition: PointsType.h:51
void Vadd(int n, const T *x, const int incx, const T *y, const int incy, T *z, const int incz)
Add vector z = x+y.
Definition: Vmath.cpp:302
virtual void v_AddTraceIntegral(const Array< OneD, const NekDouble > &Fn, Array< OneD, NekDouble > &outarray)
Add trace contributions into elemental coefficient spaces.
void Vmul(int n, const T *x, const int incx, const T *y, const int incy, T *z, const int incz)
Multiply vector z = x*y.
Definition: Vmath.cpp:186
PeriodicMap m_periodicEdges
A map which identifies groups of periodic edges.
std::map< StdRegions::ConstFactorType, Array< OneD, NekDouble > > VarFactorsMap
virtual void v_HelmSolve(const Array< OneD, const NekDouble > &inarray, Array< OneD, NekDouble > &outarray, const FlagList &flags, const StdRegions::ConstFactorMap &factors, const StdRegions::VarCoeffMap &varcoeff, const MultiRegions::VarFactorsMap &varfactors, const Array< OneD, const NekDouble > &dirForcing, const bool PhysSpaceForcing)