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>
42#include <LocalRegions/TetExp.h>
43#include <LocalRegions/TriExp.h>
47
48using namespace std;
49
50namespace Nektar
51{
52namespace 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 */
65 : ExpList(), m_bndConditions(), m_bndCondExpansions(),
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,
87 const std::string bcvariable)
88 : ExpList(pSession, graph, DeclareCoeffPhysArrays, variable, ImpType),
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 }
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 */
163void DisContField::SetUpDG(const std::string variable,
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
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
478bool DisContField::IsLeftAdjacentTrace(const int n, const int e)
479{
481 m_traceMap->GetElmtToTrace()[n][e];
482
483 PeriodicMap periodicTraces = (m_expType == e1D) ? m_periodicVerts
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 */
612 const SpatialDomains::CompositeMap &domain,
614 const std::string &variable,
615 const LibUtilities::CommSharedPtr &comm,
616 bool SetToOneSpaceDimension,
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
627 EvaluateBoundaryConditions(0.0, variable);
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;
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();
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();
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();
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
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.
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.
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.
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
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
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();
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
3103void DisContField::v_SetBndCondBwdWeight(const int index, const NekDouble value)
3104{
3105 m_bndCondBndWeight[index] = value;
3106}
3107
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;
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 */
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,
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;
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 //----------------------------------
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
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)
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)
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{
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 */
4066map<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 {
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;
4139 Array<OneD, NekDouble> out_d(m_ncoeffs), out_tmp;
4140
4142 m_traceMap->GetElmtToTrace();
4143
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
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(
4280 const LibUtilities::PointsKey PkeyQ2(
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(
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(
4314 const LibUtilities::PointsKey PkeyH2(
4316 const LibUtilities::PointsKey PkeyH3(
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(
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
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:47
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:55
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:102
virtual void v_Reset()
Reset geometry information, metrics, matrix managers and geometry information.
Definition: ExpList.cpp:3071
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:1665
std::shared_ptr< ExpList > & GetTrace()
Definition: ExpList.h:2177
int GetNcoeffs(void) const
Returns the total number of local degrees of freedom .
Definition: ExpList.h:1527
const DNekScalBlkMatSharedPtr & GetBlockMatrix(const GlobalMatrixKey &gkey)
Definition: ExpList.cpp:2415
void GetBoundaryToElmtMap(Array< OneD, int > &ElmtID, Array< OneD, int > &EdgeID)
Definition: ExpList.h:2290
static SpatialDomains::BoundaryConditionShPtr GetBoundaryCondition(const SpatialDomains::BoundaryConditionCollection &collection, unsigned int index, const std::string &variable)
Definition: ExpList.cpp:5470
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:1967
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:2053
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:2102
int GetExpSize(void)
This function returns the number of elements in the expansion.
Definition: ExpList.h:2061
Array< OneD, int > m_coeff_offset
Offset of elemental data into the array m_coeffs.
Definition: ExpList.h:1136
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:2833
NekDouble GetCoeff(int i)
Get the i th value (coefficient) of m_coeffs.
Definition: ExpList.h:2033
bool m_physState
The state of the array m_phys.
Definition: ExpList.h:1116
LibUtilities::CommSharedPtr m_comm
Communicator.
Definition: ExpList.h:1065
std::shared_ptr< LocalRegions::ExpansionVector > m_exp
The list of local expansions.
Definition: ExpList.h:1127
int m_ncoeffs
The total number of local degrees of freedom. m_ncoeffs .
Definition: ExpList.h:1072
void EvaluateBoundaryConditions(const NekDouble time=0.0, const std::string varName="", const NekDouble=NekConstants::kNekUnsetDouble, const NekDouble=NekConstants::kNekUnsetDouble)
Definition: ExpList.h:2279
const std::shared_ptr< LocalRegions::ExpansionVector > GetExp() const
This function returns the vector of elements in the expansion.
Definition: ExpList.h:2094
SpatialDomains::MeshGraphSharedPtr m_graph
Mesh associated with this expansion list.
Definition: ExpList.h:1069
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:518
void ApplyGeomInfo()
Apply geometry information to each expansion.
Definition: ExpList.cpp:3059
LibUtilities::SessionReaderSharedPtr m_session
Session.
Definition: ExpList.h:1067
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:1732
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:2109
ExpansionType m_expType
Exapnsion type.
Definition: ExpList.h:1060
Array< OneD, NekDouble > m_phys
The global expansion evaluated at the quadrature points.
Definition: ExpList.h:1108
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 BoundaryRegionCollection & GetBoundaryRegions(void) const
Definition: Conditions.h:234
const BoundaryConditionCollection & GetBoundaryConditions(void) const
Definition: Conditions.h:244
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:57
@ 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:48
std::shared_ptr< Expansion0D > Expansion0DSharedPtr
Definition: Expansion0D.h:49
std::shared_ptr< Expansion1D > Expansion1DSharedPtr
Definition: Expansion1D.h:52
std::shared_ptr< Expansion3D > Expansion3DSharedPtr
Definition: Expansion2D.h:51
std::shared_ptr< AssemblyMapDG > AssemblyMapDGSharedPtr
Definition: AssemblyMapDG.h:48
static AssemblyMapSharedPtr NullAssemblyMapSharedPtr
Definition: AssemblyMap.h:53
static ExpListSharedPtr NullExpListSharedPtr
Definition: ExpList.h:1514
std::shared_ptr< RobinBCInfo > RobinBCInfoSharedPtr
std::shared_ptr< GlobalLinSys > GlobalLinSysSharedPtr
Pointer to a GlobalLinSys object.
Definition: GlobalLinSys.h:53
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:47
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:176
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:408
std::map< StdRegions::VarCoeffType, VarCoeffEntry > VarCoeffMap
Definition: StdRegions.hpp:352
std::vector< double > w(NPUPPER)
std::vector< double > z(NPUPPER)
StdRegions::ConstFactorMap factors
The above copyright notice and this permission notice shall be included.
Definition: CoupledSolver.h:2
std::shared_ptr< DNekScalMat > DNekScalMatSharedPtr
std::shared_ptr< DNekScalBlkMat > DNekScalBlkMatSharedPtr
Definition: NekTypeDefs.hpp:79
static Array< OneD, NekDouble > NullNekDouble1DArray
NekMatrix< InnerMatrixType, BlockMatrixTag > Transpose(NekMatrix< InnerMatrixType, BlockMatrixTag > &rhs)
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:207
void Neg(int n, T *x, const int incx)
Negate x = -x.
Definition: Vmath.cpp:513
T Vsum(int n, const T *x, const int incx)
Subtract return sum(x)
Definition: Vmath.cpp:890
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:354
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:245
void Zero(int n, T *x, const int incx)
Zero vector.
Definition: Vmath.cpp:487
void Fill(int n, const T alpha, T *x, const int incx)
Fill a vector with a constant value.
Definition: Vmath.cpp:43
void Vcopy(int n, const T *x, const int incx, T *y, const int incy)
Definition: Vmath.cpp:1191
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:414
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.