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