Nektar++
Loading...
Searching...
No Matches
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
41#include <LocalRegions/HexExp.h>
43#include <LocalRegions/TetExp.h>
44#include <LocalRegions/TriExp.h>
48
49using namespace std;
50
52{
53/**
54 * @class DisContField
55 * This class augments the list of local expansions inherited from
56 * ExpList with boundary conditions. Inter-element boundaries are
57 * handled using an discontinuous Galerkin scheme.
58 */
59
60/**
61 * Constructs an empty expansion list with no boundary conditions.
62 */
64 : ExpList(), m_bndConditions(), m_bndCondExpansions(),
66{
67}
68
69/**
70 * An expansion list for the boundary expansions is generated first for
71 * the field. These are subsequently evaluated for time zero. The trace
72 * map is then constructed.
73 * @param graph1D A mesh, containing information about the domain
74 * and the spectral/hp element expansions.
75 * @param bcs Information about the enforced boundary
76 * conditions.
77 * @param variable The session variable associated with the
78 * boundary conditions to enforce.
79 * @param solnType Type of global system to use.
80 */
83 const std::string &variable, const bool SetUpJustDG,
84 const bool DeclareCoeffPhysArrays,
86 const std::string bcvariable)
87 : ExpList(pSession, graph, DeclareCoeffPhysArrays, variable, ImpType),
89{
90 std::string bcvar;
91 if (bcvariable == "NotSet")
92 {
93 bcvar = variable;
94 }
95 else
96 {
97 bcvar = bcvariable;
98 }
99
100 if (bcvar.compare("DefaultVar") != 0)
101 {
103
104 GenerateBoundaryConditionExpansion(graph, bcs, bcvar,
105 DeclareCoeffPhysArrays, ImpType);
106 if (DeclareCoeffPhysArrays)
107 {
108 EvaluateBoundaryConditions(0.0, bcvar);
109 }
111 FindPeriodicTraces(bcs, bcvar);
112 }
113
114 int i, cnt;
115 Array<OneD, int> ElmtID, TraceID;
116 GetBoundaryToElmtMap(ElmtID, TraceID);
117
118 for (cnt = i = 0; i < m_bndCondExpansions.size(); ++i)
119 {
121 locExpList = m_bndCondExpansions[i];
122
123 for (int e = 0; e < locExpList->GetExpSize(); ++e)
124 {
125 (*m_exp)[ElmtID[cnt + e]]->SetTraceExp(TraceID[cnt + e],
126 locExpList->GetExp(e));
127 locExpList->GetExp(e)->SetAdjacentElementExp(
128 TraceID[cnt + e], (*m_exp)[ElmtID[cnt + e]]);
129 }
130 cnt += m_bndCondExpansions[i]->GetExpSize();
131 }
132
133 if (SetUpJustDG)
134 {
135 SetUpDG(variable, ImpType);
136 }
137 else
138 {
139 if (m_session->DefinesSolverInfo("PROJECTION"))
140 {
141 std::string ProjectStr = m_session->GetSolverInfo("PROJECTION");
142 if ((ProjectStr == "MixedCGDG") ||
143 (ProjectStr == "Mixed_CG_Discontinuous"))
144 {
145 SetUpDG(variable);
146 }
147 else
148 {
150 }
151 }
152 else
153 {
155 }
156 }
157}
158
159/**
160 * @brief Set up all DG member variables and maps.
161 */
162void DisContField::SetUpDG(const std::string variable,
164{
165 // Check for multiple calls
167 {
168 return;
169 }
170
171 // Set up matrix map
173
174 // Set up trace space
177 m_comm, true, "DefaultVar", ImpType);
178
179 PeriodicMap periodicTraces = (m_expType == e1D) ? m_periodicVerts
182
185 m_bndConditions, periodicTraces, variable);
186
187 if (m_session->DefinesCmdLineArgument("verbose"))
188 {
189 m_traceMap->PrintStats(std::cout, variable);
190 }
191
193 m_traceMap->GetElmtToTrace();
194
195 // Scatter trace to elements. For each element, we find
196 // the trace associated to each elemental trace. The element
197 // then retains a pointer to the elemental trace, to
198 // ensure uniqueness of normals when retrieving from two
199 // adjoining elements which do not lie in a plane.
200 for (int i = 0; i < m_exp->size(); ++i)
201 {
202 for (int j = 0; j < (*m_exp)[i]->GetNtraces(); ++j)
203 {
204 LocalRegions::ExpansionSharedPtr exp = elmtToTrace[i][j];
205 ;
206
207 exp->SetAdjacentElementExp(j, (*m_exp)[i]);
208 (*m_exp)[i]->SetTraceExp(j, exp);
209 }
210 }
211
212 // Set up physical normals
214
215 // Create interface exchange object
218
219 int cnt, n;
220
221 // Identify boundary trace
222 for (cnt = 0, n = 0; n < m_bndCondExpansions.size(); ++n)
223 {
224 if (m_bndConditions[n]->GetBoundaryConditionType() !=
226 {
227 for (int v = 0; v < m_bndCondExpansions[n]->GetExpSize(); ++v)
228 {
229 m_boundaryTraces.insert(
230 m_traceMap->GetBndCondIDToGlobalTraceID(cnt + v));
231 }
232 cnt += m_bndCondExpansions[n]->GetExpSize();
233 }
234 }
235
236 // Set up information for periodic boundary conditions.
237 std::unordered_map<int, pair<int, int>> perTraceToExpMap;
238 for (cnt = n = 0; n < m_exp->size(); ++n)
239 {
240 for (int v = 0; v < (*m_exp)[n]->GetNtraces(); ++v, ++cnt)
241 {
242 auto it = periodicTraces.find((*m_exp)[n]->GetGeom()->GetTid(v));
243
244 if (it != periodicTraces.end())
245 {
246 perTraceToExpMap[it->first] = make_pair(n, v);
247 }
248 }
249 }
250
251 // Set up left-adjacent tracelist.
252 m_leftAdjacentTraces.resize(cnt);
253
254 // count size of trace
255 for (cnt = n = 0; n < m_exp->size(); ++n)
256 {
257 for (int v = 0; v < (*m_exp)[n]->GetNtraces(); ++v, ++cnt)
258 {
260 }
261 }
262
263 // Set up mapping to copy Fwd of periodic bcs to Bwd of other edge.
264 for (cnt = n = 0; n < m_exp->size(); ++n)
265 {
266 for (int v = 0; v < (*m_exp)[n]->GetNtraces(); ++v, ++cnt)
267 {
268 int GeomId = (*m_exp)[n]->GetGeom()->GetTid(v);
269
270 // Check to see if this trace is periodic.
271 auto it = periodicTraces.find(GeomId);
272
273 if (it != periodicTraces.end())
274 {
275 const PeriodicEntity &ent = it->second[0];
276 auto it2 = perTraceToExpMap.find(ent.id);
277
278 if (it2 == perTraceToExpMap.end())
279 {
280 if (m_session->GetComm()->GetRowComm()->GetSize() > 1 &&
281 !ent.isLocal)
282 {
283 continue;
284 }
285 else
286 {
287 ASSERTL1(false, "Periodic trace not found!");
288 }
289 }
290
292 "Periodic trace in non-forward space?");
293
294 int offset = m_trace->GetPhys_Offset(
295 (m_traceMap->GetElmtToTrace())[n][v]->GetElmtId());
296
297 int offset2 = m_trace->GetPhys_Offset(
298 (m_traceMap->GetElmtToTrace())[it2->second.first]
299 [it2->second.second]
300 ->GetElmtId());
301
302 switch (m_expType)
303 {
304 case e1D:
305 {
306 m_periodicFwdCopy.push_back(offset);
307 m_periodicBwdCopy.push_back(offset2);
308 }
309 break;
310 case e2D:
311 {
312 // Calculate relative orientations between trace to
313 // calculate copying map.
314 int nquad = elmtToTrace[n][v]->GetNumPoints(0);
315
316 vector<int> tmpBwd(nquad);
317 vector<int> tmpFwd(nquad);
318
319 if (ent.orient == StdRegions::eForwards)
320 {
321 for (int i = 0; i < nquad; ++i)
322 {
323 tmpBwd[i] = offset2 + i;
324 tmpFwd[i] = offset + i;
325 }
326 }
327 else
328 {
329 for (int i = 0; i < nquad; ++i)
330 {
331 tmpBwd[i] = offset2 + i;
332 tmpFwd[i] = offset + nquad - i - 1;
333 }
334 }
335
336 for (int i = 0; i < nquad; ++i)
337 {
338 m_periodicFwdCopy.push_back(tmpFwd[i]);
339 m_periodicBwdCopy.push_back(tmpBwd[i]);
340 }
341 }
342 break;
343 case e3D:
344 {
345 // Calculate relative orientations between faces to
346 // calculate copying map.
347 int nquad1 = elmtToTrace[n][v]->GetNumPoints(0);
348 int nquad2 = elmtToTrace[n][v]->GetNumPoints(1);
349
350 vector<int> tmpBwd(nquad1 * nquad2);
351 vector<int> tmpFwd(nquad1 * nquad2);
352
353 if (ent.orient ==
355 ent.orient ==
357 ent.orient ==
360 {
361 for (int i = 0; i < nquad2; ++i)
362 {
363 for (int j = 0; j < nquad1; ++j)
364 {
365 tmpBwd[i * nquad1 + j] =
366 offset2 + i * nquad1 + j;
367 tmpFwd[i * nquad1 + j] =
368 offset + j * nquad2 + i;
369 }
370 }
371 }
372 else
373 {
374 for (int i = 0; i < nquad2; ++i)
375 {
376 for (int j = 0; j < nquad1; ++j)
377 {
378 tmpBwd[i * nquad1 + j] =
379 offset2 + i * nquad1 + j;
380 tmpFwd[i * nquad1 + j] =
381 offset + i * nquad1 + j;
382 }
383 }
384 }
385
386 if (ent.orient ==
388 ent.orient ==
390 ent.orient ==
393 {
394 // Reverse x direction
395 for (int i = 0; i < nquad2; ++i)
396 {
397 for (int j = 0; j < nquad1 / 2; ++j)
398 {
399 swap(tmpFwd[i * nquad1 + j],
400 tmpFwd[i * nquad1 + nquad1 - j - 1]);
401 }
402 }
403 }
404
405 if (ent.orient ==
407 ent.orient ==
409 ent.orient ==
412 {
413 // Reverse y direction
414 for (int j = 0; j < nquad1; ++j)
415 {
416 for (int i = 0; i < nquad2 / 2; ++i)
417 {
418 swap(tmpFwd[i * nquad1 + j],
419 tmpFwd[(nquad2 - i - 1) * nquad1 + j]);
420 }
421 }
422 }
423
424 for (int i = 0; i < nquad1 * nquad2; ++i)
425 {
426 m_periodicFwdCopy.push_back(tmpFwd[i]);
427 m_periodicBwdCopy.push_back(tmpBwd[i]);
428 }
429 }
430 break;
431 default:
432 ASSERTL1(false, "not set up");
433 }
434 }
435 }
436 }
437
439 *this, m_trace, elmtToTrace, m_leftAdjacentTraces);
440}
441
442/**
443 * @brief This routine determines if an element is to the "left"
444 * of the adjacent trace, which arises from the idea there is a local
445 * normal direction between two elements (i.e. on the trace)
446 * and one elements would then be the left.
447 *
448 * This is typically required since we only define one normal
449 * direction along the trace which goes from the left adjacent
450 * element to the right adjacent element. It is also useful
451 * in DG discretisations to set up a local Riemann problem
452 * where we need to have the concept of a local normal
453 * direction which is unique between two elements.
454 *
455 * There are two cases to be checked:
456 *
457 * 1) First is the trace on a boundary condition (perioidic or
458 * otherwise) or on a partition boundary. If a partition
459 * boundary then trace is always considered to be the left
460 * adjacent trace and the normal is pointing outward of the
461 * soltuion domain. We have to perform an additional case for
462 * a periodic boundary where wer chose the element with the
463 * lowest global id. If the trace is on a parallel partition
464 * we use a member of the traceMap where one side is chosen to
465 * contribute to a unique map and have a value which is not
466 * -1 so this is the identifier for the left adjacent side
467 *
468 * 2) If the element is a elemental boundary on one element
469 * the left adjacent element is defined by a link to the left
470 * element from the trace expansion and this is consistent
471 * with the defitiion of the normal which is determined by the
472 * largest id (in contrast to the periodic boundary definition
473 * !). This reversal of convention does not really matter
474 * providing the normals are defined consistently.
475 */
476
477bool DisContField::IsLeftAdjacentTrace(const int n, const int e)
478{
480 m_traceMap->GetElmtToTrace()[n][e];
481
482 PeriodicMap periodicTraces = (m_expType == e1D) ? m_periodicVerts
485
486 bool fwd = true;
487 if (traceEl->GetLeftAdjacentElementTrace() == -1 ||
488 traceEl->GetRightAdjacentElementTrace() == -1)
489 {
490 // Boundary edge (1 connected element). Do nothing in
491 // serial.
492 auto it = m_boundaryTraces.find(traceEl->GetElmtId());
493
494 // If the edge does not have a boundary condition set on
495 // it, then assume it is a partition edge or periodic.
496 if (it == m_boundaryTraces.end())
497 {
498 fwd = true;
499 }
500 }
501 else if (traceEl->GetLeftAdjacentElementTrace() != -1 &&
502 traceEl->GetRightAdjacentElementTrace() != -1)
503 {
504 // Non-boundary edge (2 connected elements).
505 fwd = (traceEl->GetLeftAdjacentElementExp().get()) == (*m_exp)[n].get();
506 }
507 else
508 {
509 ASSERTL2(false, "Unconnected trace element!");
510 }
511
512 return fwd;
513}
514
515// Given all boundary regions for the whole solution determine
516// which ones (if any) are part of domain and proivde all other
517// conditions are given as UserDefined Dirichlet.
519 const SpatialDomains::CompositeMap &domain,
521 [[maybe_unused]] const std::string &variable)
522{
524
525 returnval =
527
528 map<int, int> GeometryToRegionsMap;
529
531 Allbcs.GetBoundaryRegions();
533 Allbcs.GetBoundaryConditions();
534
535 // Set up a map of all boundary regions
536 for (auto &it : bregions)
537 {
538 for (auto &bregionIt : *it.second)
539 {
540 // can assume that all regions only contain one point in 1D
541 // Really do not need loop above
542 int id = bregionIt.second->m_geomVec[0]->GetGlobalID();
543 GeometryToRegionsMap[id] = it.first;
544 }
545 }
546
547 map<int, SpatialDomains::Geometry *> EndOfDomain;
548
549 // Now find out which points in domain have only one vertex
550 for (auto &domIt : domain)
551 {
552 SpatialDomains::CompositeSharedPtr geomvector = domIt.second;
553 for (int i = 0; i < geomvector->m_geomVec.size(); ++i)
554 {
555 for (int j = 0; j < 2; ++j)
556 {
557 int vid = geomvector->m_geomVec[i]->GetVid(j);
558 if (EndOfDomain.count(vid) == 0)
559 {
560 EndOfDomain[vid] = geomvector->m_geomVec[i]->GetVertex(j);
561 }
562 else
563 {
564 EndOfDomain.erase(vid);
565 }
566 }
567 }
568 }
569 ASSERTL1(EndOfDomain.size() == 2, "Did not find two ends of domain");
570
571 for (auto &regIt : EndOfDomain)
572 {
573 if (GeometryToRegionsMap.count(regIt.first) != 0)
574 {
575 // Set up boundary condition up
576 auto iter = GeometryToRegionsMap.find(regIt.first);
577 ASSERTL1(iter != GeometryToRegionsMap.end(),
578 "Failied to find GeometryToRegionMap");
579
580 int regionId = iter->second;
581 auto bregionsIter = bregions.find(regionId);
582 ASSERTL1(bregionsIter != bregions.end(),
583 "Failed to find boundary region");
584
585 SpatialDomains::BoundaryRegionShPtr breg = bregionsIter->second;
586 returnval->AddBoundaryRegions(regionId, breg);
587
588 auto bconditionsIter = bconditions.find(regionId);
589 ASSERTL1(bconditionsIter != bconditions.end(),
590 "Failed to find boundary collection");
591
593 bconditionsIter->second;
594 returnval->AddBoundaryConditions(regionId, bcond);
595 }
596 }
597
598 return returnval;
599}
600
601/**
602 * Constructor for use in multidomain computations where a
603 * domain list can be passed instead of graph1D
604 *
605 * @param domain Subdomain specified in the inputfile from
606 * which the DisContField is set up
607 */
610 const SpatialDomains::CompositeMap &domain,
612 const std::string &variable,
613 const LibUtilities::CommSharedPtr &comm,
614 bool SetToOneSpaceDimension,
616 : ExpList(pSession, domain, graph1D, true, variable, SetToOneSpaceDimension,
617 comm, ImpType)
618{
619 if (variable.compare("DefaultVar") != 0)
620 {
622 GetDomainBCs(domain, Allbcs, variable);
623
624 GenerateBoundaryConditionExpansion(m_graph, *DomBCs, variable, true,
625 ImpType);
626 EvaluateBoundaryConditions(0.0, variable);
628 FindPeriodicTraces(*DomBCs, variable);
629 }
630
631 SetUpDG(variable);
632}
633
634/**
635 * Constructs a field as a copy of an existing field.
636 * @param In Existing DisContField object to copy.
637 */
639 const bool DeclareCoeffPhysArrays)
640 : ExpList(In, DeclareCoeffPhysArrays), m_bndConditions(In.m_bndConditions),
641 m_bndCondExpansions(In.m_bndCondExpansions),
642 m_bndCondBndWeight(In.m_bndCondBndWeight),
643 m_interfaceMap(In.m_interfaceMap), m_globalBndMat(In.m_globalBndMat),
644 m_traceMap(In.m_traceMap), m_boundaryTraces(In.m_boundaryTraces),
645 m_periodicVerts(In.m_periodicVerts),
646 m_periodicFwdCopy(In.m_periodicFwdCopy),
647 m_periodicBwdCopy(In.m_periodicBwdCopy),
648 m_leftAdjacentTraces(In.m_leftAdjacentTraces),
649 m_locTraceToTraceMap(In.m_locTraceToTraceMap)
650{
651 if (In.m_trace) // independent trace space
652 {
654 *In.m_trace, DeclareCoeffPhysArrays);
655 }
656
657 if (In.m_locElmtTrace)
658 {
660 *In.m_locElmtTrace, DeclareCoeffPhysArrays);
661 }
662}
663
664/*
665 * @brief Copy type constructor which declares new boundary conditions
666 * and re-uses mapping info and trace space if possible
667 */
670 const std::string &variable, const bool SetUpJustDG,
671 const bool DeclareCoeffPhysArrays)
672 : ExpList(In, DeclareCoeffPhysArrays)
673{
675
676 // Set up boundary conditions for this variable.
677 // Do not set up BCs if default variable
678 if (variable.compare("DefaultVar") != 0)
679 {
682 variable);
683
684 if (DeclareCoeffPhysArrays)
685 {
686 EvaluateBoundaryConditions(0.0, variable);
687 }
688
690 {
691 // Find periodic edges for this variable.
692 FindPeriodicTraces(bcs, variable);
693
694 if (SetUpJustDG)
695 {
696 SetUpDG(variable);
697 }
698 else
699 {
700 // set elmt edges to point to robin bc edges if required
701 int i, cnt = 0;
702 Array<OneD, int> ElmtID, TraceID;
703 GetBoundaryToElmtMap(ElmtID, TraceID);
704
705 for (i = 0; i < m_bndCondExpansions.size(); ++i)
706 {
708
709 int e;
710 locExpList = m_bndCondExpansions[i];
711
712 for (e = 0; e < locExpList->GetExpSize(); ++e)
713 {
714 (*m_exp)[ElmtID[cnt + e]]->SetTraceExp(
715 TraceID[cnt + e], locExpList->GetExp(e));
716 locExpList->GetExp(e)->SetAdjacentElementExp(
717 TraceID[cnt + e], (*m_exp)[ElmtID[cnt + e]]);
718 }
719
720 cnt += m_bndCondExpansions[i]->GetExpSize();
721 }
722
723 if (m_session->DefinesSolverInfo("PROJECTION"))
724 {
725 std::string ProjectStr =
726 m_session->GetSolverInfo("PROJECTION");
727
728 if ((ProjectStr == "MixedCGDG") ||
729 (ProjectStr == "Mixed_CG_Discontinuous"))
730 {
731 SetUpDG(variable);
732 }
733 else
734 {
736 }
737 }
738 else
739 {
741 }
742 }
743 }
744 else
745 {
747 m_trace = In.m_trace;
759
760 if (!SetUpJustDG)
761 {
762 // set elmt edges to point to robin bc edges if required
763 int i, cnt = 0;
764 Array<OneD, int> ElmtID, TraceID;
765 GetBoundaryToElmtMap(ElmtID, TraceID);
766
767 for (i = 0; i < m_bndCondExpansions.size(); ++i)
768 {
770
771 int e;
772 locExpList = m_bndCondExpansions[i];
773
774 for (e = 0; e < locExpList->GetExpSize(); ++e)
775 {
776 (*m_exp)[ElmtID[cnt + e]]->SetTraceExp(
777 TraceID[cnt + e], locExpList->GetExp(e));
778 locExpList->GetExp(e)->SetAdjacentElementExp(
779 TraceID[cnt + e], (*m_exp)[ElmtID[cnt + e]]);
780 }
781 cnt += m_bndCondExpansions[i]->GetExpSize();
782 }
783
785 }
786 }
787 }
788}
789
790/**
791 * Constructs a field as a copy of an existing explist field.
792 * @param In Existing ExpList object to copy.
793 */
795{
796}
797
798/**
799 *
800 */
804
805/**
806 * \brief This function discretises the boundary conditions by setting
807 * up a list of one-dimensions lower boundary expansions.
808 *
809 * According to their boundary region, the separate boundary
810 * expansions are bundled together in an object of the class
811 *
812 * @param graph A mesh, containing information about the domain
813 * and the spectral/hp element expansions.
814 * @param bcs Information about the enforced boundary
815 * conditions.
816 * @param variable The session variable associated with the
817 * boundary conditions to enforce.
818 * @param DeclareCoeffPhysArrays bool to identify if array
819 * space should be setup.
820 * Default is true.
821 */
824 const SpatialDomains::BoundaryConditions &bcs, const std::string variable,
825 const bool DeclareCoeffPhysArrays,
827{
828 int cnt = 0;
832 bcs.GetBoundaryRegions();
835
836 std::set<int> ProcessBnd;
837 if (m_session->DefinesTag("CreateBndRegions"))
838 {
839 // evaluate bnd regions to be generated
840 vector<unsigned int> bndRegions;
842 m_session->GetTag("CreateBndRegions"), bndRegions),
843 "Failed to interpret bnd values string");
844
845 for (auto &bnd : bndRegions)
846 {
847 if (bregions.count(bnd) ==
848 1) // this boundary may not exist on processor
849 {
850 ProcessBnd.insert(bnd);
851 }
852 }
853 }
854 else
855 {
856 for (auto &it : bregions)
857 {
858 ProcessBnd.insert(it.first);
859 }
860 }
861
866
867 m_bndCondBndWeight = Array<OneD, NekDouble>{bregions.size(), 0.0};
868
869 // count the number of non-periodic boundary points
870 for (auto &it : bregions)
871 {
872 // check to see if reduced bnd regions have been set in FieldConvert.
873 if (ProcessBnd.count(it.first) == 0)
874 {
875 continue;
876 }
877
878 bc = GetBoundaryCondition(bconditions, it.first, variable);
879
881 m_session, *(it.second), graph, DeclareCoeffPhysArrays, variable,
882 false, bc->GetComm(), ImpType);
883
884 m_bndCondExpansions[cnt] = locExpList;
885 m_bndConditions[cnt] = bc;
886
887 std::string type = m_bndConditions[cnt]->GetUserDefined();
888
889 // Set up normals on non-Dirichlet boundary conditions. Second
890 // two conditions ideally should be in local solver setup (when
891 // made into factory)
892 if (bc->GetBoundaryConditionType() != SpatialDomains::eDirichlet ||
893 boost::iequals(type, "I") || boost::iequals(type, "CalcBC"))
894 {
896 }
897 cnt++;
898 }
899}
900
901/**
902 * \brief This function discretises the boundary conditions using an already
903 * setup expansion list of one-dimensions lower boundary expansions.
904 *
905 * According to their boundary region, the separate boundary
906 * expansions are bundled together in an object of the class
907 *
908 * @param In An array of boundary conditions from an alread setup
909 * expansion and the spectral/hp element expansions.
910 * @param bcs Information about the enforced boundary
911 * conditions.
912 * @param variable The session variable associated with the
913 * boundary conditions to enforce.
914 * @param DeclareCoeffPhysArrays bool to identify if array
915 * space should be setup.
916 * Default is true.
917 */
920 const SpatialDomains::BoundaryConditions &bcs, const std::string variable,
921 const bool DeclareCoeffPhysArrays,
922 [[maybe_unused]] const Collections::ImplementationType ImpType)
923{
924 int cnt = 0;
928 bcs.GetBoundaryRegions();
931
932 std::set<int> ProcessBnd;
933 if (m_session->DefinesTag("CreateBndRegions"))
934 {
935 // evaluate bnd regions to be generated
936 vector<unsigned int> bndRegions;
938 m_session->GetTag("CreateBndRegions"), bndRegions),
939 "Failed to interpret bnd values string");
940
941 for (auto &bnd : bndRegions)
942 {
943 if (bregions.count(bnd) == 1)
944 {
945 ProcessBnd.insert(bnd);
946 }
947 }
948 }
949 else
950 {
951 for (auto &it : bregions)
952 {
953 ProcessBnd.insert(it.first);
954 }
955 }
956
961
962 m_bndCondBndWeight = Array<OneD, NekDouble>{bregions.size(), 0.0};
963
964 // count the number of non-periodic boundary points
965 for (auto &it : bregions)
966 {
967 // check to see if reduced bnd regions have been set in FieldConvert.
968 if (ProcessBnd.count(it.first) == 0)
969 {
970 continue;
971 }
972
973 bc = GetBoundaryCondition(bconditions, it.first, variable);
974
975 // make a copy of existing Bc passed to generate function
977 *(In[cnt]), DeclareCoeffPhysArrays);
978
979 m_bndCondExpansions[cnt] = locExpList;
980 m_bndConditions[cnt] = bc;
981
982 std::string type = m_bndConditions[cnt]->GetUserDefined();
983
984 // Set up normals on non-Dirichlet boundary conditions. Second
985 // two conditions ideally should be in local solver setup (when
986 // made into factory)
987 if (bc->GetBoundaryConditionType() != SpatialDomains::eDirichlet ||
988 boost::iequals(type, "I") || boost::iequals(type, "CalcBC"))
989 {
991 }
992 cnt++;
993 }
994}
995
996/**
997 * @brief Determine the periodic faces, edges and vertices for the given
998 * graph.
999 *
1000 * @param bcs Information about the boundary conditions.
1001 * @param variable Specifies the field.
1002 */
1004 const SpatialDomains::BoundaryConditions &bcs, const std::string variable)
1005{
1007 bcs.GetBoundaryRegions();
1010
1011 LibUtilities::CommSharedPtr vComm = m_comm->GetRowComm();
1012
1013 switch (m_expType)
1014 {
1015 case e1D:
1016 {
1017 int i, region1ID, region2ID;
1018
1020 map<int, int> BregionToVertMap;
1021
1022 // Construct list of all periodic Region and their
1023 // global vertex on this process.
1024 for (auto &it : bregions)
1025 {
1026 locBCond =
1027 GetBoundaryCondition(bconditions, it.first, variable);
1028
1029 if (locBCond->GetBoundaryConditionType() !=
1031 {
1032 continue;
1033 }
1034 int id =
1035 it.second->begin()->second->m_geomVec[0]->GetGlobalID();
1036
1037 BregionToVertMap[it.first] = id;
1038 }
1039
1040 set<int> islocal;
1041
1042 int n = vComm->GetSize();
1043 int p = vComm->GetRank();
1044
1045 Array<OneD, int> nregions(n, 0);
1046 nregions[p] = BregionToVertMap.size();
1047 vComm->AllReduce(nregions, LibUtilities::ReduceSum);
1048
1049 int totRegions = Vmath::Vsum(n, nregions, 1);
1050
1051 Array<OneD, int> regOffset(n, 0);
1052
1053 for (i = 1; i < n; ++i)
1054 {
1055 regOffset[i] = regOffset[i - 1] + nregions[i - 1];
1056 }
1057
1058 Array<OneD, int> bregmap(totRegions, 0);
1059 Array<OneD, int> bregid(totRegions, 0);
1060
1061 i = regOffset[p];
1062 for (auto &iit : BregionToVertMap)
1063 {
1064 bregid[i] = iit.first;
1065 bregmap[i++] = iit.second;
1066 islocal.insert(iit.first);
1067 }
1068
1069 vComm->AllReduce(bregmap, LibUtilities::ReduceSum);
1070 vComm->AllReduce(bregid, LibUtilities::ReduceSum);
1071
1072 for (int i = 0; i < totRegions; ++i)
1073 {
1074 BregionToVertMap[bregid[i]] = bregmap[i];
1075 }
1076
1077 // Construct list of all periodic pairs local to this process.
1078 for (auto &it : bregions)
1079 {
1080 locBCond =
1081 GetBoundaryCondition(bconditions, it.first, variable);
1082
1083 if (locBCond->GetBoundaryConditionType() !=
1085 {
1086 continue;
1087 }
1088
1089 // Identify periodic boundary region IDs.
1090 region1ID = it.first;
1091 region2ID =
1092 std::static_pointer_cast<
1094 ->m_connectedBoundaryRegion;
1095
1096 ASSERTL0(BregionToVertMap.count(region1ID) != 0,
1097 "Cannot determine vertex of region1ID");
1098
1099 ASSERTL0(BregionToVertMap.count(region2ID) != 0,
1100 "Cannot determine vertex of region2ID");
1101
1102 PeriodicEntity ent(BregionToVertMap[region2ID],
1104 islocal.count(region2ID) != 0);
1105
1106 m_periodicVerts[BregionToVertMap[region1ID]].push_back(ent);
1107 }
1108 }
1109 break;
1110 case e2D:
1111 {
1112 int region1ID, region2ID, i, j, k, cnt;
1114
1116 m_graph->GetCompositeOrdering();
1118 m_graph->GetBndRegionOrdering();
1119 SpatialDomains::CompositeMap compMap = m_graph->GetComposites();
1120
1121 // Unique collection of pairs of periodic composites
1122 // (i.e. if composites 1 and 2 are periodic then this
1123 // map will contain either the pair (1,2) or (2,1) but
1124 // not both).
1125 map<int, int> perComps;
1126 map<int, vector<int>> allVerts;
1127 set<int> locVerts;
1128 map<int, pair<int, StdRegions::Orientation>> allEdges;
1129
1130 // Set up a set of all local verts and edges.
1131 for (i = 0; i < (*m_exp).size(); ++i)
1132 {
1133 for (j = 0; j < (*m_exp)[i]->GetNverts(); ++j)
1134 {
1135 int id = (*m_exp)[i]->GetGeom()->GetVid(j);
1136 locVerts.insert(id);
1137 }
1138 }
1139
1140 // Construct list of all periodic pairs local to this process.
1141 for (auto &it : bregions)
1142 {
1143 locBCond =
1144 GetBoundaryCondition(bconditions, it.first, variable);
1145
1146 if (locBCond->GetBoundaryConditionType() !=
1148 {
1149 continue;
1150 }
1151
1152 // Identify periodic boundary region IDs.
1153 region1ID = it.first;
1154 region2ID =
1155 std::static_pointer_cast<
1157 ->m_connectedBoundaryRegion;
1158
1159 // From this identify composites. Note that in
1160 // serial this will be an empty map.
1161 int cId1, cId2;
1162 if (vComm->GetSize() == 1)
1163 {
1164 cId1 = it.second->begin()->first;
1165 cId2 = bregions.find(region2ID)->second->begin()->first;
1166 }
1167 else
1168 {
1169 cId1 = bndRegOrder.find(region1ID)->second[0];
1170 cId2 = bndRegOrder.find(region2ID)->second[0];
1171 }
1172
1173 ASSERTL0(it.second->size() == 1,
1174 "Boundary region " + std::to_string(region1ID) +
1175 " should only contain 1 composite.");
1176
1177 vector<unsigned int> tmpOrder;
1178
1179 // Construct set containing all periodic edgesd on
1180 // this process
1182 it.second->begin()->second;
1183
1184 for (i = 0; i < c->m_geomVec.size(); ++i)
1185 {
1186 SpatialDomains::SegGeom *segGeom =
1187 dynamic_cast<SpatialDomains::SegGeom *>(
1188 c->m_geomVec[i]);
1189 ASSERTL0(segGeom, "Unable to cast to SegGeom*");
1190
1192 m_graph->GetElementsFromEdge(segGeom);
1193 ASSERTL0(elmt->size() == 1,
1194 "The periodic boundaries belong to "
1195 "more than one element of the mesh");
1196
1198 dynamic_cast<SpatialDomains::Geometry2D *>(
1199 elmt->at(0).first);
1200
1201 allEdges[c->m_geomVec[i]->GetGlobalID()] =
1202 make_pair(elmt->at(0).second,
1203 geom->GetEorient(elmt->at(0).second));
1204
1205 // In serial mesh partitioning will not have occurred so
1206 // need to fill composite ordering map manually.
1207 if (vComm->GetSize() == 1)
1208 {
1209 tmpOrder.push_back(c->m_geomVec[i]->GetGlobalID());
1210 }
1211
1212 vector<int> vertList(2);
1213 vertList[0] = segGeom->GetVid(0);
1214 vertList[1] = segGeom->GetVid(1);
1215 allVerts[c->m_geomVec[i]->GetGlobalID()] = vertList;
1216 }
1217
1218 if (vComm->GetSize() == 1)
1219 {
1220 compOrder[it.second->begin()->first] = tmpOrder;
1221 }
1222
1223 // See if we already have either region1 or
1224 // region2 stored in perComps map.
1225 if (perComps.count(cId1) == 0)
1226 {
1227 if (perComps.count(cId2) == 0)
1228 {
1229 perComps[cId1] = cId2;
1230 }
1231 else
1232 {
1233 std::stringstream ss;
1234 ss << "Boundary region " << cId2 << " should be "
1235 << "periodic with " << perComps[cId2] << " but "
1236 << "found " << cId1 << " instead!";
1237 ASSERTL0(perComps[cId2] == cId1, ss.str());
1238 }
1239 }
1240 else
1241 {
1242 std::stringstream ss;
1243 ss << "Boundary region " << cId1 << " should be "
1244 << "periodic with " << perComps[cId1] << " but "
1245 << "found " << cId2 << " instead!";
1246 ASSERTL0(perComps[cId1] == cId1, ss.str());
1247 }
1248 }
1249
1250 // In case of periodic partition being split over many composites
1251 // may not have all periodic matches so check this
1252 int idmax = -1;
1253 for (auto &cIt : perComps)
1254 {
1255 idmax = max(idmax, cIt.first);
1256 idmax = max(idmax, cIt.second);
1257 }
1258 vComm->AllReduce(idmax, LibUtilities::ReduceMax);
1259 idmax++;
1260 Array<OneD, int> perid(idmax, -1);
1261 for (auto &cIt : perComps)
1262 {
1263 perid[cIt.first] = cIt.second;
1264 }
1265 vComm->AllReduce(perid, LibUtilities::ReduceMax);
1266 // update all partitions
1267 for (int i = 0; i < idmax; ++i)
1268 {
1269 if (perid[i] > -1)
1270 {
1271 // skip if complemetary relationship has
1272 // already been speficied in list
1273 if (perComps.count(perid[i]))
1274 {
1275 continue;
1276 }
1277 perComps[i] = perid[i];
1278 }
1279 }
1280
1281 // Process local edge list to obtain relative edge
1282 // orientations.
1283 int n = vComm->GetSize();
1284 int p = vComm->GetRank();
1285 int totEdges;
1286 Array<OneD, int> edgecounts(n, 0);
1287 Array<OneD, int> edgeoffset(n, 0);
1288 Array<OneD, int> vertoffset(n, 0);
1289
1290 edgecounts[p] = allEdges.size();
1291 vComm->AllReduce(edgecounts, LibUtilities::ReduceSum);
1292
1293 edgeoffset[0] = 0;
1294 for (i = 1; i < n; ++i)
1295 {
1296 edgeoffset[i] = edgeoffset[i - 1] + edgecounts[i - 1];
1297 }
1298
1299 totEdges = Vmath::Vsum(n, edgecounts, 1);
1300 Array<OneD, int> edgeIds(totEdges, 0);
1301 Array<OneD, int> edgeIdx(totEdges, 0);
1302 Array<OneD, int> edgeOrient(totEdges, 0);
1303 Array<OneD, int> edgeVerts(totEdges, 0);
1304
1305 auto sIt = allEdges.begin();
1306
1307 for (i = 0; sIt != allEdges.end(); ++sIt)
1308 {
1309 edgeIds[edgeoffset[p] + i] = sIt->first;
1310 edgeIdx[edgeoffset[p] + i] = sIt->second.first;
1311 edgeOrient[edgeoffset[p] + i] = sIt->second.second;
1312 edgeVerts[edgeoffset[p] + i++] = allVerts[sIt->first].size();
1313 }
1314
1315 vComm->AllReduce(edgeIds, LibUtilities::ReduceSum);
1316 vComm->AllReduce(edgeIdx, LibUtilities::ReduceSum);
1317 vComm->AllReduce(edgeOrient, LibUtilities::ReduceSum);
1318 vComm->AllReduce(edgeVerts, LibUtilities::ReduceSum);
1319
1320 // Calculate number of vertices on each processor.
1321 Array<OneD, int> procVerts(n, 0);
1322 int nTotVerts;
1323
1324 // Note if there are no periodic edges at all calling Vsum will
1325 // cause a segfault.
1326 if (totEdges > 0)
1327 {
1328 nTotVerts = Vmath::Vsum(totEdges, edgeVerts, 1);
1329 }
1330 else
1331 {
1332 nTotVerts = 0;
1333 }
1334
1335 for (i = 0; i < n; ++i)
1336 {
1337 if (edgecounts[i] > 0)
1338 {
1339 procVerts[i] = Vmath::Vsum(edgecounts[i],
1340 edgeVerts + edgeoffset[i], 1);
1341 }
1342 else
1343 {
1344 procVerts[i] = 0;
1345 }
1346 }
1347 vertoffset[0] = 0;
1348
1349 for (i = 1; i < n; ++i)
1350 {
1351 vertoffset[i] = vertoffset[i - 1] + procVerts[i - 1];
1352 }
1353
1354 Array<OneD, int> traceIds(nTotVerts, 0);
1355 for (i = 0, sIt = allEdges.begin(); sIt != allEdges.end(); ++sIt)
1356 {
1357 for (j = 0; j < allVerts[sIt->first].size(); ++j)
1358 {
1359 traceIds[vertoffset[p] + i++] = allVerts[sIt->first][j];
1360 }
1361 }
1362
1363 vComm->AllReduce(traceIds, LibUtilities::ReduceSum);
1364
1365 // For simplicity's sake create a map of edge id -> orientation.
1366 map<int, StdRegions::Orientation> orientMap;
1367 map<int, vector<int>> vertMap;
1368
1369 for (cnt = i = 0; i < totEdges; ++i)
1370 {
1371 ASSERTL0(orientMap.count(edgeIds[i]) == 0,
1372 "Already found edge in orientation map!");
1373
1374 // Work out relative orientations. To avoid having
1375 // to exchange vertex locations, we figure out if
1376 // the edges are backwards or forwards orientated
1377 // with respect to a forwards orientation that is
1378 // CCW. Since our local geometries are
1379 // forwards-orientated with respect to the
1380 // Cartesian axes, we need to invert the
1381 // orientation for the top and left edges of a
1382 // quad and the left edge of a triangle.
1384 (StdRegions::Orientation)edgeOrient[i];
1385
1386 if (edgeIdx[i] > 1)
1387 {
1390 }
1391
1392 orientMap[edgeIds[i]] = o;
1393
1394 vector<int> verts(edgeVerts[i]);
1395
1396 for (j = 0; j < edgeVerts[i]; ++j)
1397 {
1398 verts[j] = traceIds[cnt++];
1399 }
1400 vertMap[edgeIds[i]] = verts;
1401 }
1402
1403 // Go through list of composites and figure out which
1404 // edges are parallel from original ordering in
1405 // session file. This includes composites which are
1406 // not necessarily on this process.
1407 map<int, int> allCompPairs;
1408
1409 // Store temporary map of periodic vertices which will hold all
1410 // periodic vertices on the entire mesh so that doubly periodic
1411 // vertices can be counted properly across partitions. Local
1412 // vertices are copied into m_periodicVerts at the end of the
1413 // function.
1414 PeriodicMap periodicVerts;
1415
1416 for (auto &cIt : perComps)
1417 {
1419 const int id1 = cIt.first;
1420 const int id2 = cIt.second;
1421 std::string id1s = std::to_string(id1);
1422 std::string id2s = std::to_string(id2);
1423
1424 if (compMap.count(id1) > 0)
1425 {
1426 c[0] = compMap[id1];
1427 }
1428
1429 if (compMap.count(id2) > 0)
1430 {
1431 c[1] = compMap[id2];
1432 }
1433
1434 // Loop over composite ordering to construct list of all
1435 // periodic edges regardless of whether they are on this
1436 // process.
1437 map<int, int> compPairs;
1438
1439 ASSERTL0(compOrder.count(id1) > 0,
1440 "Unable to find composite " + id1s + " in order map.");
1441 ASSERTL0(compOrder.count(id2) > 0,
1442 "Unable to find composite " + id2s + " in order map.");
1443 WARNINGL0(
1444 compOrder[id1].size() == compOrder[id2].size(),
1445 "Periodic composites " + id1s + " and " + id2s +
1446 " should have the same number of elements. Have " +
1447 std::to_string(compOrder[id1].size()) + " vs " +
1448 std::to_string(compOrder[id2].size()));
1449 ASSERTL0(compOrder[id1].size() > 0, "Periodic composites " +
1450 id1s + " and " + id2s +
1451 " are empty!");
1452
1453 // TODO: Add more checks.
1454 for (i = 0; i < compOrder[id1].size(); ++i)
1455 {
1456 int eId1 = compOrder[id1][i];
1457 int eId2 = compOrder[id2][i];
1458
1459 ASSERTL0(compPairs.count(eId1) == 0, "Already paired.");
1460
1461 if (compPairs.count(eId2) != 0)
1462 {
1463 ASSERTL0(compPairs[eId2] == eId1, "Pairing incorrect");
1464 }
1465 compPairs[eId1] = eId2;
1466 }
1467
1468 // Construct set of all edges that we have
1469 // locally on this processor.
1470 set<int> locEdges;
1471 for (i = 0; i < 2; ++i)
1472 {
1473 if (!c[i])
1474 {
1475 continue;
1476 }
1477
1478 if (c[i]->m_geomVec.size() > 0)
1479 {
1480 for (j = 0; j < c[i]->m_geomVec.size(); ++j)
1481 {
1482 locEdges.insert(c[i]->m_geomVec[j]->GetGlobalID());
1483 }
1484 }
1485 }
1486
1487 // Loop over all edges in the geometry composite.
1488 for (auto &pIt : compPairs)
1489 {
1490 int ids[2] = {pIt.first, pIt.second};
1491 bool local[2] = {locEdges.count(pIt.first) > 0,
1492 locEdges.count(pIt.second) > 0};
1493
1494 ASSERTL0(orientMap.count(ids[0]) > 0 &&
1495 orientMap.count(ids[1]) > 0,
1496 "Unable to find edge in orientation map.");
1497
1498 allCompPairs[pIt.first] = pIt.second;
1499 allCompPairs[pIt.second] = pIt.first;
1500
1501 for (i = 0; i < 2; ++i)
1502 {
1503 if (!local[i])
1504 {
1505 continue;
1506 }
1507
1508 int other = (i + 1) % 2;
1509
1511 orientMap[ids[i]] == orientMap[ids[other]]
1514
1515 PeriodicEntity ent(ids[other], o, local[other]);
1516 m_periodicEdges[ids[i]].push_back(ent);
1517 }
1518
1519 for (i = 0; i < 2; ++i)
1520 {
1521 int other = (i + 1) % 2;
1522
1524 orientMap[ids[i]] == orientMap[ids[other]]
1527
1528 // Determine periodic vertices.
1529 vector<int> perVerts1 = vertMap[ids[i]];
1530 vector<int> perVerts2 = vertMap[ids[other]];
1531
1532 map<int, pair<int, bool>> tmpMap;
1533 if (o == StdRegions::eForwards)
1534 {
1535 tmpMap[perVerts1[0]] = make_pair(
1536 perVerts2[0], locVerts.count(perVerts2[0]) > 0);
1537 tmpMap[perVerts1[1]] = make_pair(
1538 perVerts2[1], locVerts.count(perVerts2[1]) > 0);
1539 }
1540 else
1541 {
1542 tmpMap[perVerts1[0]] = make_pair(
1543 perVerts2[1], locVerts.count(perVerts2[1]) > 0);
1544 tmpMap[perVerts1[1]] = make_pair(
1545 perVerts2[0], locVerts.count(perVerts2[0]) > 0);
1546 }
1547
1548 for (auto &mIt : tmpMap)
1549 {
1550 // See if this vertex has been recorded already.
1551 PeriodicEntity ent2(mIt.second.first,
1553 mIt.second.second);
1554 auto perIt = periodicVerts.find(mIt.first);
1555
1556 if (perIt == periodicVerts.end())
1557 {
1558 periodicVerts[mIt.first].push_back(ent2);
1559 perIt = periodicVerts.find(mIt.first);
1560 }
1561 else
1562 {
1563 bool doAdd = true;
1564 for (j = 0; j < perIt->second.size(); ++j)
1565 {
1566 if (perIt->second[j].id == mIt.second.first)
1567 {
1568 doAdd = false;
1569 break;
1570 }
1571 }
1572
1573 if (doAdd)
1574 {
1575 perIt->second.push_back(ent2);
1576 }
1577 }
1578 }
1579 }
1580 }
1581 }
1582
1583 // Search for periodic vertices and edges which are not in
1584 // a periodic composite but lie in this process. First, loop
1585 // over all information we have from other processors.
1586 for (cnt = i = 0; i < totEdges; ++i)
1587 {
1588 int edgeId = edgeIds[i];
1589
1590 ASSERTL0(allCompPairs.count(edgeId) > 0,
1591 "Unable to find matching periodic edge.");
1592
1593 int perEdgeId = allCompPairs[edgeId];
1594
1595 for (j = 0; j < edgeVerts[i]; ++j, ++cnt)
1596 {
1597 int vId = traceIds[cnt];
1598
1599 auto perId = periodicVerts.find(vId);
1600
1601 if (perId == periodicVerts.end())
1602 {
1603 // This vertex is not included in the
1604 // map. Figure out which vertex it is
1605 // supposed to be periodic with. perEdgeId
1606 // is the edge ID which is periodic with
1607 // edgeId. The logic is much the same as
1608 // the loop above.
1609 int perVertexId =
1610 orientMap[edgeId] == orientMap[perEdgeId]
1611 ? vertMap[perEdgeId][(j + 1) % 2]
1612 : vertMap[perEdgeId][j];
1613
1614 PeriodicEntity ent(perVertexId,
1616 locVerts.count(perVertexId) > 0);
1617
1618 periodicVerts[vId].push_back(ent);
1619 }
1620 }
1621 }
1622
1623 // Loop over all periodic vertices to complete connectivity
1624 // information.
1625 for (auto &perIt : periodicVerts)
1626 {
1627 // Loop over associated vertices.
1628 for (i = 0; i < perIt.second.size(); ++i)
1629 {
1630 auto perIt2 = periodicVerts.find(perIt.second[i].id);
1631 ASSERTL0(perIt2 != periodicVerts.end(),
1632 "Couldn't find periodic vertex.");
1633
1634 for (j = 0; j < perIt2->second.size(); ++j)
1635 {
1636 if (perIt2->second[j].id == perIt.first)
1637 {
1638 continue;
1639 }
1640
1641 bool doAdd = true;
1642 for (k = 0; k < perIt.second.size(); ++k)
1643 {
1644 if (perIt2->second[j].id == perIt.second[k].id)
1645 {
1646 doAdd = false;
1647 break;
1648 }
1649 }
1650
1651 if (doAdd)
1652 {
1653 perIt.second.push_back(perIt2->second[j]);
1654 }
1655 }
1656 }
1657 }
1658
1659 // Do one final loop over periodic vertices to remove non-local
1660 // vertices from map.
1661 for (auto &perIt : periodicVerts)
1662 {
1663 if (locVerts.count(perIt.first) > 0)
1664 {
1665 m_periodicVerts.insert(perIt);
1666 }
1667 }
1668 }
1669 break;
1670 case e3D:
1671 {
1673 m_graph->GetCompositeOrdering();
1675 m_graph->GetBndRegionOrdering();
1676 SpatialDomains::CompositeMap compMap = m_graph->GetComposites();
1677
1678 // perComps: Stores a unique collection of pairs of periodic
1679 // composites (i.e. if composites 1 and 2 are periodic then this map
1680 // will contain either the pair (1,2) or (2,1) but not both).
1681 //
1682 // The four maps allVerts, allCoord, allEdges and allOrient map a
1683 // periodic face to a vector containing the vertex ids of the face;
1684 // their coordinates; the edge ids of the face; and their
1685 // orientation within that face respectively.
1686 //
1687 // Finally the three sets locVerts, locEdges and locFaces store any
1688 // vertices, edges and faces that belong to a periodic composite and
1689 // lie on this process.
1690 map<int, RotPeriodicInfo> rotComp;
1691 map<int, int> perComps;
1692 map<int, vector<int>> allVerts;
1693 map<int, std::vector<SpatialDomains::PointGeom *>> allCoord;
1694 map<int, vector<int>> allEdges;
1695 map<int, vector<StdRegions::Orientation>> allOrient;
1696 set<int> locVerts;
1697 set<int> locEdges;
1698 set<int> locFaces;
1699
1700 int region1ID, region2ID, i, j, k, cnt;
1702
1703 // Set up a set of all local verts and edges.
1704 for (i = 0; i < (*m_exp).size(); ++i)
1705 {
1706 for (j = 0; j < (*m_exp)[i]->GetNverts(); ++j)
1707 {
1708 int id = (*m_exp)[i]->GetGeom()->GetVid(j);
1709 locVerts.insert(id);
1710 }
1711
1712 for (j = 0; j < (*m_exp)[i]->GetGeom()->GetNumEdges(); ++j)
1713 {
1714 int id = (*m_exp)[i]->GetGeom()->GetEid(j);
1715 locEdges.insert(id);
1716 }
1717 }
1718
1719 // Begin by populating the perComps map. We loop over all periodic
1720 // boundary conditions and determine the composite associated with
1721 // it, then fill out the all* maps.
1722 for (auto &it : bregions)
1723 {
1724
1725 locBCond =
1726 GetBoundaryCondition(bconditions, it.first, variable);
1727
1728 if (locBCond->GetBoundaryConditionType() !=
1730 {
1731 continue;
1732 }
1733
1734 // Identify periodic boundary region IDs.
1735 region1ID = it.first;
1736 region2ID =
1737 std::static_pointer_cast<
1739 ->m_connectedBoundaryRegion;
1740
1741 // Check the region only contains a single composite.
1742 ASSERTL0(it.second->size() == 1,
1743 "Boundary region " + std::to_string(region1ID) +
1744 " should only contain 1 composite.");
1745
1746 // From this identify composites by looking at the original
1747 // boundary region ordering. Note that in serial the mesh
1748 // partitioner is not run, so this map will be empty and
1749 // therefore needs to be populated by using the corresponding
1750 // boundary region.
1751 int cId1, cId2;
1752 if (vComm->GetSize() == 1)
1753 {
1754 cId1 = it.second->begin()->first;
1755 cId2 = bregions.find(region2ID)->second->begin()->first;
1756 }
1757 else
1758 {
1759 cId1 = bndRegOrder.find(region1ID)->second[0];
1760 cId2 = bndRegOrder.find(region2ID)->second[0];
1761 }
1762
1763 // check to see if boundary is rotationally aligned
1764 if (boost::icontains(locBCond->GetUserDefined(), "Rotated"))
1765 {
1766 vector<string> tmpstr;
1767
1768 boost::split(tmpstr, locBCond->GetUserDefined(),
1769 boost::is_any_of(":"));
1770
1771 if (boost::iequals(tmpstr[0], "Rotated"))
1772 {
1773 ASSERTL1(tmpstr.size() > 2,
1774 "Expected Rotated user defined string to "
1775 "contain direction and rotation angle "
1776 "and optionally a tolerance, "
1777 "i.e. Rotated:dir:PI/2:1e-6");
1778
1779 ASSERTL1((tmpstr[1] == "x") || (tmpstr[1] == "y") ||
1780 (tmpstr[1] == "z"),
1781 "Rotated Dir is "
1782 "not specified as x,y or z");
1783
1784 RotPeriodicInfo RotInfo;
1785 RotInfo.m_dir = (tmpstr[1] == "x") ? 0
1786 : (tmpstr[1] == "y") ? 1
1787 : 2;
1788
1790 int ExprId = strEval.DefineFunction("", tmpstr[2]);
1791 RotInfo.m_angle = strEval.Evaluate(ExprId);
1792
1793 if (tmpstr.size() == 4)
1794 {
1795 try
1796 {
1797 RotInfo.m_tol = std::stod(tmpstr[3]);
1798 }
1799 catch (...)
1800 {
1802 "failed to cast tolerance input "
1803 "to a double value in Rotated"
1804 "boundary information");
1805 }
1806 }
1807 else
1808 {
1809 RotInfo.m_tol = 1e-8;
1810 }
1811 rotComp[cId1] = RotInfo;
1812 }
1813 }
1814
1816 it.second->begin()->second;
1817
1818 vector<unsigned int> tmpOrder;
1819
1820 // store the rotation info of this
1821
1822 // From the composite, we now construct the allVerts, allEdges
1823 // and allCoord map so that they can be transferred across
1824 // processors. We also populate the locFaces set to store a
1825 // record of all faces local to this process.
1826 for (i = 0; i < c->m_geomVec.size(); ++i)
1827 {
1828 SpatialDomains::Geometry2D *faceGeom =
1829 dynamic_cast<SpatialDomains::Geometry2D *>(
1830 c->m_geomVec[i]);
1831 ASSERTL1(faceGeom, "Unable to cast to Geometry2D*");
1832
1833 // Get geometry ID of this face and store in locFaces.
1834 int faceId = c->m_geomVec[i]->GetGlobalID();
1835 locFaces.insert(faceId);
1836
1837 // In serial, mesh partitioning will not have occurred so
1838 // need to fill composite ordering map manually.
1839 if (vComm->GetSize() == 1)
1840 {
1841 tmpOrder.push_back(c->m_geomVec[i]->GetGlobalID());
1842 }
1843
1844 // Loop over vertices and edges of the face to populate
1845 // allVerts, allEdges and allCoord maps.
1846 vector<int> vertList, edgeList;
1847 std::vector<SpatialDomains::PointGeom *> coordVec;
1848 vector<StdRegions::Orientation> orientVec;
1849 for (j = 0; j < faceGeom->GetNumVerts(); ++j)
1850 {
1851 vertList.push_back(faceGeom->GetVid(j));
1852 edgeList.push_back(faceGeom->GetEid(j));
1853 coordVec.push_back(faceGeom->GetVertex(j));
1854 orientVec.push_back(faceGeom->GetEorient(j));
1855 }
1856
1857 allVerts[faceId] = vertList;
1858 allEdges[faceId] = edgeList;
1859 allCoord[faceId] = coordVec;
1860 allOrient[faceId] = orientVec;
1861 }
1862
1863 // In serial, record the composite ordering in compOrder for
1864 // later in the routine.
1865 if (vComm->GetSize() == 1)
1866 {
1867 compOrder[it.second->begin()->first] = tmpOrder;
1868 }
1869
1870 // See if we already have either region1 or region2 stored in
1871 // perComps map already and do a sanity check to ensure regions
1872 // are mutually periodic.
1873 if (perComps.count(cId1) == 0)
1874 {
1875 if (perComps.count(cId2) == 0)
1876 {
1877 perComps[cId1] = cId2;
1878 }
1879 else
1880 {
1881 std::stringstream ss;
1882 ss << "Boundary region " << cId2 << " should be "
1883 << "periodic with " << perComps[cId2] << " but "
1884 << "found " << cId1 << " instead!";
1885 ASSERTL0(perComps[cId2] == cId1, ss.str());
1886 }
1887 }
1888 else
1889 {
1890 std::stringstream ss;
1891 ss << "Boundary region " << cId1 << " should be "
1892 << "periodic with " << perComps[cId1] << " but "
1893 << "found " << cId2 << " instead!";
1894 ASSERTL0(perComps[cId1] == cId1, ss.str());
1895 }
1896 }
1897
1898 // In case of periodic partition being split over many composites
1899 // may not have all periodic matches so check this
1900 int idmax = -1;
1901 for (auto &cIt : perComps)
1902 {
1903 idmax = max(idmax, cIt.first);
1904 idmax = max(idmax, cIt.second);
1905 }
1906 vComm->AllReduce(idmax, LibUtilities::ReduceMax);
1907 idmax++;
1908 Array<OneD, int> perid(idmax, -1);
1909 for (auto &cIt : perComps)
1910 {
1911 perid[cIt.first] = cIt.second;
1912 }
1913 vComm->AllReduce(perid, LibUtilities::ReduceMax);
1914 // update all partitions
1915 for (int i = 0; i < idmax; ++i)
1916 {
1917 if (perid[i] > -1)
1918 {
1919 // skip if equivlaent relationship has
1920 // already been speficied in list
1921 if (perComps.count(perid[i]))
1922 {
1923 continue;
1924 }
1925 perComps[i] = perid[i];
1926 }
1927 }
1928
1929 // The next routines process local face lists to
1930 // exchange vertices,
1931 // edges and faces.
1932 int n = vComm->GetSize();
1933 int p = vComm->GetRank();
1934 int totFaces;
1935 Array<OneD, int> facecounts(n, 0);
1936 Array<OneD, int> vertcounts(n, 0);
1937 Array<OneD, int> faceoffset(n, 0);
1938 Array<OneD, int> vertoffset(n, 0);
1939
1940 Array<OneD, int> rotcounts(n, 0);
1941 Array<OneD, int> rotoffset(n, 0);
1942
1943 rotcounts[p] = rotComp.size();
1944 vComm->AllReduce(rotcounts, LibUtilities::ReduceSum);
1945 int totrot = Vmath::Vsum(n, rotcounts, 1);
1946
1947 if (totrot)
1948 {
1949 for (i = 1; i < n; ++i)
1950 {
1951 rotoffset[i] = rotoffset[i - 1] + rotcounts[i - 1];
1952 }
1953
1954 Array<OneD, int> compid(totrot, 0);
1955 Array<OneD, int> rotdir(totrot, 0);
1956 Array<OneD, NekDouble> rotangle(totrot, 0.0);
1957 Array<OneD, NekDouble> rottol(totrot, 0.0);
1958
1959 // fill in rotational informaiton
1960 auto rIt = rotComp.begin();
1961
1962 for (i = 0; rIt != rotComp.end(); ++rIt)
1963 {
1964 compid[rotoffset[p] + i] = rIt->first;
1965 rotdir[rotoffset[p] + i] = rIt->second.m_dir;
1966 rotangle[rotoffset[p] + i] = rIt->second.m_angle;
1967 rottol[rotoffset[p] + i++] = rIt->second.m_tol;
1968 }
1969
1970 vComm->AllReduce(compid, LibUtilities::ReduceSum);
1971 vComm->AllReduce(rotdir, LibUtilities::ReduceSum);
1972 vComm->AllReduce(rotangle, LibUtilities::ReduceSum);
1973 vComm->AllReduce(rottol, LibUtilities::ReduceSum);
1974
1975 // Fill in full rotational composite list
1976 for (i = 0; i < totrot; ++i)
1977 {
1978 RotPeriodicInfo rinfo(rotdir[i], rotangle[i], rottol[i]);
1979
1980 rotComp[compid[i]] = rinfo;
1981 }
1982 }
1983
1984 // First exchange the number of faces on each process.
1985 facecounts[p] = locFaces.size();
1986 vComm->AllReduce(facecounts, LibUtilities::ReduceSum);
1987
1988 // Set up an offset map to allow us to distribute face IDs to all
1989 // processors.
1990 faceoffset[0] = 0;
1991 for (i = 1; i < n; ++i)
1992 {
1993 faceoffset[i] = faceoffset[i - 1] + facecounts[i - 1];
1994 }
1995
1996 // Calculate total number of faces.
1997 totFaces = Vmath::Vsum(n, facecounts, 1);
1998
1999 // faceIds holds face IDs for each periodic face. faceVerts holds
2000 // the number of vertices in this face.
2001 Array<OneD, int> faceIds(totFaces, 0);
2002 Array<OneD, int> faceVerts(totFaces, 0);
2003
2004 // Process p writes IDs of its faces into position faceoffset[p] of
2005 // faceIds which allows us to perform an AllReduce to distribute
2006 // information amongst processors.
2007 auto sIt = locFaces.begin();
2008 for (i = 0; sIt != locFaces.end(); ++sIt)
2009 {
2010 faceIds[faceoffset[p] + i] = *sIt;
2011 faceVerts[faceoffset[p] + i++] = allVerts[*sIt].size();
2012 }
2013
2014 vComm->AllReduce(faceIds, LibUtilities::ReduceSum);
2015 vComm->AllReduce(faceVerts, LibUtilities::ReduceSum);
2016
2017 // procVerts holds number of vertices (and also edges since each
2018 // face is 2D) on each process.
2019 Array<OneD, int> procVerts(n, 0);
2020 int nTotVerts;
2021
2022 // Note if there are no periodic faces at all calling Vsum will
2023 // cause a segfault.
2024 if (totFaces > 0)
2025 {
2026 // Calculate number of vertices on each processor.
2027 nTotVerts = Vmath::Vsum(totFaces, faceVerts, 1);
2028 }
2029 else
2030 {
2031 nTotVerts = 0;
2032 }
2033
2034 for (i = 0; i < n; ++i)
2035 {
2036 if (facecounts[i] > 0)
2037 {
2038 procVerts[i] = Vmath::Vsum(facecounts[i],
2039 faceVerts + faceoffset[i], 1);
2040 }
2041 else
2042 {
2043 procVerts[i] = 0;
2044 }
2045 }
2046
2047 // vertoffset is defined in the same manner as edgeoffset
2048 // beforehand.
2049 vertoffset[0] = 0;
2050 for (i = 1; i < n; ++i)
2051 {
2052 vertoffset[i] = vertoffset[i - 1] + procVerts[i - 1];
2053 }
2054
2055 // At this point we exchange all vertex IDs, edge IDs and vertex
2056 // coordinates for each face. The coordinates are necessary because
2057 // we need to calculate relative face orientations between periodic
2058 // faces to determined edge and vertex connectivity.
2059 Array<OneD, int> vertIds(nTotVerts, 0);
2060 Array<OneD, int> edgeIds(nTotVerts, 0);
2061 Array<OneD, int> edgeOrt(nTotVerts, 0);
2062 Array<OneD, NekDouble> vertX(nTotVerts, 0.0);
2063 Array<OneD, NekDouble> vertY(nTotVerts, 0.0);
2064 Array<OneD, NekDouble> vertZ(nTotVerts, 0.0);
2065
2066 for (cnt = 0, sIt = locFaces.begin(); sIt != locFaces.end(); ++sIt)
2067 {
2068 for (j = 0; j < allVerts[*sIt].size(); ++j)
2069 {
2070 int vertId = allVerts[*sIt][j];
2071 vertIds[vertoffset[p] + cnt] = vertId;
2072 vertX[vertoffset[p] + cnt] = (*allCoord[*sIt][j])(0);
2073 vertY[vertoffset[p] + cnt] = (*allCoord[*sIt][j])(1);
2074 vertZ[vertoffset[p] + cnt] = (*allCoord[*sIt][j])(2);
2075 edgeIds[vertoffset[p] + cnt] = allEdges[*sIt][j];
2076 edgeOrt[vertoffset[p] + cnt++] = allOrient[*sIt][j];
2077 }
2078 }
2079
2080 vComm->AllReduce(vertIds, LibUtilities::ReduceSum);
2081 vComm->AllReduce(vertX, LibUtilities::ReduceSum);
2082 vComm->AllReduce(vertY, LibUtilities::ReduceSum);
2083 vComm->AllReduce(vertZ, LibUtilities::ReduceSum);
2084 vComm->AllReduce(edgeIds, LibUtilities::ReduceSum);
2085 vComm->AllReduce(edgeOrt, LibUtilities::ReduceSum);
2086
2087 // Finally now we have all of this information, we construct maps
2088 // which make accessing the information easier. These are
2089 // conceptually the same as all* maps at the beginning of the
2090 // routine, but now hold information for all periodic vertices.
2091 map<int, vector<int>> vertMap;
2092 map<int, vector<int>> edgeMap;
2093 map<int, std::vector<SpatialDomains::PointGeom *>> coordMap;
2094
2095 // These final two maps are required for determining the relative
2096 // orientation of periodic edges. vCoMap associates vertex IDs with
2097 // their coordinates, and eIdMap maps an edge ID to the two vertices
2098 // which construct it.
2099 map<int, SpatialDomains::PointGeomUniquePtr> vCoMap;
2100 map<int, pair<int, int>> eIdMap;
2101
2102 for (cnt = i = 0; i < totFaces; ++i)
2103 {
2104 vector<int> edges(faceVerts[i]);
2105 vector<int> verts(faceVerts[i]);
2106 std::vector<SpatialDomains::PointGeom *> coord(faceVerts[i]);
2107
2108 // Keep track of cnt to enable correct edge vertices to be
2109 // inserted into eIdMap.
2110 int tmp = cnt;
2111 for (j = 0; j < faceVerts[i]; ++j, ++cnt)
2112 {
2113 edges[j] = edgeIds[cnt];
2114 verts[j] = vertIds[cnt];
2115
2116 auto vIt = vCoMap.find(vertIds[cnt]);
2117
2118 if (vIt == vCoMap.end())
2119 {
2121 AllocateUniquePtr(3, verts[j], vertX[cnt],
2122 vertY[cnt], vertZ[cnt]);
2123 coord[j] = pt.get();
2124 vCoMap[vertIds[cnt]] = std::move(pt);
2125 }
2126 else
2127 {
2128 coord[j] = vIt->second.get();
2129 }
2130
2131 // Try to insert edge into the eIdMap to avoid re-inserting.
2132 auto testIns = eIdMap.insert(make_pair(
2133 edgeIds[cnt],
2134 make_pair(vertIds[tmp + j],
2135 vertIds[tmp + ((j + 1) % faceVerts[i])])));
2136
2137 if (testIns.second == false)
2138 {
2139 continue;
2140 }
2141
2142 // If the edge is reversed with respect to the face, then
2143 // swap the edges so that we have the original ordering of
2144 // the edge in the 3D element. This is necessary to properly
2145 // determine edge orientation. Note that the logic relies on
2146 // the fact that all edge forward directions are CCW
2147 // orientated: we use a tensor product ordering for 2D
2148 // elements so need to reverse this for edge IDs 2 and 3.
2149 StdRegions::Orientation edgeOrient =
2150 static_cast<StdRegions::Orientation>(edgeOrt[cnt]);
2151 if (j > 1)
2152 {
2153 edgeOrient = edgeOrient == StdRegions::eForwards
2156 }
2157
2158 if (edgeOrient == StdRegions::eBackwards)
2159 {
2160 swap(testIns.first->second.first,
2161 testIns.first->second.second);
2162 }
2163 }
2164
2165 vertMap[faceIds[i]] = verts;
2166 edgeMap[faceIds[i]] = edges;
2167 coordMap[faceIds[i]] = coord;
2168 }
2169
2170 // Go through list of composites and figure out which edges are
2171 // parallel from original ordering in session file. This includes
2172 // composites which are not necessarily on this process.
2173
2174 // Store temporary map of periodic vertices which will hold all
2175 // periodic vertices on the entire mesh so that doubly periodic
2176 // vertices/edges can be counted properly across partitions. Local
2177 // vertices/edges are copied into m_periodicVerts and
2178 // m_periodicEdges at the end of the function.
2179 PeriodicMap periodicVerts, periodicEdges;
2180
2181 // Construct two maps which determine how vertices and edges of
2182 // faces connect given a specific face orientation. The key of the
2183 // map is the number of vertices in the face, used to determine
2184 // difference between tris and quads.
2185 map<int, map<StdRegions::Orientation, vector<int>>> vmap;
2186 map<int, map<StdRegions::Orientation, vector<int>>> emap;
2187
2188 map<StdRegions::Orientation, vector<int>> quadVertMap;
2189 quadVertMap[StdRegions::eDir1FwdDir1_Dir2FwdDir2] = {0, 1, 2, 3};
2190 quadVertMap[StdRegions::eDir1FwdDir1_Dir2BwdDir2] = {3, 2, 1, 0};
2191 quadVertMap[StdRegions::eDir1BwdDir1_Dir2FwdDir2] = {1, 0, 3, 2};
2192 quadVertMap[StdRegions::eDir1BwdDir1_Dir2BwdDir2] = {2, 3, 0, 1};
2193 quadVertMap[StdRegions::eDir1FwdDir2_Dir2FwdDir1] = {0, 3, 2, 1};
2194 quadVertMap[StdRegions::eDir1FwdDir2_Dir2BwdDir1] = {1, 2, 3, 0};
2195 quadVertMap[StdRegions::eDir1BwdDir2_Dir2FwdDir1] = {3, 0, 1, 2};
2196 quadVertMap[StdRegions::eDir1BwdDir2_Dir2BwdDir1] = {2, 1, 0, 3};
2197
2198 map<StdRegions::Orientation, vector<int>> quadEdgeMap;
2199 quadEdgeMap[StdRegions::eDir1FwdDir1_Dir2FwdDir2] = {0, 1, 2, 3};
2200 quadEdgeMap[StdRegions::eDir1FwdDir1_Dir2BwdDir2] = {2, 1, 0, 3};
2201 quadEdgeMap[StdRegions::eDir1BwdDir1_Dir2FwdDir2] = {0, 3, 2, 1};
2202 quadEdgeMap[StdRegions::eDir1BwdDir1_Dir2BwdDir2] = {2, 3, 0, 1};
2203 quadEdgeMap[StdRegions::eDir1FwdDir2_Dir2FwdDir1] = {3, 2, 1, 0};
2204 quadEdgeMap[StdRegions::eDir1FwdDir2_Dir2BwdDir1] = {1, 2, 3, 0};
2205 quadEdgeMap[StdRegions::eDir1BwdDir2_Dir2FwdDir1] = {3, 0, 1, 2};
2206 quadEdgeMap[StdRegions::eDir1BwdDir2_Dir2BwdDir1] = {1, 0, 3, 2};
2207
2208 map<StdRegions::Orientation, vector<int>> triVertMap;
2209 triVertMap[StdRegions::eDir1FwdDir1_Dir2FwdDir2] = {0, 1, 2};
2210 triVertMap[StdRegions::eDir1BwdDir1_Dir2FwdDir2] = {1, 0, 2};
2211
2212 map<StdRegions::Orientation, vector<int>> triEdgeMap;
2213 triEdgeMap[StdRegions::eDir1FwdDir1_Dir2FwdDir2] = {0, 1, 2};
2214 triEdgeMap[StdRegions::eDir1BwdDir1_Dir2FwdDir2] = {0, 2, 1};
2215
2216 vmap[3] = triVertMap;
2217 vmap[4] = quadVertMap;
2218 emap[3] = triEdgeMap;
2219 emap[4] = quadEdgeMap;
2220
2221 map<int, int> allCompPairs;
2222
2223 // Collect composite id's of each periodic face for use if rotation
2224 // is required
2225 map<int, int> fIdToCompId;
2226
2227 // Finally we have enough information to populate the periodic
2228 // vertex, edge and face maps. Begin by looping over all pairs of
2229 // periodic composites to determine pairs of periodic faces.
2230 for (auto &cIt : perComps)
2231 {
2233 const int id1 = cIt.first;
2234 const int id2 = cIt.second;
2235 std::string id1s = std::to_string(id1);
2236 std::string id2s = std::to_string(id2);
2237
2238 if (compMap.count(id1) > 0)
2239 {
2240 c[0] = compMap[id1];
2241 }
2242
2243 if (compMap.count(id2) > 0)
2244 {
2245 c[1] = compMap[id2];
2246 }
2247
2248 // Loop over composite ordering to construct list of all
2249 // periodic faces, regardless of whether they are on this
2250 // process.
2251 map<int, int> compPairs;
2252
2253 ASSERTL0(compOrder.count(id1) > 0,
2254 "Unable to find composite " + id1s + " in order map.");
2255 ASSERTL0(compOrder.count(id2) > 0,
2256 "Unable to find composite " + id2s + " in order map.");
2257 WARNINGL0(
2258 compOrder[id1].size() == compOrder[id2].size(),
2259 "Periodic composites " + id1s + " and " + id2s +
2260 " should have the same number of elements. Have " +
2261 std::to_string(compOrder[id1].size()) + " vs " +
2262 std::to_string(compOrder[id2].size()));
2263 ASSERTL0(compOrder[id1].size() > 0, "Periodic composites " +
2264 id1s + " and " + id2s +
2265 " are empty!");
2266
2267 // Look up composite ordering to determine pairs.
2268 for (i = 0; i < compOrder[id1].size(); ++i)
2269 {
2270 int eId1 = compOrder[id1][i];
2271 int eId2 = compOrder[id2][i];
2272
2273 ASSERTL0(compPairs.count(eId1) == 0, "Already paired.");
2274
2275 // Sanity check that the faces are mutually periodic.
2276 if (compPairs.count(eId2) != 0)
2277 {
2278 ASSERTL0(compPairs[eId2] == eId1, "Pairing incorrect");
2279 }
2280 compPairs[eId1] = eId2;
2281
2282 // store a map of face ids to composite ids
2283 fIdToCompId[eId1] = id1;
2284 fIdToCompId[eId2] = id2;
2285 }
2286
2287 // Now that we have all pairs of periodic faces, loop over the
2288 // ones local on this process and populate face/edge/vertex
2289 // maps.
2290 for (auto &pIt : compPairs)
2291 {
2292 int ids[2] = {pIt.first, pIt.second};
2293 bool local[2] = {locFaces.count(pIt.first) > 0,
2294 locFaces.count(pIt.second) > 0};
2295
2296 ASSERTL0(coordMap.count(ids[0]) > 0 &&
2297 coordMap.count(ids[1]) > 0,
2298 "Unable to find face in coordinate map");
2299
2300 allCompPairs[pIt.first] = pIt.second;
2301 allCompPairs[pIt.second] = pIt.first;
2302
2303 // Loop up coordinates of the faces, check they have the
2304 // same number of vertices.
2305 std::vector<SpatialDomains::PointGeom *> tmpVec[2] = {
2306 coordMap[ids[0]], coordMap[ids[1]]};
2307
2308 ASSERTL0(tmpVec[0].size() == tmpVec[1].size(),
2309 "Two periodic faces have different number "
2310 "of vertices!");
2311
2312 // o will store relative orientation of faces. Note that in
2313 // some transpose cases (Dir1FwdDir2_Dir2BwdDir1 and
2314 // Dir1BwdDir1_Dir2FwdDir1) it seems orientation will be
2315 // different going from face1->face2 instead of face2->face1
2316 // (check this).
2318 bool rotbnd = false;
2319 int dir = 0;
2320 NekDouble angle = 0.0;
2321 NekDouble sign = 0.0;
2322 NekDouble tol = 1e-8;
2323
2324 // check to see if perioid boundary is rotated
2325 if (rotComp.count(fIdToCompId[pIt.first]))
2326 {
2327 rotbnd = true;
2328 dir = rotComp[fIdToCompId[pIt.first]].m_dir;
2329 angle = rotComp[fIdToCompId[pIt.first]].m_angle;
2330 tol = rotComp[fIdToCompId[pIt.first]].m_tol;
2331 }
2332
2333 // Record periodic faces.
2334 for (i = 0; i < 2; ++i)
2335 {
2336 if (!local[i])
2337 {
2338 continue;
2339 }
2340
2341 // Reference to the other face.
2342 int other = (i + 1) % 2;
2343
2344 // angle is set up for i = 0 to i = 1
2345 sign = (i == 0) ? 1.0 : -1.0;
2346
2347 // Calculate relative face orientation.
2348 if (tmpVec[0].size() == 3)
2349 {
2351 {tmpVec[i][0], tmpVec[i][1], tmpVec[i][2]},
2352 {tmpVec[other][0], tmpVec[other][1],
2353 tmpVec[other][2]},
2354 rotbnd, dir, sign * angle, tol);
2355 }
2356 else
2357 {
2359 {tmpVec[i][0], tmpVec[i][1], tmpVec[i][2],
2360 tmpVec[i][3]},
2361 {tmpVec[other][0], tmpVec[other][1],
2362 tmpVec[other][2], tmpVec[other][3]},
2363 rotbnd, dir, sign * angle, tol);
2364 }
2365
2366 // Record face ID, orientation and whether other face is
2367 // local.
2368 PeriodicEntity ent(ids[other], o, local[other]);
2369 m_periodicFaces[ids[i]].push_back(ent);
2370 }
2371
2372 int nFaceVerts = vertMap[ids[0]].size();
2373
2374 // Determine periodic vertices.
2375 for (i = 0; i < 2; ++i)
2376 {
2377 int other = (i + 1) % 2;
2378
2379 // angle is set up for i = 0 to i = 1
2380 sign = (i == 0) ? 1.0 : -1.0;
2381
2382 // Calculate relative face orientation.
2383 if (tmpVec[0].size() == 3)
2384 {
2386 {tmpVec[i][0], tmpVec[i][1], tmpVec[i][2]},
2387 {tmpVec[other][0], tmpVec[other][1],
2388 tmpVec[other][2]},
2389 rotbnd, dir, sign * angle, tol);
2390 }
2391 else
2392 {
2394 {tmpVec[i][0], tmpVec[i][1], tmpVec[i][2],
2395 tmpVec[i][3]},
2396 {tmpVec[other][0], tmpVec[other][1],
2397 tmpVec[other][2], tmpVec[other][3]},
2398 rotbnd, dir, sign * angle, tol);
2399 }
2400
2401 if (nFaceVerts == 3)
2402 {
2403 ASSERTL0(
2406 "Unsupported face orientation for face " +
2407 std::to_string(ids[i]));
2408 }
2409
2410 // Look up vertices for this face.
2411 vector<int> per1 = vertMap[ids[i]];
2412 vector<int> per2 = vertMap[ids[other]];
2413
2414 // tmpMap will hold the pairs of vertices which are
2415 // periodic.
2416 map<int, pair<int, bool>> tmpMap;
2417
2418 // Use vmap to determine which vertices connect given
2419 // the orientation o.
2420 for (j = 0; j < nFaceVerts; ++j)
2421 {
2422 int v = vmap[nFaceVerts][o][j];
2423 tmpMap[per1[j]] =
2424 make_pair(per2[v], locVerts.count(per2[v]) > 0);
2425 }
2426
2427 // Now loop over tmpMap to associate periodic vertices.
2428 for (auto &mIt : tmpMap)
2429 {
2430 PeriodicEntity ent2(mIt.second.first,
2432 mIt.second.second);
2433
2434 // See if this vertex has been recorded already.
2435 auto perIt = periodicVerts.find(mIt.first);
2436
2437 if (perIt == periodicVerts.end())
2438 {
2439 // Vertex is new - just record this entity as
2440 // usual.
2441 periodicVerts[mIt.first].push_back(ent2);
2442 perIt = periodicVerts.find(mIt.first);
2443 }
2444 else
2445 {
2446 // Vertex is known - loop over the vertices
2447 // inside the record and potentially add vertex
2448 // mIt.second to the list.
2449 for (k = 0; k < perIt->second.size(); ++k)
2450 {
2451 if (perIt->second[k].id == mIt.second.first)
2452 {
2453 break;
2454 }
2455 }
2456
2457 if (k == perIt->second.size())
2458 {
2459 perIt->second.push_back(ent2);
2460 }
2461 }
2462 }
2463 }
2464
2465 // Determine periodic edges. Logic is the same as above,
2466 // and perhaps should be condensed to avoid replication.
2467 for (i = 0; i < 2; ++i)
2468 {
2469 int other = (i + 1) % 2;
2470
2471 // angle is set up for i = 0 to i = 1
2472 sign = (i == 0) ? 1.0 : -1.0;
2473
2474 if (tmpVec[0].size() == 3)
2475 {
2477 {tmpVec[i][0], tmpVec[i][1], tmpVec[i][2]},
2478 {tmpVec[other][0], tmpVec[other][1],
2479 tmpVec[other][2]},
2480 rotbnd, dir, sign * angle, tol);
2481 }
2482 else
2483 {
2485 {tmpVec[i][0], tmpVec[i][1], tmpVec[i][2],
2486 tmpVec[i][3]},
2487 {tmpVec[other][0], tmpVec[other][1],
2488 tmpVec[other][2], tmpVec[other][3]},
2489 rotbnd, dir, sign * angle, tol);
2490 }
2491
2492 vector<int> per1 = edgeMap[ids[i]];
2493 vector<int> per2 = edgeMap[ids[other]];
2494
2495 map<int, pair<int, bool>> tmpMap;
2496
2497 for (j = 0; j < nFaceVerts; ++j)
2498 {
2499 int e = emap[nFaceVerts][o][j];
2500 tmpMap[per1[j]] =
2501 make_pair(per2[e], locEdges.count(per2[e]) > 0);
2502 }
2503
2504 for (auto &mIt : tmpMap)
2505 {
2506 // Note we assume orientation of edges is forwards -
2507 // this may be reversed later.
2508 PeriodicEntity ent2(mIt.second.first,
2510 mIt.second.second);
2511 auto perIt = periodicEdges.find(mIt.first);
2512
2513 if (perIt == periodicEdges.end())
2514 {
2515 periodicEdges[mIt.first].push_back(ent2);
2516 perIt = periodicEdges.find(mIt.first);
2517 }
2518 else
2519 {
2520 for (k = 0; k < perIt->second.size(); ++k)
2521 {
2522 if (perIt->second[k].id == mIt.second.first)
2523 {
2524 break;
2525 }
2526 }
2527
2528 if (k == perIt->second.size())
2529 {
2530 perIt->second.push_back(ent2);
2531 }
2532 }
2533 }
2534 }
2535 }
2536 }
2537
2538 // Make sure that the nubmer of face pairs and the
2539 // face Id to composite Id map match in size
2540 ASSERTL1(allCompPairs.size() == fIdToCompId.size(),
2541 "At this point the size of allCompPairs "
2542 "should have been the same as fIdToCompId");
2543
2544 // also will need an edge id to composite id at
2545 // end of routine
2546 map<int, int> eIdToCompId;
2547
2548 // Search for periodic vertices and edges which are not
2549 // in a periodic composite but lie in this process. First,
2550 // loop over all information we have from other
2551 // processors.
2552 for (cnt = i = 0; i < totFaces; ++i)
2553 {
2554 bool rotbnd = false;
2555 int dir = 0;
2556 NekDouble angle = 0.0;
2557 NekDouble tol = 1e-8;
2558
2559 int faceId = faceIds[i];
2560
2561 ASSERTL0(allCompPairs.count(faceId) > 0,
2562 "Unable to find matching periodic face. faceId = " +
2563 std::to_string(faceId));
2564
2565 int perFaceId = allCompPairs[faceId];
2566
2567 // check to see if periodic boundary is rotated
2568 ASSERTL1((rotComp.size() == 0) || fIdToCompId.count(faceId) > 0,
2569 "Face " + std::to_string(faceId) +
2570 " not found in fIdtoCompId map");
2571 if (rotComp.count(fIdToCompId[faceId]))
2572 {
2573 rotbnd = true;
2574 dir = rotComp[fIdToCompId[faceId]].m_dir;
2575 angle = rotComp[fIdToCompId[faceId]].m_angle;
2576 tol = rotComp[fIdToCompId[faceId]].m_tol;
2577 }
2578
2579 for (j = 0; j < faceVerts[i]; ++j, ++cnt)
2580 {
2581 int vId = vertIds[cnt];
2582
2583 auto perId = periodicVerts.find(vId);
2584
2585 if (perId == periodicVerts.end())
2586 {
2587
2588 // This vertex is not included in the
2589 // map. Figure out which vertex it is supposed
2590 // to be periodic with. perFaceId is the face
2591 // ID which is periodic with faceId. The logic
2592 // is much the same as the loop above.
2593 std::vector<SpatialDomains::PointGeom *> tmpVec[2] = {
2594 coordMap[faceId], coordMap[perFaceId]};
2595
2596 int nFaceVerts = tmpVec[0].size();
2598 nFaceVerts == 3
2600 {tmpVec[0][0], tmpVec[0][1],
2601 tmpVec[0][2]},
2602 {tmpVec[1][0], tmpVec[1][1],
2603 tmpVec[1][2]},
2604 rotbnd, dir, angle, tol)
2606 {tmpVec[0][0], tmpVec[0][1], tmpVec[0][2],
2607 tmpVec[0][3]},
2608 {tmpVec[1][0], tmpVec[1][1], tmpVec[1][2],
2609 tmpVec[1][3]},
2610 rotbnd, dir, angle, tol);
2611
2612 // Use vmap to determine which vertex of the other face
2613 // should be periodic with this one.
2614 int perVertexId =
2615 vertMap[perFaceId][vmap[nFaceVerts][o][j]];
2616
2617 PeriodicEntity ent(perVertexId,
2619 locVerts.count(perVertexId) > 0);
2620
2621 periodicVerts[vId].push_back(ent);
2622 }
2623
2624 int eId = edgeIds[cnt];
2625
2626 perId = periodicEdges.find(eId);
2627
2628 // this map is required at very end to determine rotation of
2629 // edges.
2630 if (rotbnd)
2631 {
2632 eIdToCompId[eId] = fIdToCompId[faceId];
2633 }
2634
2635 if (perId == periodicEdges.end())
2636 {
2637 // This edge is not included in the map. Figure
2638 // out which edge it is supposed to be periodic
2639 // with. perFaceId is the face ID which is
2640 // periodic with faceId. The logic is much the
2641 // same as the loop above.
2642 std::vector<SpatialDomains::PointGeom *> tmpVec[2] = {
2643 coordMap[faceId], coordMap[perFaceId]};
2644
2645 int nFaceEdges = tmpVec[0].size();
2647 nFaceEdges == 3
2649 {tmpVec[0][0], tmpVec[0][1],
2650 tmpVec[0][2]},
2651 {tmpVec[1][0], tmpVec[1][1],
2652 tmpVec[1][2]},
2653 rotbnd, dir, angle, tol)
2655 {tmpVec[0][0], tmpVec[0][1], tmpVec[0][2],
2656 tmpVec[0][3]},
2657 {tmpVec[1][0], tmpVec[1][1], tmpVec[1][2],
2658 tmpVec[1][3]},
2659 rotbnd, dir, angle, tol);
2660
2661 // Use emap to determine which edge of the other
2662 // face should be periodic with this one.
2663 int perEdgeId =
2664 edgeMap[perFaceId][emap[nFaceEdges][o][j]];
2665
2667 locEdges.count(perEdgeId) > 0);
2668
2669 periodicEdges[eId].push_back(ent);
2670
2671 // this map is required at very end to
2672 // determine rotation of edges.
2673 if (rotbnd)
2674 {
2675 eIdToCompId[perEdgeId] = fIdToCompId[perFaceId];
2676 }
2677 }
2678 }
2679 }
2680
2681 // Finally, we must loop over the periodicVerts and periodicEdges
2682 // map to complete connectivity information.
2683 for (auto &perIt : periodicVerts)
2684 {
2685 // For each vertex that is periodic with this one...
2686 for (i = 0; i < perIt.second.size(); ++i)
2687 {
2688 // Find the vertex in the periodicVerts map...
2689 auto perIt2 = periodicVerts.find(perIt.second[i].id);
2690 ASSERTL0(perIt2 != periodicVerts.end(),
2691 "Couldn't find periodic vertex.");
2692
2693 // Now search through this vertex's list and make sure that
2694 // we have a record of any vertices which aren't in the
2695 // original list.
2696 for (j = 0; j < perIt2->second.size(); ++j)
2697 {
2698 if (perIt2->second[j].id == perIt.first)
2699 {
2700 continue;
2701 }
2702
2703 for (k = 0; k < perIt.second.size(); ++k)
2704 {
2705 if (perIt2->second[j].id == perIt.second[k].id)
2706 {
2707 break;
2708 }
2709 }
2710
2711 if (k == perIt.second.size())
2712 {
2713 perIt.second.push_back(perIt2->second[j]);
2714 }
2715 }
2716 }
2717 }
2718
2719 for (auto &perIt : periodicEdges)
2720 {
2721 for (i = 0; i < perIt.second.size(); ++i)
2722 {
2723 auto perIt2 = periodicEdges.find(perIt.second[i].id);
2724 ASSERTL0(perIt2 != periodicEdges.end(),
2725 "Couldn't find periodic edge.");
2726
2727 for (j = 0; j < perIt2->second.size(); ++j)
2728 {
2729 if (perIt2->second[j].id == perIt.first)
2730 {
2731 continue;
2732 }
2733
2734 for (k = 0; k < perIt.second.size(); ++k)
2735 {
2736 if (perIt2->second[j].id == perIt.second[k].id)
2737 {
2738 break;
2739 }
2740 }
2741
2742 if (k == perIt.second.size())
2743 {
2744 perIt.second.push_back(perIt2->second[j]);
2745 }
2746 }
2747 }
2748 }
2749
2750 // Loop over periodic edges to determine relative edge orientations.
2751 for (auto &perIt : periodicEdges)
2752 {
2753 bool rotbnd = false;
2754 int dir = 0;
2755 NekDouble angle = 0.0;
2756 NekDouble tol = 1e-8;
2757
2758 // Find edge coordinates
2759 auto eIt = eIdMap.find(perIt.first);
2760 SpatialDomains::PointGeom v[2] = {*vCoMap[eIt->second.first],
2761 *vCoMap[eIt->second.second]};
2762
2763 // check to see if perioid boundary is rotated
2764 if (rotComp.count(eIdToCompId[perIt.first]))
2765 {
2766 rotbnd = true;
2767 dir = rotComp[eIdToCompId[perIt.first]].m_dir;
2768 angle = rotComp[eIdToCompId[perIt.first]].m_angle;
2769 tol = rotComp[eIdToCompId[perIt.first]].m_tol;
2770 }
2771
2772 // Loop over each edge, and construct a vector that takes us
2773 // from one vertex to another. Use this to figure out which
2774 // vertex maps to which.
2775 for (i = 0; i < perIt.second.size(); ++i)
2776 {
2777 eIt = eIdMap.find(perIt.second[i].id);
2778
2780 *vCoMap[eIt->second.first],
2781 *vCoMap[eIt->second.second]};
2782
2783 int vMap[2] = {-1, -1};
2784 if (rotbnd)
2785 {
2786
2788
2789 r.Rotate(v[0], dir, angle);
2790
2791 if (r.dist(w[0]) < tol)
2792 {
2793 vMap[0] = 0;
2794 }
2795 else
2796 {
2797 r.Rotate(v[1], dir, angle);
2798 if (r.dist(w[0]) < tol)
2799 {
2800 vMap[0] = 1;
2801 }
2802 else
2803 {
2805 "Unable to align rotationally "
2806 "periodic edge vertex");
2807 }
2808 }
2809 }
2810 else // translation test
2811 {
2812 NekDouble cx =
2813 0.5 * (w[0](0) - v[0](0) + w[1](0) - v[1](0));
2814 NekDouble cy =
2815 0.5 * (w[0](1) - v[0](1) + w[1](1) - v[1](1));
2816 NekDouble cz =
2817 0.5 * (w[0](2) - v[0](2) + w[1](2) - v[1](2));
2818
2819 for (j = 0; j < 2; ++j)
2820 {
2821 NekDouble x = v[j](0);
2822 NekDouble y = v[j](1);
2823 NekDouble z = v[j](2);
2824 for (k = 0; k < 2; ++k)
2825 {
2826 NekDouble x1 = w[k](0) - cx;
2827 NekDouble y1 = w[k](1) - cy;
2828 NekDouble z1 = w[k](2) - cz;
2829
2830 if (sqrt((x1 - x) * (x1 - x) +
2831 (y1 - y) * (y1 - y) +
2832 (z1 - z) * (z1 - z)) < 1e-8)
2833 {
2834 vMap[k] = j;
2835 break;
2836 }
2837 }
2838 }
2839
2840 // Sanity check the map.
2841 ASSERTL0(vMap[0] >= 0 && vMap[1] >= 0,
2842 "Unable to align periodic edge vertex.");
2843 ASSERTL0((vMap[0] == 0 || vMap[0] == 1) &&
2844 (vMap[1] == 0 || vMap[1] == 1) &&
2845 (vMap[0] != vMap[1]),
2846 "Unable to align periodic edge vertex.");
2847 }
2848
2849 // If 0 -> 0 then edges are aligned already; otherwise
2850 // reverse the orientation.
2851 if (vMap[0] != 0)
2852 {
2853 perIt.second[i].orient = StdRegions::eBackwards;
2854 }
2855 }
2856 }
2857
2858 // Do one final loop over periodic vertices/edges to remove
2859 // non-local vertices/edges from map.
2860 for (auto &perIt : periodicVerts)
2861 {
2862 if (locVerts.count(perIt.first) > 0)
2863 {
2864 m_periodicVerts.insert(perIt);
2865 }
2866 }
2867
2868 for (auto &perIt : periodicEdges)
2869 {
2870 if (locEdges.count(perIt.first) > 0)
2871 {
2872 m_periodicEdges.insert(perIt);
2873 }
2874 }
2875 }
2876 break;
2877 default:
2878 ASSERTL1(false, "Not setup for this expansion");
2879 break;
2880 }
2881}
2882
2883/**
2884 *
2885 */
2887 const GlobalLinSysKey &mkey)
2888{
2890 "Routine currently only tested for HybridDGHelmholtz");
2891
2893 "Full matrix global systems are not supported for HDG "
2894 "expansions");
2895
2896 ASSERTL1(mkey.GetGlobalSysSolnType() == m_traceMap->GetGlobalSysSolnType(),
2897 "The local to global map is not set up for the requested "
2898 "solution type");
2899
2900 GlobalLinSysSharedPtr glo_matrix;
2901 auto matrixIter = m_globalBndMat->find(mkey);
2902
2903 if (matrixIter == m_globalBndMat->end())
2904 {
2905 glo_matrix = GenGlobalBndLinSys(mkey, m_traceMap);
2906 (*m_globalBndMat)[mkey] = glo_matrix;
2907 }
2908 else
2909 {
2910 glo_matrix = matrixIter->second;
2911 }
2912
2913 return glo_matrix;
2914}
2915
2916/**
2917 * For each boundary region, checks that the types and number of
2918 * boundary expansions in that region match.
2919 * @param In Field to compare with.
2920 * @return True if boundary conditions match.
2921 */
2923{
2924 int i;
2925 bool returnval = true;
2926
2927 for (i = 0; i < m_bndConditions.size(); ++i)
2928 {
2929 // check to see if boundary condition type is the same
2930 // and there are the same number of boundary
2931 // conditions in the boundary definition.
2932 if ((m_bndConditions[i]->GetBoundaryConditionType() !=
2933 In.m_bndConditions[i]->GetBoundaryConditionType()) ||
2934 (m_bndCondExpansions[i]->GetExpSize() !=
2935 In.m_bndCondExpansions[i]->GetExpSize()))
2936 {
2937 returnval = false;
2938 break;
2939 }
2940 }
2941
2942 // Compare with all other processes. Return true only if all
2943 // processes report having the same boundary conditions.
2944 int vSame = (returnval ? 1 : 0);
2945 m_comm->GetRowComm()->AllReduce(vSame, LibUtilities::ReduceMin);
2946
2947 return (vSame == 1);
2948}
2949
2951{
2952 if (m_negatedFluxNormal.size() == 0)
2953 {
2955 &elmtToTrace = m_traceMap->GetElmtToTrace();
2956
2957 m_negatedFluxNormal.resize(2 * GetExpSize());
2958
2959 for (int i = 0; i < GetExpSize(); ++i)
2960 {
2961
2962 for (int v = 0; v < 2; ++v)
2963 {
2965 elmtToTrace[i][v]->as<LocalRegions::Expansion0D>();
2966
2967 if (vertExp->GetLeftAdjacentElementExp()
2968 ->GetGeom()
2969 ->GetGlobalID() !=
2970 (*m_exp)[i]->GetGeom()->GetGlobalID())
2971 {
2972 m_negatedFluxNormal[2 * i + v] = true;
2973 }
2974 else
2975 {
2976 m_negatedFluxNormal[2 * i + v] = false;
2977 }
2978 }
2979 }
2980 }
2981
2982 return m_negatedFluxNormal;
2983}
2984
2986{
2988 {
2989 SetUpDG();
2990 }
2991
2992 return m_trace;
2993}
2994
2999
3004
3010
3011// Return the internal vector which identifieds if trace
3012// is left adjacent definiing which trace the normal
3013// points otwards from
3015{
3016 return m_leftAdjacentTraces;
3017}
3018
3024
3030
3035
3041
3044{
3045 for (int n = 0; n < m_periodicFwdCopy.size(); ++n)
3046 {
3047 Bwd[m_periodicBwdCopy[n]] = Fwd[m_periodicFwdCopy[n]];
3048 }
3049}
3050
3052{
3053 int cnt = 0;
3054 for (int n = 0; n < m_bndConditions.size(); ++n)
3055 {
3056 // check to see if boundary is rotationally aligned
3057 if (boost::icontains(m_bndConditions[n]->GetUserDefined(), "Rotated"))
3058 {
3059 vector<string> tmpstr;
3060
3061 boost::split(tmpstr, m_bndConditions[n]->GetUserDefined(),
3062 boost::is_any_of(":"));
3063
3064 ASSERTL1(tmpstr.size() > 2,
3065 "Expected Rotated user defined string to "
3066 "contain direction and rotation angle "
3067 "and optionally a tolerance, "
3068 "i.e. Rotated:dir:PI/2:1e-6");
3069
3070 ASSERTL1((tmpstr[1] == "x") || (tmpstr[1] == "y") ||
3071 (tmpstr[1] == "z"),
3072 "Rotated Dir is "
3073 "not specified as x,y or z");
3074
3075 RotPeriodicInfo RotInfo;
3076 RotInfo.m_dir = (tmpstr[1] == "x") ? 0 : (tmpstr[1] == "y") ? 1 : 2;
3077
3079 int ExprId = strEval.DefineFunction("", tmpstr[2]);
3080 RotInfo.m_angle = strEval.Evaluate(ExprId);
3081
3082 auto ne = m_bndCondExpansions[n]->GetExpSize();
3083
3084 // Loop over each element in the boundary condition and rotate
3085 // velocity
3086 for (int e = 0; e < ne; ++e)
3087 {
3088 auto id2 = m_trace->GetPhys_Offset(
3089 m_traceMap->GetPerBndCondIDToGlobalTraceID(e + cnt));
3090 int npts = m_bndCondExpansions[n]->GetExp(e)->GetTotPoints();
3091 Rotate(Bwd, RotInfo.m_dir, -RotInfo.m_angle, id2, npts);
3092 }
3093 cnt += ne;
3094 }
3095 }
3096}
3097
3099{
3100 int cnt = 0;
3101 for (int n = 0; n < m_bndConditions.size(); ++n)
3102 {
3103 // check to see if boundary is rotationally aligned
3104 if (boost::icontains(m_bndConditions[n]->GetUserDefined(), "Rotated"))
3105 {
3106 vector<string> tmpstr;
3107
3108 boost::split(tmpstr, m_bndConditions[n]->GetUserDefined(),
3109 boost::is_any_of(":"));
3110
3111 ASSERTL1(tmpstr.size() > 2,
3112 "Expected Rotated user defined string to "
3113 "contain direction and rotation angle "
3114 "and optionally a tolerance, "
3115 "i.e. Rotated:dir:PI/2:1e-6");
3116
3117 ASSERTL1((tmpstr[1] == "x") || (tmpstr[1] == "y") ||
3118 (tmpstr[1] == "z"),
3119 "Rotated Dir is "
3120 "not specified as x,y or z");
3121
3122 RotPeriodicInfo RotInfo;
3123 RotInfo.m_dir = (tmpstr[1] == "x") ? 0 : (tmpstr[1] == "y") ? 1 : 2;
3124
3126 int ExprId = strEval.DefineFunction("", tmpstr[2]);
3127 RotInfo.m_angle = strEval.Evaluate(ExprId);
3128
3129 auto ne = m_bndCondExpansions[n]->GetExpSize();
3130 // Loop over each element in the boundary condition and rotate
3131 // velocity
3132 for (int e = 0; e < ne; ++e)
3133 {
3134 auto id2 = m_trace->GetPhys_Offset(
3135 m_traceMap->GetPerBndCondIDToGlobalTraceID(e + cnt));
3136 int npts = m_bndCondExpansions[n]->GetExp(e)->GetTotPoints();
3137 DeriveRotate(Bwd, RotInfo.m_dir, -RotInfo.m_angle, id2, npts);
3138 }
3139 cnt += ne;
3140 }
3141 }
3142}
3143
3145{
3146 int nDim = GetCoordim(0);
3147 for (auto &interfaceTrace : m_interfaceMap->GetLocalInterface())
3148 {
3149
3150 interfaceTrace->RotLocalBwdTrace(Bwd, nDim);
3151 }
3152}
3153
3155{
3156 int nDim = GetCoordim(0);
3157 for (auto &interfaceTrace : m_interfaceMap->GetLocalInterface())
3158 {
3159
3160 interfaceTrace->RotLocalBwdDeriveTrace(Bwd, nDim);
3161 }
3162}
3163
3169
3170/**
3171 * @brief Obtain a copy of the periodic edges and vertices for this
3172 * field.
3173 */
3175 PeriodicMap &periodicEdges,
3176 PeriodicMap &periodicFaces)
3177{
3178 periodicVerts = m_periodicVerts;
3179 periodicEdges = m_periodicEdges;
3180 periodicFaces = m_periodicFaces;
3181}
3182/**
3183 * \brief This method extracts the "forward" and "backward" trace
3184 * data from the array \a field and puts the data into output
3185 * vectors \a Fwd and \a Bwd.
3186 *
3187 * We first define the convention which defines "forwards" and
3188 * "backwards". First an association is made between the vertex/edge/face of
3189 * each element and its corresponding vertex/edge/face in the trace space
3190 * using the mapping #m_traceMap. The element can either be
3191 * left-adjacent or right-adjacent to this trace face (see
3192 * Expansion2D::GetLeftAdjacentElementExp). Boundary faces are
3193 * always left-adjacent since left-adjacency is populated first.
3194 *
3195 * If the element is left-adjacent we extract the trace data
3196 * from \a field into the forward trace space \a Fwd; otherwise,
3197 * we place it in the backwards trace space \a Bwd. In this way,
3198 * we form a unique set of trace normals since these are always
3199 * extracted from left-adjacent elements.
3200 *
3201 * \param field is a NekDouble array which contains the fielddata
3202 * from which we wish to extract the backward and forward
3203 * orientated trace/face arrays.
3204 *
3205 * \return Updates a NekDouble array \a Fwd and \a Bwd
3206 */
3211 const Array<OneD, const ExpListSharedPtr> &BndCondExp)
3212{
3213 ASSERTL1((*m_exp)[0]->GetBasis(0)->GetBasisType() !=
3215 "Routine not set up for Gauss Lagrange points distribution");
3216
3217 // blocked routine
3218 Array<OneD, NekDouble> tracevals(m_locTraceToTraceMap->GetNLocTracePts());
3219
3220 m_locTraceToTraceMap->LocTracesFromField(field, tracevals);
3221 m_locTraceToTraceMap->InterpLocTracesToTrace(0, tracevals, Fwd);
3222
3223 Array<OneD, NekDouble> invals =
3224 tracevals + m_locTraceToTraceMap->GetNFwdLocTracePts();
3225 m_locTraceToTraceMap->InterpLocTracesToTrace(1, invals, Bwd);
3226
3228
3229 FillBwdWithBoundCond(Fwd, Bwd, BndCond, BndCondExp, false);
3230
3231 // Do parallel exchange for forwards/backwards spaces.
3232 m_traceMap->GetAssemblyCommDG()->PerformExchange(Fwd, Bwd);
3233
3234 // Do exchange of interface traces (local and parallel)
3235 // We may have to split this out into separate local and
3236 // parallel for IP method???
3237 m_interfaceMap->ExchangeTrace(Fwd, Bwd);
3238}
3239
3243 bool FillBnd, // these should be template params
3244 bool PutFwdInBwdOnBCs, bool DoExchange)
3245{
3246 // Is this zeroing necessary?
3247 // Zero forward/backward vectors.
3248 Vmath::Zero(Fwd.size(), Fwd, 1);
3249 Vmath::Zero(Bwd.size(), Bwd, 1);
3250
3251 // Basis definition on each element
3252 LibUtilities::BasisSharedPtr basis = (*m_exp)[0]->GetBasis(0);
3253 if ((basis->GetBasisType() != LibUtilities::eGauss_Lagrange))
3254 {
3255 // blocked routine
3256 Array<OneD, NekDouble> tracevals(
3257 m_locTraceToTraceMap->GetNLocTracePts());
3258
3259 m_locTraceToTraceMap->LocTracesFromField(field, tracevals);
3260 m_locTraceToTraceMap->InterpLocTracesToTrace(0, tracevals, Fwd);
3261
3262 Array<OneD, NekDouble> invals =
3263 tracevals + m_locTraceToTraceMap->GetNFwdLocTracePts();
3264 m_locTraceToTraceMap->InterpLocTracesToTrace(1, invals, Bwd);
3265 }
3266 else
3267 {
3268 // Loop over elements and collect forward expansion
3269 auto nexp = GetExpSize();
3272
3274 &elmtToTrace = m_traceMap->GetElmtToTrace();
3275
3276 for (int n = 0, cnt = 0; n < nexp; ++n)
3277 {
3278 exp = (*m_exp)[n];
3279 auto phys_offset = GetPhys_Offset(n);
3280
3281 for (int e = 0; e < exp->GetNtraces(); ++e, ++cnt)
3282 {
3283 auto offset =
3284 m_trace->GetPhys_Offset(elmtToTrace[n][e]->GetElmtId());
3285
3286 e_tmp =
3287 (m_leftAdjacentTraces[cnt]) ? Fwd + offset : Bwd + offset;
3288
3289 exp->GetTracePhysVals(e, elmtToTrace[n][e], field + phys_offset,
3290 e_tmp);
3291 }
3292 }
3293 }
3294
3296
3297 if (FillBnd)
3298 {
3299 v_FillBwdWithBoundCond(Fwd, Bwd, PutFwdInBwdOnBCs);
3300 }
3301
3302 if (DoExchange)
3303 {
3304 // Do parallel exchange for forwards/backwards spaces.
3305 m_traceMap->GetAssemblyCommDG()->PerformExchange(Fwd, Bwd);
3306
3307 // Do exchange of interface traces (local and parallel)
3308 // We may have to split this out into separate local and
3309 // parallel for IP method???
3310 m_interfaceMap->ExchangeTrace(Fwd, Bwd);
3311 }
3312}
3313
3316 bool PutFwdInBwdOnBCs)
3317{
3319 PutFwdInBwdOnBCs);
3320}
3321
3322/** function allowing different BCs to be passed to routine
3323 */
3327 &bndConditions,
3328 const Array<OneD, const ExpListSharedPtr> &bndCondExpansions,
3329 bool PutFwdInBwdOnBCs)
3330{
3331
3332 // Fill boundary conditions into missing elements
3333 if (PutFwdInBwdOnBCs) // just set Bwd value to be Fwd value on BCs
3334 {
3335 // Fill boundary conditions into missing elements
3336 for (int n = 0, cnt = 0; n < bndCondExpansions.size(); ++n)
3337 {
3338 if (bndConditions[n]->GetBoundaryConditionType() ==
3340 {
3341 auto ne = bndCondExpansions[n]->GetExpSize();
3342 for (int e = 0; e < ne; ++e)
3343 {
3344 auto npts = bndCondExpansions[n]->GetExp(e)->GetTotPoints();
3345 auto id2 = m_trace->GetPhys_Offset(
3346 m_traceMap->GetBndCondIDToGlobalTraceID(cnt + e));
3347 Vmath::Vcopy(npts, &Fwd[id2], 1, &Bwd[id2], 1);
3348 }
3349
3350 cnt += ne;
3351 }
3352 else if (bndConditions[n]->GetBoundaryConditionType() ==
3354 bndConditions[n]->GetBoundaryConditionType() ==
3356 {
3357 auto ne = bndCondExpansions[n]->GetExpSize();
3358 for (int e = 0; e < ne; ++e)
3359 {
3360 auto npts = bndCondExpansions[n]->GetExp(e)->GetTotPoints();
3361 auto id2 = m_trace->GetPhys_Offset(
3362 m_traceMap->GetBndCondIDToGlobalTraceID(cnt + e));
3363 Vmath::Vcopy(npts, &Fwd[id2], 1, &Bwd[id2], 1);
3364 }
3365 cnt += ne;
3366 }
3367 else if (bndConditions[n]->GetBoundaryConditionType() !=
3369 {
3370 ASSERTL0(false, "Method not set up for this "
3371 "boundary condition.");
3372 }
3373 }
3374 }
3375 else
3376 {
3377 for (int n = 0, cnt = 0; n < bndCondExpansions.size(); ++n)
3378 {
3379 if (bndConditions[n]->GetBoundaryConditionType() ==
3381 {
3382 auto ne = bndCondExpansions[n]->GetExpSize();
3383 for (int e = 0; e < ne; ++e)
3384 {
3385 auto npts = bndCondExpansions[n]->GetExp(e)->GetTotPoints();
3386 auto id1 = bndCondExpansions[n]->GetPhys_Offset(e);
3387 auto id2 = m_trace->GetPhys_Offset(
3388 m_traceMap->GetBndCondIDToGlobalTraceID(cnt + e));
3389
3390 Vmath::Vcopy(npts, &(bndCondExpansions[n]->GetPhys())[id1],
3391 1, &Bwd[id2], 1);
3392 }
3393 cnt += ne;
3394 }
3395 else if (bndConditions[n]->GetBoundaryConditionType() ==
3397 bndConditions[n]->GetBoundaryConditionType() ==
3399 {
3400 auto ne = m_bndCondExpansions[n]->GetExpSize();
3401 for (int e = 0; e < ne; ++e)
3402 {
3403 auto npts = bndCondExpansions[n]->GetExp(e)->GetTotPoints();
3404 auto id1 = bndCondExpansions[n]->GetPhys_Offset(e);
3405
3406 ASSERTL0((bndCondExpansions[n]->GetPhys())[id1] == 0.0,
3407 "Method not set up for non-zero "
3408 "Neumann boundary condition");
3409 auto id2 = m_trace->GetPhys_Offset(
3410 m_traceMap->GetBndCondIDToGlobalTraceID(cnt + e));
3411
3412 Vmath::Vcopy(npts, &Fwd[id2], 1, &Bwd[id2], 1);
3413 }
3414
3415 cnt += ne;
3416 }
3417 else if (bndConditions[n]->GetBoundaryConditionType() ==
3419 {
3420 // Do nothing
3421 }
3422 else if (bndConditions[n]->GetBoundaryConditionType() !=
3424 {
3426 "Method not set up for this boundary "
3427 "condition.");
3428 }
3429 }
3430 }
3431}
3432
3437
3438void DisContField::v_SetBndCondBwdWeight(const int index, const NekDouble value)
3439{
3440 m_bndCondBndWeight[index] = value;
3441}
3442
3446{
3447 // Basis definition on each element
3448 LibUtilities::BasisSharedPtr basis = (*m_exp)[0]->GetBasis(0);
3449 if (basis->GetBasisType() != LibUtilities::eGauss_Lagrange)
3450 {
3451 Array<OneD, NekDouble> tracevals(
3452 m_locTraceToTraceMap->GetNLocTracePts(), 0.0);
3453
3454 Array<OneD, NekDouble> invals =
3455 tracevals + m_locTraceToTraceMap->GetNFwdLocTracePts();
3456
3457 m_locTraceToTraceMap->InterpTraceToLocTrace(1, Bwd, invals);
3458
3459 m_locTraceToTraceMap->InterpTraceToLocTrace(0, Fwd, tracevals);
3460
3461 m_locTraceToTraceMap->AddLocTracesToField(tracevals, field);
3462 }
3463 else
3464 {
3466 "v_AddTraceQuadPhysToField not coded for eGauss_Lagrange");
3467 }
3468}
3469
3471{
3472 ASSERTL1(m_physState == true, "local physical space is not true ");
3473 v_ExtractTracePhys(m_phys, outarray);
3474}
3475/**
3476 * @brief This method extracts the trace (verts in 1D) from
3477 * the field @a inarray and puts the values in @a outarray.
3478 *
3479 * It assumes the field is C0 continuous so that it can
3480 * overwrite the edge data when visited by the two adjacent
3481 * elements.
3482 *
3483 * @param inarray An array containing the 1D data from which we wish
3484 * to extract the edge data.
3485 * @param outarray The resulting edge information.
3486 *
3487 * @param gridVelocity Avoid performing parallel exchanges of the grid velocity
3488 *
3489 * This will not work for non-boundary expansions
3490 */
3492 const Array<OneD, const NekDouble> &inarray,
3493 Array<OneD, NekDouble> &outarray, bool gridVelocity)
3494{
3495 LibUtilities::BasisSharedPtr basis = (*m_exp)[0]->GetBasis(0);
3496 if ((basis->GetBasisType() != LibUtilities::eGauss_Lagrange))
3497 {
3498 Vmath::Zero(outarray.size(), outarray, 1);
3499 Array<OneD, NekDouble> tracevals(
3500 m_locTraceToTraceMap->GetNFwdLocTracePts());
3501 m_locTraceToTraceMap->FwdLocTracesFromField(inarray, tracevals);
3502 m_locTraceToTraceMap->InterpLocTracesToTrace(0, tracevals, outarray);
3503 if (!gridVelocity)
3504 {
3505 m_traceMap->GetAssemblyCommDG()->PerformExchange(outarray,
3506 outarray);
3507 }
3508 }
3509 else
3510 {
3511 // Loop over elemente and collect forward expansion
3512 int nexp = GetExpSize();
3513 int n, p, offset, phys_offset;
3516 &elmtToTrace = m_traceMap->GetElmtToTrace();
3517 ASSERTL1(outarray.size() >= m_trace->GetNpoints(),
3518 "input array is of insufficient length");
3519 for (n = 0; n < nexp; ++n)
3520 {
3521 phys_offset = GetPhys_Offset(n);
3522 for (p = 0; p < (*m_exp)[n]->GetNtraces(); ++p)
3523 {
3524 offset =
3525 m_trace->GetPhys_Offset(elmtToTrace[n][p]->GetElmtId());
3526 (*m_exp)[n]->GetTracePhysVals(p, elmtToTrace[n][p],
3527 inarray + phys_offset,
3528 t_tmp = outarray + offset);
3529 }
3530 }
3531 }
3532}
3533/**
3534 * @brief Add trace contributions into elemental coefficient spaces.
3535 *
3536 * Given some quantity \f$ \vec{Fn} \f$, which conatins this
3537 * routine calculates the integral
3538 *
3539 * \f[
3540 * \int_{\Omega^e} \vec{Fn}, \mathrm{d}S
3541 * \f]
3542 *
3543 * and adds this to the coefficient space provided by outarray.
3544 *
3545 * @see Expansion3D::AddFaceNormBoundaryInt
3546 *
3547 * @param Fn The trace quantities.
3548 * @param outarray Resulting 3D coefficient space.
3549 *
3550 */
3552 Array<OneD, NekDouble> &outarray)
3553{
3554 if (m_expType == e1D)
3555 {
3556 int n, offset, t_offset;
3558 &elmtToTrace = m_traceMap->GetElmtToTrace();
3559 vector<bool> negatedFluxNormal = GetNegatedFluxNormal();
3560 for (n = 0; n < GetExpSize(); ++n)
3561 {
3562 // Number of coefficients on each element
3563 int e_ncoeffs = (*m_exp)[n]->GetNcoeffs();
3564 offset = GetCoeff_Offset(n);
3565 // Implementation for every points except Gauss points
3566 if ((*m_exp)[n]->GetBasis(0)->GetBasisType() !=
3568 {
3569 t_offset =
3570 GetTrace()->GetCoeff_Offset(elmtToTrace[n][0]->GetElmtId());
3571 if (negatedFluxNormal[2 * n])
3572 {
3573 outarray[offset] -= Fn[t_offset];
3574 }
3575 else
3576 {
3577 outarray[offset] += Fn[t_offset];
3578 }
3579 t_offset =
3580 GetTrace()->GetCoeff_Offset(elmtToTrace[n][1]->GetElmtId());
3581 if (negatedFluxNormal[2 * n + 1])
3582 {
3583 outarray[offset + (*m_exp)[n]->GetVertexMap(1)] -=
3584 Fn[t_offset];
3585 }
3586 else
3587 {
3588 outarray[offset + (*m_exp)[n]->GetVertexMap(1)] +=
3589 Fn[t_offset];
3590 }
3591 }
3592 else
3593 {
3594 int j;
3595 static DNekMatSharedPtr m_Ixm, m_Ixp;
3596 static int sav_ncoeffs = 0;
3597 if (!m_Ixm.get() || e_ncoeffs != sav_ncoeffs)
3598 {
3600 const LibUtilities::PointsKey BS_p(
3602 const LibUtilities::BasisKey BS_k(
3603 LibUtilities::eGauss_Lagrange, e_ncoeffs, BS_p);
3604 BASE = LibUtilities::BasisManager()[BS_k];
3605 Array<OneD, NekDouble> coords(1, 0.0);
3606 coords[0] = -1.0;
3607 m_Ixm = BASE->GetI(coords);
3608 coords[0] = 1.0;
3609 m_Ixp = BASE->GetI(coords);
3610 sav_ncoeffs = e_ncoeffs;
3611 }
3612 t_offset =
3613 GetTrace()->GetCoeff_Offset(elmtToTrace[n][0]->GetElmtId());
3614 if (negatedFluxNormal[2 * n])
3615 {
3616 for (j = 0; j < e_ncoeffs; j++)
3617 {
3618 outarray[offset + j] -=
3619 (m_Ixm->GetPtr())[j] * Fn[t_offset];
3620 }
3621 }
3622 else
3623 {
3624 for (j = 0; j < e_ncoeffs; j++)
3625 {
3626 outarray[offset + j] +=
3627 (m_Ixm->GetPtr())[j] * Fn[t_offset];
3628 }
3629 }
3630 t_offset =
3631 GetTrace()->GetCoeff_Offset(elmtToTrace[n][1]->GetElmtId());
3632 if (negatedFluxNormal[2 * n + 1])
3633 {
3634 for (j = 0; j < e_ncoeffs; j++)
3635 {
3636 outarray[offset + j] -=
3637 (m_Ixp->GetPtr())[j] * Fn[t_offset];
3638 }
3639 }
3640 else
3641 {
3642 for (j = 0; j < e_ncoeffs; j++)
3643 {
3644 outarray[offset + j] +=
3645 (m_Ixp->GetPtr())[j] * Fn[t_offset];
3646 }
3647 }
3648 }
3649 }
3650 }
3651 else // other dimensions
3652 {
3653 // Basis definition on each element
3654 LibUtilities::BasisSharedPtr basis = (*m_exp)[0]->GetBasis(0);
3655 if ((m_expType != e1D) &&
3656 (basis->GetBasisType() != LibUtilities::eGauss_Lagrange))
3657 {
3658 Array<OneD, NekDouble> Fcoeffs(m_trace->GetNcoeffs());
3659 m_trace->IProductWRTBase(Fn, Fcoeffs);
3660 m_locTraceToTraceMap->AddTraceCoeffsToFieldCoeffs(Fcoeffs,
3661 outarray);
3662 }
3663 else
3664 {
3665 int e, n, offset, t_offset;
3666 Array<OneD, NekDouble> e_outarray;
3668 &elmtToTrace = m_traceMap->GetElmtToTrace();
3669 if (m_expType == e2D)
3670 {
3671 for (n = 0; n < GetExpSize(); ++n)
3672 {
3673 offset = GetCoeff_Offset(n);
3674 for (e = 0; e < (*m_exp)[n]->GetNtraces(); ++e)
3675 {
3676 t_offset = GetTrace()->GetPhys_Offset(
3677 elmtToTrace[n][e]->GetElmtId());
3678 (*m_exp)[n]->AddEdgeNormBoundaryInt(
3679 e, elmtToTrace[n][e], Fn + t_offset,
3680 e_outarray = outarray + offset);
3681 }
3682 }
3683 }
3684 else if (m_expType == e3D)
3685 {
3686 for (n = 0; n < GetExpSize(); ++n)
3687 {
3688 offset = GetCoeff_Offset(n);
3689 for (e = 0; e < (*m_exp)[n]->GetNtraces(); ++e)
3690 {
3691 t_offset = GetTrace()->GetPhys_Offset(
3692 elmtToTrace[n][e]->GetElmtId());
3693 (*m_exp)[n]->AddFaceNormBoundaryInt(
3694 e, elmtToTrace[n][e], Fn + t_offset,
3695 e_outarray = outarray + offset);
3696 }
3697 }
3698 }
3699 }
3700 }
3701}
3702/**
3703 * @brief Add trace contributions into elemental coefficient spaces.
3704 *
3705 * Given some quantity \f$ \vec{q} \f$, calculate the elemental integral
3706 *
3707 * \f[
3708 * \int_{\Omega^e} \vec{q}, \mathrm{d}S
3709 * \f]
3710 *
3711 * and adds this to the coefficient space provided by
3712 * outarray. The value of q is determined from the routine
3713 * IsLeftAdjacentTrace() which if true we use Fwd else we use
3714 * Bwd
3715 *
3716 * @param Fwd The trace quantities associated with left (fwd)
3717 * adjancent elmt.
3718 * @param Bwd The trace quantities associated with right (bwd)
3719 * adjacent elet.
3720 * @param outarray Resulting coefficient space.
3721 */
3725{
3726 ASSERTL0(m_expType != e1D, "This method is not setup or "
3727 "tested for 1D expansion");
3728 Array<OneD, NekDouble> Coeffs(m_trace->GetNcoeffs());
3729 m_trace->IProductWRTBase(Fwd, Coeffs);
3730 m_locTraceToTraceMap->AddTraceCoeffsToFieldCoeffs(0, Coeffs, outarray);
3731 m_trace->IProductWRTBase(Bwd, Coeffs);
3732 m_locTraceToTraceMap->AddTraceCoeffsToFieldCoeffs(1, Coeffs, outarray);
3733}
3734
3736 const Array<OneD, const NekDouble> &inarray,
3737 Array<OneD, NekDouble> &outarray, const StdRegions::ConstFactorMap &factors,
3738 const StdRegions::VarCoeffMap &varcoeff,
3739 [[maybe_unused]] const StdRegions::VarFactorsMap &varfactors,
3740 [[maybe_unused]] const Array<OneD, const NekDouble> &dirForcing,
3741 const bool PhysSpaceForcing)
3742{
3743 int i, n, cnt, nbndry;
3744 int nexp = GetExpSize();
3746 DNekVec F(m_ncoeffs, f, eWrapper);
3747 Array<OneD, NekDouble> e_f, e_l;
3748 //----------------------------------
3749 // Setup RHS Inner product if required
3750 //----------------------------------
3751 if (PhysSpaceForcing)
3752 {
3753 IProductWRTBase(inarray, f);
3754 Vmath::Neg(m_ncoeffs, f, 1);
3755 }
3756 else
3757 {
3758 Vmath::Smul(m_ncoeffs, -1.0, inarray, 1, f, 1);
3759 }
3760 //----------------------------------
3761 // Solve continuous Boundary System
3762 //----------------------------------
3763 int GloBndDofs = m_traceMap->GetNumGlobalBndCoeffs();
3764 int NumDirBCs = m_traceMap->GetNumLocalDirBndCoeffs();
3765 int e_ncoeffs;
3767 NullAssemblyMapSharedPtr, factors, varcoeff);
3768 const DNekScalBlkMatSharedPtr &HDGLamToU = GetBlockMatrix(HDGLamToUKey);
3769 // Retrieve number of local trace space coefficients N_{\lambda},
3770 // and set up local elemental trace solution \lambda^e.
3771 int LocBndCoeffs = m_traceMap->GetNumLocalBndCoeffs();
3772 Array<OneD, NekDouble> bndrhs(LocBndCoeffs, 0.0);
3773 Array<OneD, NekDouble> loclambda(LocBndCoeffs, 0.0);
3774 DNekVec LocLambda(LocBndCoeffs, loclambda, eWrapper);
3775 //----------------------------------
3776 // Evaluate Trace Forcing
3777 // Kirby et al, 2010, P23, Step 5.
3778 //----------------------------------
3779 // Determing <u_lam,f> terms using HDGLamToU matrix
3780 for (cnt = n = 0; n < nexp; ++n)
3781 {
3782 nbndry = (*m_exp)[n]->NumDGBndryCoeffs();
3783 e_ncoeffs = (*m_exp)[n]->GetNcoeffs();
3784 e_f = f + m_coeff_offset[n];
3785 e_l = bndrhs + cnt;
3786 // use outarray as tmp space
3787 DNekVec Floc(nbndry, e_l, eWrapper);
3788 DNekVec ElmtFce(e_ncoeffs, e_f, eWrapper);
3789 Floc = Transpose(*(HDGLamToU->GetBlock(n, n))) * ElmtFce;
3790 cnt += nbndry;
3791 }
3792 Array<OneD, const int> bndCondMap =
3793 m_traceMap->GetBndCondCoeffsToLocalTraceMap();
3794 Array<OneD, const NekDouble> Sign = m_traceMap->GetLocalToGlobalBndSign();
3795 // Copy Dirichlet boundary conditions and weak forcing
3796 // into trace space
3797 int locid;
3798 cnt = 0;
3799 for (i = 0; i < m_bndCondExpansions.size(); ++i)
3800 {
3801 const Array<OneD, const NekDouble> bndcoeffs =
3802 m_bndCondExpansions[i]->GetCoeffs();
3803
3804 if (m_bndConditions[i]->GetBoundaryConditionType() ==
3806 {
3807 for (int j = 0; j < (m_bndCondExpansions[i])->GetNcoeffs(); ++j)
3808 {
3809 locid = bndCondMap[cnt + j];
3810 loclambda[locid] = Sign[locid] * bndcoeffs[j];
3811 }
3812 }
3813 else if (m_bndConditions[i]->GetBoundaryConditionType() ==
3815 m_bndConditions[i]->GetBoundaryConditionType() ==
3817 {
3818 // Add weak boundary condition to trace forcing
3819 for (int j = 0; j < (m_bndCondExpansions[i])->GetNcoeffs(); ++j)
3820 {
3821 locid = bndCondMap[cnt + j];
3822 bndrhs[locid] += Sign[locid] * bndcoeffs[j];
3823 }
3824 }
3825 else if (m_bndConditions[i]->GetBoundaryConditionType() ==
3827 {
3828 // skip increment of cnt if ePeriodic
3829 // because bndCondMap does not include ePeriodic
3830 continue;
3831 }
3832 cnt += (m_bndCondExpansions[i])->GetNcoeffs();
3833 }
3834
3835 //----------------------------------
3836 // Solve trace problem: \Lambda = K^{-1} F
3837 // K is the HybridDGHelmBndLam matrix.
3838 //----------------------------------
3839 if (GloBndDofs - NumDirBCs > 0)
3840 {
3842 factors, varcoeff);
3844 LinSys->Solve(bndrhs, loclambda, m_traceMap);
3845
3846 // For consistency with previous version put global
3847 // solution into m_trace->m_coeffs
3848 m_traceMap->LocalToGlobal(loclambda, m_trace->UpdateCoeffs());
3849 }
3850
3851 //----------------------------------
3852 // Internal element solves
3853 //----------------------------------
3855 NullAssemblyMapSharedPtr, factors, varcoeff);
3856
3857 const DNekScalBlkMatSharedPtr &InvHDGHelm = GetBlockMatrix(invHDGhelmkey);
3858 DNekVec out(m_ncoeffs, outarray, eWrapper);
3859 Vmath::Zero(m_ncoeffs, outarray, 1);
3860
3861 // out = u_f + u_lam = (*InvHDGHelm)*f + (LamtoU)*Lam
3862 out = (*InvHDGHelm) * F + (*HDGLamToU) * LocLambda;
3863
3864 // Return empty GlobalLinSysKey
3865 return NullGlobalLinSysKey;
3866}
3867
3868/* \brief This function evaluates the boundary conditions at a certain
3869 * time-level.
3870 *
3871 * Based on the boundary condition \f$g(\boldsymbol{x},t)\f$ evaluated
3872 * at a given time-level \a t, this function transforms the boundary
3873 * conditions onto the coefficients of the (one-dimensional) boundary
3874 * expansion. Depending on the type of boundary conditions, these
3875 * expansion coefficients are calculated in different ways:
3876 * - <b>Dirichlet boundary conditions</b><BR>
3877 * In order to ensure global \f$C^0\f$ continuity of the spectral/hp
3878 * approximation, the Dirichlet boundary conditions are projected onto
3879 * the boundary expansion by means of a modified \f$C^0\f$ continuous
3880 * Galerkin projection. This projection can be viewed as a collocation
3881 * projection at the vertices, followed by an \f$L^2\f$ projection on
3882 * the interior modes of the edges. The resulting coefficients
3883 * \f$\boldsymbol{\hat{u}}^{\mathcal{D}}\f$ will be stored for the
3884 * boundary expansion.
3885 * - <b>Neumann boundary conditions</b>
3886 * In the discrete Galerkin formulation of the problem to be solved,
3887 * the Neumann boundary conditions appear as the set of surface
3888 * integrals: \f[\boldsymbol{\hat{g}}=\int_{\Gamma}
3889 * \phi^e_n(\boldsymbol{x})g(\boldsymbol{x})d(\boldsymbol{x})\quad
3890 * \forall n \f]
3891 * As a result, it are the coefficients \f$\boldsymbol{\hat{g}}\f$
3892 * that will be stored in the boundary expansion
3893 *
3894 * @param time The time at which the boundary conditions
3895 * should be evaluated.
3896 * @param bndCondExpansions List of boundary conditions.
3897 * @param bndConditions Information about the boundary conditions.
3898 *
3899 * This will only be undertaken for time dependent
3900 * boundary conditions unless time == 0.0 which is the
3901 * case when the method is called from the constructor.
3902 */
3904 const std::string varName,
3905 const NekDouble x2_in,
3906 const NekDouble x3_in)
3907{
3908 int i;
3909 int npoints;
3910
3912
3913 for (i = 0; i < m_bndCondExpansions.size(); ++i)
3914 {
3915 if (m_bndCondExpansions[i] &&
3916 (time == 0.0 || m_bndConditions[i]->IsTimeDependent()))
3917 {
3918 m_bndCondBndWeight[i] = 1.0;
3919 locExpList = m_bndCondExpansions[i];
3920
3921 npoints = locExpList->GetNpoints();
3922 Array<OneD, NekDouble> x0(npoints, 0.0);
3923 Array<OneD, NekDouble> x1(npoints, 0.0);
3924 Array<OneD, NekDouble> x2(npoints, 0.0);
3925
3926 locExpList->GetCoords(x0, x1, x2);
3927
3928 if (x2_in != NekConstants::kNekUnsetDouble &&
3930 {
3931 Vmath::Fill(npoints, x2_in, x1, 1);
3932 Vmath::Fill(npoints, x3_in, x2, 1);
3933 }
3934 else if (x2_in != NekConstants::kNekUnsetDouble)
3935 {
3936 Vmath::Fill(npoints, x2_in, x2, 1);
3937 }
3938
3939 // treat 1D expansions separately since we only
3940 // require an evaluation at a point rather than
3941 // any projections or inner products that are not
3942 // available in a PointExp
3943 if (m_expType == e1D)
3944 {
3945 if (m_bndConditions[i]->GetBoundaryConditionType() ==
3947 {
3948 for (int n = 0; n < npoints; ++n)
3949 {
3950 m_bndCondExpansions[i]->SetCoeff(
3951 n, (std::static_pointer_cast<
3953 m_bndConditions[i])
3954 ->m_dirichletCondition)
3955 ->Evaluate(x0[n], x1[n], x2[n], time));
3956 m_bndCondExpansions[i]->SetPhys(
3957 n, m_bndCondExpansions[i]->GetCoeff(n));
3958 }
3959 }
3960 else if (m_bndConditions[i]->GetBoundaryConditionType() ==
3962 {
3963 for (int n = 0; n < npoints; ++n)
3964 {
3965 m_bndCondExpansions[i]->SetCoeff(
3966 n, (std::static_pointer_cast<
3968 m_bndConditions[i])
3969 ->m_neumannCondition)
3970 ->Evaluate(x0[n], x1[n], x2[n], time));
3971 }
3972 }
3973 else if (m_bndConditions[i]->GetBoundaryConditionType() ==
3975 {
3976 for (int n = 0; n < npoints; ++n)
3977 {
3978 m_bndCondExpansions[i]->SetCoeff(
3979 n, (std::static_pointer_cast<
3981 m_bndConditions[i])
3982 ->m_robinFunction)
3983 ->Evaluate(x0[n], x1[n], x2[n], time));
3984 }
3985 }
3986 else if (m_bndConditions[i]->GetBoundaryConditionType() ==
3988 {
3989 continue;
3990 }
3991 else if (m_bndConditions[i]->GetBoundaryConditionType() ==
3993 {
3994 }
3995 else
3996 {
3998 "This type of BC not implemented yet");
3999 }
4000 }
4001 else // 2D and 3D versions
4002 {
4003 if (m_bndConditions[i]->GetBoundaryConditionType() ==
4005 {
4007 std::static_pointer_cast<
4009 m_bndConditions[i]);
4010
4011 Array<OneD, NekDouble> valuesFile(npoints, 1.0),
4012 valuesExp(npoints, 1.0);
4013
4014 string bcfilename = bcPtr->m_filename;
4015 string exprbcs = bcPtr->m_expr;
4016
4017 if (bcfilename != "")
4018 {
4019 locExpList->ExtractCoeffsFromFile(
4020 bcfilename, bcPtr->GetComm(), varName,
4021 locExpList->UpdateCoeffs());
4022 locExpList->BwdTrans(locExpList->GetCoeffs(),
4023 locExpList->UpdatePhys());
4024 valuesFile = locExpList->GetPhys();
4025 }
4026
4027 if (exprbcs != "")
4028 {
4030 std::static_pointer_cast<
4032 m_bndConditions[i])
4033 ->m_dirichletCondition;
4034
4035 condition->Evaluate(x0, x1, x2, time, valuesExp);
4036 }
4037
4038 Vmath::Vmul(npoints, valuesExp, 1, valuesFile, 1,
4039 locExpList->UpdatePhys(), 1);
4040 locExpList->FwdTransBndConstrained(
4041 locExpList->GetPhys(), locExpList->UpdateCoeffs());
4042 }
4043 else if (m_bndConditions[i]->GetBoundaryConditionType() ==
4045 {
4047 std::static_pointer_cast<
4049 m_bndConditions[i]);
4050 string bcfilename = bcPtr->m_filename;
4051
4052 if (bcfilename != "")
4053 {
4054 locExpList->ExtractCoeffsFromFile(
4055 bcfilename, bcPtr->GetComm(), varName,
4056 locExpList->UpdateCoeffs());
4057 locExpList->BwdTrans(locExpList->GetCoeffs(),
4058 locExpList->UpdatePhys());
4059 }
4060 else
4061 {
4063 std::static_pointer_cast<
4065 m_bndConditions[i])
4066 ->m_neumannCondition;
4067 condition->Evaluate(x0, x1, x2, time,
4068 locExpList->UpdatePhys());
4069 }
4070
4071 locExpList->IProductWRTBase(locExpList->GetPhys(),
4072 locExpList->UpdateCoeffs());
4073 }
4074 else if (m_bndConditions[i]->GetBoundaryConditionType() ==
4076 {
4078 std::static_pointer_cast<
4080 m_bndConditions[i]);
4081
4082 string bcfilename = bcPtr->m_filename;
4083
4084 if (bcfilename != "")
4085 {
4086 locExpList->ExtractCoeffsFromFile(
4087 bcfilename, bcPtr->GetComm(), varName,
4088 locExpList->UpdateCoeffs());
4089 locExpList->BwdTrans(locExpList->GetCoeffs(),
4090 locExpList->UpdatePhys());
4091 }
4092 else
4093 {
4095 std::static_pointer_cast<
4097 m_bndConditions[i])
4098 ->m_robinFunction;
4099 condition->Evaluate(x0, x1, x2, time,
4100 locExpList->UpdatePhys());
4101 }
4102
4103 locExpList->IProductWRTBase(locExpList->GetPhys(),
4104 locExpList->UpdateCoeffs());
4105 }
4106 else if (m_bndConditions[i]->GetBoundaryConditionType() ==
4108 {
4109 continue;
4110 }
4111 else
4112 {
4114 "This type of BC not implemented yet");
4115 }
4116 }
4117 }
4118 }
4119}
4120
4122{
4123 int i;
4124 int npoints;
4125
4127
4128 for (i = 0; i < m_bndCondExpansions.size(); ++i)
4129 {
4130 locExpList = m_bndCondExpansions[i];
4131
4132 npoints = locExpList->GetNpoints();
4133
4134 // treat 1D expansions separately since we only
4135 // require an evaluation at a point rather than
4136 // any projections or inner products that are not
4137 // available in a PointExp
4138 if (m_expType == e1D)
4139 {
4141 "Function needs setting up for 1D boundary expansion");
4142 }
4143 else // 2D and 3D versions
4144 {
4145 if (m_bndConditions[i]->GetBoundaryConditionType() ==
4147 {
4149 std::static_pointer_cast<
4151 m_bndConditions[i]);
4152
4153 Array<OneD, NekDouble> valuesExp(npoints, 0.0);
4154 bcPtr->m_filename = "";
4155 bcPtr->m_expr = "0.0";
4156
4157 bcPtr->m_dirichletCondition =
4158 std::make_shared<LibUtilities::Equation>(
4159 m_session->GetInterpreter(), bcPtr->m_expr);
4160
4161 Vmath::Zero(locExpList->GetNcoeffs(),
4162 locExpList->UpdateCoeffs(), 1);
4163 Vmath::Zero(locExpList->GetNpoints(), locExpList->UpdatePhys(),
4164 1);
4165 }
4166 else if (m_bndConditions[i]->GetBoundaryConditionType() ==
4168 {
4169 SpatialDomains::NeumannBCShPtr bcPtr = std::static_pointer_cast<
4171 m_bndConditions[i]);
4172
4173 bcPtr->m_filename = "";
4174 std::string eqn = "0.0";
4175
4176 bcPtr->m_neumannCondition =
4177 std::make_shared<LibUtilities::Equation>(
4178 m_session->GetInterpreter(), eqn);
4179
4180 Vmath::Zero(locExpList->GetNcoeffs(),
4181 locExpList->UpdateCoeffs(), 1);
4182 Vmath::Zero(locExpList->GetNpoints(), locExpList->UpdatePhys(),
4183 1);
4184 }
4185 else if (m_bndConditions[i]->GetBoundaryConditionType() ==
4187 {
4188 SpatialDomains::RobinBCShPtr bcPtr = std::static_pointer_cast<
4190
4191 bcPtr->m_filename = "";
4192 std::string eqn = "0.0";
4193
4194 bcPtr->m_robinFunction =
4195 std::make_shared<LibUtilities::Equation>(
4196 m_session->GetInterpreter(), eqn);
4197
4198 Vmath::Zero(locExpList->GetNcoeffs(),
4199 locExpList->UpdateCoeffs(), 1);
4200 Vmath::Zero(locExpList->GetNpoints(), locExpList->UpdatePhys(),
4201 1);
4202 }
4203 else if (m_bndConditions[i]->GetBoundaryConditionType() ==
4205 {
4206 continue;
4207 }
4208 else
4209 {
4211 "This type of BC not implemented yet");
4212 }
4213 }
4214 }
4215}
4216
4217/**
4218 * @brief Fill the weight with m_bndCondBndWeight.
4219 */
4221 Array<OneD, NekDouble> &weightjmp)
4222{
4223 int cnt;
4224 int npts;
4225 int e = 0;
4226
4227 // Fill boundary conditions into missing elements
4228 int id2 = 0;
4229
4230 for (int n = cnt = 0; n < m_bndCondExpansions.size(); ++n)
4231 {
4232
4233 if (m_bndConditions[n]->GetBoundaryConditionType() ==
4235 {
4236 for (e = 0; e < m_bndCondExpansions[n]->GetExpSize(); ++e)
4237 {
4238 npts = m_bndCondExpansions[n]->GetExp(e)->GetTotPoints();
4239 id2 = m_trace->GetPhys_Offset(
4240 m_traceMap->GetBndCondIDToGlobalTraceID(cnt + e));
4241 Vmath::Fill(npts, m_bndCondBndWeight[n], &weightave[id2], 1);
4242 Vmath::Fill(npts, 0.0, &weightjmp[id2], 1);
4243 }
4244
4245 cnt += e;
4246 }
4247 else if (m_bndConditions[n]->GetBoundaryConditionType() ==
4249 m_bndConditions[n]->GetBoundaryConditionType() ==
4251 {
4252 for (e = 0; e < m_bndCondExpansions[n]->GetExpSize(); ++e)
4253 {
4254 npts = m_bndCondExpansions[n]->GetExp(e)->GetTotPoints();
4255 id2 = m_trace->GetPhys_Offset(
4256 m_traceMap->GetBndCondIDToGlobalTraceID(cnt + e));
4257 Vmath::Fill(npts, m_bndCondBndWeight[n], &weightave[id2], 1);
4258 Vmath::Fill(npts, 0.0, &weightjmp[id2], 1);
4259 }
4260
4261 cnt += e;
4262 }
4263 else if (m_bndConditions[n]->GetBoundaryConditionType() !=
4265 {
4267 "Method not set up for this boundary condition.");
4268 }
4269 }
4270}
4271
4272// Set up a list of element ids and trace ids that link to the
4273// boundary conditions
4275 Array<OneD, int> &TraceID)
4276{
4277
4278 if (m_BCtoElmMap.size() == 0)
4279 {
4280 switch (m_expType)
4281 {
4282 case e1D:
4283 {
4284 map<int, int> VertGID;
4285 int i, n, id;
4286 int bid, cnt, Vid;
4287 int nbcs = 0;
4288 // Determine number of boundary condition expansions.
4289 for (i = 0; i < m_bndConditions.size(); ++i)
4290 {
4291 nbcs += m_bndCondExpansions[i]->GetExpSize();
4292 }
4293
4294 // make sure arrays are of sufficient length
4295 m_BCtoElmMap = Array<OneD, int>(nbcs, -1);
4297
4298 // setup map of all global ids along boundary
4299 cnt = 0;
4300 for (n = 0; n < m_bndCondExpansions.size(); ++n)
4301 {
4302 for (i = 0; i < m_bndCondExpansions[n]->GetExpSize(); ++i)
4303 {
4304 Vid = m_bndCondExpansions[n]
4305 ->GetExp(i)
4306 ->GetGeom()
4307 ->GetVid(0);
4308 VertGID[Vid] = cnt++;
4309 }
4310 }
4311
4312 // Loop over elements and find verts that match;
4314 for (cnt = n = 0; n < GetExpSize(); ++n)
4315 {
4316 exp = (*m_exp)[n];
4317 for (i = 0; i < exp->GetNverts(); ++i)
4318 {
4319 id = exp->GetGeom()->GetVid(i);
4320
4321 if (VertGID.count(id) > 0)
4322 {
4323 bid = VertGID.find(id)->second;
4324 ASSERTL1(m_BCtoElmMap[bid] == -1,
4325 "Edge already set");
4326 m_BCtoElmMap[bid] = n;
4327 m_BCtoTraceMap[bid] = i;
4328 cnt++;
4329 }
4330 }
4331 }
4332 ASSERTL1(cnt == nbcs,
4333 "Failed to visit all boundary condtiions");
4334 }
4335 break;
4336 case e2D:
4337 {
4338 map<int, int> globalIdMap;
4339 int i, n;
4340 int cnt;
4341 int nbcs = 0;
4342
4343 // Populate global ID map (takes global geometry
4344 // ID to local expansion list ID).
4345 for (i = 0; i < GetExpSize(); ++i)
4346 {
4347 globalIdMap[(*m_exp)[i]->GetGeom()->GetGlobalID()] = i;
4348 }
4349
4350 // Determine number of boundary condition expansions.
4351 for (i = 0; i < m_bndConditions.size(); ++i)
4352 {
4353 nbcs += m_bndCondExpansions[i]->GetExpSize();
4354 }
4355
4356 // Initialize arrays
4359
4361 cnt = 0;
4362 for (n = 0; n < m_bndCondExpansions.size(); ++n)
4363 {
4364 for (i = 0; i < m_bndCondExpansions[n]->GetExpSize();
4365 ++i, ++cnt)
4366 {
4367 exp1d = m_bndCondExpansions[n]
4368 ->GetExp(i)
4370
4371 // Use edge to element map from MeshGraph.
4373 m_graph->GetElementsFromEdge(exp1d->GetGeom1D());
4374
4375 m_BCtoElmMap[cnt] =
4376 globalIdMap[(*tmp)[0].first->GetGlobalID()];
4377 m_BCtoTraceMap[cnt] = (*tmp)[0].second;
4378 }
4379 }
4380 }
4381 break;
4382 case e3D:
4383 {
4384 map<int, int> globalIdMap;
4385 int i, n;
4386 int cnt;
4387 int nbcs = 0;
4388
4389 // Populate global ID map (takes global geometry ID to local
4390 // expansion list ID).
4392 for (i = 0; i < GetExpSize(); ++i)
4393 {
4394 globalIdMap[(*m_exp)[i]->GetGeom()->GetGlobalID()] = i;
4395 }
4396
4397 // Determine number of boundary condition expansions.
4398 for (i = 0; i < m_bndConditions.size(); ++i)
4399 {
4400 if (m_bndCondExpansions[i])
4401 {
4402 nbcs += m_bndCondExpansions[i]->GetExpSize();
4403 }
4404 }
4405
4406 // Initialize arrays
4409
4411 for (cnt = n = 0; n < m_bndCondExpansions.size(); ++n)
4412 {
4413 for (i = 0; i < m_bndCondExpansions[n]->GetExpSize();
4414 ++i, ++cnt)
4415 {
4416 exp2d = m_bndCondExpansions[n]
4417 ->GetExp(i)
4419
4421 m_graph->GetElementsFromFace(exp2d->GetGeom2D());
4422 m_BCtoElmMap[cnt] =
4423 globalIdMap[tmp->at(0).first->GetGlobalID()];
4424 m_BCtoTraceMap[cnt] = tmp->at(0).second;
4425 }
4426 }
4427 }
4428 break;
4429 default:
4430 ASSERTL1(false, "Not setup for this expansion");
4431 break;
4432 }
4433 }
4434
4435 ElmtID = m_BCtoElmMap;
4436 TraceID = m_BCtoTraceMap;
4437}
4438
4440 std::shared_ptr<ExpList> &result,
4441 const bool DeclareCoeffPhysArrays)
4442{
4443 int n, cnt;
4444 std::vector<unsigned int> eIDs;
4445
4446 Array<OneD, int> ElmtID, TraceID;
4447 GetBoundaryToElmtMap(ElmtID, TraceID);
4448
4449 // Skip other boundary regions
4450 for (cnt = n = 0; n < i; ++n)
4451 {
4452 cnt += m_bndCondExpansions[n]->GetExpSize();
4453 }
4454
4455 // Populate eIDs with information from BoundaryToElmtMap
4456 for (n = 0; n < m_bndCondExpansions[i]->GetExpSize(); ++n)
4457 {
4458 eIDs.push_back(ElmtID[cnt + n]);
4459 }
4460
4461 // Create expansion list
4463 *this, eIDs, DeclareCoeffPhysArrays, Collections::eNoCollection);
4464 // Copy phys and coeffs to new explist
4465 if (DeclareCoeffPhysArrays)
4466 {
4467 int nq;
4468 int offsetOld, offsetNew;
4469 Array<OneD, NekDouble> tmp1, tmp2;
4470 for (n = 0; n < result->GetExpSize(); ++n)
4471 {
4472 nq = GetExp(ElmtID[cnt + n])->GetTotPoints();
4473 offsetOld = GetPhys_Offset(ElmtID[cnt + n]);
4474 offsetNew = result->GetPhys_Offset(n);
4475 Vmath::Vcopy(nq, tmp1 = GetPhys() + offsetOld, 1,
4476 tmp2 = result->UpdatePhys() + offsetNew, 1);
4477 nq = GetExp(ElmtID[cnt + n])->GetNcoeffs();
4478 offsetOld = GetCoeff_Offset(ElmtID[cnt + n]);
4479 offsetNew = result->GetCoeff_Offset(n);
4480 Vmath::Vcopy(nq, tmp1 = GetCoeffs() + offsetOld, 1,
4481 tmp2 = result->UpdateCoeffs() + offsetNew, 1);
4482 }
4483 }
4484}
4485
4486/**
4487 * @brief Reset this field, so that geometry information can be updated.
4488 */
4490{
4492
4493 // Reset boundary condition expansions.
4494 for (int n = 0; n < m_bndCondExpansions.size(); ++n)
4495 {
4496 m_bndCondExpansions[n]->Reset();
4497 }
4498}
4499
4500/**
4501 * Search through the edge expansions and identify which ones
4502 * have Robin/Mixed type boundary conditions. If find a Robin
4503 * boundary then store the edge id of the boundary condition
4504 * and the array of points of the physical space boundary
4505 * condition which are hold the boundary condition primitive
4506 * variable coefficient at the quatrature points
4507 *
4508 * \return std map containing the robin boundary condition
4509 * info using a key of the element id
4510 *
4511 * There is a next member to allow for more than one Robin
4512 * boundary condition per element
4513 */
4514map<int, RobinBCInfoSharedPtr> DisContField::v_GetRobinBCInfo(void)
4515{
4516 int i, cnt;
4517 map<int, RobinBCInfoSharedPtr> returnval;
4518 Array<OneD, int> ElmtID, TraceID;
4519 GetBoundaryToElmtMap(ElmtID, TraceID);
4520
4521 for (cnt = i = 0; i < m_bndCondExpansions.size(); ++i)
4522 {
4524
4525 if (m_bndConditions[i]->GetBoundaryConditionType() ==
4527 {
4528 int e, elmtid;
4529 Array<OneD, NekDouble> Array_tmp;
4530
4531 locExpList = m_bndCondExpansions[i];
4532
4533 int npoints = locExpList->GetNpoints();
4534 Array<OneD, NekDouble> x0(npoints, 0.0);
4535 Array<OneD, NekDouble> x1(npoints, 0.0);
4536 Array<OneD, NekDouble> x2(npoints, 0.0);
4537 Array<OneD, NekDouble> coeffphys(npoints);
4538
4539 locExpList->GetCoords(x0, x1, x2);
4540
4542 std::static_pointer_cast<
4544 ->m_robinPrimitiveCoeff;
4545
4546 // evalaute coefficient
4547 coeffeqn->Evaluate(x0, x1, x2, 0.0, coeffphys);
4548
4549 for (e = 0; e < locExpList->GetExpSize(); ++e)
4550 {
4551 RobinBCInfoSharedPtr rInfo =
4553 TraceID[cnt + e],
4554 Array_tmp = coeffphys + locExpList->GetPhys_Offset(e));
4555
4556 elmtid = ElmtID[cnt + e];
4557 // make link list if necessary
4558 if (returnval.count(elmtid) != 0)
4559 {
4560 rInfo->next = returnval.find(elmtid)->second;
4561 }
4562 returnval[elmtid] = rInfo;
4563 }
4564 }
4565 cnt += m_bndCondExpansions[i]->GetExpSize();
4566 }
4567
4568 return returnval;
4569}
4570
4571/**
4572 * @brief Calculate the \f$ L^2 \f$ error of the \f$ Q_{\rm dir} \f$
4573 * derivative using the consistent DG evaluation of \f$ Q_{\rm dir} \f$.
4574 *
4575 * The solution provided is of the primative variation at the quadrature
4576 * points and the derivative is compared to the discrete derivative at
4577 * these points, which is likely to be undesirable unless using a much
4578 * higher number of quadrature points than the polynomial order used to
4579 * evaluate \f$ Q_{\rm dir} \f$.
4580 */
4582 const Array<OneD, const NekDouble> &coeffs,
4583 const Array<OneD, const NekDouble> &soln)
4584{
4585 int i, e, ncoeff_edge;
4587 Array<OneD, NekDouble> out_d(m_ncoeffs), out_tmp;
4588
4590 m_traceMap->GetElmtToTrace();
4591
4593
4594 int cnt;
4595 int LocBndCoeffs = m_traceMap->GetNumLocalBndCoeffs();
4596 Array<OneD, NekDouble> loc_lambda(LocBndCoeffs), edge_lambda;
4597 m_traceMap->GlobalToLocalBnd(m_trace->GetCoeffs(), loc_lambda);
4598
4599 edge_lambda = loc_lambda;
4600
4601 // Calculate Q using standard DG formulation.
4602 for (i = cnt = 0; i < GetExpSize(); ++i)
4603 {
4604 // Probably a better way of setting up lambda than this.
4605 // Note cannot use PutCoeffsInToElmts since lambda space
4606 // is mapped during the solve.
4607 int nEdges = (*m_exp)[i]->GetGeom()->GetNumEdges();
4608 Array<OneD, Array<OneD, NekDouble>> edgeCoeffs(nEdges);
4609
4610 for (e = 0; e < nEdges; ++e)
4611 {
4612 edgedir = (*m_exp)[i]->GetTraceOrient(e);
4613 ncoeff_edge = elmtToTrace[i][e]->GetNcoeffs();
4614 edgeCoeffs[e] = Array<OneD, NekDouble>(ncoeff_edge);
4615 Vmath::Vcopy(ncoeff_edge, edge_lambda, 1, edgeCoeffs[e], 1);
4616 elmtToTrace[i][e]->SetCoeffsToOrientation(edgedir, edgeCoeffs[e],
4617 edgeCoeffs[e]);
4618 edge_lambda = edge_lambda + ncoeff_edge;
4619 }
4620
4621 (*m_exp)[i]->DGDeriv(dir, tmp_coeffs = coeffs + m_coeff_offset[i],
4622 elmtToTrace[i], edgeCoeffs, out_tmp = out_d + cnt);
4623 cnt += (*m_exp)[i]->GetNcoeffs();
4624 }
4625
4627 BwdTrans(out_d, phys);
4628 Vmath::Vsub(m_npoints, phys, 1, soln, 1, phys, 1);
4629 return L2(phys);
4630}
4631
4632/**
4633 * @brief Evaluate HDG post-processing to increase polynomial order of
4634 * solution.
4635 *
4636 * This function takes the solution (assumed to be one order lower) in
4637 * physical space, and postprocesses at the current polynomial order by
4638 * solving the system:
4639 *
4640 * \f[
4641 * \begin{aligned}
4642 * (\nabla w, \nabla u^*) &= (\nabla w, u), \\
4643 * \langle \nabla u^*, 1 \rangle &= \langle \nabla u, 1 \rangle
4644 * \end{aligned}
4645 * \f]
4646 *
4647 * where \f$ u \f$ corresponds with the current solution as stored
4648 * inside #m_coeffs.
4649 *
4650 * @param outarray The resulting field \f$ u^* \f$.
4651 */
4653 const Array<OneD, const NekDouble> &coeffs,
4654 Array<OneD, NekDouble> &outarray)
4655{
4656 int i, cnt, e, ncoeff_trace;
4657 Array<OneD, NekDouble> force, out_tmp, qrhs, qrhs1;
4659 m_traceMap->GetElmtToTrace();
4660
4662
4663 int nq_elmt, nm_elmt;
4664 int LocBndCoeffs = m_traceMap->GetNumLocalBndCoeffs();
4665 Array<OneD, NekDouble> loc_lambda(LocBndCoeffs), trace_lambda;
4666 Array<OneD, NekDouble> tmp_coeffs;
4667 m_traceMap->GlobalToLocalBnd(m_trace->GetCoeffs(), loc_lambda);
4668
4669 trace_lambda = loc_lambda;
4670
4671 int dim = (m_expType == e2D) ? 2 : 3;
4672
4673 int num_points[] = {0, 0, 0};
4674 int num_modes[] = {0, 0, 0};
4675
4676 // Calculate Q using standard DG formulation.
4677 for (i = cnt = 0; i < GetExpSize(); ++i)
4678 {
4679 nq_elmt = (*m_exp)[i]->GetTotPoints();
4680 nm_elmt = (*m_exp)[i]->GetNcoeffs();
4681 qrhs = Array<OneD, NekDouble>(nq_elmt);
4682 qrhs1 = Array<OneD, NekDouble>(nq_elmt);
4683 force = Array<OneD, NekDouble>(2 * nm_elmt);
4684 out_tmp = force + nm_elmt;
4686
4687 for (int j = 0; j < dim; ++j)
4688 {
4689 num_points[j] = (*m_exp)[i]->GetBasis(j)->GetNumPoints();
4690 num_modes[j] = (*m_exp)[i]->GetBasis(j)->GetNumModes();
4691 }
4692
4693 // Probably a better way of setting up lambda than this. Note
4694 // cannot use PutCoeffsInToElmts since lambda space is mapped
4695 // during the solve.
4696 int nTraces = (*m_exp)[i]->GetNtraces();
4697 Array<OneD, Array<OneD, NekDouble>> traceCoeffs(nTraces);
4698
4699 for (e = 0; e < (*m_exp)[i]->GetNtraces(); ++e)
4700 {
4701 edgedir = (*m_exp)[i]->GetTraceOrient(e);
4702 ncoeff_trace = elmtToTrace[i][e]->GetNcoeffs();
4703 traceCoeffs[e] = Array<OneD, NekDouble>(ncoeff_trace);
4704 Vmath::Vcopy(ncoeff_trace, trace_lambda, 1, traceCoeffs[e], 1);
4705 if (dim == 2)
4706 {
4707 elmtToTrace[i][e]->SetCoeffsToOrientation(
4708 edgedir, traceCoeffs[e], traceCoeffs[e]);
4709 }
4710 else
4711 {
4712 (*m_exp)[i]
4714 ->SetFaceToGeomOrientation(e, traceCoeffs[e]);
4715 }
4716 trace_lambda = trace_lambda + ncoeff_trace;
4717 }
4718
4719 // creating orthogonal expansion (checking if we have quads or
4720 // triangles)
4721 LibUtilities::ShapeType shape = (*m_exp)[i]->DetShapeType();
4722 switch (shape)
4723 {
4725 {
4726 const LibUtilities::PointsKey PkeyQ1(
4728 const LibUtilities::PointsKey PkeyQ2(
4731 num_modes[0], PkeyQ1);
4733 num_modes[1], PkeyQ2);
4735 dynamic_cast<SpatialDomains::QuadGeom *>(
4736 (*m_exp)[i]->GetGeom());
4738 BkeyQ1, BkeyQ2, qGeom);
4739 }
4740 break;
4742 {
4743 const LibUtilities::PointsKey PkeyT1(
4745 const LibUtilities::PointsKey PkeyT2(
4746 num_points[1], LibUtilities::eGaussRadauMAlpha1Beta0);
4748 num_modes[0], PkeyT1);
4750 num_modes[1], PkeyT2);
4752 dynamic_cast<SpatialDomains::TriGeom *>(
4753 (*m_exp)[i]->GetGeom());
4755 BkeyT1, BkeyT2, tGeom);
4756 }
4757 break;
4759 {
4760 const LibUtilities::PointsKey PkeyH1(
4762 const LibUtilities::PointsKey PkeyH2(
4764 const LibUtilities::PointsKey PkeyH3(
4767 num_modes[0], PkeyH1);
4769 num_modes[1], PkeyH2);
4771 num_modes[2], PkeyH3);
4773 dynamic_cast<SpatialDomains::HexGeom *>(
4774 (*m_exp)[i]->GetGeom());
4776 BkeyH1, BkeyH2, BkeyH3, hGeom);
4777 }
4778 break;
4780 {
4781 const LibUtilities::PointsKey PkeyT1(
4783 const LibUtilities::PointsKey PkeyT2(
4784 num_points[1], LibUtilities::eGaussRadauMAlpha1Beta0);
4785 const LibUtilities::PointsKey PkeyT3(
4786 num_points[2], LibUtilities::eGaussRadauMAlpha2Beta0);
4788 num_modes[0], PkeyT1);
4790 num_modes[1], PkeyT2);
4792 num_modes[2], PkeyT3);
4794 dynamic_cast<SpatialDomains::TetGeom *>(
4795 (*m_exp)[i]->GetGeom());
4797 BkeyT1, BkeyT2, BkeyT3, tGeom);
4798 }
4799 break;
4800 default:
4802 "Wrong shape type, HDG postprocessing is not "
4803 "implemented");
4804 };
4805
4806 // DGDeriv
4807 // (d/dx w, d/dx q_0)
4808 (*m_exp)[i]->DGDeriv(0, tmp_coeffs = coeffs + m_coeff_offset[i],
4809 elmtToTrace[i], traceCoeffs, out_tmp);
4810 (*m_exp)[i]->BwdTrans(out_tmp, qrhs);
4811 ppExp->IProductWRTDerivBase(0, qrhs, force);
4812
4813 // + (d/dy w, d/dy q_1)
4814 (*m_exp)[i]->DGDeriv(1, tmp_coeffs = coeffs + m_coeff_offset[i],
4815 elmtToTrace[i], traceCoeffs, out_tmp);
4816
4817 (*m_exp)[i]->BwdTrans(out_tmp, qrhs);
4818 ppExp->IProductWRTDerivBase(1, qrhs, out_tmp);
4819
4820 Vmath::Vadd(nm_elmt, force, 1, out_tmp, 1, force, 1);
4821
4822 // determine force[0] = (1,u)
4823 (*m_exp)[i]->BwdTrans(tmp_coeffs = coeffs + m_coeff_offset[i], qrhs);
4824 force[0] = (*m_exp)[i]->Integral(qrhs);
4825
4826 // multiply by inverse Laplacian matrix
4827 // get matrix inverse
4829 ppExp->DetShapeType(), *ppExp);
4830 DNekScalMatSharedPtr lapsys = ppExp->GetLocMatrix(lapkey);
4831
4832 NekVector<NekDouble> in(nm_elmt, force, eWrapper);
4833 NekVector<NekDouble> out(nm_elmt);
4834
4835 out = (*lapsys) * in;
4836
4837 // Transforming back to modified basis
4838 Array<OneD, NekDouble> work(nq_elmt);
4839 ppExp->BwdTrans(out.GetPtr(), work);
4840 (*m_exp)[i]->FwdTrans(work, tmp_coeffs = outarray + m_coeff_offset[i]);
4841 }
4842}
4843
4847 Array<OneD, NekDouble> &locTraceFwd, Array<OneD, NekDouble> &locTraceBwd)
4848{
4849 if (locTraceFwd != NullNekDouble1DArray)
4850 {
4851 m_locTraceToTraceMap->InterpLocTracesToTraceTranspose(0, Fwd,
4852 locTraceFwd);
4853 }
4854
4855 if (locTraceBwd != NullNekDouble1DArray)
4856 {
4857 m_locTraceToTraceMap->InterpLocTracesToTraceTranspose(1, Bwd,
4858 locTraceBwd);
4859 }
4860}
4861
4863 const Array<OneD, const NekDouble> &FwdFlux,
4864 const Array<OneD, const NekDouble> &BwdFlux,
4865 Array<OneD, NekDouble> &outarray)
4866{
4867 Array<OneD, NekDouble> FCoeffs(m_trace->GetNcoeffs());
4868
4869 m_trace->IProductWRTBase(FwdFlux, FCoeffs);
4870 m_locTraceToTraceMap->AddTraceCoeffsToFieldCoeffs(1, FCoeffs, outarray);
4871 m_trace->IProductWRTBase(BwdFlux, FCoeffs);
4872 m_locTraceToTraceMap->AddTraceCoeffsToFieldCoeffs(0, FCoeffs, outarray);
4873}
4874
4875/// \brief Rotate the slice [offset, offset + npts) of the
4876/// 3D vector field in Bwd around the axis 'dir' by 'angle'.
4878 const int dir, const NekDouble angle,
4879 const int offset, const int npts)
4880{
4881 // Precompute trigonometric values for improved precision
4882 const NekDouble cosA = cos(angle);
4883 const NekDouble sinA = sin(angle);
4884
4885 switch (dir)
4886 {
4887 // ----------------------------------------------------------
4888 // Rotate around the x-axis by 'angle':
4889 // y' = y*cos(angle) - z*sin(angle)
4890 // z' = y*sin(angle) + z*cos(angle)
4891 // x' = x (unchanged)
4892 // ----------------------------------------------------------
4893 case 0:
4894 {
4895 for (int i = offset; i < offset + npts; ++i)
4896 {
4897 NekDouble tmpY = Bwd[2][i];
4898 NekDouble tmpZ = Bwd[3][i];
4899
4900 NekDouble yrot = cosA * tmpY - sinA * tmpZ;
4901 NekDouble zrot = sinA * tmpY + cosA * tmpZ;
4902
4903 Bwd[2][i] = yrot;
4904 Bwd[3][i] = zrot;
4905 }
4906 }
4907 break;
4908
4909 // ----------------------------------------------------------
4910 // Rotate around the y-axis by 'angle':
4911 // z' = z*cos(angle) - x*sin(angle)
4912 // x' = z*sin(angle) + x*cos(angle)
4913 // y' = y (unchanged)
4914 // ----------------------------------------------------------
4915 case 1:
4916 {
4917 for (int i = offset; i < offset + npts; ++i)
4918 {
4919 NekDouble tmpX = Bwd[1][i];
4920 NekDouble tmpZ = Bwd[3][i];
4921
4922 NekDouble zrot = cosA * tmpZ - sinA * tmpX;
4923 NekDouble xrot = sinA * tmpZ + cosA * tmpX;
4924
4925 Bwd[1][i] = xrot;
4926 Bwd[3][i] = zrot;
4927 }
4928 }
4929 break;
4930
4931 // ----------------------------------------------------------
4932 // Rotate around the z-axis by 'angle':
4933 // x' = x*cos(angle) - y*sin(angle)
4934 // y' = x*sin(angle) + y*cos(angle)
4935 // z' = z (unchanged)
4936 // ----------------------------------------------------------
4937 case 2:
4938 {
4939 for (int i = offset; i < offset + npts; ++i)
4940 {
4941 NekDouble tmpX = Bwd[1][i];
4942 NekDouble tmpY = Bwd[2][i];
4943
4944 NekDouble xrot = cosA * tmpX - sinA * tmpY;
4945 NekDouble yrot = sinA * tmpX + cosA * tmpY;
4946
4947 Bwd[1][i] = xrot;
4948 Bwd[2][i] = yrot;
4949 }
4950 }
4951 break;
4952
4953 default:
4955 "Rotate() axis must be 0 (x), 1 (y), or 2 (z).");
4956 break;
4957 }
4958}
4959
4960/// \brief Rotate the slice [offset, offset + npts) of the
4961/// 3D vector field in Bwd around the axis 'dir' by 'angle'.
4963 const NekDouble angle, const int offset,
4964 const int npts)
4965{
4966 // Precompute trigonometric values using correct angle input
4967 const NekDouble cosA = cos(angle);
4968 const NekDouble sinA = sin(angle);
4969
4970 // Define rotation matrix R (3x3) using Array<OneD, NekDouble> in
4971 // **column-major order**
4972 Array<OneD, NekDouble> R(9, 0.0);
4973
4974 if (dir == 0) // Rotation around X-axis
4975 {
4976 R[0] = 1.0;
4977 R[3] = 0.0;
4978 R[6] = 0.0;
4979 R[1] = 0.0;
4980 R[4] = cosA;
4981 R[7] = -sinA;
4982 R[2] = 0.0;
4983 R[5] = sinA;
4984 R[8] = cosA;
4985 }
4986 else if (dir == 1) // Rotation around Y-axis
4987 {
4988 R[0] = cosA;
4989 R[3] = 0.0;
4990 R[6] = sinA;
4991 R[1] = 0.0;
4992 R[4] = 1.0;
4993 R[7] = 0.0;
4994 R[2] = -sinA;
4995 R[5] = 0.0;
4996 R[8] = cosA;
4997 }
4998 else if (dir == 2) // Rotation around Z-axis
4999 {
5000 R[0] = cosA;
5001 R[3] = -sinA;
5002 R[6] = 0.0;
5003 R[1] = sinA;
5004 R[4] = cosA;
5005 R[7] = 0.0;
5006 R[2] = 0.0;
5007 R[5] = 0.0;
5008 R[8] = 1.0;
5009 }
5010 else
5011 {
5012 NEKERROR(
5014 "Invalid rotation axis for first-order derivative transformation.");
5015 }
5016
5017 for (int p = offset; p < offset + npts; ++p)
5018 {
5019 // Allocate gradient tensor (3x3) in **column-major order**
5020 Array<OneD, NekDouble> gradU(9, 0.0);
5021
5022 // Load original velocity gradient tensor into **column-major** format
5023 gradU[0] = Bwd[0][1][p]; // ∂u_x/∂x
5024 gradU[3] = Bwd[1][1][p]; // ∂u_x/∂y
5025 gradU[6] = Bwd[2][1][p]; // ∂u_x/∂z
5026
5027 gradU[1] = Bwd[0][2][p]; // ∂u_y/∂x
5028 gradU[4] = Bwd[1][2][p]; // ∂u_y/∂y
5029 gradU[7] = Bwd[2][2][p]; // ∂u_y/∂z
5030
5031 gradU[2] = Bwd[0][3][p]; // ∂u_z/∂x
5032 gradU[5] = Bwd[1][3][p]; // ∂u_z/∂y
5033 gradU[8] = Bwd[2][3][p]; // ∂u_z/∂z
5034
5035 // Allocate intermediate matrix R * gradU in **column-major order**
5036 Array<OneD, NekDouble> R_gradU(9, 0.0);
5037
5038 // Perform matrix multiplication R * gradU using Blas::Dgemm in
5039 // **column-major order**
5040 Blas::Dgemm('N', 'N', 3, 3, 3, 1.0, R.data(), 3, gradU.data(), 3, 0.0,
5041 R_gradU.data(), 3);
5042
5043 // Allocate final rotated gradient (R * gradU) * R^T in **column-major
5044 // order**
5045 Array<OneD, NekDouble> gradU_rotated(9, 0.0);
5046
5047 // Perform matrix multiplication (R * gradU) * R^T using Blas::Dgemm in
5048 // **column-major order**
5049 Blas::Dgemm('N', 'T', 3, 3, 3, 1.0, R_gradU.data(), 3, R.data(), 3, 0.0,
5050 gradU_rotated.data(), 3);
5051
5052 // Store rotated gradients back in Bwd in **column-major order**
5053 Bwd[0][1][p] = gradU_rotated[0]; // ∂u_x'/∂x'
5054 Bwd[1][1][p] = gradU_rotated[3]; // ∂u_x'/∂y'
5055 Bwd[2][1][p] = gradU_rotated[6]; // ∂u_x'/∂z'
5056
5057 Bwd[0][2][p] = gradU_rotated[1]; // ∂u_y'/∂x'
5058 Bwd[1][2][p] = gradU_rotated[4]; // ∂u_y'/∂y'
5059 Bwd[2][2][p] = gradU_rotated[7]; // ∂u_y'/∂z'
5060
5061 Bwd[0][3][p] = gradU_rotated[2]; // ∂u_z'/∂x'
5062 Bwd[1][3][p] = gradU_rotated[5]; // ∂u_z'/∂y'
5063 Bwd[2][3][p] = gradU_rotated[8]; // ∂u_z'/∂z'
5064 }
5065}
5066
5067} // namespace Nektar::MultiRegions
#define ASSERTL0(condition, msg)
#define NEKERROR(type, msg)
Assert Level 0 – Fundamental assert which is used whether in FULLDEBUG, DEBUG or OPT compilation mode...
#define WARNINGL0(condition, msg)
#define ASSERTL1(condition, msg)
Assert Level 1 – Debugging which is used whether in FULLDEBUG or DEBUG compilation mode....
#define ASSERTL2(condition, msg)
Assert Level 2 – Debugging which is used FULLDEBUG compilation mode. This level assert is designed to...
#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...
~DisContField() override
Destructor.
Array< OneD, SpatialDomains::BoundaryConditionShPtr > & v_UpdateBndConditions() override
void v_SetBCsToHomogeneous(void) override
Set boundary conditions to be homogeneous.
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.
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.
std::set< int > m_boundaryTraces
A set storing the global IDs of any boundary Verts.
LocTraceToTraceMapSharedPtr m_locTraceToTraceMap
std::vector< bool > m_negatedFluxNormal
MultiRegions::ExpListSharedPtr m_locElmtTrace
Local Elemental trace expansions.
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.
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_PeriodicBwdRot(Array< OneD, Array< OneD, NekDouble > > &Bwd) override
void v_ExtractTracePhys(const Array< OneD, const NekDouble > &inarray, Array< OneD, NekDouble > &outarray, bool gridVelocity=false) override
This method extracts the trace (verts in 1D) from the field inarray and puts the values in outarray.
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
GlobalLinSysKey v_HelmSolve(const Array< OneD, const NekDouble > &inarray, Array< OneD, NekDouble > &outarray, const StdRegions::ConstFactorMap &factors, const StdRegions::VarCoeffMap &varcoeff, const StdRegions::VarFactorsMap &varfactors, const Array< OneD, const NekDouble > &dirForcing, const bool PhysSpaceForcing) override
Solve the Helmholtz equation.
void GenerateBoundaryConditionExpansion(const SpatialDomains::MeshGraphSharedPtr &graph1D, const SpatialDomains::BoundaryConditions &bcs, const std::string variable, const bool DeclareCoeffPhysArrays=true, const Collections::ImplementationType ImpType=Collections::eNoImpType)
Discretises the boundary conditions.
void v_RotLocalBwdDeriveTrace(TensorOfArray3D< NekDouble > &Bwd) override
InterfaceMapDGSharedPtr & v_GetInterfaceMap(void) override
void v_RotLocalBwdTrace(Array< OneD, Array< OneD, NekDouble > > &Bwd) override
InterfaceMapDGSharedPtr m_interfaceMap
Interfaces mapping for trace space.
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
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...
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 Rotate(Array< OneD, Array< OneD, NekDouble > > &Bwd, const int dir, const NekDouble angle, const int offset, const int npts)
Rotate the slice [offset, offset + npts) of the 3D vector field in Bwd around the axis 'dir' by 'angl...
void v_FillBwdWithBoundCond(const Array< OneD, NekDouble > &Fwd, Array< OneD, NekDouble > &Bwd, bool PutFwdInBwdOnBCs) override
GlobalLinSysMapShPtr m_globalBndMat
Global boundary matrix.
void DeriveRotate(TensorOfArray3D< NekDouble > &Bwd, const int dir, const NekDouble angle, const int offset, const int npts)
Rotate the slice [offset, offset + npts) of the 3D vector field in Bwd around the axis 'dir' by 'angl...
PeriodicMap m_periodicVerts
A map which identifies groups of periodic vertices.
ExpListSharedPtr m_trace
Trace space storage for points between elements.
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.
std::vector< int > m_periodicFwdCopy
A vector indicating degress of freedom which need to be copied from forwards to backwards space in ca...
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
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
void v_PeriodicDeriveBwdRot(TensorOfArray3D< NekDouble > &Bwd) override
std::map< int, RobinBCInfoSharedPtr > v_GetRobinBCInfo() override
void v_GetFwdBwdTracePhys(Array< OneD, NekDouble > &Fwd, Array< OneD, NekDouble > &Bwd) override
Base class for all multi-elemental spectral/hp expansions.
Definition ExpList.h:98
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:1749
std::shared_ptr< ExpList > & GetTrace()
Definition ExpList.h:2272
int GetNcoeffs(void) const
Returns the total number of local degrees of freedom .
Definition ExpList.h:1607
const DNekScalBlkMatSharedPtr & GetBlockMatrix(const GlobalMatrixKey &gkey)
void GetBoundaryToElmtMap(Array< OneD, int > &ElmtID, Array< OneD, int > &EdgeID)
Definition ExpList.h:2406
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:2046
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:2149
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:2197
int GetExpSize(void)
This function returns the number of elements in the expansion.
Definition ExpList.h:2157
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...
Array< OneD, int > m_coeff_offset
Offset of elemental data into the array m_coeffs.
Definition ExpList.h:1193
NekDouble GetCoeff(int i)
Get the i th value (coefficient) of m_coeffs.
Definition ExpList.h:2129
bool m_physState
The state of the array m_phys.
Definition ExpList.h:1173
LibUtilities::CommSharedPtr m_comm
Communicator.
Definition ExpList.h:1122
std::shared_ptr< LocalRegions::ExpansionVector > m_exp
The list of local expansions.
Definition ExpList.h:1184
int m_ncoeffs
The total number of local degrees of freedom. m_ncoeffs .
Definition ExpList.h:1129
void EvaluateBoundaryConditions(const NekDouble time=0.0, const std::string varName="", const NekDouble=NekConstants::kNekUnsetDouble, const NekDouble=NekConstants::kNekUnsetDouble)
Definition ExpList.h:2389
const std::shared_ptr< LocalRegions::ExpansionVector > GetExp() const
This function returns the vector of elements in the expansion.
Definition ExpList.h:2189
SpatialDomains::MeshGraphSharedPtr m_graph
Mesh associated with this expansion list.
Definition ExpList.h:1126
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:527
void ApplyGeomInfo()
Apply geometry information to each expansion.
LibUtilities::SessionReaderSharedPtr m_session
Session.
Definition ExpList.h:1124
static SpatialDomains::BoundaryConditionShPtr GetBoundaryCondition(const SpatialDomains::BoundaryConditionCollection &collection, unsigned int index, const std::string &variable)
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:1816
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:2204
ExpansionType m_expType
Expansion type.
Definition ExpList.h:1117
Array< OneD, NekDouble > m_phys
The global expansion evaluated at the quadrature points.
Definition ExpList.h:1165
int GetCoordim(int eid)
This function returns the dimension of the coordinates of the element eid.
Definition ExpList.h:2005
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()
static std::unique_ptr< DataType, UniquePtrDeleter > AllocateUniquePtr(const Args &...args)
static bool GenerateVector(const std::string &str, std::vector< T > &out)
Takes a comma-separated string and converts it to entries in a vector.
const BoundaryRegionCollection & GetBoundaryRegions(void) const
Definition Conditions.h:273
const BoundaryConditionCollection & GetBoundaryConditions(void) const
Definition Conditions.h:283
2D geometry information
Definition Geometry2D.h:50
int GetVid(int i) const
Returns global id of vertex i of this object.
Definition Geometry.h:345
PointGeom * GetVertex(int i) const
Returns vertex i of this object.
Definition Geometry.h:353
int GetNumVerts() const
Get the number of vertices of this object.
Definition Geometry.h:395
StdRegions::Orientation GetEorient(const int i) const
Returns the orientation of edge i with respect to the ordering of edges in the standard element.
Definition Geometry.h:378
int GetEid(int i) const
Get the ID of edge i of this object.
Definition Geometry.cpp:83
void Rotate(PointGeom &a, int dir, NekDouble angle)
_this = rotation of a by angle 'angle' around axis dir
NekDouble dist(PointGeom &a)
return distance between this and input a
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:228
static StdRegions::Orientation GetFaceOrientation(const TriGeom &face1, const TriGeom &face2, bool doRot, int dir, NekDouble angle, NekDouble tol)
Get the orientation of face1.
Definition TriGeom.cpp:207
static void Dgemm(const char &transa, const char &transb, const int &m, const int &n, const int &k, const double &alpha, const double *a, const int &lda, const double *b, const int &ldb, const double &beta, double *c, const int &ldc)
BLAS level 3: Matrix-matrix multiply C = A x B where op(A)[m x k], op(B)[k x n], C[m x n] DGEMM perfo...
Definition Blas.hpp:324
BasisManagerT & BasisManager(void)
std::shared_ptr< Basis > BasisSharedPtr
std::shared_ptr< SessionReader > SessionReaderSharedPtr
std::shared_ptr< Equation > EquationSharedPtr
Definition Equation.h:131
@ 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:46
std::shared_ptr< Expansion1D > Expansion1DSharedPtr
Definition Expansion1D.h:50
std::shared_ptr< Expansion3D > Expansion3DSharedPtr
Definition Expansion2D.h:47
std::shared_ptr< AssemblyMapDG > AssemblyMapDGSharedPtr
static AssemblyMapSharedPtr NullAssemblyMapSharedPtr
Definition AssemblyMap.h:51
static ExpListSharedPtr NullExpListSharedPtr
Definition ExpList.h:1594
std::shared_ptr< RobinBCInfo > RobinBCInfoSharedPtr
std::shared_ptr< InterfaceMapDG > InterfaceMapDGSharedPtr
std::shared_ptr< GlobalLinSys > GlobalLinSysSharedPtr
Pointer to a GlobalLinSys object.
std::shared_ptr< ExpList > ExpListSharedPtr
Shared pointer to an ExpList object.
static GlobalLinSysKey NullGlobalLinSysKey(StdRegions::eNoMatrixType)
std::shared_ptr< LocTraceToTraceMap > LocTraceToTraceMapSharedPtr
std::map< int, std::vector< PeriodicEntity > > PeriodicMap
static const NekDouble kNekUnsetDouble
std::shared_ptr< std::vector< std::pair< Geometry *, int > > > GeometryLinkSharedPtr
Definition MeshGraph.h:216
std::map< int, BoundaryRegionShPtr > BoundaryRegionCollection
Definition Conditions.h:249
std::map< int, std::vector< unsigned int > > CompositeOrdering
Definition MeshGraph.h:177
std::shared_ptr< BoundaryRegion > BoundaryRegionShPtr
Definition Conditions.h:247
std::shared_ptr< BoundaryConditions > BoundaryConditionsSharedPtr
Definition Conditions.h:327
std::shared_ptr< BoundaryConditionBase > BoundaryConditionShPtr
Definition Conditions.h:251
std::map< int, std::vector< unsigned int > > BndRegionOrdering
Definition MeshGraph.h:178
std::shared_ptr< DirichletBoundaryCondition > DirichletBCShPtr
Definition Conditions.h:252
std::shared_ptr< Composite > CompositeSharedPtr
Definition MeshGraph.h:185
std::map< int, BoundaryConditionMapShPtr > BoundaryConditionCollection
Definition Conditions.h:258
std::shared_ptr< MeshGraph > MeshGraphSharedPtr
Definition MeshGraph.h:224
std::shared_ptr< NeumannBoundaryCondition > NeumannBCShPtr
Definition Conditions.h:253
std::shared_ptr< BoundaryConditionMap > BoundaryConditionMapShPtr
Definition Conditions.h:257
std::shared_ptr< RobinBoundaryCondition > RobinBCShPtr
Definition Conditions.h:254
std::map< int, CompositeSharedPtr > CompositeMap
Definition MeshGraph.h:186
std::map< StdRegions::ConstFactorType, Array< OneD, NekDouble > > VarFactorsMap
std::map< ConstFactorType, NekDouble > ConstFactorMap
std::map< StdRegions::VarCoeffType, VarCoeffEntry > VarCoeffMap
std::shared_ptr< DNekScalMat > DNekScalMatSharedPtr
std::shared_ptr< DNekScalBlkMat > DNekScalBlkMatSharedPtr
static Array< OneD, NekDouble > NullNekDouble1DArray
NekMatrix< InnerMatrixType, BlockMatrixTag > Transpose(NekMatrix< InnerMatrixType, BlockMatrixTag > &rhs)
std::shared_ptr< DNekMat > DNekMatSharedPtr
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 > max(scalarT< T > lhs, scalarT< T > rhs)
Definition scalar.hpp:305
scalarT< T > sqrt(scalarT< T > in)
Definition scalar.hpp:290
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.