Nektar++
DisContField.cpp
Go to the documentation of this file.
1 //////////////////////////////////////////////////////////////////////////////
2 //
3 // File: DisContField.cpp
4 //
5 // For more information, please see: http://www.nektar.info
6 //
7 // The MIT License
8 //
9 // Copyright (c) 2006 Division of Applied Mathematics, Brown University (USA),
10 // Department of Aeronautics, Imperial College London (UK), and Scientific
11 // Computing and Imaging Institute, University of Utah (USA).
12 //
13 // License for the specific language governing rights and limitations under
14 // Permission is hereby granted, free of charge, to any person obtaining a
15 // copy of this software and associated documentation files (the "Software"),
16 // to deal in the Software without restriction, including without limitation
17 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
18 // and/or sell copies of the Software, and to permit persons to whom the
19 // Software is furnished to do so, subject to the following conditions:
20 //
21 // The above copyright notice and this permission notice shall be included
22 // in all copies or substantial portions of the Software.
23 //
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
25 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
27 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
29 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
30 // DEALINGS IN THE SOFTWARE.
31 //
32 // Description: Field definition for 1D domain with boundary
33 // conditions using LDG-H
34 //
35 ///////////////////////////////////////////////////////////////////////////////
36 
40 #include <LocalRegions/HexExp.h>
41 #include <LocalRegions/QuadExp.h>
42 #include <LocalRegions/TetExp.h>
43 #include <LocalRegions/TriExp.h>
46 #include <StdRegions/StdSegExp.h>
47 
48 using namespace std;
49 
50 namespace Nektar
51 {
52 namespace MultiRegions
53 {
54 /**
55  * @class DisContField
56  * This class augments the list of local expansions inherited from
57  * ExpList with boundary conditions. Inter-element boundaries are
58  * handled using an discontinuous Galerkin scheme.
59  */
60 
61 /**
62  * Constructs an empty expansion list with no boundary conditions.
63  */
64 DisContField::DisContField()
65  : ExpList(), m_bndConditions(), m_bndCondExpansions(),
66  m_trace(NullExpListSharedPtr)
67 {
68 }
69 
70 /**
71  * An expansion list for the boundary expansions is generated first for
72  * the field. These are subsequently evaluated for time zero. The trace
73  * map is then constructed.
74  * @param graph1D A mesh, containing information about the domain
75  * and the spectral/hp element expansions.
76  * @param bcs Information about the enforced boundary
77  * conditions.
78  * @param variable The session variable associated with the
79  * boundary conditions to enforce.
80  * @param solnType Type of global system to use.
81  */
84  const std::string &variable, const bool SetUpJustDG,
85  const bool DeclareCoeffPhysArrays,
86  const Collections::ImplementationType ImpType,
87  const std::string bcvariable)
88  : ExpList(pSession, graph, DeclareCoeffPhysArrays, variable, ImpType),
89  m_trace(NullExpListSharedPtr)
90 {
91  std::string bcvar;
92  if (bcvariable == "NotSet")
93  {
94  bcvar = variable;
95  }
96  else
97  {
98  bcvar = bcvariable;
99  }
100 
101  if (bcvar.compare("DefaultVar") != 0)
102  {
104 
105  GenerateBoundaryConditionExpansion(graph, bcs, bcvar,
106  DeclareCoeffPhysArrays);
107  if (DeclareCoeffPhysArrays)
108  {
109  EvaluateBoundaryConditions(0.0, bcvar);
110  }
111  ApplyGeomInfo();
112  FindPeriodicTraces(bcs, bcvar);
113  }
114 
115  int i, cnt;
116  Array<OneD, int> ElmtID, TraceID;
117  GetBoundaryToElmtMap(ElmtID, TraceID);
118 
119  for (cnt = i = 0; i < m_bndCondExpansions.size(); ++i)
120  {
122  locExpList = m_bndCondExpansions[i];
123 
124  for (int e = 0; e < locExpList->GetExpSize(); ++e)
125  {
126  (*m_exp)[ElmtID[cnt + e]]->SetTraceExp(TraceID[cnt + e],
127  locExpList->GetExp(e));
128  locExpList->GetExp(e)->SetAdjacentElementExp(
129  TraceID[cnt + e], (*m_exp)[ElmtID[cnt + e]]);
130  }
131  cnt += m_bndCondExpansions[i]->GetExpSize();
132  }
133 
134  if (SetUpJustDG)
135  {
136  SetUpDG(variable, ImpType);
137  }
138  else
139  {
140  if (m_session->DefinesSolverInfo("PROJECTION"))
141  {
142  std::string ProjectStr = m_session->GetSolverInfo("PROJECTION");
143  if ((ProjectStr == "MixedCGDG") ||
144  (ProjectStr == "Mixed_CG_Discontinuous"))
145  {
146  SetUpDG();
147  }
148  else
149  {
151  }
152  }
153  else
154  {
156  }
157  }
158 }
159 
160 /**
161  * @brief Set up all DG member variables and maps.
162  */
163 void DisContField::SetUpDG(const std::string variable,
164  const Collections::ImplementationType ImpType)
165 {
166  // Check for multiple calls
168  {
169  return;
170  }
171 
172  // Set up matrix map
174 
175  // Set up trace space
178  m_comm, true, "DefaultVar", ImpType);
179 
180  PeriodicMap periodicTraces = (m_expType == e1D) ? m_periodicVerts
181  : (m_expType == e2D) ? m_periodicEdges
182  : m_periodicFaces;
183 
186  m_bndConditions, periodicTraces, variable);
187 
188  if (m_session->DefinesCmdLineArgument("verbose"))
189  {
190  m_traceMap->PrintStats(std::cout, variable);
191  }
192 
194  m_traceMap->GetElmtToTrace();
195 
196  // Scatter trace to elements. For each element, we find
197  // the trace associated to each elemental trace. The element
198  // then retains a pointer to the elemental trace, to
199  // ensure uniqueness of normals when retrieving from two
200  // adjoining elements which do not lie in a plane.
201  for (int i = 0; i < m_exp->size(); ++i)
202  {
203  for (int j = 0; j < (*m_exp)[i]->GetNtraces(); ++j)
204  {
205  LocalRegions::ExpansionSharedPtr exp = elmtToTrace[i][j];
206  ;
207 
208  exp->SetAdjacentElementExp(j, (*m_exp)[i]);
209  (*m_exp)[i]->SetTraceExp(j, exp);
210  }
211  }
212 
213  // Set up physical normals
215 
216  // Create interface exchange object
219 
220  int cnt, n;
221 
222  // Identify boundary trace
223  for (cnt = 0, n = 0; n < m_bndCondExpansions.size(); ++n)
224  {
225  if (m_bndConditions[n]->GetBoundaryConditionType() !=
227  {
228  for (int v = 0; v < m_bndCondExpansions[n]->GetExpSize(); ++v)
229  {
230  m_boundaryTraces.insert(
231  m_traceMap->GetBndCondIDToGlobalTraceID(cnt + v));
232  }
233  cnt += m_bndCondExpansions[n]->GetExpSize();
234  }
235  }
236 
237  // Set up information for periodic boundary conditions.
238  std::unordered_map<int, pair<int, int>> perTraceToExpMap;
239  for (cnt = n = 0; n < m_exp->size(); ++n)
240  {
241  for (int v = 0; v < (*m_exp)[n]->GetNtraces(); ++v, ++cnt)
242  {
243  auto it = periodicTraces.find((*m_exp)[n]->GetGeom()->GetTid(v));
244 
245  if (it != periodicTraces.end())
246  {
247  perTraceToExpMap[it->first] = make_pair(n, v);
248  }
249  }
250  }
251 
252  // Set up left-adjacent tracelist.
253  m_leftAdjacentTraces.resize(cnt);
254 
255  // count size of trace
256  for (cnt = n = 0; n < m_exp->size(); ++n)
257  {
258  for (int v = 0; v < (*m_exp)[n]->GetNtraces(); ++v, ++cnt)
259  {
261  }
262  }
263 
264  // Set up mapping to copy Fwd of periodic bcs to Bwd of other edge.
265  for (cnt = n = 0; n < m_exp->size(); ++n)
266  {
267  for (int v = 0; v < (*m_exp)[n]->GetNtraces(); ++v, ++cnt)
268  {
269  int GeomId = (*m_exp)[n]->GetGeom()->GetTid(v);
270 
271  // Check to see if this trace is periodic.
272  auto it = periodicTraces.find(GeomId);
273 
274  if (it != periodicTraces.end())
275  {
276  const PeriodicEntity &ent = it->second[0];
277  auto it2 = perTraceToExpMap.find(ent.id);
278 
279  if (it2 == perTraceToExpMap.end())
280  {
281  if (m_session->GetComm()->GetRowComm()->GetSize() > 1 &&
282  !ent.isLocal)
283  {
284  continue;
285  }
286  else
287  {
288  ASSERTL1(false, "Periodic trace not found!");
289  }
290  }
291 
293  "Periodic trace in non-forward space?");
294 
295  int offset = m_trace->GetPhys_Offset(
296  (m_traceMap->GetElmtToTrace())[n][v]->GetElmtId());
297 
298  int offset2 = m_trace->GetPhys_Offset(
299  (m_traceMap->GetElmtToTrace())[it2->second.first]
300  [it2->second.second]
301  ->GetElmtId());
302 
303  switch (m_expType)
304  {
305  case e1D:
306  {
307  m_periodicFwdCopy.push_back(offset);
308  m_periodicBwdCopy.push_back(offset2);
309  }
310  break;
311  case e2D:
312  {
313  // Calculate relative orientations between trace to
314  // calculate copying map.
315  int nquad = elmtToTrace[n][v]->GetNumPoints(0);
316 
317  vector<int> tmpBwd(nquad);
318  vector<int> tmpFwd(nquad);
319 
320  if (ent.orient == StdRegions::eForwards)
321  {
322  for (int i = 0; i < nquad; ++i)
323  {
324  tmpBwd[i] = offset2 + i;
325  tmpFwd[i] = offset + i;
326  }
327  }
328  else
329  {
330  for (int i = 0; i < nquad; ++i)
331  {
332  tmpBwd[i] = offset2 + i;
333  tmpFwd[i] = offset + nquad - i - 1;
334  }
335  }
336 
337  for (int i = 0; i < nquad; ++i)
338  {
339  m_periodicFwdCopy.push_back(tmpFwd[i]);
340  m_periodicBwdCopy.push_back(tmpBwd[i]);
341  }
342  }
343  break;
344  case e3D:
345  {
346  // Calculate relative orientations between faces to
347  // calculate copying map.
348  int nquad1 = elmtToTrace[n][v]->GetNumPoints(0);
349  int nquad2 = elmtToTrace[n][v]->GetNumPoints(1);
350 
351  vector<int> tmpBwd(nquad1 * nquad2);
352  vector<int> tmpFwd(nquad1 * nquad2);
353 
354  if (ent.orient ==
356  ent.orient ==
358  ent.orient ==
361  {
362  for (int i = 0; i < nquad2; ++i)
363  {
364  for (int j = 0; j < nquad1; ++j)
365  {
366  tmpBwd[i * nquad1 + j] =
367  offset2 + i * nquad1 + j;
368  tmpFwd[i * nquad1 + j] =
369  offset + j * nquad2 + i;
370  }
371  }
372  }
373  else
374  {
375  for (int i = 0; i < nquad2; ++i)
376  {
377  for (int j = 0; j < nquad1; ++j)
378  {
379  tmpBwd[i * nquad1 + j] =
380  offset2 + i * nquad1 + j;
381  tmpFwd[i * nquad1 + j] =
382  offset + i * nquad1 + j;
383  }
384  }
385  }
386 
387  if (ent.orient ==
389  ent.orient ==
391  ent.orient ==
394  {
395  // Reverse x direction
396  for (int i = 0; i < nquad2; ++i)
397  {
398  for (int j = 0; j < nquad1 / 2; ++j)
399  {
400  swap(tmpFwd[i * nquad1 + j],
401  tmpFwd[i * nquad1 + nquad1 - j - 1]);
402  }
403  }
404  }
405 
406  if (ent.orient ==
408  ent.orient ==
410  ent.orient ==
413  {
414  // Reverse y direction
415  for (int j = 0; j < nquad1; ++j)
416  {
417  for (int i = 0; i < nquad2 / 2; ++i)
418  {
419  swap(tmpFwd[i * nquad1 + j],
420  tmpFwd[(nquad2 - i - 1) * nquad1 + j]);
421  }
422  }
423  }
424 
425  for (int i = 0; i < nquad1 * nquad2; ++i)
426  {
427  m_periodicFwdCopy.push_back(tmpFwd[i]);
428  m_periodicBwdCopy.push_back(tmpBwd[i]);
429  }
430  }
431  break;
432  default:
433  ASSERTL1(false, "not set up");
434  }
435  }
436  }
437  }
438 
440  *this, m_trace, elmtToTrace, m_leftAdjacentTraces);
441 }
442 
443 /**
444  * @brief This routine determines if an element is to the "left"
445  * of the adjacent trace, which arises from the idea there is a local
446  * normal direction between two elements (i.e. on the trace)
447  * and one elements would then be the left.
448  *
449  * This is typically required since we only define one normal
450  * direction along the trace which goes from the left adjacent
451  * element to the right adjacent element. It is also useful
452  * in DG discretisations to set up a local Riemann problem
453  * where we need to have the concept of a local normal
454  * direction which is unique between two elements.
455  *
456  * There are two cases to be checked:
457  *
458  * 1) First is the trace on a boundary condition (perioidic or
459  * otherwise) or on a partition boundary. If a partition
460  * boundary then trace is always considered to be the left
461  * adjacent trace and the normal is pointing outward of the
462  * soltuion domain. We have to perform an additional case for
463  * a periodic boundary where wer chose the element with the
464  * lowest global id. If the trace is on a parallel partition
465  * we use a member of the traceMap where one side is chosen to
466  * contribute to a unique map and have a value which is not
467  * -1 so this is the identifier for the left adjacent side
468  *
469  * 2) If the element is a elemental boundary on one element
470  * the left adjacent element is defined by a link to the left
471  * element from the trace expansion and this is consistent
472  * with the defitiion of the normal which is determined by the
473  * largest id (in contrast to the periodic boundary definition
474  * !). This reversal of convention does not really matter
475  * providing the normals are defined consistently.
476  */
477 
478 bool DisContField::IsLeftAdjacentTrace(const int n, const int e)
479 {
481  m_traceMap->GetElmtToTrace()[n][e];
482 
483  PeriodicMap periodicTraces = (m_expType == e1D) ? m_periodicVerts
484  : (m_expType == e2D) ? m_periodicEdges
485  : m_periodicFaces;
486 
487  bool fwd = true;
488  if (traceEl->GetLeftAdjacentElementTrace() == -1 ||
489  traceEl->GetRightAdjacentElementTrace() == -1)
490  {
491  // Boundary edge (1 connected element). Do nothing in
492  // serial.
493  auto it = m_boundaryTraces.find(traceEl->GetElmtId());
494 
495  // If the edge does not have a boundary condition set on
496  // it, then assume it is a partition edge or periodic.
497  if (it == m_boundaryTraces.end())
498  {
499  fwd = true;
500  }
501  }
502  else if (traceEl->GetLeftAdjacentElementTrace() != -1 &&
503  traceEl->GetRightAdjacentElementTrace() != -1)
504  {
505  // Non-boundary edge (2 connected elements).
506  fwd = (traceEl->GetLeftAdjacentElementExp().get()) == (*m_exp)[n].get();
507  }
508  else
509  {
510  ASSERTL2(false, "Unconnected trace element!");
511  }
512 
513  return fwd;
514 }
515 
516 // Given all boundary regions for the whole solution determine
517 // which ones (if any) are part of domain and proivde all other
518 // conditions are given as UserDefined Dirichlet.
520  const SpatialDomains::CompositeMap &domain,
522  const std::string &variable)
523 {
524  boost::ignore_unused(variable);
526 
527  returnval =
529 
530  map<int, int> GeometryToRegionsMap;
531 
533  Allbcs.GetBoundaryRegions();
535  Allbcs.GetBoundaryConditions();
536 
537  // Set up a map of all boundary regions
538  for (auto &it : bregions)
539  {
540  for (auto &bregionIt : *it.second)
541  {
542  // can assume that all regions only contain one point in 1D
543  // Really do not need loop above
544  int id = bregionIt.second->m_geomVec[0]->GetGlobalID();
545  GeometryToRegionsMap[id] = it.first;
546  }
547  }
548 
549  map<int, SpatialDomains::GeometrySharedPtr> EndOfDomain;
550 
551  // Now find out which points in domain have only one vertex
552  for (auto &domIt : domain)
553  {
554  SpatialDomains::CompositeSharedPtr geomvector = domIt.second;
555  for (int i = 0; i < geomvector->m_geomVec.size(); ++i)
556  {
557  for (int j = 0; j < 2; ++j)
558  {
559  int vid = geomvector->m_geomVec[i]->GetVid(j);
560  if (EndOfDomain.count(vid) == 0)
561  {
562  EndOfDomain[vid] = geomvector->m_geomVec[i]->GetVertex(j);
563  }
564  else
565  {
566  EndOfDomain.erase(vid);
567  }
568  }
569  }
570  }
571  ASSERTL1(EndOfDomain.size() == 2, "Did not find two ends of domain");
572 
573  for (auto &regIt : EndOfDomain)
574  {
575  if (GeometryToRegionsMap.count(regIt.first) != 0)
576  {
577  // Set up boundary condition up
578  auto iter = GeometryToRegionsMap.find(regIt.first);
579  ASSERTL1(iter != GeometryToRegionsMap.end(),
580  "Failied to find GeometryToRegionMap");
581 
582  int regionId = iter->second;
583  auto bregionsIter = bregions.find(regionId);
584  ASSERTL1(bregionsIter != bregions.end(),
585  "Failed to find boundary region");
586 
587  SpatialDomains::BoundaryRegionShPtr breg = bregionsIter->second;
588  returnval->AddBoundaryRegions(regionId, breg);
589 
590  auto bconditionsIter = bconditions.find(regionId);
591  ASSERTL1(bconditionsIter != bconditions.end(),
592  "Failed to find boundary collection");
593 
595  bconditionsIter->second;
596  returnval->AddBoundaryConditions(regionId, bcond);
597  }
598  }
599 
600  return returnval;
601 }
602 
603 /**
604  * Constructor for use in multidomain computations where a
605  * domain list can be passed instead of graph1D
606  *
607  * @param domain Subdomain specified in the inputfile from
608  * which the DisContField is set up
609  */
611  const SpatialDomains::MeshGraphSharedPtr &graph1D,
612  const SpatialDomains::CompositeMap &domain,
614  const std::string &variable,
615  const LibUtilities::CommSharedPtr &comm,
616  bool SetToOneSpaceDimension,
617  const Collections::ImplementationType ImpType)
618  : ExpList(pSession, domain, graph1D, true, variable, SetToOneSpaceDimension,
619  comm, ImpType)
620 {
621  if (variable.compare("DefaultVar") != 0)
622  {
624  GetDomainBCs(domain, Allbcs, variable);
625 
626  GenerateBoundaryConditionExpansion(m_graph, *DomBCs, variable);
627  EvaluateBoundaryConditions(0.0, variable);
628  ApplyGeomInfo();
629  FindPeriodicTraces(*DomBCs, variable);
630  }
631 
632  SetUpDG(variable);
633 }
634 
635 /**
636  * Constructs a field as a copy of an existing field.
637  * @param In Existing DisContField object to copy.
638  */
640  const bool DeclareCoeffPhysArrays)
641  : ExpList(In, DeclareCoeffPhysArrays), m_bndConditions(In.m_bndConditions),
642  m_bndCondExpansions(In.m_bndCondExpansions),
643  m_globalBndMat(In.m_globalBndMat), m_traceMap(In.m_traceMap),
644  m_boundaryTraces(In.m_boundaryTraces),
645  m_periodicVerts(In.m_periodicVerts),
646  m_periodicFwdCopy(In.m_periodicFwdCopy),
647  m_periodicBwdCopy(In.m_periodicBwdCopy),
648  m_leftAdjacentTraces(In.m_leftAdjacentTraces),
649  m_locTraceToTraceMap(In.m_locTraceToTraceMap)
650 {
651  if (In.m_trace)
652  {
654  *In.m_trace, DeclareCoeffPhysArrays);
655  }
656 }
657 
658 /*
659  * @brief Copy type constructor which declares new boundary conditions
660  * and re-uses mapping info and trace space if possible
661  */
664  const std::string &variable, const bool SetUpJustDG,
665  const bool DeclareCoeffPhysArrays)
666  : ExpList(In, DeclareCoeffPhysArrays)
667 {
668 
670 
671  // Set up boundary conditions for this variable.
672  // Do not set up BCs if default variable
673  if (variable.compare("DefaultVar") != 0)
674  {
676  GenerateBoundaryConditionExpansion(graph, bcs, variable);
677 
678  if (DeclareCoeffPhysArrays)
679  {
680  EvaluateBoundaryConditions(0.0, variable);
681  }
682 
684  {
685  // Find periodic edges for this variable.
686  FindPeriodicTraces(bcs, variable);
687 
688  if (SetUpJustDG)
689  {
690  SetUpDG(variable);
691  }
692  else
693  {
694  // set elmt edges to point to robin bc edges if required
695  int i, cnt = 0;
696  Array<OneD, int> ElmtID, TraceID;
697  GetBoundaryToElmtMap(ElmtID, TraceID);
698 
699  for (i = 0; i < m_bndCondExpansions.size(); ++i)
700  {
702 
703  int e;
704  locExpList = m_bndCondExpansions[i];
705 
706  for (e = 0; e < locExpList->GetExpSize(); ++e)
707  {
708  (*m_exp)[ElmtID[cnt + e]]->SetTraceExp(
709  TraceID[cnt + e], locExpList->GetExp(e));
710  locExpList->GetExp(e)->SetAdjacentElementExp(
711  TraceID[cnt + e], (*m_exp)[ElmtID[cnt + e]]);
712  }
713 
714  cnt += m_bndCondExpansions[i]->GetExpSize();
715  }
716 
717  if (m_session->DefinesSolverInfo("PROJECTION"))
718  {
719  std::string ProjectStr =
720  m_session->GetSolverInfo("PROJECTION");
721 
722  if ((ProjectStr == "MixedCGDG") ||
723  (ProjectStr == "Mixed_CG_Discontinuous"))
724  {
725  SetUpDG();
726  }
727  else
728  {
730  }
731  }
732  else
733  {
735  }
736  }
737  }
738  else
739  {
741  m_trace = In.m_trace;
742  m_traceMap = In.m_traceMap;
752 
753  if (!SetUpJustDG)
754  {
755  // set elmt edges to point to robin bc edges if required
756  int i, cnt = 0;
757  Array<OneD, int> ElmtID, TraceID;
758  GetBoundaryToElmtMap(ElmtID, TraceID);
759 
760  for (i = 0; i < m_bndCondExpansions.size(); ++i)
761  {
763 
764  int e;
765  locExpList = m_bndCondExpansions[i];
766 
767  for (e = 0; e < locExpList->GetExpSize(); ++e)
768  {
769  (*m_exp)[ElmtID[cnt + e]]->SetTraceExp(
770  TraceID[cnt + e], locExpList->GetExp(e));
771  locExpList->GetExp(e)->SetAdjacentElementExp(
772  TraceID[cnt + e], (*m_exp)[ElmtID[cnt + e]]);
773  }
774  cnt += m_bndCondExpansions[i]->GetExpSize();
775  }
776 
778  }
779  }
780  }
781 }
782 
783 /**
784  * Constructs a field as a copy of an existing explist field.
785  * @param In Existing ExpList object to copy.
786  */
788 {
789 }
790 
791 /**
792  *
793  */
795 {
796 }
797 
798 /**
799  * \brief This function discretises the boundary conditions by setting
800  * up a list of one-dimensions lower boundary expansions.
801  *
802  * According to their boundary region, the separate boundary
803  * expansions are bundled together in an object of the class
804  *
805  * @param graph A mesh, containing information about the domain
806  * and the spectral/hp element expansions.
807  * @param bcs Information about the enforced boundary
808  * conditions.
809  * @param variable The session variable associated with the
810  * boundary conditions to enforce.
811  * @param DeclareCoeffPhysArrays bool to identify if array
812  * space should be setup.
813  * Default is true.
814  */
817  const SpatialDomains::BoundaryConditions &bcs, const std::string variable,
818  const bool DeclareCoeffPhysArrays)
819 {
820  int cnt = 0;
824  bcs.GetBoundaryRegions();
826  bcs.GetBoundaryConditions();
827 
832 
833  m_bndCondBndWeight = Array<OneD, NekDouble>{bregions.size(), 0.0};
834 
835  // count the number of non-periodic boundary points
836  for (auto &it : bregions)
837  {
838  bc = GetBoundaryCondition(bconditions, it.first, variable);
839 
841  m_session, *(it.second), graph, DeclareCoeffPhysArrays, variable,
842  false, bc->GetComm());
843 
844  m_bndCondExpansions[cnt] = locExpList;
845  m_bndConditions[cnt] = bc;
846 
847  std::string type = m_bndConditions[cnt]->GetUserDefined();
848 
849  // Set up normals on non-Dirichlet boundary conditions. Second
850  // two conditions ideally should be in local solver setup (when
851  // made into factory)
852  if (bc->GetBoundaryConditionType() != SpatialDomains::eDirichlet ||
853  boost::iequals(type, "I") || boost::iequals(type, "CalcBC"))
854  {
856  }
857  cnt++;
858  }
859 }
860 
861 /**
862  * @brief Determine the periodic faces, edges and vertices for the given
863  * graph.
864  *
865  * @param bcs Information about the boundary conditions.
866  * @param variable Specifies the field.
867  */
869  const SpatialDomains::BoundaryConditions &bcs, const std::string variable)
870 {
872  bcs.GetBoundaryRegions();
874  bcs.GetBoundaryConditions();
875 
876  LibUtilities::CommSharedPtr vComm = m_comm->GetRowComm();
877 
878  switch (m_expType)
879  {
880  case e1D:
881  {
882  int i, region1ID, region2ID;
883 
885  map<int, int> BregionToVertMap;
886 
887  // Construct list of all periodic Region and their
888  // global vertex on this process.
889  for (auto &it : bregions)
890  {
891  locBCond =
892  GetBoundaryCondition(bconditions, it.first, variable);
893 
894  if (locBCond->GetBoundaryConditionType() !=
896  {
897  continue;
898  }
899  int id =
900  it.second->begin()->second->m_geomVec[0]->GetGlobalID();
901 
902  BregionToVertMap[it.first] = id;
903  }
904 
905  set<int> islocal;
906 
907  int n = vComm->GetSize();
908  int p = vComm->GetRank();
909 
910  Array<OneD, int> nregions(n, 0);
911  nregions[p] = BregionToVertMap.size();
912  vComm->AllReduce(nregions, LibUtilities::ReduceSum);
913 
914  int totRegions = Vmath::Vsum(n, nregions, 1);
915 
916  Array<OneD, int> regOffset(n, 0);
917 
918  for (i = 1; i < n; ++i)
919  {
920  regOffset[i] = regOffset[i - 1] + nregions[i - 1];
921  }
922 
923  Array<OneD, int> bregmap(totRegions, 0);
924  Array<OneD, int> bregid(totRegions, 0);
925 
926  i = regOffset[p];
927  for (auto &iit : BregionToVertMap)
928  {
929  bregid[i] = iit.first;
930  bregmap[i++] = iit.second;
931  islocal.insert(iit.first);
932  }
933 
934  vComm->AllReduce(bregmap, LibUtilities::ReduceSum);
935  vComm->AllReduce(bregid, LibUtilities::ReduceSum);
936 
937  for (int i = 0; i < totRegions; ++i)
938  {
939  BregionToVertMap[bregid[i]] = bregmap[i];
940  }
941 
942  // Construct list of all periodic pairs local to this process.
943  for (auto &it : bregions)
944  {
945  locBCond =
946  GetBoundaryCondition(bconditions, it.first, variable);
947 
948  if (locBCond->GetBoundaryConditionType() !=
950  {
951  continue;
952  }
953 
954  // Identify periodic boundary region IDs.
955  region1ID = it.first;
956  region2ID =
957  std::static_pointer_cast<
959  ->m_connectedBoundaryRegion;
960 
961  ASSERTL0(BregionToVertMap.count(region1ID) != 0,
962  "Cannot determine vertex of region1ID");
963 
964  ASSERTL0(BregionToVertMap.count(region2ID) != 0,
965  "Cannot determine vertex of region2ID");
966 
967  PeriodicEntity ent(BregionToVertMap[region2ID],
969  islocal.count(region2ID) != 0);
970 
971  m_periodicVerts[BregionToVertMap[region1ID]].push_back(ent);
972  }
973  }
974  break;
975  case e2D:
976  {
977  int region1ID, region2ID, i, j, k, cnt;
979 
981  m_graph->GetCompositeOrdering();
983  m_graph->GetBndRegionOrdering();
984  SpatialDomains::CompositeMap compMap = m_graph->GetComposites();
985 
986  // Unique collection of pairs of periodic composites
987  // (i.e. if composites 1 and 2 are periodic then this
988  // map will contain either the pair (1,2) or (2,1) but
989  // not both).
990  map<int, int> perComps;
991  map<int, vector<int>> allVerts;
992  set<int> locVerts;
993  map<int, pair<int, StdRegions::Orientation>> allEdges;
994 
995  // Set up a set of all local verts and edges.
996  for (i = 0; i < (*m_exp).size(); ++i)
997  {
998  for (j = 0; j < (*m_exp)[i]->GetNverts(); ++j)
999  {
1000  int id = (*m_exp)[i]->GetGeom()->GetVid(j);
1001  locVerts.insert(id);
1002  }
1003  }
1004 
1005  // Construct list of all periodic pairs local to this process.
1006  for (auto &it : bregions)
1007  {
1008  locBCond =
1009  GetBoundaryCondition(bconditions, it.first, variable);
1010 
1011  if (locBCond->GetBoundaryConditionType() !=
1013  {
1014  continue;
1015  }
1016 
1017  // Identify periodic boundary region IDs.
1018  region1ID = it.first;
1019  region2ID =
1020  std::static_pointer_cast<
1022  ->m_connectedBoundaryRegion;
1023 
1024  // From this identify composites. Note that in
1025  // serial this will be an empty map.
1026  int cId1, cId2;
1027  if (vComm->GetSize() == 1)
1028  {
1029  cId1 = it.second->begin()->first;
1030  cId2 = bregions.find(region2ID)->second->begin()->first;
1031  }
1032  else
1033  {
1034  cId1 = bndRegOrder.find(region1ID)->second[0];
1035  cId2 = bndRegOrder.find(region2ID)->second[0];
1036  }
1037 
1038  ASSERTL0(it.second->size() == 1,
1039  "Boundary region " +
1040  boost::lexical_cast<string>(region1ID) +
1041  " should only contain 1 composite.");
1042 
1043  vector<unsigned int> tmpOrder;
1044 
1045  // Construct set containing all periodic edgesd on
1046  // this process
1048  it.second->begin()->second;
1049 
1050  for (i = 0; i < c->m_geomVec.size(); ++i)
1051  {
1053  std::dynamic_pointer_cast<SpatialDomains::SegGeom>(
1054  c->m_geomVec[i]);
1055  ASSERTL0(segGeom, "Unable to cast to shared ptr");
1056 
1058  m_graph->GetElementsFromEdge(segGeom);
1059  ASSERTL0(elmt->size() == 1,
1060  "The periodic boundaries belong to "
1061  "more than one element of the mesh");
1062 
1064  std::dynamic_pointer_cast<SpatialDomains::Geometry2D>(
1065  elmt->at(0).first);
1066 
1067  allEdges[c->m_geomVec[i]->GetGlobalID()] =
1068  make_pair(elmt->at(0).second,
1069  geom->GetEorient(elmt->at(0).second));
1070 
1071  // In serial mesh partitioning will not have occurred so
1072  // need to fill composite ordering map manually.
1073  if (vComm->GetSize() == 1)
1074  {
1075  tmpOrder.push_back(c->m_geomVec[i]->GetGlobalID());
1076  }
1077 
1078  vector<int> vertList(2);
1079  vertList[0] = segGeom->GetVid(0);
1080  vertList[1] = segGeom->GetVid(1);
1081  allVerts[c->m_geomVec[i]->GetGlobalID()] = vertList;
1082  }
1083 
1084  if (vComm->GetSize() == 1)
1085  {
1086  compOrder[it.second->begin()->first] = tmpOrder;
1087  }
1088 
1089  // See if we already have either region1 or
1090  // region2 stored in perComps map.
1091  if (perComps.count(cId1) == 0)
1092  {
1093  if (perComps.count(cId2) == 0)
1094  {
1095  perComps[cId1] = cId2;
1096  }
1097  else
1098  {
1099  std::stringstream ss;
1100  ss << "Boundary region " << cId2 << " should be "
1101  << "periodic with " << perComps[cId2] << " but "
1102  << "found " << cId1 << " instead!";
1103  ASSERTL0(perComps[cId2] == cId1, ss.str());
1104  }
1105  }
1106  else
1107  {
1108  std::stringstream ss;
1109  ss << "Boundary region " << cId1 << " should be "
1110  << "periodic with " << perComps[cId1] << " but "
1111  << "found " << cId2 << " instead!";
1112  ASSERTL0(perComps[cId1] == cId1, ss.str());
1113  }
1114  }
1115 
1116  // In case of periodic partition being split over many composites
1117  // may not have all periodic matches so check this
1118  int idmax = -1;
1119  for (auto &cIt : perComps)
1120  {
1121  idmax = max(idmax, cIt.first);
1122  idmax = max(idmax, cIt.second);
1123  }
1124  vComm->AllReduce(idmax, LibUtilities::ReduceMax);
1125  idmax++;
1126  Array<OneD, int> perid(idmax, -1);
1127  for (auto &cIt : perComps)
1128  {
1129  perid[cIt.first] = cIt.second;
1130  }
1131  vComm->AllReduce(perid, LibUtilities::ReduceMax);
1132  // update all partitions
1133  for (int i = 0; i < idmax; ++i)
1134  {
1135  if (perid[i] > -1)
1136  {
1137  // skip if complemetary relationship has
1138  // already been speficied in list
1139  if (perComps.count(perid[i]))
1140  {
1141  continue;
1142  }
1143  perComps[i] = perid[i];
1144  }
1145  }
1146 
1147  // Process local edge list to obtain relative edge
1148  // orientations.
1149  int n = vComm->GetSize();
1150  int p = vComm->GetRank();
1151  int totEdges;
1152  Array<OneD, int> edgecounts(n, 0);
1153  Array<OneD, int> edgeoffset(n, 0);
1154  Array<OneD, int> vertoffset(n, 0);
1155 
1156  edgecounts[p] = allEdges.size();
1157  vComm->AllReduce(edgecounts, LibUtilities::ReduceSum);
1158 
1159  edgeoffset[0] = 0;
1160  for (i = 1; i < n; ++i)
1161  {
1162  edgeoffset[i] = edgeoffset[i - 1] + edgecounts[i - 1];
1163  }
1164 
1165  totEdges = Vmath::Vsum(n, edgecounts, 1);
1166  Array<OneD, int> edgeIds(totEdges, 0);
1167  Array<OneD, int> edgeIdx(totEdges, 0);
1168  Array<OneD, int> edgeOrient(totEdges, 0);
1169  Array<OneD, int> edgeVerts(totEdges, 0);
1170 
1171  auto sIt = allEdges.begin();
1172 
1173  for (i = 0; sIt != allEdges.end(); ++sIt)
1174  {
1175  edgeIds[edgeoffset[p] + i] = sIt->first;
1176  edgeIdx[edgeoffset[p] + i] = sIt->second.first;
1177  edgeOrient[edgeoffset[p] + i] = sIt->second.second;
1178  edgeVerts[edgeoffset[p] + i++] = allVerts[sIt->first].size();
1179  }
1180 
1181  vComm->AllReduce(edgeIds, LibUtilities::ReduceSum);
1182  vComm->AllReduce(edgeIdx, LibUtilities::ReduceSum);
1183  vComm->AllReduce(edgeOrient, LibUtilities::ReduceSum);
1184  vComm->AllReduce(edgeVerts, LibUtilities::ReduceSum);
1185 
1186  // Calculate number of vertices on each processor.
1187  Array<OneD, int> procVerts(n, 0);
1188  int nTotVerts;
1189 
1190  // Note if there are no periodic edges at all calling Vsum will
1191  // cause a segfault.
1192  if (totEdges > 0)
1193  {
1194  nTotVerts = Vmath::Vsum(totEdges, edgeVerts, 1);
1195  }
1196  else
1197  {
1198  nTotVerts = 0;
1199  }
1200 
1201  for (i = 0; i < n; ++i)
1202  {
1203  if (edgecounts[i] > 0)
1204  {
1205  procVerts[i] = Vmath::Vsum(edgecounts[i],
1206  edgeVerts + edgeoffset[i], 1);
1207  }
1208  else
1209  {
1210  procVerts[i] = 0;
1211  }
1212  }
1213  vertoffset[0] = 0;
1214 
1215  for (i = 1; i < n; ++i)
1216  {
1217  vertoffset[i] = vertoffset[i - 1] + procVerts[i - 1];
1218  }
1219 
1220  Array<OneD, int> traceIds(nTotVerts, 0);
1221  for (i = 0, sIt = allEdges.begin(); sIt != allEdges.end(); ++sIt)
1222  {
1223  for (j = 0; j < allVerts[sIt->first].size(); ++j)
1224  {
1225  traceIds[vertoffset[p] + i++] = allVerts[sIt->first][j];
1226  }
1227  }
1228 
1229  vComm->AllReduce(traceIds, LibUtilities::ReduceSum);
1230 
1231  // For simplicity's sake create a map of edge id -> orientation.
1232  map<int, StdRegions::Orientation> orientMap;
1233  map<int, vector<int>> vertMap;
1234 
1235  for (cnt = i = 0; i < totEdges; ++i)
1236  {
1237  ASSERTL0(orientMap.count(edgeIds[i]) == 0,
1238  "Already found edge in orientation map!");
1239 
1240  // Work out relative orientations. To avoid having
1241  // to exchange vertex locations, we figure out if
1242  // the edges are backwards or forwards orientated
1243  // with respect to a forwards orientation that is
1244  // CCW. Since our local geometries are
1245  // forwards-orientated with respect to the
1246  // Cartesian axes, we need to invert the
1247  // orientation for the top and left edges of a
1248  // quad and the left edge of a triangle.
1250  (StdRegions::Orientation)edgeOrient[i];
1251 
1252  if (edgeIdx[i] > 1)
1253  {
1256  }
1257 
1258  orientMap[edgeIds[i]] = o;
1259 
1260  vector<int> verts(edgeVerts[i]);
1261 
1262  for (j = 0; j < edgeVerts[i]; ++j)
1263  {
1264  verts[j] = traceIds[cnt++];
1265  }
1266  vertMap[edgeIds[i]] = verts;
1267  }
1268 
1269  // Go through list of composites and figure out which
1270  // edges are parallel from original ordering in
1271  // session file. This includes composites which are
1272  // not necessarily on this process.
1273  map<int, int> allCompPairs;
1274 
1275  // Store temporary map of periodic vertices which will hold all
1276  // periodic vertices on the entire mesh so that doubly periodic
1277  // vertices can be counted properly across partitions. Local
1278  // vertices are copied into m_periodicVerts at the end of the
1279  // function.
1280  PeriodicMap periodicVerts;
1281 
1282  for (auto &cIt : perComps)
1283  {
1285  const int id1 = cIt.first;
1286  const int id2 = cIt.second;
1287  std::string id1s = boost::lexical_cast<string>(id1);
1288  std::string id2s = boost::lexical_cast<string>(id2);
1289 
1290  if (compMap.count(id1) > 0)
1291  {
1292  c[0] = compMap[id1];
1293  }
1294 
1295  if (compMap.count(id2) > 0)
1296  {
1297  c[1] = compMap[id2];
1298  }
1299 
1300  // Loop over composite ordering to construct list of all
1301  // periodic edges regardless of whether they are on this
1302  // process.
1303  map<int, int> compPairs;
1304 
1305  ASSERTL0(compOrder.count(id1) > 0,
1306  "Unable to find composite " + id1s + " in order map.");
1307  ASSERTL0(compOrder.count(id2) > 0,
1308  "Unable to find composite " + id2s + " in order map.");
1309  ASSERTL0(compOrder[id1].size() == compOrder[id2].size(),
1310  "Periodic composites " + id1s + " and " + id2s +
1311  " should have the same number of elements.");
1312  ASSERTL0(compOrder[id1].size() > 0, "Periodic composites " +
1313  id1s + " and " + id2s +
1314  " are empty!");
1315 
1316  // TODO: Add more checks.
1317  for (i = 0; i < compOrder[id1].size(); ++i)
1318  {
1319  int eId1 = compOrder[id1][i];
1320  int eId2 = compOrder[id2][i];
1321 
1322  ASSERTL0(compPairs.count(eId1) == 0, "Already paired.");
1323 
1324  if (compPairs.count(eId2) != 0)
1325  {
1326  ASSERTL0(compPairs[eId2] == eId1, "Pairing incorrect");
1327  }
1328  compPairs[eId1] = eId2;
1329  }
1330 
1331  // Construct set of all edges that we have
1332  // locally on this processor.
1333  set<int> locEdges;
1334  for (i = 0; i < 2; ++i)
1335  {
1336  if (!c[i])
1337  {
1338  continue;
1339  }
1340 
1341  if (c[i]->m_geomVec.size() > 0)
1342  {
1343  for (j = 0; j < c[i]->m_geomVec.size(); ++j)
1344  {
1345  locEdges.insert(c[i]->m_geomVec[j]->GetGlobalID());
1346  }
1347  }
1348  }
1349 
1350  // Loop over all edges in the geometry composite.
1351  for (auto &pIt : compPairs)
1352  {
1353  int ids[2] = {pIt.first, pIt.second};
1354  bool local[2] = {locEdges.count(pIt.first) > 0,
1355  locEdges.count(pIt.second) > 0};
1356 
1357  ASSERTL0(orientMap.count(ids[0]) > 0 &&
1358  orientMap.count(ids[1]) > 0,
1359  "Unable to find edge in orientation map.");
1360 
1361  allCompPairs[pIt.first] = pIt.second;
1362  allCompPairs[pIt.second] = pIt.first;
1363 
1364  for (i = 0; i < 2; ++i)
1365  {
1366  if (!local[i])
1367  {
1368  continue;
1369  }
1370 
1371  int other = (i + 1) % 2;
1372 
1374  orientMap[ids[i]] == orientMap[ids[other]]
1377 
1378  PeriodicEntity ent(ids[other], o, local[other]);
1379  m_periodicEdges[ids[i]].push_back(ent);
1380  }
1381 
1382  for (i = 0; i < 2; ++i)
1383  {
1384  int other = (i + 1) % 2;
1385 
1387  orientMap[ids[i]] == orientMap[ids[other]]
1390 
1391  // Determine periodic vertices.
1392  vector<int> perVerts1 = vertMap[ids[i]];
1393  vector<int> perVerts2 = vertMap[ids[other]];
1394 
1395  map<int, pair<int, bool>> tmpMap;
1396  if (o == StdRegions::eForwards)
1397  {
1398  tmpMap[perVerts1[0]] = make_pair(
1399  perVerts2[0], locVerts.count(perVerts2[0]) > 0);
1400  tmpMap[perVerts1[1]] = make_pair(
1401  perVerts2[1], locVerts.count(perVerts2[1]) > 0);
1402  }
1403  else
1404  {
1405  tmpMap[perVerts1[0]] = make_pair(
1406  perVerts2[1], locVerts.count(perVerts2[1]) > 0);
1407  tmpMap[perVerts1[1]] = make_pair(
1408  perVerts2[0], locVerts.count(perVerts2[0]) > 0);
1409  }
1410 
1411  for (auto &mIt : tmpMap)
1412  {
1413  // See if this vertex has been recorded already.
1414  PeriodicEntity ent2(mIt.second.first,
1416  mIt.second.second);
1417  auto perIt = periodicVerts.find(mIt.first);
1418 
1419  if (perIt == periodicVerts.end())
1420  {
1421  periodicVerts[mIt.first].push_back(ent2);
1422  perIt = periodicVerts.find(mIt.first);
1423  }
1424  else
1425  {
1426  bool doAdd = true;
1427  for (j = 0; j < perIt->second.size(); ++j)
1428  {
1429  if (perIt->second[j].id == mIt.second.first)
1430  {
1431  doAdd = false;
1432  break;
1433  }
1434  }
1435 
1436  if (doAdd)
1437  {
1438  perIt->second.push_back(ent2);
1439  }
1440  }
1441  }
1442  }
1443  }
1444  }
1445 
1446  // Search for periodic vertices and edges which are not in
1447  // a periodic composite but lie in this process. First, loop
1448  // over all information we have from other processors.
1449  for (cnt = i = 0; i < totEdges; ++i)
1450  {
1451  int edgeId = edgeIds[i];
1452 
1453  ASSERTL0(allCompPairs.count(edgeId) > 0,
1454  "Unable to find matching periodic edge.");
1455 
1456  int perEdgeId = allCompPairs[edgeId];
1457 
1458  for (j = 0; j < edgeVerts[i]; ++j, ++cnt)
1459  {
1460  int vId = traceIds[cnt];
1461 
1462  auto perId = periodicVerts.find(vId);
1463 
1464  if (perId == periodicVerts.end())
1465  {
1466  // This vertex is not included in the
1467  // map. Figure out which vertex it is
1468  // supposed to be periodic with. perEdgeId
1469  // is the edge ID which is periodic with
1470  // edgeId. The logic is much the same as
1471  // the loop above.
1472  int perVertexId =
1473  orientMap[edgeId] == orientMap[perEdgeId]
1474  ? vertMap[perEdgeId][(j + 1) % 2]
1475  : vertMap[perEdgeId][j];
1476 
1477  PeriodicEntity ent(perVertexId,
1479  locVerts.count(perVertexId) > 0);
1480 
1481  periodicVerts[vId].push_back(ent);
1482  }
1483  }
1484  }
1485 
1486  // Loop over all periodic vertices to complete connectivity
1487  // information.
1488  for (auto &perIt : periodicVerts)
1489  {
1490  // Loop over associated vertices.
1491  for (i = 0; i < perIt.second.size(); ++i)
1492  {
1493  auto perIt2 = periodicVerts.find(perIt.second[i].id);
1494  ASSERTL0(perIt2 != periodicVerts.end(),
1495  "Couldn't find periodic vertex.");
1496 
1497  for (j = 0; j < perIt2->second.size(); ++j)
1498  {
1499  if (perIt2->second[j].id == perIt.first)
1500  {
1501  continue;
1502  }
1503 
1504  bool doAdd = true;
1505  for (k = 0; k < perIt.second.size(); ++k)
1506  {
1507  if (perIt2->second[j].id == perIt.second[k].id)
1508  {
1509  doAdd = false;
1510  break;
1511  }
1512  }
1513 
1514  if (doAdd)
1515  {
1516  perIt.second.push_back(perIt2->second[j]);
1517  }
1518  }
1519  }
1520  }
1521 
1522  // Do one final loop over periodic vertices to remove non-local
1523  // vertices from map.
1524  for (auto &perIt : periodicVerts)
1525  {
1526  if (locVerts.count(perIt.first) > 0)
1527  {
1528  m_periodicVerts.insert(perIt);
1529  }
1530  }
1531  }
1532  break;
1533  case e3D:
1534  {
1536  m_graph->GetCompositeOrdering();
1537  SpatialDomains::BndRegionOrdering bndRegOrder =
1538  m_graph->GetBndRegionOrdering();
1539  SpatialDomains::CompositeMap compMap = m_graph->GetComposites();
1540 
1541  // perComps: Stores a unique collection of pairs of periodic
1542  // composites (i.e. if composites 1 and 2 are periodic then this map
1543  // will contain either the pair (1,2) or (2,1) but not both).
1544  //
1545  // The four maps allVerts, allCoord, allEdges and allOrient map a
1546  // periodic face to a vector containing the vertex ids of the face;
1547  // their coordinates; the edge ids of the face; and their
1548  // orientation within that face respectively.
1549  //
1550  // Finally the three sets locVerts, locEdges and locFaces store any
1551  // vertices, edges and faces that belong to a periodic composite and
1552  // lie on this process.
1553  map<int, RotPeriodicInfo> rotComp;
1554  map<int, int> perComps;
1555  map<int, vector<int>> allVerts;
1556  map<int, SpatialDomains::PointGeomVector> allCoord;
1557  map<int, vector<int>> allEdges;
1558  map<int, vector<StdRegions::Orientation>> allOrient;
1559  set<int> locVerts;
1560  set<int> locEdges;
1561  set<int> locFaces;
1562 
1563  int region1ID, region2ID, i, j, k, cnt;
1565 
1566  // Set up a set of all local verts and edges.
1567  for (i = 0; i < (*m_exp).size(); ++i)
1568  {
1569  for (j = 0; j < (*m_exp)[i]->GetNverts(); ++j)
1570  {
1571  int id = (*m_exp)[i]->GetGeom()->GetVid(j);
1572  locVerts.insert(id);
1573  }
1574 
1575  for (j = 0; j < (*m_exp)[i]->GetGeom()->GetNumEdges(); ++j)
1576  {
1577  int id = (*m_exp)[i]->GetGeom()->GetEid(j);
1578  locEdges.insert(id);
1579  }
1580  }
1581 
1582  // Begin by populating the perComps map. We loop over all periodic
1583  // boundary conditions and determine the composite associated with
1584  // it, then fill out the all* maps.
1585  for (auto &it : bregions)
1586  {
1587 
1588  locBCond =
1589  GetBoundaryCondition(bconditions, it.first, variable);
1590 
1591  if (locBCond->GetBoundaryConditionType() !=
1593  {
1594  continue;
1595  }
1596 
1597  // Identify periodic boundary region IDs.
1598  region1ID = it.first;
1599  region2ID =
1600  std::static_pointer_cast<
1602  ->m_connectedBoundaryRegion;
1603 
1604  // Check the region only contains a single composite.
1605  ASSERTL0(it.second->size() == 1,
1606  "Boundary region " +
1607  boost::lexical_cast<string>(region1ID) +
1608  " should only contain 1 composite.");
1609 
1610  // From this identify composites by looking at the original
1611  // boundary region ordering. Note that in serial the mesh
1612  // partitioner is not run, so this map will be empty and
1613  // therefore needs to be populated by using the corresponding
1614  // boundary region.
1615  int cId1, cId2;
1616  if (vComm->GetSize() == 1)
1617  {
1618  cId1 = it.second->begin()->first;
1619  cId2 = bregions.find(region2ID)->second->begin()->first;
1620  }
1621  else
1622  {
1623  cId1 = bndRegOrder.find(region1ID)->second[0];
1624  cId2 = bndRegOrder.find(region2ID)->second[0];
1625  }
1626 
1627  // check to see if boundary is rotationally aligned
1628  if (boost::icontains(locBCond->GetUserDefined(), "Rotated"))
1629  {
1630  vector<string> tmpstr;
1631 
1632  boost::split(tmpstr, locBCond->GetUserDefined(),
1633  boost::is_any_of(":"));
1634 
1635  if (boost::iequals(tmpstr[0], "Rotated"))
1636  {
1637  ASSERTL1(tmpstr.size() > 2,
1638  "Expected Rotated user defined string to "
1639  "contain direction and rotation angle "
1640  "and optionally a tolerance, "
1641  "i.e. Rotated:dir:PI/2:1e-6");
1642 
1643  ASSERTL1((tmpstr[1] == "x") || (tmpstr[1] == "y") ||
1644  (tmpstr[1] == "z"),
1645  "Rotated Dir is "
1646  "not specified as x,y or z");
1647 
1648  RotPeriodicInfo RotInfo;
1649  RotInfo.m_dir = (tmpstr[1] == "x") ? 0
1650  : (tmpstr[1] == "y") ? 1
1651  : 2;
1652 
1653  LibUtilities::Interpreter strEval;
1654  int ExprId = strEval.DefineFunction("", tmpstr[2]);
1655  RotInfo.m_angle = strEval.Evaluate(ExprId);
1656 
1657  if (tmpstr.size() == 4)
1658  {
1659  try
1660  {
1661  RotInfo.m_tol =
1662  boost::lexical_cast<NekDouble>(tmpstr[3]);
1663  }
1664  catch (...)
1665  {
1667  "failed to cast tolerance input "
1668  "to a double value in Rotated"
1669  "boundary information");
1670  }
1671  }
1672  else
1673  {
1674  RotInfo.m_tol = 1e-8;
1675  }
1676  rotComp[cId1] = RotInfo;
1677  }
1678  }
1679 
1681  it.second->begin()->second;
1682 
1683  vector<unsigned int> tmpOrder;
1684 
1685  // store the rotation info of this
1686 
1687  // From the composite, we now construct the allVerts, allEdges
1688  // and allCoord map so that they can be transferred across
1689  // processors. We also populate the locFaces set to store a
1690  // record of all faces local to this process.
1691  for (i = 0; i < c->m_geomVec.size(); ++i)
1692  {
1694  std::dynamic_pointer_cast<SpatialDomains::Geometry2D>(
1695  c->m_geomVec[i]);
1696  ASSERTL1(faceGeom, "Unable to cast to shared ptr");
1697 
1698  // Get geometry ID of this face and store in locFaces.
1699  int faceId = c->m_geomVec[i]->GetGlobalID();
1700  locFaces.insert(faceId);
1701 
1702  // In serial, mesh partitioning will not have occurred so
1703  // need to fill composite ordering map manually.
1704  if (vComm->GetSize() == 1)
1705  {
1706  tmpOrder.push_back(c->m_geomVec[i]->GetGlobalID());
1707  }
1708 
1709  // Loop over vertices and edges of the face to populate
1710  // allVerts, allEdges and allCoord maps.
1711  vector<int> vertList, edgeList;
1713  vector<StdRegions::Orientation> orientVec;
1714  for (j = 0; j < faceGeom->GetNumVerts(); ++j)
1715  {
1716  vertList.push_back(faceGeom->GetVid(j));
1717  edgeList.push_back(faceGeom->GetEid(j));
1718  coordVec.push_back(faceGeom->GetVertex(j));
1719  orientVec.push_back(faceGeom->GetEorient(j));
1720  }
1721 
1722  allVerts[faceId] = vertList;
1723  allEdges[faceId] = edgeList;
1724  allCoord[faceId] = coordVec;
1725  allOrient[faceId] = orientVec;
1726  }
1727 
1728  // In serial, record the composite ordering in compOrder for
1729  // later in the routine.
1730  if (vComm->GetSize() == 1)
1731  {
1732  compOrder[it.second->begin()->first] = tmpOrder;
1733  }
1734 
1735  // See if we already have either region1 or region2 stored in
1736  // perComps map already and do a sanity check to ensure regions
1737  // are mutually periodic.
1738  if (perComps.count(cId1) == 0)
1739  {
1740  if (perComps.count(cId2) == 0)
1741  {
1742  perComps[cId1] = cId2;
1743  }
1744  else
1745  {
1746  std::stringstream ss;
1747  ss << "Boundary region " << cId2 << " should be "
1748  << "periodic with " << perComps[cId2] << " but "
1749  << "found " << cId1 << " instead!";
1750  ASSERTL0(perComps[cId2] == cId1, ss.str());
1751  }
1752  }
1753  else
1754  {
1755  std::stringstream ss;
1756  ss << "Boundary region " << cId1 << " should be "
1757  << "periodic with " << perComps[cId1] << " but "
1758  << "found " << cId2 << " instead!";
1759  ASSERTL0(perComps[cId1] == cId1, ss.str());
1760  }
1761  }
1762 
1763  // In case of periodic partition being split over many composites
1764  // may not have all periodic matches so check this
1765  int idmax = -1;
1766  for (auto &cIt : perComps)
1767  {
1768  idmax = max(idmax, cIt.first);
1769  idmax = max(idmax, cIt.second);
1770  }
1771  vComm->AllReduce(idmax, LibUtilities::ReduceMax);
1772  idmax++;
1773  Array<OneD, int> perid(idmax, -1);
1774  for (auto &cIt : perComps)
1775  {
1776  perid[cIt.first] = cIt.second;
1777  }
1778  vComm->AllReduce(perid, LibUtilities::ReduceMax);
1779  // update all partitions
1780  for (int i = 0; i < idmax; ++i)
1781  {
1782  if (perid[i] > -1)
1783  {
1784  // skip if equivlaent relationship has
1785  // already been speficied in list
1786  if (perComps.count(perid[i]))
1787  {
1788  continue;
1789  }
1790  perComps[i] = perid[i];
1791  }
1792  }
1793 
1794  // The next routines process local face lists to
1795  // exchange vertices,
1796  // edges and faces.
1797  int n = vComm->GetSize();
1798  int p = vComm->GetRank();
1799  int totFaces;
1800  Array<OneD, int> facecounts(n, 0);
1801  Array<OneD, int> vertcounts(n, 0);
1802  Array<OneD, int> faceoffset(n, 0);
1803  Array<OneD, int> vertoffset(n, 0);
1804 
1805  Array<OneD, int> rotcounts(n, 0);
1806  Array<OneD, int> rotoffset(n, 0);
1807 
1808  rotcounts[p] = rotComp.size();
1809  vComm->AllReduce(rotcounts, LibUtilities::ReduceSum);
1810  int totrot = Vmath::Vsum(n, rotcounts, 1);
1811 
1812  if (totrot)
1813  {
1814  for (i = 1; i < n; ++i)
1815  {
1816  rotoffset[i] = rotoffset[i - 1] + rotcounts[i - 1];
1817  }
1818 
1819  Array<OneD, int> compid(totrot, 0);
1820  Array<OneD, int> rotdir(totrot, 0);
1821  Array<OneD, NekDouble> rotangle(totrot, 0.0);
1822  Array<OneD, NekDouble> rottol(totrot, 0.0);
1823 
1824  // fill in rotational informaiton
1825  auto rIt = rotComp.begin();
1826 
1827  for (i = 0; rIt != rotComp.end(); ++rIt)
1828  {
1829  compid[rotoffset[p] + i] = rIt->first;
1830  rotdir[rotoffset[p] + i] = rIt->second.m_dir;
1831  rotangle[rotoffset[p] + i] = rIt->second.m_angle;
1832  rottol[rotoffset[p] + i++] = rIt->second.m_tol;
1833  }
1834 
1835  vComm->AllReduce(compid, LibUtilities::ReduceSum);
1836  vComm->AllReduce(rotdir, LibUtilities::ReduceSum);
1837  vComm->AllReduce(rotangle, LibUtilities::ReduceSum);
1838  vComm->AllReduce(rottol, LibUtilities::ReduceSum);
1839 
1840  // Fill in full rotational composite list
1841  for (i = 0; i < totrot; ++i)
1842  {
1843  RotPeriodicInfo rinfo(rotdir[i], rotangle[i], rottol[i]);
1844 
1845  rotComp[compid[i]] = rinfo;
1846  }
1847  }
1848 
1849  // First exchange the number of faces on each process.
1850  facecounts[p] = locFaces.size();
1851  vComm->AllReduce(facecounts, LibUtilities::ReduceSum);
1852 
1853  // Set up an offset map to allow us to distribute face IDs to all
1854  // processors.
1855  faceoffset[0] = 0;
1856  for (i = 1; i < n; ++i)
1857  {
1858  faceoffset[i] = faceoffset[i - 1] + facecounts[i - 1];
1859  }
1860 
1861  // Calculate total number of faces.
1862  totFaces = Vmath::Vsum(n, facecounts, 1);
1863 
1864  // faceIds holds face IDs for each periodic face. faceVerts holds
1865  // the number of vertices in this face.
1866  Array<OneD, int> faceIds(totFaces, 0);
1867  Array<OneD, int> faceVerts(totFaces, 0);
1868 
1869  // Process p writes IDs of its faces into position faceoffset[p] of
1870  // faceIds which allows us to perform an AllReduce to distribute
1871  // information amongst processors.
1872  auto sIt = locFaces.begin();
1873  for (i = 0; sIt != locFaces.end(); ++sIt)
1874  {
1875  faceIds[faceoffset[p] + i] = *sIt;
1876  faceVerts[faceoffset[p] + i++] = allVerts[*sIt].size();
1877  }
1878 
1879  vComm->AllReduce(faceIds, LibUtilities::ReduceSum);
1880  vComm->AllReduce(faceVerts, LibUtilities::ReduceSum);
1881 
1882  // procVerts holds number of vertices (and also edges since each
1883  // face is 2D) on each process.
1884  Array<OneD, int> procVerts(n, 0);
1885  int nTotVerts;
1886 
1887  // Note if there are no periodic faces at all calling Vsum will
1888  // cause a segfault.
1889  if (totFaces > 0)
1890  {
1891  // Calculate number of vertices on each processor.
1892  nTotVerts = Vmath::Vsum(totFaces, faceVerts, 1);
1893  }
1894  else
1895  {
1896  nTotVerts = 0;
1897  }
1898 
1899  for (i = 0; i < n; ++i)
1900  {
1901  if (facecounts[i] > 0)
1902  {
1903  procVerts[i] = Vmath::Vsum(facecounts[i],
1904  faceVerts + faceoffset[i], 1);
1905  }
1906  else
1907  {
1908  procVerts[i] = 0;
1909  }
1910  }
1911 
1912  // vertoffset is defined in the same manner as edgeoffset
1913  // beforehand.
1914  vertoffset[0] = 0;
1915  for (i = 1; i < n; ++i)
1916  {
1917  vertoffset[i] = vertoffset[i - 1] + procVerts[i - 1];
1918  }
1919 
1920  // At this point we exchange all vertex IDs, edge IDs and vertex
1921  // coordinates for each face. The coordinates are necessary because
1922  // we need to calculate relative face orientations between periodic
1923  // faces to determined edge and vertex connectivity.
1924  Array<OneD, int> vertIds(nTotVerts, 0);
1925  Array<OneD, int> edgeIds(nTotVerts, 0);
1926  Array<OneD, int> edgeOrt(nTotVerts, 0);
1927  Array<OneD, NekDouble> vertX(nTotVerts, 0.0);
1928  Array<OneD, NekDouble> vertY(nTotVerts, 0.0);
1929  Array<OneD, NekDouble> vertZ(nTotVerts, 0.0);
1930 
1931  for (cnt = 0, sIt = locFaces.begin(); sIt != locFaces.end(); ++sIt)
1932  {
1933  for (j = 0; j < allVerts[*sIt].size(); ++j)
1934  {
1935  int vertId = allVerts[*sIt][j];
1936  vertIds[vertoffset[p] + cnt] = vertId;
1937  vertX[vertoffset[p] + cnt] = (*allCoord[*sIt][j])(0);
1938  vertY[vertoffset[p] + cnt] = (*allCoord[*sIt][j])(1);
1939  vertZ[vertoffset[p] + cnt] = (*allCoord[*sIt][j])(2);
1940  edgeIds[vertoffset[p] + cnt] = allEdges[*sIt][j];
1941  edgeOrt[vertoffset[p] + cnt++] = allOrient[*sIt][j];
1942  }
1943  }
1944 
1945  vComm->AllReduce(vertIds, LibUtilities::ReduceSum);
1946  vComm->AllReduce(vertX, LibUtilities::ReduceSum);
1947  vComm->AllReduce(vertY, LibUtilities::ReduceSum);
1948  vComm->AllReduce(vertZ, LibUtilities::ReduceSum);
1949  vComm->AllReduce(edgeIds, LibUtilities::ReduceSum);
1950  vComm->AllReduce(edgeOrt, LibUtilities::ReduceSum);
1951 
1952  // Finally now we have all of this information, we construct maps
1953  // which make accessing the information easier. These are
1954  // conceptually the same as all* maps at the beginning of the
1955  // routine, but now hold information for all periodic vertices.
1956  map<int, vector<int>> vertMap;
1957  map<int, vector<int>> edgeMap;
1958  map<int, SpatialDomains::PointGeomVector> coordMap;
1959 
1960  // These final two maps are required for determining the relative
1961  // orientation of periodic edges. vCoMap associates vertex IDs with
1962  // their coordinates, and eIdMap maps an edge ID to the two vertices
1963  // which construct it.
1964  map<int, SpatialDomains::PointGeomSharedPtr> vCoMap;
1965  map<int, pair<int, int>> eIdMap;
1966 
1967  for (cnt = i = 0; i < totFaces; ++i)
1968  {
1969  vector<int> edges(faceVerts[i]);
1970  vector<int> verts(faceVerts[i]);
1971  SpatialDomains::PointGeomVector coord(faceVerts[i]);
1972 
1973  // Keep track of cnt to enable correct edge vertices to be
1974  // inserted into eIdMap.
1975  int tmp = cnt;
1976  for (j = 0; j < faceVerts[i]; ++j, ++cnt)
1977  {
1978  edges[j] = edgeIds[cnt];
1979  verts[j] = vertIds[cnt];
1981  AllocateSharedPtr(3, verts[j], vertX[cnt], vertY[cnt],
1982  vertZ[cnt]);
1983  vCoMap[vertIds[cnt]] = coord[j];
1984 
1985  // Try to insert edge into the eIdMap to avoid re-inserting.
1986  auto testIns = eIdMap.insert(make_pair(
1987  edgeIds[cnt],
1988  make_pair(vertIds[tmp + j],
1989  vertIds[tmp + ((j + 1) % faceVerts[i])])));
1990 
1991  if (testIns.second == false)
1992  {
1993  continue;
1994  }
1995 
1996  // If the edge is reversed with respect to the face, then
1997  // swap the edges so that we have the original ordering of
1998  // the edge in the 3D element. This is necessary to properly
1999  // determine edge orientation. Note that the logic relies on
2000  // the fact that all edge forward directions are CCW
2001  // orientated: we use a tensor product ordering for 2D
2002  // elements so need to reverse this for edge IDs 2 and 3.
2003  StdRegions::Orientation edgeOrient =
2004  static_cast<StdRegions::Orientation>(edgeOrt[cnt]);
2005  if (j > 1)
2006  {
2007  edgeOrient = edgeOrient == StdRegions::eForwards
2010  }
2011 
2012  if (edgeOrient == StdRegions::eBackwards)
2013  {
2014  swap(testIns.first->second.first,
2015  testIns.first->second.second);
2016  }
2017  }
2018 
2019  vertMap[faceIds[i]] = verts;
2020  edgeMap[faceIds[i]] = edges;
2021  coordMap[faceIds[i]] = coord;
2022  }
2023 
2024  // Go through list of composites and figure out which edges are
2025  // parallel from original ordering in session file. This includes
2026  // composites which are not necessarily on this process.
2027 
2028  // Store temporary map of periodic vertices which will hold all
2029  // periodic vertices on the entire mesh so that doubly periodic
2030  // vertices/edges can be counted properly across partitions. Local
2031  // vertices/edges are copied into m_periodicVerts and
2032  // m_periodicEdges at the end of the function.
2033  PeriodicMap periodicVerts, periodicEdges;
2034 
2035  // Construct two maps which determine how vertices and edges of
2036  // faces connect given a specific face orientation. The key of the
2037  // map is the number of vertices in the face, used to determine
2038  // difference between tris and quads.
2039  map<int, map<StdRegions::Orientation, vector<int>>> vmap;
2040  map<int, map<StdRegions::Orientation, vector<int>>> emap;
2041 
2042  map<StdRegions::Orientation, vector<int>> quadVertMap;
2043  quadVertMap[StdRegions::eDir1FwdDir1_Dir2FwdDir2] = {0, 1, 2, 3};
2044  quadVertMap[StdRegions::eDir1FwdDir1_Dir2BwdDir2] = {3, 2, 1, 0};
2045  quadVertMap[StdRegions::eDir1BwdDir1_Dir2FwdDir2] = {1, 0, 3, 2};
2046  quadVertMap[StdRegions::eDir1BwdDir1_Dir2BwdDir2] = {2, 3, 0, 1};
2047  quadVertMap[StdRegions::eDir1FwdDir2_Dir2FwdDir1] = {0, 3, 2, 1};
2048  quadVertMap[StdRegions::eDir1FwdDir2_Dir2BwdDir1] = {1, 2, 3, 0};
2049  quadVertMap[StdRegions::eDir1BwdDir2_Dir2FwdDir1] = {3, 0, 1, 2};
2050  quadVertMap[StdRegions::eDir1BwdDir2_Dir2BwdDir1] = {2, 1, 0, 3};
2051 
2052  map<StdRegions::Orientation, vector<int>> quadEdgeMap;
2053  quadEdgeMap[StdRegions::eDir1FwdDir1_Dir2FwdDir2] = {0, 1, 2, 3};
2054  quadEdgeMap[StdRegions::eDir1FwdDir1_Dir2BwdDir2] = {2, 1, 0, 3};
2055  quadEdgeMap[StdRegions::eDir1BwdDir1_Dir2FwdDir2] = {0, 3, 2, 1};
2056  quadEdgeMap[StdRegions::eDir1BwdDir1_Dir2BwdDir2] = {2, 3, 0, 1};
2057  quadEdgeMap[StdRegions::eDir1FwdDir2_Dir2FwdDir1] = {3, 2, 1, 0};
2058  quadEdgeMap[StdRegions::eDir1FwdDir2_Dir2BwdDir1] = {1, 2, 3, 0};
2059  quadEdgeMap[StdRegions::eDir1BwdDir2_Dir2FwdDir1] = {3, 0, 1, 2};
2060  quadEdgeMap[StdRegions::eDir1BwdDir2_Dir2BwdDir1] = {1, 0, 3, 2};
2061 
2062  map<StdRegions::Orientation, vector<int>> triVertMap;
2063  triVertMap[StdRegions::eDir1FwdDir1_Dir2FwdDir2] = {0, 1, 2};
2064  triVertMap[StdRegions::eDir1BwdDir1_Dir2FwdDir2] = {1, 0, 2};
2065 
2066  map<StdRegions::Orientation, vector<int>> triEdgeMap;
2067  triEdgeMap[StdRegions::eDir1FwdDir1_Dir2FwdDir2] = {0, 1, 2};
2068  triEdgeMap[StdRegions::eDir1BwdDir1_Dir2FwdDir2] = {0, 2, 1};
2069 
2070  vmap[3] = triVertMap;
2071  vmap[4] = quadVertMap;
2072  emap[3] = triEdgeMap;
2073  emap[4] = quadEdgeMap;
2074 
2075  map<int, int> allCompPairs;
2076 
2077  // Collect composite id's of each periodic face for use if rotation
2078  // is required
2079  map<int, int> fIdToCompId;
2080 
2081  // Finally we have enough information to populate the periodic
2082  // vertex, edge and face maps. Begin by looping over all pairs of
2083  // periodic composites to determine pairs of periodic faces.
2084  for (auto &cIt : perComps)
2085  {
2087  const int id1 = cIt.first;
2088  const int id2 = cIt.second;
2089  std::string id1s = boost::lexical_cast<string>(id1);
2090  std::string id2s = boost::lexical_cast<string>(id2);
2091 
2092  if (compMap.count(id1) > 0)
2093  {
2094  c[0] = compMap[id1];
2095  }
2096 
2097  if (compMap.count(id2) > 0)
2098  {
2099  c[1] = compMap[id2];
2100  }
2101 
2102  // Loop over composite ordering to construct list of all
2103  // periodic faces, regardless of whether they are on this
2104  // process.
2105  map<int, int> compPairs;
2106 
2107  ASSERTL0(compOrder.count(id1) > 0,
2108  "Unable to find composite " + id1s + " in order map.");
2109  ASSERTL0(compOrder.count(id2) > 0,
2110  "Unable to find composite " + id2s + " in order map.");
2111  ASSERTL0(compOrder[id1].size() == compOrder[id2].size(),
2112  "Periodic composites " + id1s + " and " + id2s +
2113  " should have the same number of elements.");
2114  ASSERTL0(compOrder[id1].size() > 0, "Periodic composites " +
2115  id1s + " and " + id2s +
2116  " are empty!");
2117 
2118  // Look up composite ordering to determine pairs.
2119  for (i = 0; i < compOrder[id1].size(); ++i)
2120  {
2121  int eId1 = compOrder[id1][i];
2122  int eId2 = compOrder[id2][i];
2123 
2124  ASSERTL0(compPairs.count(eId1) == 0, "Already paired.");
2125 
2126  // Sanity check that the faces are mutually periodic.
2127  if (compPairs.count(eId2) != 0)
2128  {
2129  ASSERTL0(compPairs[eId2] == eId1, "Pairing incorrect");
2130  }
2131  compPairs[eId1] = eId2;
2132 
2133  // store a map of face ids to composite ids
2134  fIdToCompId[eId1] = id1;
2135  fIdToCompId[eId2] = id2;
2136  }
2137 
2138  // Now that we have all pairs of periodic faces, loop over the
2139  // ones local on this process and populate face/edge/vertex
2140  // maps.
2141  for (auto &pIt : compPairs)
2142  {
2143  int ids[2] = {pIt.first, pIt.second};
2144  bool local[2] = {locFaces.count(pIt.first) > 0,
2145  locFaces.count(pIt.second) > 0};
2146 
2147  ASSERTL0(coordMap.count(ids[0]) > 0 &&
2148  coordMap.count(ids[1]) > 0,
2149  "Unable to find face in coordinate map");
2150 
2151  allCompPairs[pIt.first] = pIt.second;
2152  allCompPairs[pIt.second] = pIt.first;
2153 
2154  // Loop up coordinates of the faces, check they have the
2155  // same number of vertices.
2156  SpatialDomains::PointGeomVector tmpVec[2] = {
2157  coordMap[ids[0]], coordMap[ids[1]]};
2158 
2159  ASSERTL0(tmpVec[0].size() == tmpVec[1].size(),
2160  "Two periodic faces have different number "
2161  "of vertices!");
2162 
2163  // o will store relative orientation of faces. Note that in
2164  // some transpose cases (Dir1FwdDir2_Dir2BwdDir1 and
2165  // Dir1BwdDir1_Dir2FwdDir1) it seems orientation will be
2166  // different going from face1->face2 instead of face2->face1
2167  // (check this).
2169  bool rotbnd = false;
2170  int dir = 0;
2171  NekDouble angle = 0.0;
2172  NekDouble sign = 0.0;
2173  NekDouble tol = 1e-8;
2174 
2175  // check to see if perioid boundary is rotated
2176  if (rotComp.count(fIdToCompId[pIt.first]))
2177  {
2178  rotbnd = true;
2179  dir = rotComp[fIdToCompId[pIt.first]].m_dir;
2180  angle = rotComp[fIdToCompId[pIt.first]].m_angle;
2181  tol = rotComp[fIdToCompId[pIt.first]].m_tol;
2182  }
2183 
2184  // Record periodic faces.
2185  for (i = 0; i < 2; ++i)
2186  {
2187  if (!local[i])
2188  {
2189  continue;
2190  }
2191 
2192  // Reference to the other face.
2193  int other = (i + 1) % 2;
2194 
2195  // angle is set up for i = 0 to i = 1
2196  sign = (i == 0) ? 1.0 : -1.0;
2197 
2198  // Calculate relative face orientation.
2199  if (tmpVec[0].size() == 3)
2200  {
2202  tmpVec[i], tmpVec[other], rotbnd, dir,
2203  sign * angle, tol);
2204  }
2205  else
2206  {
2208  tmpVec[i], tmpVec[other], rotbnd, dir,
2209  sign * angle, tol);
2210  }
2211 
2212  // Record face ID, orientation and whether other face is
2213  // local.
2214  PeriodicEntity ent(ids[other], o, local[other]);
2215  m_periodicFaces[ids[i]].push_back(ent);
2216  }
2217 
2218  int nFaceVerts = vertMap[ids[0]].size();
2219 
2220  // Determine periodic vertices.
2221  for (i = 0; i < 2; ++i)
2222  {
2223  int other = (i + 1) % 2;
2224 
2225  // angle is set up for i = 0 to i = 1
2226  sign = (i == 0) ? 1.0 : -1.0;
2227 
2228  // Calculate relative face orientation.
2229  if (tmpVec[0].size() == 3)
2230  {
2232  tmpVec[i], tmpVec[other], rotbnd, dir,
2233  sign * angle, tol);
2234  }
2235  else
2236  {
2238  tmpVec[i], tmpVec[other], rotbnd, dir,
2239  sign * angle, tol);
2240  }
2241 
2242  if (nFaceVerts == 3)
2243  {
2244  ASSERTL0(
2247  "Unsupported face orientation for face " +
2248  boost::lexical_cast<string>(ids[i]));
2249  }
2250 
2251  // Look up vertices for this face.
2252  vector<int> per1 = vertMap[ids[i]];
2253  vector<int> per2 = vertMap[ids[other]];
2254 
2255  // tmpMap will hold the pairs of vertices which are
2256  // periodic.
2257  map<int, pair<int, bool>> tmpMap;
2258 
2259  // Use vmap to determine which vertices connect given
2260  // the orientation o.
2261  for (j = 0; j < nFaceVerts; ++j)
2262  {
2263  int v = vmap[nFaceVerts][o][j];
2264  tmpMap[per1[j]] =
2265  make_pair(per2[v], locVerts.count(per2[v]) > 0);
2266  }
2267 
2268  // Now loop over tmpMap to associate periodic vertices.
2269  for (auto &mIt : tmpMap)
2270  {
2271  PeriodicEntity ent2(mIt.second.first,
2273  mIt.second.second);
2274 
2275  // See if this vertex has been recorded already.
2276  auto perIt = periodicVerts.find(mIt.first);
2277 
2278  if (perIt == periodicVerts.end())
2279  {
2280  // Vertex is new - just record this entity as
2281  // usual.
2282  periodicVerts[mIt.first].push_back(ent2);
2283  perIt = periodicVerts.find(mIt.first);
2284  }
2285  else
2286  {
2287  // Vertex is known - loop over the vertices
2288  // inside the record and potentially add vertex
2289  // mIt.second to the list.
2290  for (k = 0; k < perIt->second.size(); ++k)
2291  {
2292  if (perIt->second[k].id == mIt.second.first)
2293  {
2294  break;
2295  }
2296  }
2297 
2298  if (k == perIt->second.size())
2299  {
2300  perIt->second.push_back(ent2);
2301  }
2302  }
2303  }
2304  }
2305 
2306  // Determine periodic edges. Logic is the same as above,
2307  // and perhaps should be condensed to avoid replication.
2308  for (i = 0; i < 2; ++i)
2309  {
2310  int other = (i + 1) % 2;
2311 
2312  // angle is set up for i = 0 to i = 1
2313  sign = (i == 0) ? 1.0 : -1.0;
2314 
2315  if (tmpVec[0].size() == 3)
2316  {
2318  tmpVec[i], tmpVec[other], rotbnd, dir,
2319  sign * angle, tol);
2320  }
2321  else
2322  {
2324  tmpVec[i], tmpVec[other], rotbnd, dir,
2325  sign * angle, tol);
2326  }
2327 
2328  vector<int> per1 = edgeMap[ids[i]];
2329  vector<int> per2 = edgeMap[ids[other]];
2330 
2331  map<int, pair<int, bool>> tmpMap;
2332 
2333  for (j = 0; j < nFaceVerts; ++j)
2334  {
2335  int e = emap[nFaceVerts][o][j];
2336  tmpMap[per1[j]] =
2337  make_pair(per2[e], locEdges.count(per2[e]) > 0);
2338  }
2339 
2340  for (auto &mIt : tmpMap)
2341  {
2342  // Note we assume orientation of edges is forwards -
2343  // this may be reversed later.
2344  PeriodicEntity ent2(mIt.second.first,
2346  mIt.second.second);
2347  auto perIt = periodicEdges.find(mIt.first);
2348 
2349  if (perIt == periodicEdges.end())
2350  {
2351  periodicEdges[mIt.first].push_back(ent2);
2352  perIt = periodicEdges.find(mIt.first);
2353  }
2354  else
2355  {
2356  for (k = 0; k < perIt->second.size(); ++k)
2357  {
2358  if (perIt->second[k].id == mIt.second.first)
2359  {
2360  break;
2361  }
2362  }
2363 
2364  if (k == perIt->second.size())
2365  {
2366  perIt->second.push_back(ent2);
2367  }
2368  }
2369  }
2370  }
2371  }
2372  }
2373 
2374  // Make sure that the nubmer of face pairs and the
2375  // face Id to composite Id map match in size
2376  ASSERTL1(allCompPairs.size() == fIdToCompId.size(),
2377  "At this point the size of allCompPairs "
2378  "should have been the same as fIdToCompId");
2379 
2380  // also will need an edge id to composite id at
2381  // end of routine
2382  map<int, int> eIdToCompId;
2383 
2384  // Search for periodic vertices and edges which are not
2385  // in a periodic composite but lie in this process. First,
2386  // loop over all information we have from other
2387  // processors.
2388  for (cnt = i = 0; i < totFaces; ++i)
2389  {
2390  bool rotbnd = false;
2391  int dir = 0;
2392  NekDouble angle = 0.0;
2393  NekDouble tol = 1e-8;
2394 
2395  int faceId = faceIds[i];
2396 
2397  ASSERTL0(allCompPairs.count(faceId) > 0,
2398  "Unable to find matching periodic face. faceId = " +
2399  boost::lexical_cast<string>(faceId));
2400 
2401  int perFaceId = allCompPairs[faceId];
2402 
2403  // check to see if periodic boundary is rotated
2404  ASSERTL1((rotComp.size() == 0) || fIdToCompId.count(faceId) > 0,
2405  "Face " + boost::lexical_cast<string>(faceId) +
2406  " not found in fIdtoCompId map");
2407  if (rotComp.count(fIdToCompId[faceId]))
2408  {
2409  rotbnd = true;
2410  dir = rotComp[fIdToCompId[faceId]].m_dir;
2411  angle = rotComp[fIdToCompId[faceId]].m_angle;
2412  tol = rotComp[fIdToCompId[faceId]].m_tol;
2413  }
2414 
2415  for (j = 0; j < faceVerts[i]; ++j, ++cnt)
2416  {
2417  int vId = vertIds[cnt];
2418 
2419  auto perId = periodicVerts.find(vId);
2420 
2421  if (perId == periodicVerts.end())
2422  {
2423 
2424  // This vertex is not included in the
2425  // map. Figure out which vertex it is supposed
2426  // to be periodic with. perFaceId is the face
2427  // ID which is periodic with faceId. The logic
2428  // is much the same as the loop above.
2429  SpatialDomains::PointGeomVector tmpVec[2] = {
2430  coordMap[faceId], coordMap[perFaceId]};
2431 
2432  int nFaceVerts = tmpVec[0].size();
2434  nFaceVerts == 3
2436  tmpVec[0], tmpVec[1], rotbnd, dir, angle,
2437  tol)
2439  tmpVec[0], tmpVec[1], rotbnd, dir, angle,
2440  tol);
2441 
2442  // Use vmap to determine which vertex of the other face
2443  // should be periodic with this one.
2444  int perVertexId =
2445  vertMap[perFaceId][vmap[nFaceVerts][o][j]];
2446 
2447  PeriodicEntity ent(perVertexId,
2449  locVerts.count(perVertexId) > 0);
2450 
2451  periodicVerts[vId].push_back(ent);
2452  }
2453 
2454  int eId = edgeIds[cnt];
2455 
2456  perId = periodicEdges.find(eId);
2457 
2458  // this map is required at very end to determine rotation of
2459  // edges.
2460  if (rotbnd)
2461  {
2462  eIdToCompId[eId] = fIdToCompId[faceId];
2463  }
2464 
2465  if (perId == periodicEdges.end())
2466  {
2467  // This edge is not included in the map. Figure
2468  // out which edge it is supposed to be periodic
2469  // with. perFaceId is the face ID which is
2470  // periodic with faceId. The logic is much the
2471  // same as the loop above.
2472  SpatialDomains::PointGeomVector tmpVec[2] = {
2473  coordMap[faceId], coordMap[perFaceId]};
2474 
2475  int nFaceEdges = tmpVec[0].size();
2477  nFaceEdges == 3
2479  tmpVec[0], tmpVec[1], rotbnd, dir, angle,
2480  tol)
2482  tmpVec[0], tmpVec[1], rotbnd, dir, angle,
2483  tol);
2484 
2485  // Use emap to determine which edge of the other
2486  // face should be periodic with this one.
2487  int perEdgeId =
2488  edgeMap[perFaceId][emap[nFaceEdges][o][j]];
2489 
2490  PeriodicEntity ent(perEdgeId, StdRegions::eForwards,
2491  locEdges.count(perEdgeId) > 0);
2492 
2493  periodicEdges[eId].push_back(ent);
2494 
2495  // this map is required at very end to
2496  // determine rotation of edges.
2497  if (rotbnd)
2498  {
2499  eIdToCompId[perEdgeId] = fIdToCompId[perFaceId];
2500  }
2501  }
2502  }
2503  }
2504 
2505  // Finally, we must loop over the periodicVerts and periodicEdges
2506  // map to complete connectivity information.
2507  for (auto &perIt : periodicVerts)
2508  {
2509  // For each vertex that is periodic with this one...
2510  for (i = 0; i < perIt.second.size(); ++i)
2511  {
2512  // Find the vertex in the periodicVerts map...
2513  auto perIt2 = periodicVerts.find(perIt.second[i].id);
2514  ASSERTL0(perIt2 != periodicVerts.end(),
2515  "Couldn't find periodic vertex.");
2516 
2517  // Now search through this vertex's list and make sure that
2518  // we have a record of any vertices which aren't in the
2519  // original list.
2520  for (j = 0; j < perIt2->second.size(); ++j)
2521  {
2522  if (perIt2->second[j].id == perIt.first)
2523  {
2524  continue;
2525  }
2526 
2527  for (k = 0; k < perIt.second.size(); ++k)
2528  {
2529  if (perIt2->second[j].id == perIt.second[k].id)
2530  {
2531  break;
2532  }
2533  }
2534 
2535  if (k == perIt.second.size())
2536  {
2537  perIt.second.push_back(perIt2->second[j]);
2538  }
2539  }
2540  }
2541  }
2542 
2543  for (auto &perIt : periodicEdges)
2544  {
2545  for (i = 0; i < perIt.second.size(); ++i)
2546  {
2547  auto perIt2 = periodicEdges.find(perIt.second[i].id);
2548  ASSERTL0(perIt2 != periodicEdges.end(),
2549  "Couldn't find periodic edge.");
2550 
2551  for (j = 0; j < perIt2->second.size(); ++j)
2552  {
2553  if (perIt2->second[j].id == perIt.first)
2554  {
2555  continue;
2556  }
2557 
2558  for (k = 0; k < perIt.second.size(); ++k)
2559  {
2560  if (perIt2->second[j].id == perIt.second[k].id)
2561  {
2562  break;
2563  }
2564  }
2565 
2566  if (k == perIt.second.size())
2567  {
2568  perIt.second.push_back(perIt2->second[j]);
2569  }
2570  }
2571  }
2572  }
2573 
2574  // Loop over periodic edges to determine relative edge orientations.
2575  for (auto &perIt : periodicEdges)
2576  {
2577  bool rotbnd = false;
2578  int dir = 0;
2579  NekDouble angle = 0.0;
2580  NekDouble tol = 1e-8;
2581 
2582  // Find edge coordinates
2583  auto eIt = eIdMap.find(perIt.first);
2584  SpatialDomains::PointGeom v[2] = {*vCoMap[eIt->second.first],
2585  *vCoMap[eIt->second.second]};
2586 
2587  // check to see if perioid boundary is rotated
2588  if (rotComp.count(eIdToCompId[perIt.first]))
2589  {
2590  rotbnd = true;
2591  dir = rotComp[eIdToCompId[perIt.first]].m_dir;
2592  angle = rotComp[eIdToCompId[perIt.first]].m_angle;
2593  tol = rotComp[eIdToCompId[perIt.first]].m_tol;
2594  }
2595 
2596  // Loop over each edge, and construct a vector that takes us
2597  // from one vertex to another. Use this to figure out which
2598  // vertex maps to which.
2599  for (i = 0; i < perIt.second.size(); ++i)
2600  {
2601  eIt = eIdMap.find(perIt.second[i].id);
2602 
2603  SpatialDomains::PointGeom w[2] = {
2604  *vCoMap[eIt->second.first],
2605  *vCoMap[eIt->second.second]};
2606 
2607  int vMap[2] = {-1, -1};
2608  if (rotbnd)
2609  {
2610 
2612 
2613  r.Rotate(v[0], dir, angle);
2614 
2615  if (r.dist(w[0]) < tol)
2616  {
2617  vMap[0] = 0;
2618  }
2619  else
2620  {
2621  r.Rotate(v[1], dir, angle);
2622  if (r.dist(w[0]) < tol)
2623  {
2624  vMap[0] = 1;
2625  }
2626  else
2627  {
2629  "Unable to align rotationally "
2630  "periodic edge vertex");
2631  }
2632  }
2633  }
2634  else // translation test
2635  {
2636  NekDouble cx =
2637  0.5 * (w[0](0) - v[0](0) + w[1](0) - v[1](0));
2638  NekDouble cy =
2639  0.5 * (w[0](1) - v[0](1) + w[1](1) - v[1](1));
2640  NekDouble cz =
2641  0.5 * (w[0](2) - v[0](2) + w[1](2) - v[1](2));
2642 
2643  for (j = 0; j < 2; ++j)
2644  {
2645  NekDouble x = v[j](0);
2646  NekDouble y = v[j](1);
2647  NekDouble z = v[j](2);
2648  for (k = 0; k < 2; ++k)
2649  {
2650  NekDouble x1 = w[k](0) - cx;
2651  NekDouble y1 = w[k](1) - cy;
2652  NekDouble z1 = w[k](2) - cz;
2653 
2654  if (sqrt((x1 - x) * (x1 - x) +
2655  (y1 - y) * (y1 - y) +
2656  (z1 - z) * (z1 - z)) < 1e-8)
2657  {
2658  vMap[k] = j;
2659  break;
2660  }
2661  }
2662  }
2663 
2664  // Sanity check the map.
2665  ASSERTL0(vMap[0] >= 0 && vMap[1] >= 0,
2666  "Unable to align periodic edge vertex.");
2667  ASSERTL0((vMap[0] == 0 || vMap[0] == 1) &&
2668  (vMap[1] == 0 || vMap[1] == 1) &&
2669  (vMap[0] != vMap[1]),
2670  "Unable to align periodic edge vertex.");
2671  }
2672 
2673  // If 0 -> 0 then edges are aligned already; otherwise
2674  // reverse the orientation.
2675  if (vMap[0] != 0)
2676  {
2677  perIt.second[i].orient = StdRegions::eBackwards;
2678  }
2679  }
2680  }
2681 
2682  // Do one final loop over periodic vertices/edges to remove
2683  // non-local vertices/edges from map.
2684  for (auto &perIt : periodicVerts)
2685  {
2686  if (locVerts.count(perIt.first) > 0)
2687  {
2688  m_periodicVerts.insert(perIt);
2689  }
2690  }
2691 
2692  for (auto &perIt : periodicEdges)
2693  {
2694  if (locEdges.count(perIt.first) > 0)
2695  {
2696  m_periodicEdges.insert(perIt);
2697  }
2698  }
2699  }
2700  break;
2701  default:
2702  ASSERTL1(false, "Not setup for this expansion");
2703  break;
2704  }
2705 }
2706 
2707 /**
2708  *
2709  */
2711  const GlobalLinSysKey &mkey)
2712 {
2714  "Routine currently only tested for HybridDGHelmholtz");
2715 
2717  "Full matrix global systems are not supported for HDG "
2718  "expansions");
2719 
2720  ASSERTL1(mkey.GetGlobalSysSolnType() == m_traceMap->GetGlobalSysSolnType(),
2721  "The local to global map is not set up for the requested "
2722  "solution type");
2723 
2724  GlobalLinSysSharedPtr glo_matrix;
2725  auto matrixIter = m_globalBndMat->find(mkey);
2726 
2727  if (matrixIter == m_globalBndMat->end())
2728  {
2729  glo_matrix = GenGlobalBndLinSys(mkey, m_traceMap);
2730  (*m_globalBndMat)[mkey] = glo_matrix;
2731  }
2732  else
2733  {
2734  glo_matrix = matrixIter->second;
2735  }
2736 
2737  return glo_matrix;
2738 }
2739 
2740 /**
2741  * For each boundary region, checks that the types and number of
2742  * boundary expansions in that region match.
2743  * @param In Field to compare with.
2744  * @return True if boundary conditions match.
2745  */
2747 {
2748  int i;
2749  bool returnval = true;
2750 
2751  for (i = 0; i < m_bndConditions.size(); ++i)
2752  {
2753  // check to see if boundary condition type is the same
2754  // and there are the same number of boundary
2755  // conditions in the boundary definition.
2756  if ((m_bndConditions[i]->GetBoundaryConditionType() !=
2757  In.m_bndConditions[i]->GetBoundaryConditionType()) ||
2758  (m_bndCondExpansions[i]->GetExpSize() !=
2759  In.m_bndCondExpansions[i]->GetExpSize()))
2760  {
2761  returnval = false;
2762  break;
2763  }
2764  }
2765 
2766  // Compare with all other processes. Return true only if all
2767  // processes report having the same boundary conditions.
2768  int vSame = (returnval ? 1 : 0);
2769  m_comm->GetRowComm()->AllReduce(vSame, LibUtilities::ReduceMin);
2770 
2771  return (vSame == 1);
2772 }
2773 
2775 {
2776  if (m_negatedFluxNormal.size() == 0)
2777  {
2779  &elmtToTrace = m_traceMap->GetElmtToTrace();
2780 
2781  m_negatedFluxNormal.resize(2 * GetExpSize());
2782 
2783  for (int i = 0; i < GetExpSize(); ++i)
2784  {
2785 
2786  for (int v = 0; v < 2; ++v)
2787  {
2789  elmtToTrace[i][v]->as<LocalRegions::Expansion0D>();
2790 
2791  if (vertExp->GetLeftAdjacentElementExp()
2792  ->GetGeom()
2793  ->GetGlobalID() !=
2794  (*m_exp)[i]->GetGeom()->GetGlobalID())
2795  {
2796  m_negatedFluxNormal[2 * i + v] = true;
2797  }
2798  else
2799  {
2800  m_negatedFluxNormal[2 * i + v] = false;
2801  }
2802  }
2803  }
2804  }
2805 
2806  return m_negatedFluxNormal;
2807 }
2808 
2810 {
2812  {
2813  SetUpDG();
2814  }
2815 
2816  return m_trace;
2817 }
2818 
2820 {
2821  return m_traceMap;
2822 }
2823 
2825  void) const
2826 {
2827  return m_locTraceToTraceMap;
2828 }
2829 
2830 // Return the internal vector which identifieds if trace
2831 // is left adjacent definiing which trace the normal
2832 // points otwards from
2834 {
2835  return m_leftAdjacentTraces;
2836 }
2837 
2840 {
2841  return m_bndCondExpansions;
2842 }
2843 
2846 {
2847  return m_bndConditions;
2848 }
2849 
2851 {
2852  return m_bndCondExpansions[i];
2853 }
2854 
2857 {
2858  return m_bndConditions;
2859 }
2860 
2863 {
2864  for (int n = 0; n < m_periodicFwdCopy.size(); ++n)
2865  {
2866  Bwd[m_periodicBwdCopy[n]] = Fwd[m_periodicFwdCopy[n]];
2867  }
2868 }
2869 
2872 {
2873  v_GetFwdBwdTracePhys(m_phys, Fwd, Bwd);
2874 }
2875 
2876 /**
2877  * @brief Obtain a copy of the periodic edges and vertices for this
2878  * field.
2879  */
2881  PeriodicMap &periodicEdges,
2882  PeriodicMap &periodicFaces)
2883 {
2884  periodicVerts = m_periodicVerts;
2885  periodicEdges = m_periodicEdges;
2886  periodicFaces = m_periodicFaces;
2887 }
2888 /**
2889  * \brief This method extracts the "forward" and "backward" trace
2890  * data from the array \a field and puts the data into output
2891  * vectors \a Fwd and \a Bwd.
2892  *
2893  * We first define the convention which defines "forwards" and
2894  * "backwards". First an association is made between the vertex/edge/face of
2895  * each element and its corresponding vertex/edge/face in the trace space
2896  * using the mapping #m_traceMap. The element can either be
2897  * left-adjacent or right-adjacent to this trace face (see
2898  * Expansion2D::GetLeftAdjacentElementExp). Boundary faces are
2899  * always left-adjacent since left-adjacency is populated first.
2900  *
2901  * If the element is left-adjacent we extract the trace data
2902  * from \a field into the forward trace space \a Fwd; otherwise,
2903  * we place it in the backwards trace space \a Bwd. In this way,
2904  * we form a unique set of trace normals since these are always
2905  * extracted from left-adjacent elements.
2906  *
2907  * \param field is a NekDouble array which contains the fielddata
2908  * from which we wish to extract the backward and forward
2909  * orientated trace/face arrays.
2910  *
2911  * \return Updates a NekDouble array \a Fwd and \a Bwd
2912  */
2916  bool FillBnd, // these should be template params so that compiler can remove
2917  // them
2918  bool PutFwdInBwdOnBCs, bool DoExchange)
2919 {
2920  // Is this zeroing necessary?
2921  // Zero forward/backward vectors.
2922  Vmath::Zero(Fwd.size(), Fwd, 1);
2923  Vmath::Zero(Bwd.size(), Bwd, 1);
2924 
2925  // Basis definition on each element
2926  LibUtilities::BasisSharedPtr basis = (*m_exp)[0]->GetBasis(0);
2927  if ((basis->GetBasisType() != LibUtilities::eGauss_Lagrange))
2928  {
2929  // blocked routine
2930  Array<OneD, NekDouble> tracevals(
2931  m_locTraceToTraceMap->GetNLocTracePts());
2932 
2933  m_locTraceToTraceMap->LocTracesFromField(field, tracevals);
2934  m_locTraceToTraceMap->InterpLocTracesToTrace(0, tracevals, Fwd);
2935 
2936  Array<OneD, NekDouble> invals =
2937  tracevals + m_locTraceToTraceMap->GetNFwdLocTracePts();
2938  m_locTraceToTraceMap->InterpLocTracesToTrace(1, invals, Bwd);
2939  }
2940  else
2941  {
2942  // Loop over elements and collect forward expansion
2943  auto nexp = GetExpSize();
2944  Array<OneD, NekDouble> e_tmp;
2946 
2948  &elmtToTrace = m_traceMap->GetElmtToTrace();
2949 
2950  for (int n = 0, cnt = 0; n < nexp; ++n)
2951  {
2952  exp = (*m_exp)[n];
2953  auto phys_offset = GetPhys_Offset(n);
2954 
2955  for (int e = 0; e < exp->GetNtraces(); ++e, ++cnt)
2956  {
2957  auto offset =
2958  m_trace->GetPhys_Offset(elmtToTrace[n][e]->GetElmtId());
2959 
2960  e_tmp =
2961  (m_leftAdjacentTraces[cnt]) ? Fwd + offset : Bwd + offset;
2962 
2963  exp->GetTracePhysVals(e, elmtToTrace[n][e], field + phys_offset,
2964  e_tmp);
2965  }
2966  }
2967  }
2968 
2970 
2971  if (FillBnd)
2972  {
2973  v_FillBwdWithBoundCond(Fwd, Bwd, PutFwdInBwdOnBCs);
2974  }
2975 
2976  if (DoExchange)
2977  {
2978  // Do parallel exchange for forwards/backwards spaces.
2979  m_traceMap->GetAssemblyCommDG()->PerformExchange(Fwd, Bwd);
2980 
2981  // Do exchange of interface traces (local and parallel)
2982  // We may have to split this out into separate local and
2983  // parallel for IP method???
2984  m_interfaceMap->ExchangeTrace(Fwd, Bwd);
2985  }
2986 }
2987 
2990  bool PutFwdInBwdOnBCs)
2991 {
2992  // Fill boundary conditions into missing elements
2993  if (PutFwdInBwdOnBCs) // just set Bwd value to be Fwd value on BCs
2994  {
2995  // Fill boundary conditions into missing elements
2996  for (int n = 0, cnt = 0; n < m_bndCondExpansions.size(); ++n)
2997  {
2998  if (m_bndConditions[n]->GetBoundaryConditionType() ==
3000  {
3001  auto ne = m_bndCondExpansions[n]->GetExpSize();
3002  for (int e = 0; e < ne; ++e)
3003  {
3004  auto npts =
3005  m_bndCondExpansions[n]->GetExp(e)->GetTotPoints();
3006  auto id2 = m_trace->GetPhys_Offset(
3007  m_traceMap->GetBndCondIDToGlobalTraceID(cnt + e));
3008  Vmath::Vcopy(npts, &Fwd[id2], 1, &Bwd[id2], 1);
3009  }
3010 
3011  cnt += ne;
3012  }
3013  else if (m_bndConditions[n]->GetBoundaryConditionType() ==
3015  m_bndConditions[n]->GetBoundaryConditionType() ==
3017  {
3018  auto ne = m_bndCondExpansions[n]->GetExpSize();
3019  for (int e = 0; e < ne; ++e)
3020  {
3021  auto npts =
3022  m_bndCondExpansions[n]->GetExp(e)->GetTotPoints();
3023  auto id2 = m_trace->GetPhys_Offset(
3024  m_traceMap->GetBndCondIDToGlobalTraceID(cnt + e));
3025  Vmath::Vcopy(npts, &Fwd[id2], 1, &Bwd[id2], 1);
3026  }
3027  cnt += ne;
3028  }
3029  else if (m_bndConditions[n]->GetBoundaryConditionType() !=
3031  {
3032  ASSERTL0(false, "Method not set up for this "
3033  "boundary condition.");
3034  }
3035  }
3036  }
3037  else
3038  {
3039  for (int n = 0, cnt = 0; n < m_bndCondExpansions.size(); ++n)
3040  {
3041  if (m_bndConditions[n]->GetBoundaryConditionType() ==
3043  {
3044  auto ne = m_bndCondExpansions[n]->GetExpSize();
3045  for (int e = 0; e < ne; ++e)
3046  {
3047  auto npts =
3048  m_bndCondExpansions[n]->GetExp(e)->GetTotPoints();
3049  auto id1 = m_bndCondExpansions[n]->GetPhys_Offset(e);
3050  auto id2 = m_trace->GetPhys_Offset(
3051  m_traceMap->GetBndCondIDToGlobalTraceID(cnt + e));
3052 
3053  Vmath::Vcopy(npts,
3054  &(m_bndCondExpansions[n]->GetPhys())[id1], 1,
3055  &Bwd[id2], 1);
3056  }
3057  cnt += ne;
3058  }
3059  else if (m_bndConditions[n]->GetBoundaryConditionType() ==
3061  m_bndConditions[n]->GetBoundaryConditionType() ==
3063  {
3064  auto ne = m_bndCondExpansions[n]->GetExpSize();
3065  for (int e = 0; e < ne; ++e)
3066  {
3067  auto npts =
3068  m_bndCondExpansions[n]->GetExp(e)->GetTotPoints();
3069  auto id1 = m_bndCondExpansions[n]->GetPhys_Offset(e);
3070 
3071  ASSERTL0((m_bndCondExpansions[n]->GetPhys())[id1] == 0.0,
3072  "Method not set up for non-zero "
3073  "Neumann boundary condition");
3074  auto id2 = m_trace->GetPhys_Offset(
3075  m_traceMap->GetBndCondIDToGlobalTraceID(cnt + e));
3076 
3077  Vmath::Vcopy(npts, &Fwd[id2], 1, &Bwd[id2], 1);
3078  }
3079 
3080  cnt += ne;
3081  }
3082  else if (m_bndConditions[n]->GetBoundaryConditionType() ==
3084  {
3085  // Do nothing
3086  }
3087  else if (m_bndConditions[n]->GetBoundaryConditionType() !=
3089  {
3091  "Method not set up for this boundary "
3092  "condition.");
3093  }
3094  }
3095  }
3096 }
3097 
3099 {
3100  return m_bndCondBndWeight;
3101 }
3102 
3103 void DisContField::v_SetBndCondBwdWeight(const int index, const NekDouble value)
3104 {
3105  m_bndCondBndWeight[index] = value;
3106 }
3107 
3109  const Array<OneD, const NekDouble> &Fwd,
3111 {
3112  // Basis definition on each element
3113  LibUtilities::BasisSharedPtr basis = (*m_exp)[0]->GetBasis(0);
3114  if (basis->GetBasisType() != LibUtilities::eGauss_Lagrange)
3115  {
3116  Array<OneD, NekDouble> tracevals(
3117  m_locTraceToTraceMap->GetNLocTracePts(), 0.0);
3118 
3119  Array<OneD, NekDouble> invals =
3120  tracevals + m_locTraceToTraceMap->GetNFwdLocTracePts();
3121 
3122  m_locTraceToTraceMap->InterpTraceToLocTrace(1, Bwd, invals);
3123 
3124  m_locTraceToTraceMap->InterpTraceToLocTrace(0, Fwd, tracevals);
3125 
3126  m_locTraceToTraceMap->AddLocTracesToField(tracevals, field);
3127  }
3128  else
3129  {
3131  "v_AddTraceQuadPhysToField not coded for eGauss_Lagrange");
3132  }
3133 }
3134 
3136 {
3137  ASSERTL1(m_physState == true, "local physical space is not true ");
3138  v_ExtractTracePhys(m_phys, outarray);
3139 }
3140 /**
3141  * @brief This method extracts the trace (verts in 1D) from
3142  * the field @a inarray and puts the values in @a outarray.
3143  *
3144  * It assumes the field is C0 continuous so that it can
3145  * overwrite the edge data when visited by the two adjacent
3146  * elements.
3147  *
3148  * @param inarray An array containing the 1D data from which we wish
3149  * to extract the edge data.
3150  * @param outarray The resulting edge information.
3151  *
3152  * This will not work for non-boundary expansions
3153  */
3155  const Array<OneD, const NekDouble> &inarray,
3156  Array<OneD, NekDouble> &outarray)
3157 {
3158  LibUtilities::BasisSharedPtr basis = (*m_exp)[0]->GetBasis(0);
3159  if ((basis->GetBasisType() != LibUtilities::eGauss_Lagrange))
3160  {
3161  Vmath::Zero(outarray.size(), outarray, 1);
3162  Array<OneD, NekDouble> tracevals(
3163  m_locTraceToTraceMap->GetNFwdLocTracePts());
3164  m_locTraceToTraceMap->FwdLocTracesFromField(inarray, tracevals);
3165  m_locTraceToTraceMap->InterpLocTracesToTrace(0, tracevals, outarray);
3166  m_traceMap->GetAssemblyCommDG()->PerformExchange(outarray, outarray);
3167  }
3168  else
3169  {
3170  // Loop over elemente and collect forward expansion
3171  int nexp = GetExpSize();
3172  int n, p, offset, phys_offset;
3173  Array<OneD, NekDouble> t_tmp;
3175  &elmtToTrace = m_traceMap->GetElmtToTrace();
3176  ASSERTL1(outarray.size() >= m_trace->GetNpoints(),
3177  "input array is of insufficient length");
3178  for (n = 0; n < nexp; ++n)
3179  {
3180  phys_offset = GetPhys_Offset(n);
3181  for (p = 0; p < (*m_exp)[n]->GetNtraces(); ++p)
3182  {
3183  offset =
3184  m_trace->GetPhys_Offset(elmtToTrace[n][p]->GetElmtId());
3185  (*m_exp)[n]->GetTracePhysVals(p, elmtToTrace[n][p],
3186  inarray + phys_offset,
3187  t_tmp = outarray + offset);
3188  }
3189  }
3190  }
3191 }
3192 /**
3193  * @brief Add trace contributions into elemental coefficient spaces.
3194  *
3195  * Given some quantity \f$ \vec{Fn} \f$, which conatins this
3196  * routine calculates the integral
3197  *
3198  * \f[
3199  * \int_{\Omega^e} \vec{Fn}, \mathrm{d}S
3200  * \f]
3201  *
3202  * and adds this to the coefficient space provided by outarray.
3203  *
3204  * @see Expansion3D::AddFaceNormBoundaryInt
3205  *
3206  * @param Fn The trace quantities.
3207  * @param outarray Resulting 3D coefficient space.
3208  *
3209  */
3211  Array<OneD, NekDouble> &outarray)
3212 {
3213  if (m_expType == e1D)
3214  {
3215  int n, offset, t_offset;
3217  &elmtToTrace = m_traceMap->GetElmtToTrace();
3218  vector<bool> negatedFluxNormal = GetNegatedFluxNormal();
3219  for (n = 0; n < GetExpSize(); ++n)
3220  {
3221  // Number of coefficients on each element
3222  int e_ncoeffs = (*m_exp)[n]->GetNcoeffs();
3223  offset = GetCoeff_Offset(n);
3224  // Implementation for every points except Gauss points
3225  if ((*m_exp)[n]->GetBasis(0)->GetBasisType() !=
3227  {
3228  t_offset =
3229  GetTrace()->GetCoeff_Offset(elmtToTrace[n][0]->GetElmtId());
3230  if (negatedFluxNormal[2 * n])
3231  {
3232  outarray[offset] -= Fn[t_offset];
3233  }
3234  else
3235  {
3236  outarray[offset] += Fn[t_offset];
3237  }
3238  t_offset =
3239  GetTrace()->GetCoeff_Offset(elmtToTrace[n][1]->GetElmtId());
3240  if (negatedFluxNormal[2 * n + 1])
3241  {
3242  outarray[offset + (*m_exp)[n]->GetVertexMap(1)] -=
3243  Fn[t_offset];
3244  }
3245  else
3246  {
3247  outarray[offset + (*m_exp)[n]->GetVertexMap(1)] +=
3248  Fn[t_offset];
3249  }
3250  }
3251  else
3252  {
3253  int j;
3254  static DNekMatSharedPtr m_Ixm, m_Ixp;
3255  static int sav_ncoeffs = 0;
3256  if (!m_Ixm.get() || e_ncoeffs != sav_ncoeffs)
3257  {
3259  const LibUtilities::PointsKey BS_p(
3261  const LibUtilities::BasisKey BS_k(
3262  LibUtilities::eGauss_Lagrange, e_ncoeffs, BS_p);
3263  BASE = LibUtilities::BasisManager()[BS_k];
3264  Array<OneD, NekDouble> coords(1, 0.0);
3265  coords[0] = -1.0;
3266  m_Ixm = BASE->GetI(coords);
3267  coords[0] = 1.0;
3268  m_Ixp = BASE->GetI(coords);
3269  sav_ncoeffs = e_ncoeffs;
3270  }
3271  t_offset =
3272  GetTrace()->GetCoeff_Offset(elmtToTrace[n][0]->GetElmtId());
3273  if (negatedFluxNormal[2 * n])
3274  {
3275  for (j = 0; j < e_ncoeffs; j++)
3276  {
3277  outarray[offset + j] -=
3278  (m_Ixm->GetPtr())[j] * Fn[t_offset];
3279  }
3280  }
3281  else
3282  {
3283  for (j = 0; j < e_ncoeffs; j++)
3284  {
3285  outarray[offset + j] +=
3286  (m_Ixm->GetPtr())[j] * Fn[t_offset];
3287  }
3288  }
3289  t_offset =
3290  GetTrace()->GetCoeff_Offset(elmtToTrace[n][1]->GetElmtId());
3291  if (negatedFluxNormal[2 * n + 1])
3292  {
3293  for (j = 0; j < e_ncoeffs; j++)
3294  {
3295  outarray[offset + j] -=
3296  (m_Ixp->GetPtr())[j] * Fn[t_offset];
3297  }
3298  }
3299  else
3300  {
3301  for (j = 0; j < e_ncoeffs; j++)
3302  {
3303  outarray[offset + j] +=
3304  (m_Ixp->GetPtr())[j] * Fn[t_offset];
3305  }
3306  }
3307  }
3308  }
3309  }
3310  else // other dimensions
3311  {
3312  // Basis definition on each element
3313  LibUtilities::BasisSharedPtr basis = (*m_exp)[0]->GetBasis(0);
3314  if ((m_expType != e1D) &&
3315  (basis->GetBasisType() != LibUtilities::eGauss_Lagrange))
3316  {
3317  Array<OneD, NekDouble> Fcoeffs(m_trace->GetNcoeffs());
3318  m_trace->IProductWRTBase(Fn, Fcoeffs);
3319  m_locTraceToTraceMap->AddTraceCoeffsToFieldCoeffs(Fcoeffs,
3320  outarray);
3321  }
3322  else
3323  {
3324  int e, n, offset, t_offset;
3325  Array<OneD, NekDouble> e_outarray;
3327  &elmtToTrace = m_traceMap->GetElmtToTrace();
3328  if (m_expType == e2D)
3329  {
3330  for (n = 0; n < GetExpSize(); ++n)
3331  {
3332  offset = GetCoeff_Offset(n);
3333  for (e = 0; e < (*m_exp)[n]->GetNtraces(); ++e)
3334  {
3335  t_offset = GetTrace()->GetPhys_Offset(
3336  elmtToTrace[n][e]->GetElmtId());
3337  (*m_exp)[n]->AddEdgeNormBoundaryInt(
3338  e, elmtToTrace[n][e], Fn + t_offset,
3339  e_outarray = outarray + offset);
3340  }
3341  }
3342  }
3343  else if (m_expType == e3D)
3344  {
3345  for (n = 0; n < GetExpSize(); ++n)
3346  {
3347  offset = GetCoeff_Offset(n);
3348  for (e = 0; e < (*m_exp)[n]->GetNtraces(); ++e)
3349  {
3350  t_offset = GetTrace()->GetPhys_Offset(
3351  elmtToTrace[n][e]->GetElmtId());
3352  (*m_exp)[n]->AddFaceNormBoundaryInt(
3353  e, elmtToTrace[n][e], Fn + t_offset,
3354  e_outarray = outarray + offset);
3355  }
3356  }
3357  }
3358  }
3359  }
3360 }
3361 /**
3362  * @brief Add trace contributions into elemental coefficient spaces.
3363  *
3364  * Given some quantity \f$ \vec{q} \f$, calculate the elemental integral
3365  *
3366  * \f[
3367  * \int_{\Omega^e} \vec{q}, \mathrm{d}S
3368  * \f]
3369  *
3370  * and adds this to the coefficient space provided by
3371  * outarray. The value of q is determined from the routine
3372  * IsLeftAdjacentTrace() which if true we use Fwd else we use
3373  * Bwd
3374  *
3375  * @param Fwd The trace quantities associated with left (fwd)
3376  * adjancent elmt.
3377  * @param Bwd The trace quantities associated with right (bwd)
3378  * adjacent elet.
3379  * @param outarray Resulting coefficient space.
3380  */
3382  const Array<OneD, const NekDouble> &Fwd,
3384 {
3385  ASSERTL0(m_expType != e1D, "This method is not setup or "
3386  "tested for 1D expansion");
3387  Array<OneD, NekDouble> Coeffs(m_trace->GetNcoeffs());
3388  m_trace->IProductWRTBase(Fwd, Coeffs);
3389  m_locTraceToTraceMap->AddTraceCoeffsToFieldCoeffs(0, Coeffs, outarray);
3390  m_trace->IProductWRTBase(Bwd, Coeffs);
3391  m_locTraceToTraceMap->AddTraceCoeffsToFieldCoeffs(1, Coeffs, outarray);
3392 }
3393 
3395  const Array<OneD, const NekDouble> &inarray,
3396  Array<OneD, NekDouble> &outarray, const StdRegions::ConstFactorMap &factors,
3397  const StdRegions::VarCoeffMap &varcoeff,
3398  const MultiRegions::VarFactorsMap &varfactors,
3399  const Array<OneD, const NekDouble> &dirForcing, const bool PhysSpaceForcing)
3400 {
3401  boost::ignore_unused(varfactors, dirForcing);
3402  int i, n, cnt, nbndry;
3403  int nexp = GetExpSize();
3405  DNekVec F(m_ncoeffs, f, eWrapper);
3406  Array<OneD, NekDouble> e_f, e_l;
3407  //----------------------------------
3408  // Setup RHS Inner product if required
3409  //----------------------------------
3410  if (PhysSpaceForcing)
3411  {
3412  IProductWRTBase(inarray, f);
3413  Vmath::Neg(m_ncoeffs, f, 1);
3414  }
3415  else
3416  {
3417  Vmath::Smul(m_ncoeffs, -1.0, inarray, 1, f, 1);
3418  }
3419  //----------------------------------
3420  // Solve continuous Boundary System
3421  //----------------------------------
3422  int GloBndDofs = m_traceMap->GetNumGlobalBndCoeffs();
3423  int NumDirBCs = m_traceMap->GetNumLocalDirBndCoeffs();
3424  int e_ncoeffs;
3426  NullAssemblyMapSharedPtr, factors, varcoeff);
3427  const DNekScalBlkMatSharedPtr &HDGLamToU = GetBlockMatrix(HDGLamToUKey);
3428  // Retrieve number of local trace space coefficients N_{\lambda},
3429  // and set up local elemental trace solution \lambda^e.
3430  int LocBndCoeffs = m_traceMap->GetNumLocalBndCoeffs();
3431  Array<OneD, NekDouble> bndrhs(LocBndCoeffs, 0.0);
3432  Array<OneD, NekDouble> loclambda(LocBndCoeffs, 0.0);
3433  DNekVec LocLambda(LocBndCoeffs, loclambda, eWrapper);
3434  //----------------------------------
3435  // Evaluate Trace Forcing
3436  // Kirby et al, 2010, P23, Step 5.
3437  //----------------------------------
3438  // Determing <u_lam,f> terms using HDGLamToU matrix
3439  for (cnt = n = 0; n < nexp; ++n)
3440  {
3441  nbndry = (*m_exp)[n]->NumDGBndryCoeffs();
3442  e_ncoeffs = (*m_exp)[n]->GetNcoeffs();
3443  e_f = f + m_coeff_offset[n];
3444  e_l = bndrhs + cnt;
3445  // use outarray as tmp space
3446  DNekVec Floc(nbndry, e_l, eWrapper);
3447  DNekVec ElmtFce(e_ncoeffs, e_f, eWrapper);
3448  Floc = Transpose(*(HDGLamToU->GetBlock(n, n))) * ElmtFce;
3449  cnt += nbndry;
3450  }
3451  Array<OneD, const int> bndCondMap =
3452  m_traceMap->GetBndCondCoeffsToLocalTraceMap();
3453  Array<OneD, const NekDouble> Sign = m_traceMap->GetLocalToGlobalBndSign();
3454  // Copy Dirichlet boundary conditions and weak forcing
3455  // into trace space
3456  int locid;
3457  cnt = 0;
3458  for (i = 0; i < m_bndCondExpansions.size(); ++i)
3459  {
3460  const Array<OneD, const NekDouble> bndcoeffs =
3461  m_bndCondExpansions[i]->GetCoeffs();
3462 
3463  if (m_bndConditions[i]->GetBoundaryConditionType() ==
3465  {
3466  for (int j = 0; j < (m_bndCondExpansions[i])->GetNcoeffs(); ++j)
3467  {
3468  locid = bndCondMap[cnt + j];
3469  loclambda[locid] = Sign[locid] * bndcoeffs[j];
3470  }
3471  }
3472  else if (m_bndConditions[i]->GetBoundaryConditionType() ==
3474  m_bndConditions[i]->GetBoundaryConditionType() ==
3476  {
3477  // Add weak boundary condition to trace forcing
3478  for (int j = 0; j < (m_bndCondExpansions[i])->GetNcoeffs(); ++j)
3479  {
3480  locid = bndCondMap[cnt + j];
3481  bndrhs[locid] += Sign[locid] * bndcoeffs[j];
3482  }
3483  }
3484  else if (m_bndConditions[i]->GetBoundaryConditionType() ==
3486  {
3487  // skip increment of cnt if ePeriodic
3488  // because bndCondMap does not include ePeriodic
3489  continue;
3490  }
3491  cnt += (m_bndCondExpansions[i])->GetNcoeffs();
3492  }
3493 
3494  //----------------------------------
3495  // Solve trace problem: \Lambda = K^{-1} F
3496  // K is the HybridDGHelmBndLam matrix.
3497  //----------------------------------
3498  if (GloBndDofs - NumDirBCs > 0)
3499  {
3501  factors, varcoeff);
3503  LinSys->Solve(bndrhs, loclambda, m_traceMap);
3504 
3505  // For consistency with previous version put global
3506  // solution into m_trace->m_coeffs
3507  m_traceMap->LocalToGlobal(loclambda, m_trace->UpdateCoeffs());
3508  }
3509 
3510  //----------------------------------
3511  // Internal element solves
3512  //----------------------------------
3514  NullAssemblyMapSharedPtr, factors, varcoeff);
3515 
3516  const DNekScalBlkMatSharedPtr &InvHDGHelm = GetBlockMatrix(invHDGhelmkey);
3517  DNekVec out(m_ncoeffs, outarray, eWrapper);
3518  Vmath::Zero(m_ncoeffs, outarray, 1);
3519 
3520  // out = u_f + u_lam = (*InvHDGHelm)*f + (LamtoU)*Lam
3521  out = (*InvHDGHelm) * F + (*HDGLamToU) * LocLambda;
3522 
3523  // Return empty GlobalLinSysKey
3524  return NullGlobalLinSysKey;
3525 }
3526 
3527 /* \brief This function evaluates the boundary conditions at a certain
3528  * time-level.
3529  *
3530  * Based on the boundary condition \f$g(\boldsymbol{x},t)\f$ evaluated
3531  * at a given time-level \a t, this function transforms the boundary
3532  * conditions onto the coefficients of the (one-dimensional) boundary
3533  * expansion. Depending on the type of boundary conditions, these
3534  * expansion coefficients are calculated in different ways:
3535  * - <b>Dirichlet boundary conditions</b><BR>
3536  * In order to ensure global \f$C^0\f$ continuity of the spectral/hp
3537  * approximation, the Dirichlet boundary conditions are projected onto
3538  * the boundary expansion by means of a modified \f$C^0\f$ continuous
3539  * Galerkin projection. This projection can be viewed as a collocation
3540  * projection at the vertices, followed by an \f$L^2\f$ projection on
3541  * the interior modes of the edges. The resulting coefficients
3542  * \f$\boldsymbol{\hat{u}}^{\mathcal{D}}\f$ will be stored for the
3543  * boundary expansion.
3544  * - <b>Neumann boundary conditions</b>
3545  * In the discrete Galerkin formulation of the problem to be solved,
3546  * the Neumann boundary conditions appear as the set of surface
3547  * integrals: \f[\boldsymbol{\hat{g}}=\int_{\Gamma}
3548  * \phi^e_n(\boldsymbol{x})g(\boldsymbol{x})d(\boldsymbol{x})\quad
3549  * \forall n \f]
3550  * As a result, it are the coefficients \f$\boldsymbol{\hat{g}}\f$
3551  * that will be stored in the boundary expansion
3552  *
3553  * @param time The time at which the boundary conditions
3554  * should be evaluated.
3555  * @param bndCondExpansions List of boundary conditions.
3556  * @param bndConditions Information about the boundary conditions.
3557  *
3558  * This will only be undertaken for time dependent
3559  * boundary conditions unless time == 0.0 which is the
3560  * case when the method is called from the constructor.
3561  */
3563  const std::string varName,
3564  const NekDouble x2_in,
3565  const NekDouble x3_in)
3566 {
3567  int i;
3568  int npoints;
3569 
3570  MultiRegions::ExpListSharedPtr locExpList;
3571 
3572  for (i = 0; i < m_bndCondExpansions.size(); ++i)
3573  {
3574  if (time == 0.0 || m_bndConditions[i]->IsTimeDependent())
3575  {
3576  m_bndCondBndWeight[i] = 1.0;
3577  locExpList = m_bndCondExpansions[i];
3578 
3579  npoints = locExpList->GetNpoints();
3580  Array<OneD, NekDouble> x0(npoints, 0.0);
3581  Array<OneD, NekDouble> x1(npoints, 0.0);
3582  Array<OneD, NekDouble> x2(npoints, 0.0);
3583 
3584  locExpList->GetCoords(x0, x1, x2);
3585 
3586  if (x2_in != NekConstants::kNekUnsetDouble &&
3588  {
3589  Vmath::Fill(npoints, x2_in, x1, 1);
3590  Vmath::Fill(npoints, x3_in, x2, 1);
3591  }
3592  else if (x2_in != NekConstants::kNekUnsetDouble)
3593  {
3594  Vmath::Fill(npoints, x2_in, x2, 1);
3595  }
3596 
3597  // treat 1D expansions separately since we only
3598  // require an evaluation at a point rather than
3599  // any projections or inner products that are not
3600  // available in a PointExp
3601  if (m_expType == e1D)
3602  {
3603  if (m_bndConditions[i]->GetBoundaryConditionType() ==
3605  {
3606 
3607  m_bndCondExpansions[i]->SetCoeff(
3608  0, (std::static_pointer_cast<
3610  m_bndConditions[i])
3611  ->m_dirichletCondition)
3612  .Evaluate(x0[0], x1[0], x2[0], time));
3613  m_bndCondExpansions[i]->SetPhys(
3614  0, m_bndCondExpansions[i]->GetCoeff(0));
3615  }
3616  else if (m_bndConditions[i]->GetBoundaryConditionType() ==
3618  {
3619  m_bndCondExpansions[i]->SetCoeff(
3620  0, (std::static_pointer_cast<
3622  m_bndConditions[i])
3623  ->m_neumannCondition)
3624  .Evaluate(x0[0], x1[0], x2[0], time));
3625  }
3626  else if (m_bndConditions[i]->GetBoundaryConditionType() ==
3628  {
3629  m_bndCondExpansions[i]->SetCoeff(
3630  0, (std::static_pointer_cast<
3632  m_bndConditions[i])
3633  ->m_robinFunction)
3634  .Evaluate(x0[0], x1[0], x2[0], time));
3635  }
3636  else if (m_bndConditions[i]->GetBoundaryConditionType() ==
3638  {
3639  continue;
3640  }
3641  else if (m_bndConditions[i]->GetBoundaryConditionType() ==
3643  {
3644  }
3645  else
3646  {
3648  "This type of BC not implemented yet");
3649  }
3650  }
3651  else // 2D and 3D versions
3652  {
3653  if (m_bndConditions[i]->GetBoundaryConditionType() ==
3655  {
3657  std::static_pointer_cast<
3659  m_bndConditions[i]);
3660 
3661  Array<OneD, NekDouble> valuesFile(npoints, 1.0),
3662  valuesExp(npoints, 1.0);
3663 
3664  string bcfilename = bcPtr->m_filename;
3665  string exprbcs = bcPtr->m_expr;
3666 
3667  if (bcfilename != "")
3668  {
3669  locExpList->ExtractCoeffsFromFile(
3670  bcfilename, bcPtr->GetComm(), varName,
3671  locExpList->UpdateCoeffs());
3672  locExpList->BwdTrans(locExpList->GetCoeffs(),
3673  locExpList->UpdatePhys());
3674  valuesFile = locExpList->GetPhys();
3675  }
3676 
3677  if (exprbcs != "")
3678  {
3679  LibUtilities::Equation condition =
3680  std::static_pointer_cast<
3682  m_bndConditions[i])
3683  ->m_dirichletCondition;
3684 
3685  condition.Evaluate(x0, x1, x2, time, valuesExp);
3686  }
3687 
3688  Vmath::Vmul(npoints, valuesExp, 1, valuesFile, 1,
3689  locExpList->UpdatePhys(), 1);
3690  locExpList->FwdTransBndConstrained(
3691  locExpList->GetPhys(), locExpList->UpdateCoeffs());
3692  }
3693  else if (m_bndConditions[i]->GetBoundaryConditionType() ==
3695  {
3697  std::static_pointer_cast<
3699  m_bndConditions[i]);
3700  string bcfilename = bcPtr->m_filename;
3701 
3702  if (bcfilename != "")
3703  {
3704  locExpList->ExtractCoeffsFromFile(
3705  bcfilename, bcPtr->GetComm(), varName,
3706  locExpList->UpdateCoeffs());
3707  locExpList->BwdTrans(locExpList->GetCoeffs(),
3708  locExpList->UpdatePhys());
3709  }
3710  else
3711  {
3712  LibUtilities::Equation condition =
3713  std::static_pointer_cast<
3715  m_bndConditions[i])
3716  ->m_neumannCondition;
3717  condition.Evaluate(x0, x1, x2, time,
3718  locExpList->UpdatePhys());
3719  }
3720 
3721  locExpList->IProductWRTBase(locExpList->GetPhys(),
3722  locExpList->UpdateCoeffs());
3723  }
3724  else if (m_bndConditions[i]->GetBoundaryConditionType() ==
3726  {
3728  std::static_pointer_cast<
3730  m_bndConditions[i]);
3731 
3732  string bcfilename = bcPtr->m_filename;
3733 
3734  if (bcfilename != "")
3735  {
3736  locExpList->ExtractCoeffsFromFile(
3737  bcfilename, bcPtr->GetComm(), varName,
3738  locExpList->UpdateCoeffs());
3739  locExpList->BwdTrans(locExpList->GetCoeffs(),
3740  locExpList->UpdatePhys());
3741  }
3742  else
3743  {
3744  LibUtilities::Equation condition =
3745  std::static_pointer_cast<
3747  m_bndConditions[i])
3748  ->m_robinFunction;
3749  condition.Evaluate(x0, x1, x2, time,
3750  locExpList->UpdatePhys());
3751  }
3752 
3753  locExpList->IProductWRTBase(locExpList->GetPhys(),
3754  locExpList->UpdateCoeffs());
3755  }
3756  else if (m_bndConditions[i]->GetBoundaryConditionType() ==
3758  {
3759  continue;
3760  }
3761  else
3762  {
3764  "This type of BC not implemented yet");
3765  }
3766  }
3767  }
3768  }
3769 }
3770 
3771 /**
3772  * @brief Fill the weight with m_bndCondBndWeight.
3773  */
3775  Array<OneD, NekDouble> &weightjmp)
3776 {
3777  int cnt;
3778  int npts;
3779  int e = 0;
3780 
3781  // Fill boundary conditions into missing elements
3782  int id2 = 0;
3783 
3784  for (int n = cnt = 0; n < m_bndCondExpansions.size(); ++n)
3785  {
3786 
3787  if (m_bndConditions[n]->GetBoundaryConditionType() ==
3789  {
3790  for (e = 0; e < m_bndCondExpansions[n]->GetExpSize(); ++e)
3791  {
3792  npts = m_bndCondExpansions[n]->GetExp(e)->GetTotPoints();
3793  id2 = m_trace->GetPhys_Offset(
3794  m_traceMap->GetBndCondIDToGlobalTraceID(cnt + e));
3795  Vmath::Fill(npts, m_bndCondBndWeight[n], &weightave[id2], 1);
3796  Vmath::Fill(npts, 0.0, &weightjmp[id2], 1);
3797  }
3798 
3799  cnt += e;
3800  }
3801  else if (m_bndConditions[n]->GetBoundaryConditionType() ==
3803  m_bndConditions[n]->GetBoundaryConditionType() ==
3805  {
3806  for (e = 0; e < m_bndCondExpansions[n]->GetExpSize(); ++e)
3807  {
3808  npts = m_bndCondExpansions[n]->GetExp(e)->GetTotPoints();
3809  id2 = m_trace->GetPhys_Offset(
3810  m_traceMap->GetBndCondIDToGlobalTraceID(cnt + e));
3811  Vmath::Fill(npts, m_bndCondBndWeight[n], &weightave[id2], 1);
3812  Vmath::Fill(npts, 0.0, &weightjmp[id2], 1);
3813  }
3814 
3815  cnt += e;
3816  }
3817  else if (m_bndConditions[n]->GetBoundaryConditionType() !=
3819  {
3821  "Method not set up for this boundary condition.");
3822  }
3823  }
3824 }
3825 
3826 // Set up a list of element ids and trace ids that link to the
3827 // boundary conditions
3829  Array<OneD, int> &TraceID)
3830 {
3831 
3832  if (m_BCtoElmMap.size() == 0)
3833  {
3834  switch (m_expType)
3835  {
3836  case e1D:
3837  {
3838  map<int, int> VertGID;
3839  int i, n, id;
3840  int bid, cnt, Vid;
3841  int nbcs = 0;
3842  // Determine number of boundary condition expansions.
3843  for (i = 0; i < m_bndConditions.size(); ++i)
3844  {
3845  nbcs += m_bndCondExpansions[i]->GetExpSize();
3846  }
3847 
3848  // make sure arrays are of sufficient length
3849  m_BCtoElmMap = Array<OneD, int>(nbcs, -1);
3851 
3852  // setup map of all global ids along boundary
3853  cnt = 0;
3854  for (n = 0; n < m_bndCondExpansions.size(); ++n)
3855  {
3856  for (i = 0; i < m_bndCondExpansions[n]->GetExpSize(); ++i)
3857  {
3858  Vid = m_bndCondExpansions[n]
3859  ->GetExp(i)
3860  ->GetGeom()
3861  ->GetVertex(0)
3862  ->GetVid();
3863  VertGID[Vid] = cnt++;
3864  }
3865  }
3866 
3867  // Loop over elements and find verts that match;
3869  for (cnt = n = 0; n < GetExpSize(); ++n)
3870  {
3871  exp = (*m_exp)[n];
3872  for (i = 0; i < exp->GetNverts(); ++i)
3873  {
3874  id = exp->GetGeom()->GetVid(i);
3875 
3876  if (VertGID.count(id) > 0)
3877  {
3878  bid = VertGID.find(id)->second;
3879  ASSERTL1(m_BCtoElmMap[bid] == -1,
3880  "Edge already set");
3881  m_BCtoElmMap[bid] = n;
3882  m_BCtoTraceMap[bid] = i;
3883  cnt++;
3884  }
3885  }
3886  }
3887  ASSERTL1(cnt == nbcs,
3888  "Failed to visit all boundary condtiions");
3889  }
3890  break;
3891  case e2D:
3892  {
3893  map<int, int> globalIdMap;
3894  int i, n;
3895  int cnt;
3896  int nbcs = 0;
3897 
3898  // Populate global ID map (takes global geometry
3899  // ID to local expansion list ID).
3900  for (i = 0; i < GetExpSize(); ++i)
3901  {
3902  globalIdMap[(*m_exp)[i]->GetGeom()->GetGlobalID()] = i;
3903  }
3904 
3905  // Determine number of boundary condition expansions.
3906  for (i = 0; i < m_bndConditions.size(); ++i)
3907  {
3908  nbcs += m_bndCondExpansions[i]->GetExpSize();
3909  }
3910 
3911  // Initialize arrays
3914 
3916  cnt = 0;
3917  for (n = 0; n < m_bndCondExpansions.size(); ++n)
3918  {
3919  for (i = 0; i < m_bndCondExpansions[n]->GetExpSize();
3920  ++i, ++cnt)
3921  {
3922  exp1d = m_bndCondExpansions[n]
3923  ->GetExp(i)
3924  ->as<LocalRegions::Expansion1D>();
3925 
3926  // Use edge to element map from MeshGraph.
3928  m_graph->GetElementsFromEdge(exp1d->GetGeom1D());
3929 
3930  m_BCtoElmMap[cnt] =
3931  globalIdMap[(*tmp)[0].first->GetGlobalID()];
3932  m_BCtoTraceMap[cnt] = (*tmp)[0].second;
3933  }
3934  }
3935  }
3936  break;
3937  case e3D:
3938  {
3939  map<int, int> globalIdMap;
3940  int i, n;
3941  int cnt;
3942  int nbcs = 0;
3943 
3944  // Populate global ID map (takes global geometry ID to local
3945  // expansion list ID).
3947  for (i = 0; i < GetExpSize(); ++i)
3948  {
3949  globalIdMap[(*m_exp)[i]->GetGeom()->GetGlobalID()] = i;
3950  }
3951 
3952  // Determine number of boundary condition expansions.
3953  for (i = 0; i < m_bndConditions.size(); ++i)
3954  {
3955  nbcs += m_bndCondExpansions[i]->GetExpSize();
3956  }
3957 
3958  // Initialize arrays
3961 
3963  for (cnt = n = 0; n < m_bndCondExpansions.size(); ++n)
3964  {
3965  for (i = 0; i < m_bndCondExpansions[n]->GetExpSize();
3966  ++i, ++cnt)
3967  {
3968  exp2d = m_bndCondExpansions[n]
3969  ->GetExp(i)
3970  ->as<LocalRegions::Expansion2D>();
3971 
3973  m_graph->GetElementsFromFace(exp2d->GetGeom2D());
3974  m_BCtoElmMap[cnt] =
3975  globalIdMap[tmp->at(0).first->GetGlobalID()];
3976  m_BCtoTraceMap[cnt] = tmp->at(0).second;
3977  }
3978  }
3979  }
3980  break;
3981  default:
3982  ASSERTL1(false, "Not setup for this expansion");
3983  break;
3984  }
3985  }
3986 
3987  ElmtID = m_BCtoElmMap;
3988  TraceID = m_BCtoTraceMap;
3989 }
3990 
3992  std::shared_ptr<ExpList> &result,
3993  const bool DeclareCoeffPhysArrays)
3994 {
3995  int n, cnt;
3996  std::vector<unsigned int> eIDs;
3997 
3998  Array<OneD, int> ElmtID, TraceID;
3999  GetBoundaryToElmtMap(ElmtID, TraceID);
4000 
4001  // Skip other boundary regions
4002  for (cnt = n = 0; n < i; ++n)
4003  {
4004  cnt += m_bndCondExpansions[n]->GetExpSize();
4005  }
4006 
4007  // Populate eIDs with information from BoundaryToElmtMap
4008  for (n = 0; n < m_bndCondExpansions[i]->GetExpSize(); ++n)
4009  {
4010  eIDs.push_back(ElmtID[cnt + n]);
4011  }
4012 
4013  // Create expansion list
4015  *this, eIDs, DeclareCoeffPhysArrays, Collections::eNoCollection);
4016  // Copy phys and coeffs to new explist
4017  if (DeclareCoeffPhysArrays)
4018  {
4019  int nq;
4020  int offsetOld, offsetNew;
4021  Array<OneD, NekDouble> tmp1, tmp2;
4022  for (n = 0; n < result->GetExpSize(); ++n)
4023  {
4024  nq = GetExp(ElmtID[cnt + n])->GetTotPoints();
4025  offsetOld = GetPhys_Offset(ElmtID[cnt + n]);
4026  offsetNew = result->GetPhys_Offset(n);
4027  Vmath::Vcopy(nq, tmp1 = GetPhys() + offsetOld, 1,
4028  tmp2 = result->UpdatePhys() + offsetNew, 1);
4029  nq = GetExp(ElmtID[cnt + n])->GetNcoeffs();
4030  offsetOld = GetCoeff_Offset(ElmtID[cnt + n]);
4031  offsetNew = result->GetCoeff_Offset(n);
4032  Vmath::Vcopy(nq, tmp1 = GetCoeffs() + offsetOld, 1,
4033  tmp2 = result->UpdateCoeffs() + offsetNew, 1);
4034  }
4035  }
4036 }
4037 
4038 /**
4039  * @brief Reset this field, so that geometry information can be updated.
4040  */
4042 {
4043  ExpList::v_Reset();
4044 
4045  // Reset boundary condition expansions.
4046  for (int n = 0; n < m_bndCondExpansions.size(); ++n)
4047  {
4048  m_bndCondExpansions[n]->Reset();
4049  }
4050 }
4051 
4052 /**
4053  * Search through the edge expansions and identify which ones
4054  * have Robin/Mixed type boundary conditions. If find a Robin
4055  * boundary then store the edge id of the boundary condition
4056  * and the array of points of the physical space boundary
4057  * condition which are hold the boundary condition primitive
4058  * variable coefficient at the quatrature points
4059  *
4060  * \return std map containing the robin boundary condition
4061  * info using a key of the element id
4062  *
4063  * There is a next member to allow for more than one Robin
4064  * boundary condition per element
4065  */
4066 map<int, RobinBCInfoSharedPtr> DisContField::v_GetRobinBCInfo(void)
4067 {
4068  int i, cnt;
4069  map<int, RobinBCInfoSharedPtr> returnval;
4070  Array<OneD, int> ElmtID, TraceID;
4071  GetBoundaryToElmtMap(ElmtID, TraceID);
4072 
4073  for (cnt = i = 0; i < m_bndCondExpansions.size(); ++i)
4074  {
4075  MultiRegions::ExpListSharedPtr locExpList;
4076 
4077  if (m_bndConditions[i]->GetBoundaryConditionType() ==
4079  {
4080  int e, elmtid;
4081  Array<OneD, NekDouble> Array_tmp;
4082 
4083  locExpList = m_bndCondExpansions[i];
4084 
4085  int npoints = locExpList->GetNpoints();
4086  Array<OneD, NekDouble> x0(npoints, 0.0);
4087  Array<OneD, NekDouble> x1(npoints, 0.0);
4088  Array<OneD, NekDouble> x2(npoints, 0.0);
4089  Array<OneD, NekDouble> coeffphys(npoints);
4090 
4091  locExpList->GetCoords(x0, x1, x2);
4092 
4093  LibUtilities::Equation coeffeqn =
4094  std::static_pointer_cast<
4096  ->m_robinPrimitiveCoeff;
4097 
4098  // evalaute coefficient
4099  coeffeqn.Evaluate(x0, x1, x2, 0.0, coeffphys);
4100 
4101  for (e = 0; e < locExpList->GetExpSize(); ++e)
4102  {
4103  RobinBCInfoSharedPtr rInfo =
4105  TraceID[cnt + e],
4106  Array_tmp = coeffphys + locExpList->GetPhys_Offset(e));
4107 
4108  elmtid = ElmtID[cnt + e];
4109  // make link list if necessary
4110  if (returnval.count(elmtid) != 0)
4111  {
4112  rInfo->next = returnval.find(elmtid)->second;
4113  }
4114  returnval[elmtid] = rInfo;
4115  }
4116  }
4117  cnt += m_bndCondExpansions[i]->GetExpSize();
4118  }
4119 
4120  return returnval;
4121 }
4122 
4123 /**
4124  * @brief Calculate the \f$ L^2 \f$ error of the \f$ Q_{\rm dir} \f$
4125  * derivative using the consistent DG evaluation of \f$ Q_{\rm dir} \f$.
4126  *
4127  * The solution provided is of the primative variation at the quadrature
4128  * points and the derivative is compared to the discrete derivative at
4129  * these points, which is likely to be undesirable unless using a much
4130  * higher number of quadrature points than the polynomial order used to
4131  * evaluate \f$ Q_{\rm dir} \f$.
4132  */
4134  const Array<OneD, const NekDouble> &coeffs,
4135  const Array<OneD, const NekDouble> &soln)
4136 {
4137  int i, e, ncoeff_edge;
4138  Array<OneD, const NekDouble> tmp_coeffs;
4139  Array<OneD, NekDouble> out_d(m_ncoeffs), out_tmp;
4140 
4142  m_traceMap->GetElmtToTrace();
4143 
4144  StdRegions::Orientation edgedir;
4145 
4146  int cnt;
4147  int LocBndCoeffs = m_traceMap->GetNumLocalBndCoeffs();
4148  Array<OneD, NekDouble> loc_lambda(LocBndCoeffs), edge_lambda;
4149  m_traceMap->GlobalToLocalBnd(m_trace->GetCoeffs(), loc_lambda);
4150 
4151  edge_lambda = loc_lambda;
4152 
4153  // Calculate Q using standard DG formulation.
4154  for (i = cnt = 0; i < GetExpSize(); ++i)
4155  {
4156  // Probably a better way of setting up lambda than this.
4157  // Note cannot use PutCoeffsInToElmts since lambda space
4158  // is mapped during the solve.
4159  int nEdges = (*m_exp)[i]->GetGeom()->GetNumEdges();
4160  Array<OneD, Array<OneD, NekDouble>> edgeCoeffs(nEdges);
4161 
4162  for (e = 0; e < nEdges; ++e)
4163  {
4164  edgedir = (*m_exp)[i]->GetTraceOrient(e);
4165  ncoeff_edge = elmtToTrace[i][e]->GetNcoeffs();
4166  edgeCoeffs[e] = Array<OneD, NekDouble>(ncoeff_edge);
4167  Vmath::Vcopy(ncoeff_edge, edge_lambda, 1, edgeCoeffs[e], 1);
4168  elmtToTrace[i][e]->SetCoeffsToOrientation(edgedir, edgeCoeffs[e],
4169  edgeCoeffs[e]);
4170  edge_lambda = edge_lambda + ncoeff_edge;
4171  }
4172 
4173  (*m_exp)[i]->DGDeriv(dir, tmp_coeffs = coeffs + m_coeff_offset[i],
4174  elmtToTrace[i], edgeCoeffs, out_tmp = out_d + cnt);
4175  cnt += (*m_exp)[i]->GetNcoeffs();
4176  }
4177 
4179  BwdTrans(out_d, phys);
4180  Vmath::Vsub(m_npoints, phys, 1, soln, 1, phys, 1);
4181  return L2(phys);
4182 }
4183 
4184 /**
4185  * @brief Evaluate HDG post-processing to increase polynomial order of
4186  * solution.
4187  *
4188  * This function takes the solution (assumed to be one order lower) in
4189  * physical space, and postprocesses at the current polynomial order by
4190  * solving the system:
4191  *
4192  * \f[
4193  * \begin{aligned}
4194  * (\nabla w, \nabla u^*) &= (\nabla w, u), \\
4195  * \langle \nabla u^*, 1 \rangle &= \langle \nabla u, 1 \rangle
4196  * \end{aligned}
4197  * \f]
4198  *
4199  * where \f$ u \f$ corresponds with the current solution as stored
4200  * inside #m_coeffs.
4201  *
4202  * @param outarray The resulting field \f$ u^* \f$.
4203  */
4205  const Array<OneD, const NekDouble> &coeffs,
4206  Array<OneD, NekDouble> &outarray)
4207 {
4208  int i, cnt, e, ncoeff_trace;
4209  Array<OneD, NekDouble> force, out_tmp, qrhs, qrhs1;
4211  m_traceMap->GetElmtToTrace();
4212 
4213  StdRegions::Orientation edgedir;
4214 
4215  int nq_elmt, nm_elmt;
4216  int LocBndCoeffs = m_traceMap->GetNumLocalBndCoeffs();
4217  Array<OneD, NekDouble> loc_lambda(LocBndCoeffs), trace_lambda;
4218  Array<OneD, NekDouble> tmp_coeffs;
4219  m_traceMap->GlobalToLocalBnd(m_trace->GetCoeffs(), loc_lambda);
4220 
4221  trace_lambda = loc_lambda;
4222 
4223  int dim = (m_expType == e2D) ? 2 : 3;
4224 
4225  int num_points[] = {0, 0, 0};
4226  int num_modes[] = {0, 0, 0};
4227 
4228  // Calculate Q using standard DG formulation.
4229  for (i = cnt = 0; i < GetExpSize(); ++i)
4230  {
4231  nq_elmt = (*m_exp)[i]->GetTotPoints();
4232  nm_elmt = (*m_exp)[i]->GetNcoeffs();
4233  qrhs = Array<OneD, NekDouble>(nq_elmt);
4234  qrhs1 = Array<OneD, NekDouble>(nq_elmt);
4235  force = Array<OneD, NekDouble>(2 * nm_elmt);
4236  out_tmp = force + nm_elmt;
4238 
4239  for (int j = 0; j < dim; ++j)
4240  {
4241  num_points[j] = (*m_exp)[i]->GetBasis(j)->GetNumPoints();
4242  num_modes[j] = (*m_exp)[i]->GetBasis(j)->GetNumModes();
4243  }
4244 
4245  // Probably a better way of setting up lambda than this. Note
4246  // cannot use PutCoeffsInToElmts since lambda space is mapped
4247  // during the solve.
4248  int nTraces = (*m_exp)[i]->GetNtraces();
4249  Array<OneD, Array<OneD, NekDouble>> traceCoeffs(nTraces);
4250 
4251  for (e = 0; e < (*m_exp)[i]->GetNtraces(); ++e)
4252  {
4253  edgedir = (*m_exp)[i]->GetTraceOrient(e);
4254  ncoeff_trace = elmtToTrace[i][e]->GetNcoeffs();
4255  traceCoeffs[e] = Array<OneD, NekDouble>(ncoeff_trace);
4256  Vmath::Vcopy(ncoeff_trace, trace_lambda, 1, traceCoeffs[e], 1);
4257  if (dim == 2)
4258  {
4259  elmtToTrace[i][e]->SetCoeffsToOrientation(
4260  edgedir, traceCoeffs[e], traceCoeffs[e]);
4261  }
4262  else
4263  {
4264  (*m_exp)[i]
4266  ->SetFaceToGeomOrientation(e, traceCoeffs[e]);
4267  }
4268  trace_lambda = trace_lambda + ncoeff_trace;
4269  }
4270 
4271  // creating orthogonal expansion (checking if we have quads or
4272  // triangles)
4273  LibUtilities::ShapeType shape = (*m_exp)[i]->DetShapeType();
4274  switch (shape)
4275  {
4277  {
4278  const LibUtilities::PointsKey PkeyQ1(
4279  num_points[0], LibUtilities::eGaussLobattoLegendre);
4280  const LibUtilities::PointsKey PkeyQ2(
4281  num_points[1], LibUtilities::eGaussLobattoLegendre);
4283  num_modes[0], PkeyQ1);
4285  num_modes[1], PkeyQ2);
4287  std::dynamic_pointer_cast<SpatialDomains::QuadGeom>(
4288  (*m_exp)[i]->GetGeom());
4290  BkeyQ1, BkeyQ2, qGeom);
4291  }
4292  break;
4294  {
4295  const LibUtilities::PointsKey PkeyT1(
4296  num_points[0], LibUtilities::eGaussLobattoLegendre);
4297  const LibUtilities::PointsKey PkeyT2(
4298  num_points[1], LibUtilities::eGaussRadauMAlpha1Beta0);
4300  num_modes[0], PkeyT1);
4302  num_modes[1], PkeyT2);
4304  std::dynamic_pointer_cast<SpatialDomains::TriGeom>(
4305  (*m_exp)[i]->GetGeom());
4307  BkeyT1, BkeyT2, tGeom);
4308  }
4309  break;
4311  {
4312  const LibUtilities::PointsKey PkeyH1(
4313  num_points[0], LibUtilities::eGaussLobattoLegendre);
4314  const LibUtilities::PointsKey PkeyH2(
4315  num_points[1], LibUtilities::eGaussLobattoLegendre);
4316  const LibUtilities::PointsKey PkeyH3(
4317  num_points[2], LibUtilities::eGaussLobattoLegendre);
4319  num_modes[0], PkeyH1);
4321  num_modes[1], PkeyH2);
4323  num_modes[2], PkeyH3);
4325  std::dynamic_pointer_cast<SpatialDomains::HexGeom>(
4326  (*m_exp)[i]->GetGeom());
4328  BkeyH1, BkeyH2, BkeyH3, hGeom);
4329  }
4330  break;
4332  {
4333  const LibUtilities::PointsKey PkeyT1(
4334  num_points[0], LibUtilities::eGaussLobattoLegendre);
4335  const LibUtilities::PointsKey PkeyT2(
4336  num_points[1], LibUtilities::eGaussRadauMAlpha1Beta0);
4337  const LibUtilities::PointsKey PkeyT3(
4338  num_points[2], LibUtilities::eGaussRadauMAlpha2Beta0);
4340  num_modes[0], PkeyT1);
4342  num_modes[1], PkeyT2);
4344  num_modes[2], PkeyT3);
4346  std::dynamic_pointer_cast<SpatialDomains::TetGeom>(
4347  (*m_exp)[i]->GetGeom());
4349  BkeyT1, BkeyT2, BkeyT3, tGeom);
4350  }
4351  break;
4352  default:
4354  "Wrong shape type, HDG postprocessing is not "
4355  "implemented");
4356  };
4357 
4358  // DGDeriv
4359  // (d/dx w, d/dx q_0)
4360  (*m_exp)[i]->DGDeriv(0, tmp_coeffs = coeffs + m_coeff_offset[i],
4361  elmtToTrace[i], traceCoeffs, out_tmp);
4362  (*m_exp)[i]->BwdTrans(out_tmp, qrhs);
4363  ppExp->IProductWRTDerivBase(0, qrhs, force);
4364 
4365  // + (d/dy w, d/dy q_1)
4366  (*m_exp)[i]->DGDeriv(1, tmp_coeffs = coeffs + m_coeff_offset[i],
4367  elmtToTrace[i], traceCoeffs, out_tmp);
4368 
4369  (*m_exp)[i]->BwdTrans(out_tmp, qrhs);
4370  ppExp->IProductWRTDerivBase(1, qrhs, out_tmp);
4371 
4372  Vmath::Vadd(nm_elmt, force, 1, out_tmp, 1, force, 1);
4373 
4374  // determine force[0] = (1,u)
4375  (*m_exp)[i]->BwdTrans(tmp_coeffs = coeffs + m_coeff_offset[i], qrhs);
4376  force[0] = (*m_exp)[i]->Integral(qrhs);
4377 
4378  // multiply by inverse Laplacian matrix
4379  // get matrix inverse
4381  ppExp->DetShapeType(), *ppExp);
4382  DNekScalMatSharedPtr lapsys = ppExp->GetLocMatrix(lapkey);
4383 
4384  NekVector<NekDouble> in(nm_elmt, force, eWrapper);
4385  NekVector<NekDouble> out(nm_elmt);
4386 
4387  out = (*lapsys) * in;
4388 
4389  // Transforming back to modified basis
4390  Array<OneD, NekDouble> work(nq_elmt);
4391  ppExp->BwdTrans(out.GetPtr(), work);
4392  (*m_exp)[i]->FwdTrans(work, tmp_coeffs = outarray + m_coeff_offset[i]);
4393  }
4394 }
4395 
4397  const Array<OneD, const NekDouble> &Fwd,
4398  const Array<OneD, const NekDouble> &Bwd,
4399  Array<OneD, NekDouble> &locTraceFwd, Array<OneD, NekDouble> &locTraceBwd)
4400 {
4401  if (locTraceFwd != NullNekDouble1DArray)
4402  {
4403  m_locTraceToTraceMap->InterpLocTracesToTraceTranspose(0, Fwd,
4404  locTraceFwd);
4405  }
4406 
4407  if (locTraceBwd != NullNekDouble1DArray)
4408  {
4409  m_locTraceToTraceMap->InterpLocTracesToTraceTranspose(1, Bwd,
4410  locTraceBwd);
4411  }
4412 }
4413 
4415  const Array<OneD, const NekDouble> &FwdFlux,
4416  const Array<OneD, const NekDouble> &BwdFlux,
4417  Array<OneD, NekDouble> &outarray)
4418 {
4419  Array<OneD, NekDouble> FCoeffs(m_trace->GetNcoeffs());
4420 
4421  m_trace->IProductWRTBase(FwdFlux, FCoeffs);
4422  m_locTraceToTraceMap->AddTraceCoeffsToFieldCoeffs(1, FCoeffs, outarray);
4423  m_trace->IProductWRTBase(BwdFlux, FCoeffs);
4424  m_locTraceToTraceMap->AddTraceCoeffsToFieldCoeffs(0, FCoeffs, outarray);
4425 }
4426 } // namespace MultiRegions
4427 } // namespace Nektar
#define ASSERTL0(condition, msg)
Definition: ErrorUtil.hpp:215
#define NEKERROR(type, msg)
Assert Level 0 – Fundamental assert which is used whether in FULLDEBUG, DEBUG or OPT compilation mode...
Definition: ErrorUtil.hpp:209
#define ASSERTL1(condition, msg)
Assert Level 1 – Debugging which is used whether in FULLDEBUG or DEBUG compilation mode....
Definition: ErrorUtil.hpp:249
#define ASSERTL2(condition, msg)
Assert Level 2 – Debugging which is used FULLDEBUG compilation mode. This level assert is designed to...
Definition: ErrorUtil.hpp:272
#define sign(a, b)
return the sign(b)*a
Definition: Polylib.cpp:49
Describes the specification for a Basis.
Definition: Basis.h:50
Interpreter class for the evaluation of mathematical expressions.
Definition: Interpreter.h:78
int DefineFunction(const std::string &vlist, const std::string &expr)
Defines a function for the purposes of evaluation.
NekDouble Evaluate(const int id)
Evaluate a function which depends only on constants and/or parameters.
Defines a specification for a set of points.
Definition: Points.h:59
static std::shared_ptr< DataType > AllocateSharedPtr(const Args &...args)
Allocate a shared pointer from the memory pool.
This class is the abstractio n of a global discontinuous two- dimensional spectral/hp element expansi...
Definition: DisContField.h:58
virtual Array< OneD, SpatialDomains::BoundaryConditionShPtr > & v_UpdateBndConditions() override
std::vector< int > m_periodicBwdCopy
Definition: DisContField.h:192
virtual void v_AddTraceIntegral(const Array< OneD, const NekDouble > &Fn, Array< OneD, NekDouble > &outarray) override
Add trace contributions into elemental coefficient spaces.
PeriodicMap m_periodicEdges
A map which identifies pairs of periodic edges.
Definition: DisContField.h:179
void SetUpDG(const std::string="DefaultVar", const Collections::ImplementationType ImpType=Collections::eNoImpType)
Set up all DG member variables and maps.
PeriodicMap m_periodicFaces
A map which identifies pairs of periodic faces.
Definition: DisContField.h:184
std::set< int > m_boundaryTraces
A set storing the global IDs of any boundary Verts.
Definition: DisContField.h:169
LocTraceToTraceMapSharedPtr m_locTraceToTraceMap
Definition: DisContField.h:204
virtual ~DisContField()
Destructor.
std::vector< bool > m_negatedFluxNormal
Definition: DisContField.h:333
SpatialDomains::BoundaryConditionsSharedPtr GetDomainBCs(const SpatialDomains::CompositeMap &domain, const SpatialDomains::BoundaryConditions &Allbcs, const std::string &variable)
AssemblyMapDGSharedPtr m_traceMap
Local to global DG mapping for trace space.
Definition: DisContField.h:164
Array< OneD, int > m_BCtoTraceMap
Definition: DisContField.h:61
void EvaluateHDGPostProcessing(const Array< OneD, const NekDouble > &coeffs, Array< OneD, NekDouble > &outarray)
Evaluate HDG post-processing to increase polynomial order of solution.
virtual void v_GetPeriodicEntities(PeriodicMap &periodicVerts, PeriodicMap &periodicEdges, PeriodicMap &periodicFaces) override
Obtain a copy of the periodic edges and vertices for this field.
GlobalLinSysSharedPtr GetGlobalBndLinSys(const GlobalLinSysKey &mkey)
For a given key, returns the associated global linear system.
virtual const Array< OneD, const SpatialDomains::BoundaryConditionShPtr > & v_GetBndConditions() override
virtual MultiRegions::ExpListSharedPtr & v_UpdateBndCondExpansion(int i) override
virtual void v_SetBndCondBwdWeight(const int index, const NekDouble value) override
virtual void v_PeriodicBwdCopy(const Array< OneD, const NekDouble > &Fwd, Array< OneD, NekDouble > &Bwd) override
virtual void v_GetBoundaryToElmtMap(Array< OneD, int > &ElmtID, Array< OneD, int > &TraceID) override
virtual void v_ExtractTracePhys(const Array< OneD, const NekDouble > &inarray, Array< OneD, NekDouble > &outarray) override
This method extracts the trace (verts in 1D) from the field inarray and puts the values in outarray.
InterfaceMapDGSharedPtr m_interfaceMap
Interfaces mapping for trace space.
Definition: DisContField.h:155
virtual void v_GetBndElmtExpansion(int i, std::shared_ptr< ExpList > &result, const bool DeclareCoeffPhysArrays) override
virtual void v_FillBwdWithBwdWeight(Array< OneD, NekDouble > &weightave, Array< OneD, NekDouble > &weightjmp) override
Fill the weight with m_bndCondBndWeight.
virtual const Array< OneD, const NekDouble > & v_GetBndCondBwdWeight() override
virtual const Array< OneD, const MultiRegions::ExpListSharedPtr > & v_GetBndCondExpansions() override
void FindPeriodicTraces(const SpatialDomains::BoundaryConditions &bcs, const std::string variable)
Generate a associative map of periodic vertices in a mesh.
Array< OneD, NekDouble > m_bndCondBndWeight
Definition: DisContField.h:152
bool SameTypeOfBoundaryConditions(const DisContField &In)
Check to see if expansion has the same BCs as In.
virtual ExpListSharedPtr & v_GetTrace() override
Array< OneD, SpatialDomains::BoundaryConditionShPtr > m_bndConditions
An array which contains the information about the boundary condition structure definition on the diff...
Definition: DisContField.h:137
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) override
Evaluate all boundary conditions at a given time..
virtual void v_AddFwdBwdTraceIntegral(const Array< OneD, const NekDouble > &Fwd, const Array< OneD, const NekDouble > &Bwd, Array< OneD, NekDouble > &outarray) override
Add trace contributions into elemental coefficient spaces.
virtual const LocTraceToTraceMapSharedPtr & v_GetLocTraceToTraceMap(void) const override
virtual void v_AddTraceIntegralToOffDiag(const Array< OneD, const NekDouble > &FwdFlux, const Array< OneD, const NekDouble > &BwdFlux, Array< OneD, NekDouble > &outarray) override
virtual void v_FillBwdWithBoundCond(const Array< OneD, NekDouble > &Fwd, Array< OneD, NekDouble > &Bwd, bool PutFwdInBwdOnBCs) override
GlobalLinSysMapShPtr m_globalBndMat
Global boundary matrix.
Definition: DisContField.h:158
PeriodicMap m_periodicVerts
A map which identifies groups of periodic vertices.
Definition: DisContField.h:174
ExpListSharedPtr m_trace
Trace space storage for points between elements.
Definition: DisContField.h:161
virtual void v_AddTraceQuadPhysToField(const Array< OneD, const NekDouble > &Fwd, const Array< OneD, const NekDouble > &Bwd, Array< OneD, NekDouble > &field) override
virtual void v_Reset() override
Reset this field, so that geometry information can be updated.
std::vector< bool > & GetNegatedFluxNormal(void)
DisContField()
Default constructor.
virtual std::vector< bool > & v_GetLeftAdjacentTraces(void) override
Array< OneD, MultiRegions::ExpListSharedPtr > m_bndCondExpansions
An object which contains the discretised boundary conditions.
Definition: DisContField.h:150
virtual GlobalLinSysKey v_HelmSolve(const Array< OneD, const NekDouble > &inarray, Array< OneD, NekDouble > &outarray, const StdRegions::ConstFactorMap &factors, const StdRegions::VarCoeffMap &varcoeff, const MultiRegions::VarFactorsMap &varfactors, const Array< OneD, const NekDouble > &dirForcing, const bool PhysSpaceForcing) override
Solve the Helmholtz equation.
std::vector< int > m_periodicFwdCopy
A vector indicating degress of freedom which need to be copied from forwards to backwards space in ca...
Definition: DisContField.h:191
bool IsLeftAdjacentTrace(const int n, const int e)
This routine determines if an element is to the "left" of the adjacent trace, which arises from the i...
std::vector< bool > m_leftAdjacentTraces
Definition: DisContField.h:198
NekDouble L2_DGDeriv(const int dir, const Array< OneD, const NekDouble > &coeffs, const Array< OneD, const NekDouble > &soln)
Calculate the error of the derivative using the consistent DG evaluation of .
virtual void v_GetLocTraceFromTracePts(const Array< OneD, const NekDouble > &Fwd, const Array< OneD, const NekDouble > &Bwd, Array< OneD, NekDouble > &locTraceFwd, Array< OneD, NekDouble > &locTraceBwd) override
virtual AssemblyMapDGSharedPtr & v_GetTraceMap(void) override
virtual std::map< int, RobinBCInfoSharedPtr > v_GetRobinBCInfo() override
virtual void v_GetFwdBwdTracePhys(Array< OneD, NekDouble > &Fwd, Array< OneD, NekDouble > &Bwd) override
void GenerateBoundaryConditionExpansion(const SpatialDomains::MeshGraphSharedPtr &graph1D, const SpatialDomains::BoundaryConditions &bcs, const std::string variable, const bool DeclareCoeffPhysArrays=true)
Discretises the boundary conditions.
Base class for all multi-elemental spectral/hp expansions.
Definition: ExpList.h:103
virtual void v_Reset()
Reset geometry information, metrics, matrix managers and geometry information.
Definition: ExpList.cpp:2673
void IProductWRTBase(const Array< OneD, const NekDouble > &inarray, Array< OneD, NekDouble > &outarray)
This function calculates the inner product of a function with respect to all local expansion modes .
Definition: ExpList.h:1630
std::shared_ptr< ExpList > & GetTrace()
Definition: ExpList.h:2112
int GetNcoeffs(void) const
Returns the total number of local degrees of freedom .
Definition: ExpList.h:1492
const DNekScalBlkMatSharedPtr & GetBlockMatrix(const GlobalMatrixKey &gkey)
Definition: ExpList.cpp:2016
void GetBoundaryToElmtMap(Array< OneD, int > &ElmtID, Array< OneD, int > &EdgeID)
Definition: ExpList.h:2222
static SpatialDomains::BoundaryConditionShPtr GetBoundaryCondition(const SpatialDomains::BoundaryConditionCollection &collection, unsigned int index, const std::string &variable)
Definition: ExpList.cpp:5070
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:1902
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:1988
int GetCoeff_Offset(int n) const
Get the start offset position for a local contiguous list of coeffs correspoinding to element n.
Definition: ExpList.h:2037
int GetExpSize(void)
This function returns the number of elements in the expansion.
Definition: ExpList.h:1996
Array< OneD, int > m_coeff_offset
Offset of elemental data into the array m_coeffs.
Definition: ExpList.h:1120
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:2434
NekDouble GetCoeff(int i)
Get the i th value (coefficient) of m_coeffs.
Definition: ExpList.h:1968
bool m_physState
The state of the array m_phys.
Definition: ExpList.h:1100
LibUtilities::CommSharedPtr m_comm
Communicator.
Definition: ExpList.h:1049
std::shared_ptr< LocalRegions::ExpansionVector > m_exp
The list of local expansions.
Definition: ExpList.h:1111
int m_ncoeffs
The total number of local degrees of freedom. m_ncoeffs .
Definition: ExpList.h:1056
void EvaluateBoundaryConditions(const NekDouble time=0.0, const std::string varName="", const NekDouble=NekConstants::kNekUnsetDouble, const NekDouble=NekConstants::kNekUnsetDouble)
Definition: ExpList.h:2211
const std::shared_ptr< LocalRegions::ExpansionVector > GetExp() const
This function returns the vector of elements in the expansion.
Definition: ExpList.h:2029
SpatialDomains::MeshGraphSharedPtr m_graph
Mesh associated with this expansion list.
Definition: ExpList.h:1053
NekDouble L2(const Array< OneD, const NekDouble > &inarray, const Array< OneD, const NekDouble > &soln=NullNekDouble1DArray)
This function calculates the error of the global This function calculates the error with respect to...
Definition: ExpList.h:506
void ApplyGeomInfo()
Apply geometry information to each expansion.
Definition: ExpList.cpp:2661
LibUtilities::SessionReaderSharedPtr m_session
Session.
Definition: ExpList.h:1051
void BwdTrans(const Array< OneD, const NekDouble > &inarray, Array< OneD, NekDouble > &outarray)
This function elementally evaluates the backward transformation of the global spectral/hp element exp...
Definition: ExpList.h:1677
int GetPhys_Offset(int n) const
Get the start offset position for a local contiguous list of quadrature points in a full array corres...
Definition: ExpList.h:2044
ExpansionType m_expType
Exapnsion type.
Definition: ExpList.h:1044
Array< OneD, NekDouble > m_phys
The global expansion evaluated at the quadrature points.
Definition: ExpList.h:1092
GlobalSysSolnType GetGlobalSysSolnType() const
Return the associated solution type.
Describes a matrix with ordering defined by a local to global map.
StdRegions::MatrixType GetMatrixType() const
Return the matrix type.
Array< OneD, DataType > & GetPtr()
Definition: NekVector.cpp:217
const BoundaryConditionCollection & GetBoundaryConditions(void) const
Definition: Conditions.h:244
const BoundaryRegionCollection & GetBoundaryRegions(void) const
Definition: Conditions.h:234
void Rotate(PointGeom &a, int dir, NekDouble angle)
_this = rotation of a by angle 'angle' around axis dir
Definition: PointGeom.cpp:142
NekDouble dist(PointGeom &a)
return distance between this and input a
Definition: PointGeom.cpp:180
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:137
static StdRegions::Orientation GetFaceOrientation(const TriGeom &face1, const TriGeom &face2, bool doRot, int dir, NekDouble angle, NekDouble tol)
Definition: TriGeom.cpp:117
BasisManagerT & BasisManager(void)
std::shared_ptr< Basis > BasisSharedPtr
std::shared_ptr< SessionReader > SessionReaderSharedPtr
@ eGaussLobattoLegendre
1D Gauss-Lobatto-Legendre quadrature points
Definition: PointsType.h:53
@ eGaussGaussLegendre
1D Gauss-Gauss-Legendre quadrature points
Definition: PointsType.h:48
std::shared_ptr< Comm > CommSharedPtr
Pointer to a Communicator object.
Definition: Comm.h:54
@ eGauss_Lagrange
Lagrange Polynomials using the Gauss points.
Definition: BasisType.h:59
@ eOrtho_A
Principle Orthogonal Functions .
Definition: BasisType.h:44
@ eOrtho_C
Principle Orthogonal Functions .
Definition: BasisType.h:48
@ eOrtho_B
Principle Orthogonal Functions .
Definition: BasisType.h:46
std::shared_ptr< Expansion > ExpansionSharedPtr
Definition: Expansion.h:68
std::shared_ptr< Expansion2D > Expansion2DSharedPtr
Definition: Expansion1D.h:47
std::shared_ptr< Expansion0D > Expansion0DSharedPtr
Definition: Expansion0D.h:48
std::shared_ptr< Expansion1D > Expansion1DSharedPtr
Definition: Expansion1D.h:51
std::shared_ptr< Expansion3D > Expansion3DSharedPtr
Definition: Expansion2D.h:50
std::shared_ptr< AssemblyMapDG > AssemblyMapDGSharedPtr
Definition: AssemblyMapDG.h:47
static AssemblyMapSharedPtr NullAssemblyMapSharedPtr
Definition: AssemblyMap.h:53
static ExpListSharedPtr NullExpListSharedPtr
Definition: ExpList.h:1479
std::shared_ptr< RobinBCInfo > RobinBCInfoSharedPtr
std::shared_ptr< GlobalLinSys > GlobalLinSysSharedPtr
Pointer to a GlobalLinSys object.
Definition: GlobalLinSys.h:50
std::shared_ptr< ExpList > ExpListSharedPtr
Shared pointer to an ExpList object.
static GlobalLinSysKey NullGlobalLinSysKey(StdRegions::eNoMatrixType)
std::shared_ptr< LocTraceToTraceMap > LocTraceToTraceMapSharedPtr
std::map< StdRegions::ConstFactorType, Array< OneD, NekDouble > > VarFactorsMap
std::map< int, std::vector< PeriodicEntity > > PeriodicMap
static const NekDouble kNekUnsetDouble
std::map< int, BoundaryRegionShPtr > BoundaryRegionCollection
Definition: Conditions.h:210
std::map< int, std::vector< unsigned int > > CompositeOrdering
Definition: MeshGraph.h:109
std::shared_ptr< QuadGeom > QuadGeomSharedPtr
Definition: HexGeom.h:46
std::shared_ptr< BoundaryRegion > BoundaryRegionShPtr
Definition: Conditions.h:208
std::shared_ptr< BoundaryConditions > BoundaryConditionsSharedPtr
Definition: Conditions.h:289
std::shared_ptr< BoundaryConditionBase > BoundaryConditionShPtr
Definition: Conditions.h:212
std::shared_ptr< std::vector< std::pair< GeometrySharedPtr, int > > > GeometryLinkSharedPtr
Definition: MeshGraph.h:168
std::map< int, std::vector< unsigned int > > BndRegionOrdering
Definition: MeshGraph.h:110
std::shared_ptr< DirichletBoundaryCondition > DirichletBCShPtr
Definition: Conditions.h:213
std::shared_ptr< Composite > CompositeSharedPtr
Definition: MeshGraph.h:137
std::vector< PointGeomSharedPtr > PointGeomVector
Definition: Geometry2D.h:64
std::map< int, BoundaryConditionMapShPtr > BoundaryConditionCollection
Definition: Conditions.h:219
std::shared_ptr< HexGeom > HexGeomSharedPtr
Definition: HexGeom.h:87
std::shared_ptr< SegGeom > SegGeomSharedPtr
Definition: Geometry2D.h:62
std::shared_ptr< TetGeom > TetGeomSharedPtr
Definition: TetGeom.h:85
std::shared_ptr< MeshGraph > MeshGraphSharedPtr
Definition: MeshGraph.h:172
std::shared_ptr< NeumannBoundaryCondition > NeumannBCShPtr
Definition: Conditions.h:214
std::shared_ptr< BoundaryConditionMap > BoundaryConditionMapShPtr
Definition: Conditions.h:218
std::shared_ptr< Geometry2D > Geometry2DSharedPtr
Definition: Geometry.h:65
std::shared_ptr< TriGeom > TriGeomSharedPtr
Definition: TriGeom.h:58
std::shared_ptr< RobinBoundaryCondition > RobinBCShPtr
Definition: Conditions.h:215
std::map< int, CompositeSharedPtr > CompositeMap
Definition: MeshGraph.h:138
std::map< ConstFactorType, NekDouble > ConstFactorMap
Definition: StdRegions.hpp:399
std::map< StdRegions::VarCoeffType, VarCoeffEntry > VarCoeffMap
Definition: StdRegions.hpp:343
The above copyright notice and this permission notice shall be included.
Definition: CoupledSolver.h:2
std::shared_ptr< DNekScalMat > DNekScalMatSharedPtr
NekMatrix< InnerMatrixType, BlockMatrixTag > Transpose(NekMatrix< InnerMatrixType, BlockMatrixTag > &rhs)
std::shared_ptr< DNekScalBlkMat > DNekScalBlkMatSharedPtr
Definition: NekTypeDefs.hpp:79
static Array< OneD, NekDouble > NullNekDouble1DArray
std::shared_ptr< DNekMat > DNekMatSharedPtr
Definition: NekTypeDefs.hpp:75
double NekDouble
void Vmul(int n, const T *x, const int incx, const T *y, const int incy, T *z, const int incz)
Multiply vector z = x*y.
Definition: Vmath.cpp:209
void Neg(int n, T *x, const int incx)
Negate x = -x.
Definition: Vmath.cpp:518
T Vsum(int n, const T *x, const int incx)
Subtract return sum(x)
Definition: Vmath.cpp:895
void Vadd(int n, const T *x, const int incx, const T *y, const int incy, T *z, const int incz)
Add vector z = x+y.
Definition: Vmath.cpp:359
void Smul(int n, const T alpha, const T *x, const int incx, T *y, const int incy)
Scalar multiply y = alpha*x.
Definition: Vmath.cpp:248
void Zero(int n, T *x, const int incx)
Zero vector.
Definition: Vmath.cpp:492
void Fill(int n, const T alpha, T *x, const int incx)
Fill a vector with a constant value.
Definition: Vmath.cpp:45
void Vcopy(int n, const T *x, const int incx, T *y, const int incy)
Definition: Vmath.cpp:1255
void Vsub(int n, const T *x, const int incx, const T *y, const int incy, T *z, const int incz)
Subtract vector z = x-y.
Definition: Vmath.cpp:419
scalarT< T > sqrt(scalarT< T > in)
Definition: scalar.hpp:294
int id
Geometry ID of entity.
StdRegions::Orientation orient
Orientation of entity within higher dimensional entity.
bool isLocal
Flag specifying if this entity is local to this partition.
NekDouble m_tol
Tolerance to rotation is considered identical.
int m_dir
Axis of rotation. 0 = 'x', 1 = 'y', 2 = 'z'.
NekDouble m_angle
Angle of rotation in radians.