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{
2823 return m_interfaceMap;
2824}
2825
2827 void) const
2828{
2829 return m_locTraceToTraceMap;
2830}
2831
2832// Return the internal vector which identifieds if trace
2833// is left adjacent definiing which trace the normal
2834// points otwards from
2836{
2837 return m_leftAdjacentTraces;
2838}
2839
2842{
2843 return m_bndCondExpansions;
2844}
2845
2848{
2849 return m_bndConditions;
2850}
2851
2853{
2854 return m_bndCondExpansions[i];
2855}
2856
2859{
2860 return m_bndConditions;
2861}
2862
2865{
2866 for (int n = 0; n < m_periodicFwdCopy.size(); ++n)
2867 {
2868 Bwd[m_periodicBwdCopy[n]] = Fwd[m_periodicFwdCopy[n]];
2869 }
2870}
2871
2874{
2875 v_GetFwdBwdTracePhys(m_phys, Fwd, Bwd);
2876}
2877
2878/**
2879 * @brief Obtain a copy of the periodic edges and vertices for this
2880 * field.
2881 */
2883 PeriodicMap &periodicEdges,
2884 PeriodicMap &periodicFaces)
2885{
2886 periodicVerts = m_periodicVerts;
2887 periodicEdges = m_periodicEdges;
2888 periodicFaces = m_periodicFaces;
2889}
2890/**
2891 * \brief This method extracts the "forward" and "backward" trace
2892 * data from the array \a field and puts the data into output
2893 * vectors \a Fwd and \a Bwd.
2894 *
2895 * We first define the convention which defines "forwards" and
2896 * "backwards". First an association is made between the vertex/edge/face of
2897 * each element and its corresponding vertex/edge/face in the trace space
2898 * using the mapping #m_traceMap. The element can either be
2899 * left-adjacent or right-adjacent to this trace face (see
2900 * Expansion2D::GetLeftAdjacentElementExp). Boundary faces are
2901 * always left-adjacent since left-adjacency is populated first.
2902 *
2903 * If the element is left-adjacent we extract the trace data
2904 * from \a field into the forward trace space \a Fwd; otherwise,
2905 * we place it in the backwards trace space \a Bwd. In this way,
2906 * we form a unique set of trace normals since these are always
2907 * extracted from left-adjacent elements.
2908 *
2909 * \param field is a NekDouble array which contains the fielddata
2910 * from which we wish to extract the backward and forward
2911 * orientated trace/face arrays.
2912 *
2913 * \return Updates a NekDouble array \a Fwd and \a Bwd
2914 */
2919 const Array<OneD, const ExpListSharedPtr> &BndCondExp)
2920{
2921 ASSERTL1((*m_exp)[0]->GetBasis(0)->GetBasisType() !=
2923 "Routine not set up for Gauss Lagrange points distribution");
2924
2925 // blocked routine
2926 Array<OneD, NekDouble> tracevals(m_locTraceToTraceMap->GetNLocTracePts());
2927
2928 m_locTraceToTraceMap->LocTracesFromField(field, tracevals);
2929 m_locTraceToTraceMap->InterpLocTracesToTrace(0, tracevals, Fwd);
2930
2931 Array<OneD, NekDouble> invals =
2932 tracevals + m_locTraceToTraceMap->GetNFwdLocTracePts();
2933 m_locTraceToTraceMap->InterpLocTracesToTrace(1, invals, Bwd);
2934
2936
2937 FillBwdWithBoundCond(Fwd, Bwd, BndCond, BndCondExp, false);
2938
2939 // Do parallel exchange for forwards/backwards spaces.
2940 m_traceMap->GetAssemblyCommDG()->PerformExchange(Fwd, Bwd);
2941
2942 // Do exchange of interface traces (local and parallel)
2943 // We may have to split this out into separate local and
2944 // parallel for IP method???
2945 m_interfaceMap->ExchangeTrace(Fwd, Bwd);
2946}
2947
2951 bool FillBnd, // these should be template params
2952 bool PutFwdInBwdOnBCs, bool DoExchange)
2953{
2954 // Is this zeroing necessary?
2955 // Zero forward/backward vectors.
2956 Vmath::Zero(Fwd.size(), Fwd, 1);
2957 Vmath::Zero(Bwd.size(), Bwd, 1);
2958
2959 // Basis definition on each element
2960 LibUtilities::BasisSharedPtr basis = (*m_exp)[0]->GetBasis(0);
2961 if ((basis->GetBasisType() != LibUtilities::eGauss_Lagrange))
2962 {
2963 // blocked routine
2964 Array<OneD, NekDouble> tracevals(
2965 m_locTraceToTraceMap->GetNLocTracePts());
2966
2967 m_locTraceToTraceMap->LocTracesFromField(field, tracevals);
2968 m_locTraceToTraceMap->InterpLocTracesToTrace(0, tracevals, Fwd);
2969
2970 Array<OneD, NekDouble> invals =
2971 tracevals + m_locTraceToTraceMap->GetNFwdLocTracePts();
2972 m_locTraceToTraceMap->InterpLocTracesToTrace(1, invals, Bwd);
2973 }
2974 else
2975 {
2976 // Loop over elements and collect forward expansion
2977 auto nexp = GetExpSize();
2980
2982 &elmtToTrace = m_traceMap->GetElmtToTrace();
2983
2984 for (int n = 0, cnt = 0; n < nexp; ++n)
2985 {
2986 exp = (*m_exp)[n];
2987 auto phys_offset = GetPhys_Offset(n);
2988
2989 for (int e = 0; e < exp->GetNtraces(); ++e, ++cnt)
2990 {
2991 auto offset =
2992 m_trace->GetPhys_Offset(elmtToTrace[n][e]->GetElmtId());
2993
2994 e_tmp =
2995 (m_leftAdjacentTraces[cnt]) ? Fwd + offset : Bwd + offset;
2996
2997 exp->GetTracePhysVals(e, elmtToTrace[n][e], field + phys_offset,
2998 e_tmp);
2999 }
3000 }
3001 }
3002
3004
3005 if (FillBnd)
3006 {
3007 v_FillBwdWithBoundCond(Fwd, Bwd, PutFwdInBwdOnBCs);
3008 }
3009
3010 if (DoExchange)
3011 {
3012 // Do parallel exchange for forwards/backwards spaces.
3013 m_traceMap->GetAssemblyCommDG()->PerformExchange(Fwd, Bwd);
3014
3015 // Do exchange of interface traces (local and parallel)
3016 // We may have to split this out into separate local and
3017 // parallel for IP method???
3018 m_interfaceMap->ExchangeTrace(Fwd, Bwd);
3019 }
3020}
3021
3024 bool PutFwdInBwdOnBCs)
3025{
3027 PutFwdInBwdOnBCs);
3028}
3029
3030/** function allowing different BCs to be passed to routine
3031 */
3035 &bndConditions,
3036 const Array<OneD, const ExpListSharedPtr> &bndCondExpansions,
3037 bool PutFwdInBwdOnBCs)
3038{
3039
3040 // Fill boundary conditions into missing elements
3041 if (PutFwdInBwdOnBCs) // just set Bwd value to be Fwd value on BCs
3042 {
3043 // Fill boundary conditions into missing elements
3044 for (int n = 0, cnt = 0; n < bndCondExpansions.size(); ++n)
3045 {
3046 if (bndConditions[n]->GetBoundaryConditionType() ==
3048 {
3049 auto ne = bndCondExpansions[n]->GetExpSize();
3050 for (int e = 0; e < ne; ++e)
3051 {
3052 auto npts = bndCondExpansions[n]->GetExp(e)->GetTotPoints();
3053 auto id2 = m_trace->GetPhys_Offset(
3054 m_traceMap->GetBndCondIDToGlobalTraceID(cnt + e));
3055 Vmath::Vcopy(npts, &Fwd[id2], 1, &Bwd[id2], 1);
3056 }
3057
3058 cnt += ne;
3059 }
3060 else if (bndConditions[n]->GetBoundaryConditionType() ==
3062 bndConditions[n]->GetBoundaryConditionType() ==
3064 {
3065 auto ne = bndCondExpansions[n]->GetExpSize();
3066 for (int e = 0; e < ne; ++e)
3067 {
3068 auto npts = bndCondExpansions[n]->GetExp(e)->GetTotPoints();
3069 auto id2 = m_trace->GetPhys_Offset(
3070 m_traceMap->GetBndCondIDToGlobalTraceID(cnt + e));
3071 Vmath::Vcopy(npts, &Fwd[id2], 1, &Bwd[id2], 1);
3072 }
3073 cnt += ne;
3074 }
3075 else if (bndConditions[n]->GetBoundaryConditionType() !=
3077 {
3078 ASSERTL0(false, "Method not set up for this "
3079 "boundary condition.");
3080 }
3081 }
3082 }
3083 else
3084 {
3085 for (int n = 0, cnt = 0; n < bndCondExpansions.size(); ++n)
3086 {
3087 if (bndConditions[n]->GetBoundaryConditionType() ==
3089 {
3090 auto ne = bndCondExpansions[n]->GetExpSize();
3091 for (int e = 0; e < ne; ++e)
3092 {
3093 auto npts = bndCondExpansions[n]->GetExp(e)->GetTotPoints();
3094 auto id1 = bndCondExpansions[n]->GetPhys_Offset(e);
3095 auto id2 = m_trace->GetPhys_Offset(
3096 m_traceMap->GetBndCondIDToGlobalTraceID(cnt + e));
3097
3098 Vmath::Vcopy(npts, &(bndCondExpansions[n]->GetPhys())[id1],
3099 1, &Bwd[id2], 1);
3100 }
3101 cnt += ne;
3102 }
3103 else if (bndConditions[n]->GetBoundaryConditionType() ==
3105 bndConditions[n]->GetBoundaryConditionType() ==
3107 {
3108 auto ne = m_bndCondExpansions[n]->GetExpSize();
3109 for (int e = 0; e < ne; ++e)
3110 {
3111 auto npts = bndCondExpansions[n]->GetExp(e)->GetTotPoints();
3112 auto id1 = bndCondExpansions[n]->GetPhys_Offset(e);
3113
3114 ASSERTL0((bndCondExpansions[n]->GetPhys())[id1] == 0.0,
3115 "Method not set up for non-zero "
3116 "Neumann boundary condition");
3117 auto id2 = m_trace->GetPhys_Offset(
3118 m_traceMap->GetBndCondIDToGlobalTraceID(cnt + e));
3119
3120 Vmath::Vcopy(npts, &Fwd[id2], 1, &Bwd[id2], 1);
3121 }
3122
3123 cnt += ne;
3124 }
3125 else if (bndConditions[n]->GetBoundaryConditionType() ==
3127 {
3128 // Do nothing
3129 }
3130 else if (bndConditions[n]->GetBoundaryConditionType() !=
3132 {
3134 "Method not set up for this boundary "
3135 "condition.");
3136 }
3137 }
3138 }
3139}
3140
3142{
3143 return m_bndCondBndWeight;
3144}
3145
3146void DisContField::v_SetBndCondBwdWeight(const int index, const NekDouble value)
3147{
3148 m_bndCondBndWeight[index] = value;
3149}
3150
3154{
3155 // Basis definition on each element
3156 LibUtilities::BasisSharedPtr basis = (*m_exp)[0]->GetBasis(0);
3157 if (basis->GetBasisType() != LibUtilities::eGauss_Lagrange)
3158 {
3159 Array<OneD, NekDouble> tracevals(
3160 m_locTraceToTraceMap->GetNLocTracePts(), 0.0);
3161
3162 Array<OneD, NekDouble> invals =
3163 tracevals + m_locTraceToTraceMap->GetNFwdLocTracePts();
3164
3165 m_locTraceToTraceMap->InterpTraceToLocTrace(1, Bwd, invals);
3166
3167 m_locTraceToTraceMap->InterpTraceToLocTrace(0, Fwd, tracevals);
3168
3169 m_locTraceToTraceMap->AddLocTracesToField(tracevals, field);
3170 }
3171 else
3172 {
3174 "v_AddTraceQuadPhysToField not coded for eGauss_Lagrange");
3175 }
3176}
3177
3179{
3180 ASSERTL1(m_physState == true, "local physical space is not true ");
3181 v_ExtractTracePhys(m_phys, outarray);
3182}
3183/**
3184 * @brief This method extracts the trace (verts in 1D) from
3185 * the field @a inarray and puts the values in @a outarray.
3186 *
3187 * It assumes the field is C0 continuous so that it can
3188 * overwrite the edge data when visited by the two adjacent
3189 * elements.
3190 *
3191 * @param inarray An array containing the 1D data from which we wish
3192 * to extract the edge data.
3193 * @param outarray The resulting edge information.
3194 *
3195 * This will not work for non-boundary expansions
3196 */
3198 const Array<OneD, const NekDouble> &inarray,
3199 Array<OneD, NekDouble> &outarray)
3200{
3201 LibUtilities::BasisSharedPtr basis = (*m_exp)[0]->GetBasis(0);
3202 if ((basis->GetBasisType() != LibUtilities::eGauss_Lagrange))
3203 {
3204 Vmath::Zero(outarray.size(), outarray, 1);
3205 Array<OneD, NekDouble> tracevals(
3206 m_locTraceToTraceMap->GetNFwdLocTracePts());
3207 m_locTraceToTraceMap->FwdLocTracesFromField(inarray, tracevals);
3208 m_locTraceToTraceMap->InterpLocTracesToTrace(0, tracevals, outarray);
3209 m_traceMap->GetAssemblyCommDG()->PerformExchange(outarray, outarray);
3210 }
3211 else
3212 {
3213 // Loop over elemente and collect forward expansion
3214 int nexp = GetExpSize();
3215 int n, p, offset, phys_offset;
3218 &elmtToTrace = m_traceMap->GetElmtToTrace();
3219 ASSERTL1(outarray.size() >= m_trace->GetNpoints(),
3220 "input array is of insufficient length");
3221 for (n = 0; n < nexp; ++n)
3222 {
3223 phys_offset = GetPhys_Offset(n);
3224 for (p = 0; p < (*m_exp)[n]->GetNtraces(); ++p)
3225 {
3226 offset =
3227 m_trace->GetPhys_Offset(elmtToTrace[n][p]->GetElmtId());
3228 (*m_exp)[n]->GetTracePhysVals(p, elmtToTrace[n][p],
3229 inarray + phys_offset,
3230 t_tmp = outarray + offset);
3231 }
3232 }
3233 }
3234}
3235/**
3236 * @brief Add trace contributions into elemental coefficient spaces.
3237 *
3238 * Given some quantity \f$ \vec{Fn} \f$, which conatins this
3239 * routine calculates the integral
3240 *
3241 * \f[
3242 * \int_{\Omega^e} \vec{Fn}, \mathrm{d}S
3243 * \f]
3244 *
3245 * and adds this to the coefficient space provided by outarray.
3246 *
3247 * @see Expansion3D::AddFaceNormBoundaryInt
3248 *
3249 * @param Fn The trace quantities.
3250 * @param outarray Resulting 3D coefficient space.
3251 *
3252 */
3254 Array<OneD, NekDouble> &outarray)
3255{
3256 if (m_expType == e1D)
3257 {
3258 int n, offset, t_offset;
3260 &elmtToTrace = m_traceMap->GetElmtToTrace();
3261 vector<bool> negatedFluxNormal = GetNegatedFluxNormal();
3262 for (n = 0; n < GetExpSize(); ++n)
3263 {
3264 // Number of coefficients on each element
3265 int e_ncoeffs = (*m_exp)[n]->GetNcoeffs();
3266 offset = GetCoeff_Offset(n);
3267 // Implementation for every points except Gauss points
3268 if ((*m_exp)[n]->GetBasis(0)->GetBasisType() !=
3270 {
3271 t_offset =
3272 GetTrace()->GetCoeff_Offset(elmtToTrace[n][0]->GetElmtId());
3273 if (negatedFluxNormal[2 * n])
3274 {
3275 outarray[offset] -= Fn[t_offset];
3276 }
3277 else
3278 {
3279 outarray[offset] += Fn[t_offset];
3280 }
3281 t_offset =
3282 GetTrace()->GetCoeff_Offset(elmtToTrace[n][1]->GetElmtId());
3283 if (negatedFluxNormal[2 * n + 1])
3284 {
3285 outarray[offset + (*m_exp)[n]->GetVertexMap(1)] -=
3286 Fn[t_offset];
3287 }
3288 else
3289 {
3290 outarray[offset + (*m_exp)[n]->GetVertexMap(1)] +=
3291 Fn[t_offset];
3292 }
3293 }
3294 else
3295 {
3296 int j;
3297 static DNekMatSharedPtr m_Ixm, m_Ixp;
3298 static int sav_ncoeffs = 0;
3299 if (!m_Ixm.get() || e_ncoeffs != sav_ncoeffs)
3300 {
3302 const LibUtilities::PointsKey BS_p(
3304 const LibUtilities::BasisKey BS_k(
3305 LibUtilities::eGauss_Lagrange, e_ncoeffs, BS_p);
3306 BASE = LibUtilities::BasisManager()[BS_k];
3307 Array<OneD, NekDouble> coords(1, 0.0);
3308 coords[0] = -1.0;
3309 m_Ixm = BASE->GetI(coords);
3310 coords[0] = 1.0;
3311 m_Ixp = BASE->GetI(coords);
3312 sav_ncoeffs = e_ncoeffs;
3313 }
3314 t_offset =
3315 GetTrace()->GetCoeff_Offset(elmtToTrace[n][0]->GetElmtId());
3316 if (negatedFluxNormal[2 * n])
3317 {
3318 for (j = 0; j < e_ncoeffs; j++)
3319 {
3320 outarray[offset + j] -=
3321 (m_Ixm->GetPtr())[j] * Fn[t_offset];
3322 }
3323 }
3324 else
3325 {
3326 for (j = 0; j < e_ncoeffs; j++)
3327 {
3328 outarray[offset + j] +=
3329 (m_Ixm->GetPtr())[j] * Fn[t_offset];
3330 }
3331 }
3332 t_offset =
3333 GetTrace()->GetCoeff_Offset(elmtToTrace[n][1]->GetElmtId());
3334 if (negatedFluxNormal[2 * n + 1])
3335 {
3336 for (j = 0; j < e_ncoeffs; j++)
3337 {
3338 outarray[offset + j] -=
3339 (m_Ixp->GetPtr())[j] * Fn[t_offset];
3340 }
3341 }
3342 else
3343 {
3344 for (j = 0; j < e_ncoeffs; j++)
3345 {
3346 outarray[offset + j] +=
3347 (m_Ixp->GetPtr())[j] * Fn[t_offset];
3348 }
3349 }
3350 }
3351 }
3352 }
3353 else // other dimensions
3354 {
3355 // Basis definition on each element
3356 LibUtilities::BasisSharedPtr basis = (*m_exp)[0]->GetBasis(0);
3357 if ((m_expType != e1D) &&
3358 (basis->GetBasisType() != LibUtilities::eGauss_Lagrange))
3359 {
3360 Array<OneD, NekDouble> Fcoeffs(m_trace->GetNcoeffs());
3361 m_trace->IProductWRTBase(Fn, Fcoeffs);
3362 m_locTraceToTraceMap->AddTraceCoeffsToFieldCoeffs(Fcoeffs,
3363 outarray);
3364 }
3365 else
3366 {
3367 int e, n, offset, t_offset;
3368 Array<OneD, NekDouble> e_outarray;
3370 &elmtToTrace = m_traceMap->GetElmtToTrace();
3371 if (m_expType == e2D)
3372 {
3373 for (n = 0; n < GetExpSize(); ++n)
3374 {
3375 offset = GetCoeff_Offset(n);
3376 for (e = 0; e < (*m_exp)[n]->GetNtraces(); ++e)
3377 {
3378 t_offset = GetTrace()->GetPhys_Offset(
3379 elmtToTrace[n][e]->GetElmtId());
3380 (*m_exp)[n]->AddEdgeNormBoundaryInt(
3381 e, elmtToTrace[n][e], Fn + t_offset,
3382 e_outarray = outarray + offset);
3383 }
3384 }
3385 }
3386 else if (m_expType == e3D)
3387 {
3388 for (n = 0; n < GetExpSize(); ++n)
3389 {
3390 offset = GetCoeff_Offset(n);
3391 for (e = 0; e < (*m_exp)[n]->GetNtraces(); ++e)
3392 {
3393 t_offset = GetTrace()->GetPhys_Offset(
3394 elmtToTrace[n][e]->GetElmtId());
3395 (*m_exp)[n]->AddFaceNormBoundaryInt(
3396 e, elmtToTrace[n][e], Fn + t_offset,
3397 e_outarray = outarray + offset);
3398 }
3399 }
3400 }
3401 }
3402 }
3403}
3404/**
3405 * @brief Add trace contributions into elemental coefficient spaces.
3406 *
3407 * Given some quantity \f$ \vec{q} \f$, calculate the elemental integral
3408 *
3409 * \f[
3410 * \int_{\Omega^e} \vec{q}, \mathrm{d}S
3411 * \f]
3412 *
3413 * and adds this to the coefficient space provided by
3414 * outarray. The value of q is determined from the routine
3415 * IsLeftAdjacentTrace() which if true we use Fwd else we use
3416 * Bwd
3417 *
3418 * @param Fwd The trace quantities associated with left (fwd)
3419 * adjancent elmt.
3420 * @param Bwd The trace quantities associated with right (bwd)
3421 * adjacent elet.
3422 * @param outarray Resulting coefficient space.
3423 */
3427{
3428 ASSERTL0(m_expType != e1D, "This method is not setup or "
3429 "tested for 1D expansion");
3430 Array<OneD, NekDouble> Coeffs(m_trace->GetNcoeffs());
3431 m_trace->IProductWRTBase(Fwd, Coeffs);
3432 m_locTraceToTraceMap->AddTraceCoeffsToFieldCoeffs(0, Coeffs, outarray);
3433 m_trace->IProductWRTBase(Bwd, Coeffs);
3434 m_locTraceToTraceMap->AddTraceCoeffsToFieldCoeffs(1, Coeffs, outarray);
3435}
3436
3438 const Array<OneD, const NekDouble> &inarray,
3440 const StdRegions::VarCoeffMap &varcoeff,
3441 [[maybe_unused]] const MultiRegions::VarFactorsMap &varfactors,
3442 [[maybe_unused]] const Array<OneD, const NekDouble> &dirForcing,
3443 const bool PhysSpaceForcing)
3444{
3445 int i, n, cnt, nbndry;
3446 int nexp = GetExpSize();
3448 DNekVec F(m_ncoeffs, f, eWrapper);
3449 Array<OneD, NekDouble> e_f, e_l;
3450 //----------------------------------
3451 // Setup RHS Inner product if required
3452 //----------------------------------
3453 if (PhysSpaceForcing)
3454 {
3455 IProductWRTBase(inarray, f);
3456 Vmath::Neg(m_ncoeffs, f, 1);
3457 }
3458 else
3459 {
3460 Vmath::Smul(m_ncoeffs, -1.0, inarray, 1, f, 1);
3461 }
3462 //----------------------------------
3463 // Solve continuous Boundary System
3464 //----------------------------------
3465 int GloBndDofs = m_traceMap->GetNumGlobalBndCoeffs();
3466 int NumDirBCs = m_traceMap->GetNumLocalDirBndCoeffs();
3467 int e_ncoeffs;
3470 const DNekScalBlkMatSharedPtr &HDGLamToU = GetBlockMatrix(HDGLamToUKey);
3471 // Retrieve number of local trace space coefficients N_{\lambda},
3472 // and set up local elemental trace solution \lambda^e.
3473 int LocBndCoeffs = m_traceMap->GetNumLocalBndCoeffs();
3474 Array<OneD, NekDouble> bndrhs(LocBndCoeffs, 0.0);
3475 Array<OneD, NekDouble> loclambda(LocBndCoeffs, 0.0);
3476 DNekVec LocLambda(LocBndCoeffs, loclambda, eWrapper);
3477 //----------------------------------
3478 // Evaluate Trace Forcing
3479 // Kirby et al, 2010, P23, Step 5.
3480 //----------------------------------
3481 // Determing <u_lam,f> terms using HDGLamToU matrix
3482 for (cnt = n = 0; n < nexp; ++n)
3483 {
3484 nbndry = (*m_exp)[n]->NumDGBndryCoeffs();
3485 e_ncoeffs = (*m_exp)[n]->GetNcoeffs();
3486 e_f = f + m_coeff_offset[n];
3487 e_l = bndrhs + cnt;
3488 // use outarray as tmp space
3489 DNekVec Floc(nbndry, e_l, eWrapper);
3490 DNekVec ElmtFce(e_ncoeffs, e_f, eWrapper);
3491 Floc = Transpose(*(HDGLamToU->GetBlock(n, n))) * ElmtFce;
3492 cnt += nbndry;
3493 }
3494 Array<OneD, const int> bndCondMap =
3495 m_traceMap->GetBndCondCoeffsToLocalTraceMap();
3496 Array<OneD, const NekDouble> Sign = m_traceMap->GetLocalToGlobalBndSign();
3497 // Copy Dirichlet boundary conditions and weak forcing
3498 // into trace space
3499 int locid;
3500 cnt = 0;
3501 for (i = 0; i < m_bndCondExpansions.size(); ++i)
3502 {
3503 const Array<OneD, const NekDouble> bndcoeffs =
3504 m_bndCondExpansions[i]->GetCoeffs();
3505
3506 if (m_bndConditions[i]->GetBoundaryConditionType() ==
3508 {
3509 for (int j = 0; j < (m_bndCondExpansions[i])->GetNcoeffs(); ++j)
3510 {
3511 locid = bndCondMap[cnt + j];
3512 loclambda[locid] = Sign[locid] * bndcoeffs[j];
3513 }
3514 }
3515 else if (m_bndConditions[i]->GetBoundaryConditionType() ==
3517 m_bndConditions[i]->GetBoundaryConditionType() ==
3519 {
3520 // Add weak boundary condition to trace forcing
3521 for (int j = 0; j < (m_bndCondExpansions[i])->GetNcoeffs(); ++j)
3522 {
3523 locid = bndCondMap[cnt + j];
3524 bndrhs[locid] += Sign[locid] * bndcoeffs[j];
3525 }
3526 }
3527 else if (m_bndConditions[i]->GetBoundaryConditionType() ==
3529 {
3530 // skip increment of cnt if ePeriodic
3531 // because bndCondMap does not include ePeriodic
3532 continue;
3533 }
3534 cnt += (m_bndCondExpansions[i])->GetNcoeffs();
3535 }
3536
3537 //----------------------------------
3538 // Solve trace problem: \Lambda = K^{-1} F
3539 // K is the HybridDGHelmBndLam matrix.
3540 //----------------------------------
3541 if (GloBndDofs - NumDirBCs > 0)
3542 {
3544 factors, varcoeff);
3546 LinSys->Solve(bndrhs, loclambda, m_traceMap);
3547
3548 // For consistency with previous version put global
3549 // solution into m_trace->m_coeffs
3550 m_traceMap->LocalToGlobal(loclambda, m_trace->UpdateCoeffs());
3551 }
3552
3553 //----------------------------------
3554 // Internal element solves
3555 //----------------------------------
3558
3559 const DNekScalBlkMatSharedPtr &InvHDGHelm = GetBlockMatrix(invHDGhelmkey);
3560 DNekVec out(m_ncoeffs, outarray, eWrapper);
3561 Vmath::Zero(m_ncoeffs, outarray, 1);
3562
3563 // out = u_f + u_lam = (*InvHDGHelm)*f + (LamtoU)*Lam
3564 out = (*InvHDGHelm) * F + (*HDGLamToU) * LocLambda;
3565
3566 // Return empty GlobalLinSysKey
3567 return NullGlobalLinSysKey;
3568}
3569
3570/* \brief This function evaluates the boundary conditions at a certain
3571 * time-level.
3572 *
3573 * Based on the boundary condition \f$g(\boldsymbol{x},t)\f$ evaluated
3574 * at a given time-level \a t, this function transforms the boundary
3575 * conditions onto the coefficients of the (one-dimensional) boundary
3576 * expansion. Depending on the type of boundary conditions, these
3577 * expansion coefficients are calculated in different ways:
3578 * - <b>Dirichlet boundary conditions</b><BR>
3579 * In order to ensure global \f$C^0\f$ continuity of the spectral/hp
3580 * approximation, the Dirichlet boundary conditions are projected onto
3581 * the boundary expansion by means of a modified \f$C^0\f$ continuous
3582 * Galerkin projection. This projection can be viewed as a collocation
3583 * projection at the vertices, followed by an \f$L^2\f$ projection on
3584 * the interior modes of the edges. The resulting coefficients
3585 * \f$\boldsymbol{\hat{u}}^{\mathcal{D}}\f$ will be stored for the
3586 * boundary expansion.
3587 * - <b>Neumann boundary conditions</b>
3588 * In the discrete Galerkin formulation of the problem to be solved,
3589 * the Neumann boundary conditions appear as the set of surface
3590 * integrals: \f[\boldsymbol{\hat{g}}=\int_{\Gamma}
3591 * \phi^e_n(\boldsymbol{x})g(\boldsymbol{x})d(\boldsymbol{x})\quad
3592 * \forall n \f]
3593 * As a result, it are the coefficients \f$\boldsymbol{\hat{g}}\f$
3594 * that will be stored in the boundary expansion
3595 *
3596 * @param time The time at which the boundary conditions
3597 * should be evaluated.
3598 * @param bndCondExpansions List of boundary conditions.
3599 * @param bndConditions Information about the boundary conditions.
3600 *
3601 * This will only be undertaken for time dependent
3602 * boundary conditions unless time == 0.0 which is the
3603 * case when the method is called from the constructor.
3604 */
3606 const std::string varName,
3607 const NekDouble x2_in,
3608 const NekDouble x3_in)
3609{
3610 int i;
3611 int npoints;
3612
3614
3615 for (i = 0; i < m_bndCondExpansions.size(); ++i)
3616 {
3617 if (time == 0.0 || m_bndConditions[i]->IsTimeDependent())
3618 {
3619 m_bndCondBndWeight[i] = 1.0;
3620 locExpList = m_bndCondExpansions[i];
3621
3622 npoints = locExpList->GetNpoints();
3623 Array<OneD, NekDouble> x0(npoints, 0.0);
3624 Array<OneD, NekDouble> x1(npoints, 0.0);
3625 Array<OneD, NekDouble> x2(npoints, 0.0);
3626
3627 locExpList->GetCoords(x0, x1, x2);
3628
3629 if (x2_in != NekConstants::kNekUnsetDouble &&
3631 {
3632 Vmath::Fill(npoints, x2_in, x1, 1);
3633 Vmath::Fill(npoints, x3_in, x2, 1);
3634 }
3635 else if (x2_in != NekConstants::kNekUnsetDouble)
3636 {
3637 Vmath::Fill(npoints, x2_in, x2, 1);
3638 }
3639
3640 // treat 1D expansions separately since we only
3641 // require an evaluation at a point rather than
3642 // any projections or inner products that are not
3643 // available in a PointExp
3644 if (m_expType == e1D)
3645 {
3646 if (m_bndConditions[i]->GetBoundaryConditionType() ==
3648 {
3649
3650 m_bndCondExpansions[i]->SetCoeff(
3651 0, (std::static_pointer_cast<
3653 m_bndConditions[i])
3654 ->m_dirichletCondition)
3655 .Evaluate(x0[0], x1[0], x2[0], time));
3656 m_bndCondExpansions[i]->SetPhys(
3657 0, m_bndCondExpansions[i]->GetCoeff(0));
3658 }
3659 else if (m_bndConditions[i]->GetBoundaryConditionType() ==
3661 {
3662 m_bndCondExpansions[i]->SetCoeff(
3663 0, (std::static_pointer_cast<
3665 m_bndConditions[i])
3666 ->m_neumannCondition)
3667 .Evaluate(x0[0], x1[0], x2[0], time));
3668 }
3669 else if (m_bndConditions[i]->GetBoundaryConditionType() ==
3671 {
3672 m_bndCondExpansions[i]->SetCoeff(
3673 0, (std::static_pointer_cast<
3675 m_bndConditions[i])
3676 ->m_robinFunction)
3677 .Evaluate(x0[0], x1[0], x2[0], time));
3678 }
3679 else if (m_bndConditions[i]->GetBoundaryConditionType() ==
3681 {
3682 continue;
3683 }
3684 else if (m_bndConditions[i]->GetBoundaryConditionType() ==
3686 {
3687 }
3688 else
3689 {
3691 "This type of BC not implemented yet");
3692 }
3693 }
3694 else // 2D and 3D versions
3695 {
3696 if (m_bndConditions[i]->GetBoundaryConditionType() ==
3698 {
3700 std::static_pointer_cast<
3702 m_bndConditions[i]);
3703
3704 Array<OneD, NekDouble> valuesFile(npoints, 1.0),
3705 valuesExp(npoints, 1.0);
3706
3707 string bcfilename = bcPtr->m_filename;
3708 string exprbcs = bcPtr->m_expr;
3709
3710 if (bcfilename != "")
3711 {
3712 locExpList->ExtractCoeffsFromFile(
3713 bcfilename, bcPtr->GetComm(), varName,
3714 locExpList->UpdateCoeffs());
3715 locExpList->BwdTrans(locExpList->GetCoeffs(),
3716 locExpList->UpdatePhys());
3717 valuesFile = locExpList->GetPhys();
3718 }
3719
3720 if (exprbcs != "")
3721 {
3722 LibUtilities::Equation condition =
3723 std::static_pointer_cast<
3725 m_bndConditions[i])
3726 ->m_dirichletCondition;
3727
3728 condition.Evaluate(x0, x1, x2, time, valuesExp);
3729 }
3730
3731 Vmath::Vmul(npoints, valuesExp, 1, valuesFile, 1,
3732 locExpList->UpdatePhys(), 1);
3733 locExpList->FwdTransBndConstrained(
3734 locExpList->GetPhys(), locExpList->UpdateCoeffs());
3735 }
3736 else if (m_bndConditions[i]->GetBoundaryConditionType() ==
3738 {
3740 std::static_pointer_cast<
3742 m_bndConditions[i]);
3743 string bcfilename = bcPtr->m_filename;
3744
3745 if (bcfilename != "")
3746 {
3747 locExpList->ExtractCoeffsFromFile(
3748 bcfilename, bcPtr->GetComm(), varName,
3749 locExpList->UpdateCoeffs());
3750 locExpList->BwdTrans(locExpList->GetCoeffs(),
3751 locExpList->UpdatePhys());
3752 }
3753 else
3754 {
3755 LibUtilities::Equation condition =
3756 std::static_pointer_cast<
3758 m_bndConditions[i])
3759 ->m_neumannCondition;
3760 condition.Evaluate(x0, x1, x2, time,
3761 locExpList->UpdatePhys());
3762 }
3763
3764 locExpList->IProductWRTBase(locExpList->GetPhys(),
3765 locExpList->UpdateCoeffs());
3766 }
3767 else if (m_bndConditions[i]->GetBoundaryConditionType() ==
3769 {
3771 std::static_pointer_cast<
3773 m_bndConditions[i]);
3774
3775 string bcfilename = bcPtr->m_filename;
3776
3777 if (bcfilename != "")
3778 {
3779 locExpList->ExtractCoeffsFromFile(
3780 bcfilename, bcPtr->GetComm(), varName,
3781 locExpList->UpdateCoeffs());
3782 locExpList->BwdTrans(locExpList->GetCoeffs(),
3783 locExpList->UpdatePhys());
3784 }
3785 else
3786 {
3787 LibUtilities::Equation condition =
3788 std::static_pointer_cast<
3790 m_bndConditions[i])
3791 ->m_robinFunction;
3792 condition.Evaluate(x0, x1, x2, time,
3793 locExpList->UpdatePhys());
3794 }
3795
3796 locExpList->IProductWRTBase(locExpList->GetPhys(),
3797 locExpList->UpdateCoeffs());
3798 }
3799 else if (m_bndConditions[i]->GetBoundaryConditionType() ==
3801 {
3802 continue;
3803 }
3804 else
3805 {
3807 "This type of BC not implemented yet");
3808 }
3809 }
3810 }
3811 }
3812}
3813
3814/**
3815 * @brief Fill the weight with m_bndCondBndWeight.
3816 */
3818 Array<OneD, NekDouble> &weightjmp)
3819{
3820 int cnt;
3821 int npts;
3822 int e = 0;
3823
3824 // Fill boundary conditions into missing elements
3825 int id2 = 0;
3826
3827 for (int n = cnt = 0; n < m_bndCondExpansions.size(); ++n)
3828 {
3829
3830 if (m_bndConditions[n]->GetBoundaryConditionType() ==
3832 {
3833 for (e = 0; e < m_bndCondExpansions[n]->GetExpSize(); ++e)
3834 {
3835 npts = m_bndCondExpansions[n]->GetExp(e)->GetTotPoints();
3836 id2 = m_trace->GetPhys_Offset(
3837 m_traceMap->GetBndCondIDToGlobalTraceID(cnt + e));
3838 Vmath::Fill(npts, m_bndCondBndWeight[n], &weightave[id2], 1);
3839 Vmath::Fill(npts, 0.0, &weightjmp[id2], 1);
3840 }
3841
3842 cnt += e;
3843 }
3844 else if (m_bndConditions[n]->GetBoundaryConditionType() ==
3846 m_bndConditions[n]->GetBoundaryConditionType() ==
3848 {
3849 for (e = 0; e < m_bndCondExpansions[n]->GetExpSize(); ++e)
3850 {
3851 npts = m_bndCondExpansions[n]->GetExp(e)->GetTotPoints();
3852 id2 = m_trace->GetPhys_Offset(
3853 m_traceMap->GetBndCondIDToGlobalTraceID(cnt + e));
3854 Vmath::Fill(npts, m_bndCondBndWeight[n], &weightave[id2], 1);
3855 Vmath::Fill(npts, 0.0, &weightjmp[id2], 1);
3856 }
3857
3858 cnt += e;
3859 }
3860 else if (m_bndConditions[n]->GetBoundaryConditionType() !=
3862 {
3864 "Method not set up for this boundary condition.");
3865 }
3866 }
3867}
3868
3869// Set up a list of element ids and trace ids that link to the
3870// boundary conditions
3872 Array<OneD, int> &TraceID)
3873{
3874
3875 if (m_BCtoElmMap.size() == 0)
3876 {
3877 switch (m_expType)
3878 {
3879 case e1D:
3880 {
3881 map<int, int> VertGID;
3882 int i, n, id;
3883 int bid, cnt, Vid;
3884 int nbcs = 0;
3885 // Determine number of boundary condition expansions.
3886 for (i = 0; i < m_bndConditions.size(); ++i)
3887 {
3888 nbcs += m_bndCondExpansions[i]->GetExpSize();
3889 }
3890
3891 // make sure arrays are of sufficient length
3892 m_BCtoElmMap = Array<OneD, int>(nbcs, -1);
3894
3895 // setup map of all global ids along boundary
3896 cnt = 0;
3897 for (n = 0; n < m_bndCondExpansions.size(); ++n)
3898 {
3899 for (i = 0; i < m_bndCondExpansions[n]->GetExpSize(); ++i)
3900 {
3901 Vid = m_bndCondExpansions[n]
3902 ->GetExp(i)
3903 ->GetGeom()
3904 ->GetVertex(0)
3905 ->GetVid();
3906 VertGID[Vid] = cnt++;
3907 }
3908 }
3909
3910 // Loop over elements and find verts that match;
3912 for (cnt = n = 0; n < GetExpSize(); ++n)
3913 {
3914 exp = (*m_exp)[n];
3915 for (i = 0; i < exp->GetNverts(); ++i)
3916 {
3917 id = exp->GetGeom()->GetVid(i);
3918
3919 if (VertGID.count(id) > 0)
3920 {
3921 bid = VertGID.find(id)->second;
3922 ASSERTL1(m_BCtoElmMap[bid] == -1,
3923 "Edge already set");
3924 m_BCtoElmMap[bid] = n;
3925 m_BCtoTraceMap[bid] = i;
3926 cnt++;
3927 }
3928 }
3929 }
3930 ASSERTL1(cnt == nbcs,
3931 "Failed to visit all boundary condtiions");
3932 }
3933 break;
3934 case e2D:
3935 {
3936 map<int, int> globalIdMap;
3937 int i, n;
3938 int cnt;
3939 int nbcs = 0;
3940
3941 // Populate global ID map (takes global geometry
3942 // ID to local expansion list ID).
3943 for (i = 0; i < GetExpSize(); ++i)
3944 {
3945 globalIdMap[(*m_exp)[i]->GetGeom()->GetGlobalID()] = i;
3946 }
3947
3948 // Determine number of boundary condition expansions.
3949 for (i = 0; i < m_bndConditions.size(); ++i)
3950 {
3951 nbcs += m_bndCondExpansions[i]->GetExpSize();
3952 }
3953
3954 // Initialize arrays
3957
3959 cnt = 0;
3960 for (n = 0; n < m_bndCondExpansions.size(); ++n)
3961 {
3962 for (i = 0; i < m_bndCondExpansions[n]->GetExpSize();
3963 ++i, ++cnt)
3964 {
3965 exp1d = m_bndCondExpansions[n]
3966 ->GetExp(i)
3968
3969 // Use edge to element map from MeshGraph.
3971 m_graph->GetElementsFromEdge(exp1d->GetGeom1D());
3972
3973 m_BCtoElmMap[cnt] =
3974 globalIdMap[(*tmp)[0].first->GetGlobalID()];
3975 m_BCtoTraceMap[cnt] = (*tmp)[0].second;
3976 }
3977 }
3978 }
3979 break;
3980 case e3D:
3981 {
3982 map<int, int> globalIdMap;
3983 int i, n;
3984 int cnt;
3985 int nbcs = 0;
3986
3987 // Populate global ID map (takes global geometry ID to local
3988 // expansion list ID).
3990 for (i = 0; i < GetExpSize(); ++i)
3991 {
3992 globalIdMap[(*m_exp)[i]->GetGeom()->GetGlobalID()] = i;
3993 }
3994
3995 // Determine number of boundary condition expansions.
3996 for (i = 0; i < m_bndConditions.size(); ++i)
3997 {
3998 nbcs += m_bndCondExpansions[i]->GetExpSize();
3999 }
4000
4001 // Initialize arrays
4004
4006 for (cnt = n = 0; n < m_bndCondExpansions.size(); ++n)
4007 {
4008 for (i = 0; i < m_bndCondExpansions[n]->GetExpSize();
4009 ++i, ++cnt)
4010 {
4011 exp2d = m_bndCondExpansions[n]
4012 ->GetExp(i)
4014
4016 m_graph->GetElementsFromFace(exp2d->GetGeom2D());
4017 m_BCtoElmMap[cnt] =
4018 globalIdMap[tmp->at(0).first->GetGlobalID()];
4019 m_BCtoTraceMap[cnt] = tmp->at(0).second;
4020 }
4021 }
4022 }
4023 break;
4024 default:
4025 ASSERTL1(false, "Not setup for this expansion");
4026 break;
4027 }
4028 }
4029
4030 ElmtID = m_BCtoElmMap;
4031 TraceID = m_BCtoTraceMap;
4032}
4033
4035 std::shared_ptr<ExpList> &result,
4036 const bool DeclareCoeffPhysArrays)
4037{
4038 int n, cnt;
4039 std::vector<unsigned int> eIDs;
4040
4041 Array<OneD, int> ElmtID, TraceID;
4042 GetBoundaryToElmtMap(ElmtID, TraceID);
4043
4044 // Skip other boundary regions
4045 for (cnt = n = 0; n < i; ++n)
4046 {
4047 cnt += m_bndCondExpansions[n]->GetExpSize();
4048 }
4049
4050 // Populate eIDs with information from BoundaryToElmtMap
4051 for (n = 0; n < m_bndCondExpansions[i]->GetExpSize(); ++n)
4052 {
4053 eIDs.push_back(ElmtID[cnt + n]);
4054 }
4055
4056 // Create expansion list
4058 *this, eIDs, DeclareCoeffPhysArrays, Collections::eNoCollection);
4059 // Copy phys and coeffs to new explist
4060 if (DeclareCoeffPhysArrays)
4061 {
4062 int nq;
4063 int offsetOld, offsetNew;
4064 Array<OneD, NekDouble> tmp1, tmp2;
4065 for (n = 0; n < result->GetExpSize(); ++n)
4066 {
4067 nq = GetExp(ElmtID[cnt + n])->GetTotPoints();
4068 offsetOld = GetPhys_Offset(ElmtID[cnt + n]);
4069 offsetNew = result->GetPhys_Offset(n);
4070 Vmath::Vcopy(nq, tmp1 = GetPhys() + offsetOld, 1,
4071 tmp2 = result->UpdatePhys() + offsetNew, 1);
4072 nq = GetExp(ElmtID[cnt + n])->GetNcoeffs();
4073 offsetOld = GetCoeff_Offset(ElmtID[cnt + n]);
4074 offsetNew = result->GetCoeff_Offset(n);
4075 Vmath::Vcopy(nq, tmp1 = GetCoeffs() + offsetOld, 1,
4076 tmp2 = result->UpdateCoeffs() + offsetNew, 1);
4077 }
4078 }
4079}
4080
4081/**
4082 * @brief Reset this field, so that geometry information can be updated.
4083 */
4085{
4087
4088 // Reset boundary condition expansions.
4089 for (int n = 0; n < m_bndCondExpansions.size(); ++n)
4090 {
4091 m_bndCondExpansions[n]->Reset();
4092 }
4093}
4094
4095/**
4096 * Search through the edge expansions and identify which ones
4097 * have Robin/Mixed type boundary conditions. If find a Robin
4098 * boundary then store the edge id of the boundary condition
4099 * and the array of points of the physical space boundary
4100 * condition which are hold the boundary condition primitive
4101 * variable coefficient at the quatrature points
4102 *
4103 * \return std map containing the robin boundary condition
4104 * info using a key of the element id
4105 *
4106 * There is a next member to allow for more than one Robin
4107 * boundary condition per element
4108 */
4109map<int, RobinBCInfoSharedPtr> DisContField::v_GetRobinBCInfo(void)
4110{
4111 int i, cnt;
4112 map<int, RobinBCInfoSharedPtr> returnval;
4113 Array<OneD, int> ElmtID, TraceID;
4114 GetBoundaryToElmtMap(ElmtID, TraceID);
4115
4116 for (cnt = i = 0; i < m_bndCondExpansions.size(); ++i)
4117 {
4119
4120 if (m_bndConditions[i]->GetBoundaryConditionType() ==
4122 {
4123 int e, elmtid;
4124 Array<OneD, NekDouble> Array_tmp;
4125
4126 locExpList = m_bndCondExpansions[i];
4127
4128 int npoints = locExpList->GetNpoints();
4129 Array<OneD, NekDouble> x0(npoints, 0.0);
4130 Array<OneD, NekDouble> x1(npoints, 0.0);
4131 Array<OneD, NekDouble> x2(npoints, 0.0);
4132 Array<OneD, NekDouble> coeffphys(npoints);
4133
4134 locExpList->GetCoords(x0, x1, x2);
4135
4136 LibUtilities::Equation coeffeqn =
4137 std::static_pointer_cast<
4139 ->m_robinPrimitiveCoeff;
4140
4141 // evalaute coefficient
4142 coeffeqn.Evaluate(x0, x1, x2, 0.0, coeffphys);
4143
4144 for (e = 0; e < locExpList->GetExpSize(); ++e)
4145 {
4146 RobinBCInfoSharedPtr rInfo =
4148 TraceID[cnt + e],
4149 Array_tmp = coeffphys + locExpList->GetPhys_Offset(e));
4150
4151 elmtid = ElmtID[cnt + e];
4152 // make link list if necessary
4153 if (returnval.count(elmtid) != 0)
4154 {
4155 rInfo->next = returnval.find(elmtid)->second;
4156 }
4157 returnval[elmtid] = rInfo;
4158 }
4159 }
4160 cnt += m_bndCondExpansions[i]->GetExpSize();
4161 }
4162
4163 return returnval;
4164}
4165
4166/**
4167 * @brief Calculate the \f$ L^2 \f$ error of the \f$ Q_{\rm dir} \f$
4168 * derivative using the consistent DG evaluation of \f$ Q_{\rm dir} \f$.
4169 *
4170 * The solution provided is of the primative variation at the quadrature
4171 * points and the derivative is compared to the discrete derivative at
4172 * these points, which is likely to be undesirable unless using a much
4173 * higher number of quadrature points than the polynomial order used to
4174 * evaluate \f$ Q_{\rm dir} \f$.
4175 */
4177 const Array<OneD, const NekDouble> &coeffs,
4178 const Array<OneD, const NekDouble> &soln)
4179{
4180 int i, e, ncoeff_edge;
4182 Array<OneD, NekDouble> out_d(m_ncoeffs), out_tmp;
4183
4185 m_traceMap->GetElmtToTrace();
4186
4188
4189 int cnt;
4190 int LocBndCoeffs = m_traceMap->GetNumLocalBndCoeffs();
4191 Array<OneD, NekDouble> loc_lambda(LocBndCoeffs), edge_lambda;
4192 m_traceMap->GlobalToLocalBnd(m_trace->GetCoeffs(), loc_lambda);
4193
4194 edge_lambda = loc_lambda;
4195
4196 // Calculate Q using standard DG formulation.
4197 for (i = cnt = 0; i < GetExpSize(); ++i)
4198 {
4199 // Probably a better way of setting up lambda than this.
4200 // Note cannot use PutCoeffsInToElmts since lambda space
4201 // is mapped during the solve.
4202 int nEdges = (*m_exp)[i]->GetGeom()->GetNumEdges();
4203 Array<OneD, Array<OneD, NekDouble>> edgeCoeffs(nEdges);
4204
4205 for (e = 0; e < nEdges; ++e)
4206 {
4207 edgedir = (*m_exp)[i]->GetTraceOrient(e);
4208 ncoeff_edge = elmtToTrace[i][e]->GetNcoeffs();
4209 edgeCoeffs[e] = Array<OneD, NekDouble>(ncoeff_edge);
4210 Vmath::Vcopy(ncoeff_edge, edge_lambda, 1, edgeCoeffs[e], 1);
4211 elmtToTrace[i][e]->SetCoeffsToOrientation(edgedir, edgeCoeffs[e],
4212 edgeCoeffs[e]);
4213 edge_lambda = edge_lambda + ncoeff_edge;
4214 }
4215
4216 (*m_exp)[i]->DGDeriv(dir, tmp_coeffs = coeffs + m_coeff_offset[i],
4217 elmtToTrace[i], edgeCoeffs, out_tmp = out_d + cnt);
4218 cnt += (*m_exp)[i]->GetNcoeffs();
4219 }
4220
4222 BwdTrans(out_d, phys);
4223 Vmath::Vsub(m_npoints, phys, 1, soln, 1, phys, 1);
4224 return L2(phys);
4225}
4226
4227/**
4228 * @brief Evaluate HDG post-processing to increase polynomial order of
4229 * solution.
4230 *
4231 * This function takes the solution (assumed to be one order lower) in
4232 * physical space, and postprocesses at the current polynomial order by
4233 * solving the system:
4234 *
4235 * \f[
4236 * \begin{aligned}
4237 * (\nabla w, \nabla u^*) &= (\nabla w, u), \\
4238 * \langle \nabla u^*, 1 \rangle &= \langle \nabla u, 1 \rangle
4239 * \end{aligned}
4240 * \f]
4241 *
4242 * where \f$ u \f$ corresponds with the current solution as stored
4243 * inside #m_coeffs.
4244 *
4245 * @param outarray The resulting field \f$ u^* \f$.
4246 */
4248 const Array<OneD, const NekDouble> &coeffs,
4249 Array<OneD, NekDouble> &outarray)
4250{
4251 int i, cnt, e, ncoeff_trace;
4252 Array<OneD, NekDouble> force, out_tmp, qrhs, qrhs1;
4254 m_traceMap->GetElmtToTrace();
4255
4257
4258 int nq_elmt, nm_elmt;
4259 int LocBndCoeffs = m_traceMap->GetNumLocalBndCoeffs();
4260 Array<OneD, NekDouble> loc_lambda(LocBndCoeffs), trace_lambda;
4261 Array<OneD, NekDouble> tmp_coeffs;
4262 m_traceMap->GlobalToLocalBnd(m_trace->GetCoeffs(), loc_lambda);
4263
4264 trace_lambda = loc_lambda;
4265
4266 int dim = (m_expType == e2D) ? 2 : 3;
4267
4268 int num_points[] = {0, 0, 0};
4269 int num_modes[] = {0, 0, 0};
4270
4271 // Calculate Q using standard DG formulation.
4272 for (i = cnt = 0; i < GetExpSize(); ++i)
4273 {
4274 nq_elmt = (*m_exp)[i]->GetTotPoints();
4275 nm_elmt = (*m_exp)[i]->GetNcoeffs();
4276 qrhs = Array<OneD, NekDouble>(nq_elmt);
4277 qrhs1 = Array<OneD, NekDouble>(nq_elmt);
4278 force = Array<OneD, NekDouble>(2 * nm_elmt);
4279 out_tmp = force + nm_elmt;
4281
4282 for (int j = 0; j < dim; ++j)
4283 {
4284 num_points[j] = (*m_exp)[i]->GetBasis(j)->GetNumPoints();
4285 num_modes[j] = (*m_exp)[i]->GetBasis(j)->GetNumModes();
4286 }
4287
4288 // Probably a better way of setting up lambda than this. Note
4289 // cannot use PutCoeffsInToElmts since lambda space is mapped
4290 // during the solve.
4291 int nTraces = (*m_exp)[i]->GetNtraces();
4292 Array<OneD, Array<OneD, NekDouble>> traceCoeffs(nTraces);
4293
4294 for (e = 0; e < (*m_exp)[i]->GetNtraces(); ++e)
4295 {
4296 edgedir = (*m_exp)[i]->GetTraceOrient(e);
4297 ncoeff_trace = elmtToTrace[i][e]->GetNcoeffs();
4298 traceCoeffs[e] = Array<OneD, NekDouble>(ncoeff_trace);
4299 Vmath::Vcopy(ncoeff_trace, trace_lambda, 1, traceCoeffs[e], 1);
4300 if (dim == 2)
4301 {
4302 elmtToTrace[i][e]->SetCoeffsToOrientation(
4303 edgedir, traceCoeffs[e], traceCoeffs[e]);
4304 }
4305 else
4306 {
4307 (*m_exp)[i]
4309 ->SetFaceToGeomOrientation(e, traceCoeffs[e]);
4310 }
4311 trace_lambda = trace_lambda + ncoeff_trace;
4312 }
4313
4314 // creating orthogonal expansion (checking if we have quads or
4315 // triangles)
4316 LibUtilities::ShapeType shape = (*m_exp)[i]->DetShapeType();
4317 switch (shape)
4318 {
4320 {
4321 const LibUtilities::PointsKey PkeyQ1(
4323 const LibUtilities::PointsKey PkeyQ2(
4326 num_modes[0], PkeyQ1);
4328 num_modes[1], PkeyQ2);
4330 std::dynamic_pointer_cast<SpatialDomains::QuadGeom>(
4331 (*m_exp)[i]->GetGeom());
4333 BkeyQ1, BkeyQ2, qGeom);
4334 }
4335 break;
4337 {
4338 const LibUtilities::PointsKey PkeyT1(
4340 const LibUtilities::PointsKey PkeyT2(
4341 num_points[1], LibUtilities::eGaussRadauMAlpha1Beta0);
4343 num_modes[0], PkeyT1);
4345 num_modes[1], PkeyT2);
4347 std::dynamic_pointer_cast<SpatialDomains::TriGeom>(
4348 (*m_exp)[i]->GetGeom());
4350 BkeyT1, BkeyT2, tGeom);
4351 }
4352 break;
4354 {
4355 const LibUtilities::PointsKey PkeyH1(
4357 const LibUtilities::PointsKey PkeyH2(
4359 const LibUtilities::PointsKey PkeyH3(
4362 num_modes[0], PkeyH1);
4364 num_modes[1], PkeyH2);
4366 num_modes[2], PkeyH3);
4368 std::dynamic_pointer_cast<SpatialDomains::HexGeom>(
4369 (*m_exp)[i]->GetGeom());
4371 BkeyH1, BkeyH2, BkeyH3, hGeom);
4372 }
4373 break;
4375 {
4376 const LibUtilities::PointsKey PkeyT1(
4378 const LibUtilities::PointsKey PkeyT2(
4379 num_points[1], LibUtilities::eGaussRadauMAlpha1Beta0);
4380 const LibUtilities::PointsKey PkeyT3(
4381 num_points[2], LibUtilities::eGaussRadauMAlpha2Beta0);
4383 num_modes[0], PkeyT1);
4385 num_modes[1], PkeyT2);
4387 num_modes[2], PkeyT3);
4389 std::dynamic_pointer_cast<SpatialDomains::TetGeom>(
4390 (*m_exp)[i]->GetGeom());
4392 BkeyT1, BkeyT2, BkeyT3, tGeom);
4393 }
4394 break;
4395 default:
4397 "Wrong shape type, HDG postprocessing is not "
4398 "implemented");
4399 };
4400
4401 // DGDeriv
4402 // (d/dx w, d/dx q_0)
4403 (*m_exp)[i]->DGDeriv(0, tmp_coeffs = coeffs + m_coeff_offset[i],
4404 elmtToTrace[i], traceCoeffs, out_tmp);
4405 (*m_exp)[i]->BwdTrans(out_tmp, qrhs);
4406 ppExp->IProductWRTDerivBase(0, qrhs, force);
4407
4408 // + (d/dy w, d/dy q_1)
4409 (*m_exp)[i]->DGDeriv(1, tmp_coeffs = coeffs + m_coeff_offset[i],
4410 elmtToTrace[i], traceCoeffs, out_tmp);
4411
4412 (*m_exp)[i]->BwdTrans(out_tmp, qrhs);
4413 ppExp->IProductWRTDerivBase(1, qrhs, out_tmp);
4414
4415 Vmath::Vadd(nm_elmt, force, 1, out_tmp, 1, force, 1);
4416
4417 // determine force[0] = (1,u)
4418 (*m_exp)[i]->BwdTrans(tmp_coeffs = coeffs + m_coeff_offset[i], qrhs);
4419 force[0] = (*m_exp)[i]->Integral(qrhs);
4420
4421 // multiply by inverse Laplacian matrix
4422 // get matrix inverse
4424 ppExp->DetShapeType(), *ppExp);
4425 DNekScalMatSharedPtr lapsys = ppExp->GetLocMatrix(lapkey);
4426
4427 NekVector<NekDouble> in(nm_elmt, force, eWrapper);
4428 NekVector<NekDouble> out(nm_elmt);
4429
4430 out = (*lapsys) * in;
4431
4432 // Transforming back to modified basis
4433 Array<OneD, NekDouble> work(nq_elmt);
4434 ppExp->BwdTrans(out.GetPtr(), work);
4435 (*m_exp)[i]->FwdTrans(work, tmp_coeffs = outarray + m_coeff_offset[i]);
4436 }
4437}
4438
4442 Array<OneD, NekDouble> &locTraceFwd, Array<OneD, NekDouble> &locTraceBwd)
4443{
4444 if (locTraceFwd != NullNekDouble1DArray)
4445 {
4446 m_locTraceToTraceMap->InterpLocTracesToTraceTranspose(0, Fwd,
4447 locTraceFwd);
4448 }
4449
4450 if (locTraceBwd != NullNekDouble1DArray)
4451 {
4452 m_locTraceToTraceMap->InterpLocTracesToTraceTranspose(1, Bwd,
4453 locTraceBwd);
4454 }
4455}
4456
4458 const Array<OneD, const NekDouble> &FwdFlux,
4459 const Array<OneD, const NekDouble> &BwdFlux,
4460 Array<OneD, NekDouble> &outarray)
4461{
4462 Array<OneD, NekDouble> FCoeffs(m_trace->GetNcoeffs());
4463
4464 m_trace->IProductWRTBase(FwdFlux, FCoeffs);
4465 m_locTraceToTraceMap->AddTraceCoeffsToFieldCoeffs(1, FCoeffs, outarray);
4466 m_trace->IProductWRTBase(BwdFlux, FCoeffs);
4467 m_locTraceToTraceMap->AddTraceCoeffsToFieldCoeffs(0, FCoeffs, outarray);
4468}
4469} // 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:338
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 & v_GetInterfaceMap(void) override
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:99
virtual void v_Reset()
Reset geometry information, metrics, matrix managers and geometry information.
Definition: ExpList.cpp:3332
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:2153
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:2652
void GetBoundaryToElmtMap(Array< OneD, int > &ElmtID, Array< OneD, int > &EdgeID)
Definition: ExpList.h:2261
static SpatialDomains::BoundaryConditionShPtr GetBoundaryCondition(const SpatialDomains::BoundaryConditionCollection &collection, unsigned int index, const std::string &variable)
Definition: ExpList.cpp:5752
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:1944
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:2030
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:2078
int GetExpSize(void)
This function returns the number of elements in the expansion.
Definition: ExpList.h:2038
Array< OneD, int > m_coeff_offset
Offset of elemental data into the array m_coeffs.
Definition: ExpList.h:1123
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:3089
NekDouble GetCoeff(int i)
Get the i th value (coefficient) of m_coeffs.
Definition: ExpList.h:2010
bool m_physState
The state of the array m_phys.
Definition: ExpList.h:1107
LibUtilities::CommSharedPtr m_comm
Communicator.
Definition: ExpList.h:1056
std::shared_ptr< LocalRegions::ExpansionVector > m_exp
The list of local expansions.
Definition: ExpList.h:1118
int m_ncoeffs
The total number of local degrees of freedom. m_ncoeffs .
Definition: ExpList.h:1063
void EvaluateBoundaryConditions(const NekDouble time=0.0, const std::string varName="", const NekDouble=NekConstants::kNekUnsetDouble, const NekDouble=NekConstants::kNekUnsetDouble)
Definition: ExpList.h:2250
const std::shared_ptr< LocalRegions::ExpansionVector > GetExp() const
This function returns the vector of elements in the expansion.
Definition: ExpList.h:2070
SpatialDomains::MeshGraphSharedPtr m_graph
Mesh associated with this expansion list.
Definition: ExpList.h:1060
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:514
void ApplyGeomInfo()
Apply geometry information to each expansion.
Definition: ExpList.cpp:3320
LibUtilities::SessionReaderSharedPtr m_session
Session.
Definition: ExpList.h:1058
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:2085
ExpansionType m_expType
Expansion type.
Definition: ExpList.h:1051
Array< OneD, NekDouble > m_phys
The global expansion evaluated at the quadrature points.
Definition: ExpList.h:1099
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< InterfaceMapDG > InterfaceMapDGSharedPtr
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:430
std::map< StdRegions::VarCoeffType, VarCoeffEntry > VarCoeffMap
Definition: StdRegions.hpp:375
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
STL namespace.
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.