MFEM v4.7.0
Finite element discretization library
Loading...
Searching...
No Matches
ncmesh.cpp
Go to the documentation of this file.
1// Copyright (c) 2010-2024, Lawrence Livermore National Security, LLC. Produced
2// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
3// LICENSE and NOTICE for details. LLNL-CODE-806117.
4//
5// This file is part of the MFEM library. For more information and source code
6// availability visit https://mfem.org.
7//
8// MFEM is free software; you can redistribute it and/or modify it under the
9// terms of the BSD-3 license. We welcome feedback and contributions, see file
10// CONTRIBUTING.md for details.
11
12#include "mesh_headers.hpp"
14#include "../general/text.hpp"
15
16#include <string>
17#include <cmath>
18#include <map>
19
20#include "ncmesh_tables.hpp"
21
22
23namespace
24{
25/**
26 * @brief Base case of convenience variadic max function.
27 *
28 * @tparam T Base type
29 * @param arg Recursion base value
30 * @return T value to max over
31 */
32template<typename T>
33T max(T&& arg)
34{
35 return arg;
36}
37/**
38 * @brief Convenience variadic max function.
39 *
40 * @tparam T Base Type
41 * @tparam Ts Parameter pack of other types
42 * @param arg Singular argument
43 * @param args Pack of arguments
44 * @return T maximum value
45 */
46template<typename T, typename... Ts>
47T max(T arg, Ts... args)
48{
49 return std::max(std::forward<T>(arg), max(args...));
50}
51} // namespace
52namespace mfem
53{
54
55NCMesh::GeomInfo NCMesh::GI[Geometry::NumGeom];
56
58{
59 if (initialized) { return; }
60
61 mfem::Element *elem = NULL;
62 switch (geom)
63 {
64 case Geometry::CUBE: elem = new Hexahedron; break;
65 case Geometry::PRISM: elem = new Wedge; break;
66 case Geometry::TETRAHEDRON: elem = new Tetrahedron; break;
67 case Geometry::PYRAMID: elem = new Pyramid; break;
68 case Geometry::SQUARE: elem = new Quadrilateral; break;
69 case Geometry::TRIANGLE: elem = new Triangle; break;
70 case Geometry::SEGMENT: elem = new Segment; break;
71 default: MFEM_ABORT("unsupported geometry " << geom);
72 }
73
74 nv = elem->GetNVertices();
75 ne = elem->GetNEdges();
76 nf = elem->GetNFaces();
77
78 for (int i = 0; i < ne; i++)
79 {
80 for (int j = 0; j < 2; j++)
81 {
82 edges[i][j] = elem->GetEdgeVertices(i)[j];
83 }
84 }
85 for (int i = 0; i < nf; i++)
86 {
87 nfv[i] = elem->GetNFaceVertices(i);
88
89 faces[i][3] = 7; // invalid node index for 3-node faces
90 for (int j = 0; j < nfv[i]; j++)
91 {
92 faces[i][j] = elem->GetFaceVertices(i)[j];
93 }
94 }
95
96 // in 1D/2D we pretend to have faces too, so we can use NCMesh::Face::elem[2]
97 if (!nf)
98 {
99 if (ne)
100 {
101 for (int i = 0; i < ne; i++)
102 {
103 // make a degenerate face
104 faces[i][0] = faces[i][1] = edges[i][0];
105 faces[i][2] = faces[i][3] = edges[i][1];
106 nfv[i] = 2;
107 }
108 nf = ne;
109 }
110 else
111 {
112 for (int i = 0; i < nv; i++)
113 {
114 // 1D degenerate face
115 faces[i][0] = faces[i][1] = faces[i][2] = faces[i][3] = i;
116 nfv[i] = 1;
117 }
118 nf = nv;
119 }
120 }
121
122 delete elem;
123 initialized = true;
124}
125
126static void CheckSupportedGeom(Geometry::Type geom)
127{
128 MFEM_VERIFY(geom == Geometry::SEGMENT ||
129 geom == Geometry::TRIANGLE || geom == Geometry::SQUARE ||
130 geom == Geometry::CUBE || geom == Geometry::PRISM ||
131 geom == Geometry::PYRAMID || geom == Geometry::TETRAHEDRON,
132 "Element type " << geom << " is not supported by NCMesh.");
133}
134
136 : shadow(1024, 2048)
137{
138 Dim = mesh->Dimension();
139 spaceDim = mesh->SpaceDimension();
140 MyRank = 0;
141 Iso = true;
142 Legacy = false;
143
144 // create the NCMesh::Element struct for each Mesh element
145 for (int i = 0; i < mesh->GetNE(); i++)
146 {
147 const mfem::Element *elem = mesh->GetElement(i);
148
149 Geometry::Type geom = elem->GetGeometryType();
150 CheckSupportedGeom(geom);
151 GI[geom].InitGeom(geom);
152
153 // if we have pyramids we will need tets after refinement
154 if (geom == Geometry::PYRAMID)
155 {
157 }
158
159 // create NCMesh::Element for this mfem::Element
160 int root_id = AddElement(Element(geom, elem->GetAttribute()));
161 MFEM_ASSERT(root_id == i, "");
162 Element &root_elem = elements[root_id];
163
164 const int *v = elem->GetVertices();
165 for (int j = 0; j < GI[geom].nv; j++)
166 {
167 int id = v[j];
168 root_elem.node[j] = id;
169 nodes.Alloc(id, id, id);
170 // NOTE: top-level nodes are special: id == p1 == p2 == orig. vertex id
171 }
172 }
173
174 // if the user initialized any hanging nodes with Mesh::AddVertexParents,
175 // copy the hierarchy now
176 if (mesh->tmp_vertex_parents.Size())
177 {
178 for (const auto &triple : mesh->tmp_vertex_parents)
179 {
180 nodes.Reparent(triple.one, triple.two, triple.three);
181 }
182 }
183
184 // create edge nodes and faces
185 nodes.UpdateUnused();
186 for (int i = 0; i < elements.Size(); i++)
187 {
188 // increase reference count of all nodes the element is using (NOTE: this
189 // will also create and reference all edge nodes and faces)
191
192 // make links from faces back to the element
193 RegisterFaces(i);
194 }
195
196 // store boundary element attributes
197 for (int i = 0; i < mesh->GetNBE(); i++)
198 {
199 const mfem::Element *be = mesh->GetBdrElement(i);
200 const int *v = be->GetVertices();
201
202 Face* face = NULL;
203 switch (be->GetType())
204 {
206 face = faces.Find(v[0], v[1], v[2], v[3]);
207 break;
209 face = faces.Find(v[0], v[1], v[2]);
210 break;
212 face = faces.Find(v[0], v[0], v[1], v[1]);
213 break;
215 face = faces.Find(v[0], v[0], v[0], v[0]);
216 break;
217 default:
218 MFEM_ABORT("Unsupported boundary element geometry.");
219 }
220
221 MFEM_VERIFY(face, "Boundary face not found.");
222 face->attribute = be->GetAttribute();
223 }
224
225 // copy top-level vertex coordinates (leave empty if the mesh is curved)
226 if (!mesh->Nodes)
227 {
228 coordinates.SetSize(3*mesh->GetNV());
229 for (int i = 0; i < mesh->GetNV(); i++)
230 {
231 std::memcpy(&coordinates[3*i], mesh->GetVertex(i), 3*sizeof(real_t));
232 }
233 }
234
235 InitRootState(mesh->GetNE());
237
238 Update();
239}
240
242 : Dim(other.Dim)
243 , spaceDim(other.spaceDim)
244 , MyRank(other.MyRank)
245 , Iso(other.Iso)
246 , Geoms(other.Geoms)
247 , Legacy(other.Legacy)
248 , nodes(other.nodes)
249 , faces(other.faces)
250 , elements(other.elements)
251 , shadow(1024, 2048)
252{
256 Update();
257}
258
260{
261 Geoms = 0;
262 for (int i = 0; i < root_state.Size(); i++)
263 {
264 Geoms |= (1 << elements[i].Geom());
265 }
266}
267
269{
272
276
278}
279
281{
282#ifdef MFEM_DEBUG
283 // sign off of all faces and nodes
284 Array<int> elemFaces;
285 for (int i = 0; i < elements.Size(); i++)
286 {
287 if (elements[i].IsLeaf())
288 {
289 elemFaces.SetSize(0);
290 UnreferenceElement(i, elemFaces);
291 DeleteUnusedFaces(elemFaces);
292 }
293 }
294 // NOTE: in release mode, we just throw away all faces and nodes at once
295#endif
296}
297
299{
300 MFEM_ASSERT(!vert_refc && !edge_refc, "node was not unreferenced properly, "
301 "vert_refc: " << (int) vert_refc << ", edge_refc: "
302 << (int) edge_refc);
303}
304
305void NCMesh::ReparentNode(int node, int new_p1, int new_p2)
306{
307 Node &nd = nodes[node];
308 int old_p1 = nd.p1, old_p2 = nd.p2;
309
310 // assign new parents
311 nodes.Reparent(node, new_p1, new_p2);
312
313 MFEM_ASSERT(shadow.FindId(old_p1, old_p2) < 0,
314 "shadow node already exists");
315
316 // store old parent pair temporarily in 'shadow'
317 int sh = shadow.GetId(old_p1, old_p2);
318 shadow[sh].vert_index = node;
319}
320
321int NCMesh::FindMidEdgeNode(int node1, int node2) const
322{
323 int mid = nodes.FindId(node1, node2);
324 if (mid < 0 && shadow.Size())
325 {
326 // if (anisotropic) refinement is underway, some nodes may temporarily be
327 // available under alternate parents (see ReparentNode)
328 mid = shadow.FindId(node1, node2);
329 if (mid >= 0)
330 {
331 mid = shadow[mid].vert_index; // index of the original node
332 }
333 }
334 return mid;
335}
336
337int NCMesh::GetMidEdgeNode(int node1, int node2)
338{
339 int mid = FindMidEdgeNode(node1, node2);
340 if (mid < 0) { mid = nodes.GetId(node1, node2); } // create if not found
341 return mid;
342}
343
344int NCMesh::GetMidFaceNode(int en1, int en2, int en3, int en4)
345{
346 // mid-face node can be created either from (en1, en3) or from (en2, en4)
347 int midf = FindMidEdgeNode(en1, en3);
348 if (midf >= 0) { return midf; }
349 return nodes.GetId(en2, en4);
350}
351
353{
354 Element &el = elements[elem];
355 int* node = el.node;
356 GeomInfo& gi = GI[el.Geom()];
357
358 // reference all vertices
359 for (int i = 0; i < gi.nv; i++)
360 {
361 nodes[node[i]].vert_refc++;
362 }
363
364 // reference all edges (possibly creating their nodes)
365 for (int i = 0; i < gi.ne; i++)
366 {
367 const int* ev = gi.edges[i];
368 nodes.Get(node[ev[0]], node[ev[1]])->edge_refc++;
369 }
370
371 // get all faces (possibly creating them)
372 for (int i = 0; i < gi.nf; i++)
373 {
374 const int* fv = gi.faces[i];
375 faces.GetId(node[fv[0]], node[fv[1]], node[fv[2]], node[fv[3]]);
376
377 // NOTE: face->RegisterElement called separately to avoid having to store
378 // 3 element indices temporarily in the face when refining. See also
379 // NCMesh::RegisterFaces.
380 }
381}
382
383void NCMesh::UnreferenceElement(int elem, Array<int> &elemFaces)
384{
385 Element &el = elements[elem];
386 int* node = el.node;
387 GeomInfo& gi = GI[el.Geom()];
388
389 // unreference all faces
390 for (int i = 0; i < gi.nf; i++)
391 {
392 const int* fv = gi.faces[i];
393 int face = faces.FindId(node[fv[0]], node[fv[1]],
394 node[fv[2]], node[fv[3]]);
395 MFEM_ASSERT(face >= 0, "face not found.");
396 faces[face].ForgetElement(elem);
397
398 // NOTE: faces.Delete() called later to avoid destroying and recreating
399 // faces during refinement, see NCMesh::DeleteUnusedFaces.
400 elemFaces.Append(face);
401 }
402
403 // unreference all edges (possibly destroying them)
404 for (int i = 0; i < gi.ne; i++)
405 {
406 const int* ev = gi.edges[i];
407 int enode = FindMidEdgeNode(node[ev[0]], node[ev[1]]);
408 MFEM_ASSERT(enode >= 0, "edge not found.");
409 MFEM_ASSERT(nodes.IdExists(enode), "edge does not exist.");
410 if (!nodes[enode].UnrefEdge())
411 {
412 nodes.Delete(enode);
413 }
414 }
415
416 // unreference all vertices (possibly destroying them)
417 for (int i = 0; i < gi.nv; i++)
418 {
419 if (!nodes[node[i]].UnrefVertex())
420 {
421 nodes.Delete(node[i]);
422 }
423 }
424}
425
426void NCMesh::RegisterFaces(int elem, int* fattr)
427{
428 Element &el = elements[elem];
429 GeomInfo &gi = GI[el.Geom()];
430
431 for (int i = 0; i < gi.nf; i++)
432 {
433 Face* face = GetFace(el, i);
434 MFEM_ASSERT(face, "face not found.");
435 face->RegisterElement(elem);
436 if (fattr) { face->attribute = fattr[i]; }
437 }
438}
439
441{
442 for (int i = 0; i < elemFaces.Size(); i++)
443 {
444 if (faces[elemFaces[i]].Unused())
445 {
446 faces.Delete(elemFaces[i]);
447 }
448 }
449}
450
452{
453 if (elem[0] < 0) { elem[0] = e; }
454 else if (elem[1] < 0) { elem[1] = e; }
455 else { MFEM_ABORT("can't have 3 elements in Face::elem[]."); }
456}
457
459{
460 if (elem[0] == e) { elem[0] = -1; }
461 else if (elem[1] == e) { elem[1] = -1; }
462 else { MFEM_ABORT("element " << e << " not found in Face::elem[]."); }
463}
464
466{
467 GeomInfo& gi = GI[(int) elem.geom];
468 const int* fv = gi.faces[face_no];
469 int* node = elem.node;
470 return faces.Find(node[fv[0]], node[fv[1]], node[fv[2]], node[fv[3]]);
471}
472
474{
475 if (elem[0] >= 0)
476 {
477 MFEM_ASSERT(elem[1] < 0, "not a single element face.");
478 return elem[0];
479 }
480 else
481 {
482 MFEM_ASSERT(elem[1] >= 0, "no elements in face.");
483 return elem[1];
484 }
485}
486
487
488//// Refinement ////////////////////////////////////////////////////////////////
489
491 : geom(geom), ref_type(0), tet_type(0), flag(0), index(-1)
492 , rank(0), attribute(attr), parent(-1)
493{
494 for (int i = 0; i < MaxElemNodes; i++) { node[i] = -1; }
495 for (int i = 0; i < MaxElemChildren; i++) { child[i] = -1; }
496
497 // NOTE: in 2D the 8/10-element node/child arrays are not optimal, however,
498 // testing shows we would only save 17% of the total NCMesh memory if
499 // 4-element arrays were used (e.g. through templates); we thus prefer to
500 // keep the code as simple as possible.
501}
502
503int NCMesh::NewHexahedron(int n0, int n1, int n2, int n3,
504 int n4, int n5, int n6, int n7,
505 int attr,
506 int fattr0, int fattr1, int fattr2,
507 int fattr3, int fattr4, int fattr5)
508{
509 // create new element, initialize nodes
510 int new_id = AddElement(Element(Geometry::CUBE, attr));
511 Element &el = elements[new_id];
512
513 el.node[0] = n0, el.node[1] = n1, el.node[2] = n2, el.node[3] = n3;
514 el.node[4] = n4, el.node[5] = n5, el.node[6] = n6, el.node[7] = n7;
515
516 // get faces and assign face attributes
518 const GeomInfo &gi_hex = GI[Geometry::CUBE];
519 for (int i = 0; i < gi_hex.nf; i++)
520 {
521 const int* fv = gi_hex.faces[i];
522 f[i] = faces.Get(el.node[fv[0]], el.node[fv[1]],
523 el.node[fv[2]], el.node[fv[3]]);
524 }
525
526 f[0]->attribute = fattr0, f[1]->attribute = fattr1;
527 f[2]->attribute = fattr2, f[3]->attribute = fattr3;
528 f[4]->attribute = fattr4, f[5]->attribute = fattr5;
529
530 return new_id;
531}
532
533int NCMesh::NewWedge(int n0, int n1, int n2,
534 int n3, int n4, int n5,
535 int attr,
536 int fattr0, int fattr1,
537 int fattr2, int fattr3, int fattr4)
538{
539 // create new element, initialize nodes
540 int new_id = AddElement(Element(Geometry::PRISM, attr));
541 Element &el = elements[new_id];
542
543 el.node[0] = n0, el.node[1] = n1, el.node[2] = n2;
544 el.node[3] = n3, el.node[4] = n4, el.node[5] = n5;
545
546 // get faces and assign face attributes
547 Face* f[5];
548 const GeomInfo &gi_wedge = GI[Geometry::PRISM];
549 for (int i = 0; i < gi_wedge.nf; i++)
550 {
551 const int* fv = gi_wedge.faces[i];
552 f[i] = faces.Get(el.node[fv[0]], el.node[fv[1]],
553 el.node[fv[2]], el.node[fv[3]]);
554 }
555
556 f[0]->attribute = fattr0;
557 f[1]->attribute = fattr1;
558 f[2]->attribute = fattr2;
559 f[3]->attribute = fattr3;
560 f[4]->attribute = fattr4;
561
562 return new_id;
563}
564
565int NCMesh::NewTetrahedron(int n0, int n1, int n2, int n3, int attr,
566 int fattr0, int fattr1, int fattr2, int fattr3)
567{
568 // create new element, initialize nodes
569 int new_id = AddElement(Element(Geometry::TETRAHEDRON, attr));
570 Element &el = elements[new_id];
571
572 el.node[0] = n0, el.node[1] = n1, el.node[2] = n2, el.node[3] = n3;
573
574 // get faces and assign face attributes
575 Face* f[4];
576 const GeomInfo &gi_tet = GI[Geometry::TETRAHEDRON];
577 for (int i = 0; i < gi_tet.nf; i++)
578 {
579 const int* fv = gi_tet.faces[i];
580 f[i] = faces.Get(el.node[fv[0]], el.node[fv[1]], el.node[fv[2]]);
581 }
582
583 f[0]->attribute = fattr0;
584 f[1]->attribute = fattr1;
585 f[2]->attribute = fattr2;
586 f[3]->attribute = fattr3;
587
588 return new_id;
589}
590int NCMesh::NewPyramid(int n0, int n1, int n2, int n3, int n4, int attr,
591 int fattr0, int fattr1, int fattr2, int fattr3,
592 int fattr4)
593{
594 // create new element, initialize nodes
595 int new_id = AddElement(Element(Geometry::PYRAMID, attr));
596 Element &el = elements[new_id];
597
598 el.node[0] = n0, el.node[1] = n1, el.node[2] = n2, el.node[3] = n3;
599 el.node[4] = n4;
600
601 // get faces and assign face attributes
602 Face* f[5];
603 const GeomInfo &gi_pyr = GI[Geometry::PYRAMID];
604 for (int i = 0; i < gi_pyr.nf; i++)
605 {
606 const int* fv = gi_pyr.faces[i];
607 f[i] = faces.Get(el.node[fv[0]], el.node[fv[1]],
608 el.node[fv[2]], el.node[fv[3]]);
609 }
610
611 f[0]->attribute = fattr0;
612 f[1]->attribute = fattr1;
613 f[2]->attribute = fattr2;
614 f[3]->attribute = fattr3;
615 f[4]->attribute = fattr4;
616
617 return new_id;
618}
619
620int NCMesh::NewQuadrilateral(int n0, int n1, int n2, int n3,
621 int attr,
622 int eattr0, int eattr1, int eattr2, int eattr3)
623{
624 // create new element, initialize nodes
625 int new_id = AddElement(Element(Geometry::SQUARE, attr));
626 Element &el = elements[new_id];
627
628 el.node[0] = n0, el.node[1] = n1, el.node[2] = n2, el.node[3] = n3;
629
630 // get (degenerate) faces and assign face attributes
631 Face* f[4];
632 const GeomInfo &gi_quad = GI[Geometry::SQUARE];
633 for (int i = 0; i < gi_quad.nf; i++)
634 {
635 const int* fv = gi_quad.faces[i];
636 f[i] = faces.Get(el.node[fv[0]], el.node[fv[1]],
637 el.node[fv[2]], el.node[fv[3]]);
638 }
639
640 f[0]->attribute = eattr0, f[1]->attribute = eattr1;
641 f[2]->attribute = eattr2, f[3]->attribute = eattr3;
642
643 return new_id;
644}
645
646int NCMesh::NewTriangle(int n0, int n1, int n2,
647 int attr, int eattr0, int eattr1, int eattr2)
648{
649 // create new element, initialize nodes
650 int new_id = AddElement(Element(Geometry::TRIANGLE, attr));
651 Element &el = elements[new_id];
652
653 el.node[0] = n0, el.node[1] = n1, el.node[2] = n2;
654
655 // get (degenerate) faces and assign face attributes
656 Face* f[3];
657 const GeomInfo &gi_tri = GI[Geometry::TRIANGLE];
658 for (int i = 0; i < gi_tri.nf; i++)
659 {
660 const int* fv = gi_tri.faces[i];
661 f[i] = faces.Get(el.node[fv[0]], el.node[fv[1]],
662 el.node[fv[2]], el.node[fv[3]]);
663 }
664
665 f[0]->attribute = eattr0;
666 f[1]->attribute = eattr1;
667 f[2]->attribute = eattr2;
668
669 return new_id;
670}
671
672int NCMesh::NewSegment(int n0, int n1, int attr, int vattr1, int vattr2)
673{
674 // create new element, initialize nodes
675 int new_id = AddElement(Element(Geometry::SEGMENT, attr));
676 Element &el = elements[new_id];
677 el.node[0] = n0, el.node[1] = n1;
678
679 // get (degenerate) faces and assign face attributes
680 int v0 = el.node[0], v1 = el.node[1];
681 faces.Get(v0, v0, v0, v0)->attribute = vattr1;
682 faces.Get(v1, v1, v1, v1)->attribute = vattr2;
683
684 return new_id;
685}
686
687inline bool CubeFaceLeft(int node, int* n)
688{ return node == n[0] || node == n[3] || node == n[4] || node == n[7]; }
689
690inline bool CubeFaceRight(int node, int* n)
691{ return node == n[1] || node == n[2] || node == n[5] || node == n[6]; }
692
693inline bool CubeFaceFront(int node, int* n)
694{ return node == n[0] || node == n[1] || node == n[4] || node == n[5]; }
695
696inline bool CubeFaceBack(int node, int* n)
697{ return node == n[2] || node == n[3] || node == n[6] || node == n[7]; }
698
699inline bool CubeFaceBottom(int node, int* n)
700{ return node == n[0] || node == n[1] || node == n[2] || node == n[3]; }
701
702inline bool CubeFaceTop(int node, int* n)
703{ return node == n[4] || node == n[5] || node == n[6] || node == n[7]; }
704
705inline bool PrismFaceBottom(int node, int* n)
706{ return node == n[0] || node == n[1] || node == n[2]; }
707
708inline bool PrismFaceTop(int node, int* n)
709{ return node == n[3] || node == n[4] || node == n[5]; }
710
711
712void NCMesh::ForceRefinement(int vn1, int vn2, int vn3, int vn4)
713{
714 // get the element this face belongs to
715 Face* face = faces.Find(vn1, vn2, vn3, vn4);
716 if (!face) { return; }
717
718 int elem = face->GetSingleElement();
719 Element &el = elements[elem];
720 MFEM_ASSERT(!el.ref_type, "element already refined.");
721
722 int* el_nodes = el.node;
723 if (el.Geom() == Geometry::CUBE)
724 {
725 // schedule the right split depending on face orientation
726 if ((CubeFaceLeft(vn1, el_nodes) && CubeFaceRight(vn2, el_nodes)) ||
727 (CubeFaceLeft(vn2, el_nodes) && CubeFaceRight(vn1, el_nodes)))
728 {
729 ref_stack.Append(Refinement(elem, 1)); // X split
730 }
731 else if ((CubeFaceFront(vn1, el_nodes) && CubeFaceBack(vn2, el_nodes)) ||
732 (CubeFaceFront(vn2, el_nodes) && CubeFaceBack(vn1, el_nodes)))
733 {
734 ref_stack.Append(Refinement(elem, 2)); // Y split
735 }
736 else if ((CubeFaceBottom(vn1, el_nodes) && CubeFaceTop(vn2, el_nodes)) ||
737 (CubeFaceBottom(vn2, el_nodes) && CubeFaceTop(vn1, el_nodes)))
738 {
739 ref_stack.Append(Refinement(elem, 4)); // Z split
740 }
741 else
742 {
743 MFEM_ABORT("Inconsistent element/face structure.");
744 }
745 }
746 else if (el.Geom() == Geometry::PRISM)
747 {
748 if ((PrismFaceTop(vn1, el_nodes) && PrismFaceBottom(vn4, el_nodes)) ||
749 (PrismFaceTop(vn4, el_nodes) && PrismFaceBottom(vn1, el_nodes)))
750 {
751 ref_stack.Append(Refinement(elem, 3)); // XY split
752 }
753 else if ((PrismFaceTop(vn1, el_nodes) && PrismFaceBottom(vn2, el_nodes)) ||
754 (PrismFaceTop(vn2, el_nodes) && PrismFaceBottom(vn1, el_nodes)))
755 {
756 ref_stack.Append(Refinement(elem, 4)); // Z split
757 }
758 else
759 {
760 MFEM_ABORT("Inconsistent element/face structure.");
761 }
762 }
763 else
764 {
765 MFEM_ABORT("Unsupported geometry.")
766 }
767}
768
769
770void NCMesh::FindEdgeElements(int vn1, int vn2, int vn3, int vn4,
771 Array<MeshId> &elem_edge) const
772{
773 // Assuming that f = (vn1, vn2, vn3, vn4) is a quad face and e = (vn1, vn4)
774 // is its edge, this function finds the N elements sharing e, and returns the
775 // N different MeshIds of the edge (i.e., different element-local pairs
776 // describing the edge).
777
778 int ev1 = vn1, ev2 = vn4;
779
780 // follow face refinement towards 'vn1', get an existing face
781 int split, mid[5];
782 while ((split = QuadFaceSplitType(vn1, vn2, vn3, vn4, mid)) > 0)
783 {
784 if (split == 1) // vertical
785 {
786 vn2 = mid[0]; vn3 = mid[2];
787 }
788 else // horizontal
789 {
790 vn3 = mid[1]; vn4 = mid[3];
791 }
792 }
793
794 const Face *face = faces.Find(vn1, vn2, vn3, vn4);
795 MFEM_ASSERT(face != NULL, "Face not found: "
796 << vn1 << ", " << vn2 << ", " << vn3 << ", " << vn4
797 << " (edge " << ev1 << "-" << ev2 << ").");
798
799 int elem = face->GetSingleElement();
800 int local = find_node(elements[elem], vn1);
801
802 Array<int> cousins;
803 FindVertexCousins(elem, local, cousins);
804
805 elem_edge.SetSize(0);
806 for (int i = 0; i < cousins.Size(); i++)
807 {
808 local = find_element_edge(elements[cousins[i]], ev1, ev2, false);
809 if (local > 0)
810 {
811 elem_edge.Append(MeshId(-1, cousins[i], local, Geometry::SEGMENT));
812 }
813 }
814}
815
816
817void NCMesh::CheckAnisoPrism(int vn1, int vn2, int vn3, int vn4,
818 const Refinement *refs, int nref)
819{
820 MeshId buf[4];
821 Array<MeshId> eid(buf, 4);
822 FindEdgeElements(vn1, vn2, vn3, vn4, eid);
823
824 // see if there is an element that has not been force-refined yet
825 for (int i = 0, j; i < eid.Size(); i++)
826 {
827 int elem = eid[i].element;
828 for (j = 0; j < nref; j++)
829 {
830 if (refs[j].index == elem) { break; }
831 }
832 if (j == nref) // elem not found in refs[]
833 {
834 // schedule prism refinement along Z axis
835 MFEM_ASSERT(elements[elem].Geom() == Geometry::PRISM, "");
836 ref_stack.Append(Refinement(elem, 4));
837 }
838 }
839}
840
841
842void NCMesh::CheckAnisoFace(int vn1, int vn2, int vn3, int vn4,
843 int mid12, int mid34, int level)
844{
845 // When a face is getting split anisotropically (without loss of generality
846 // we assume a "vertical" split here, see picture), it is important to make
847 // sure that the mid-face vertex node (midf) has mid34 and mid12 as parents.
848 // This is necessary for the face traversal algorithm and at places like
849 // Refine() that assume the mid-edge nodes to be accessible through the right
850 // parents. However, midf may already exist under the parents mid41 and
851 // mid23. In that case we need to "reparent" midf, i.e., reinsert it to the
852 // hash-table under the correct parents. This doesn't affect other nodes as
853 // all IDs stay the same, only the face refinement "tree" is affected.
854 //
855 // vn4 mid34 vn3
856 // *------*------*
857 // | | |
858 // | |midf |
859 // mid41 *- - - *- - - * mid23
860 // | | |
861 // | | |
862 // *------*------*
863 // vn1 mid12 vn2
864 //
865 // This function is recursive, because the above applies to any node along
866 // the middle vertical edge. The function calls itself again for the bottom
867 // and upper half of the above picture.
868
869 int mid23 = FindMidEdgeNode(vn2, vn3);
870 int mid41 = FindMidEdgeNode(vn4, vn1);
871 if (mid23 >= 0 && mid41 >= 0)
872 {
873 int midf = nodes.FindId(mid23, mid41);
874 if (midf >= 0)
875 {
876 reparents.Append(Triple<int, int, int>(midf, mid12, mid34));
877
878 int rs = ref_stack.Size();
879
880 CheckAnisoFace(vn1, vn2, mid23, mid41, mid12, midf, level+1);
881 CheckAnisoFace(mid41, mid23, vn3, vn4, midf, mid34, level+1);
882
883 if (HavePrisms() && nodes[midf].HasEdge())
884 {
885 // Check if there is a prism with edge (mid23, mid41) that we may
886 // have missed in 'CheckAnisoFace', and force-refine it if present.
887
888 if (ref_stack.Size() > rs)
889 {
890 CheckAnisoPrism(mid23, vn3, vn4, mid41,
891 &ref_stack[rs], ref_stack.Size() - rs);
892 }
893 else
894 {
895 CheckAnisoPrism(mid23, vn3, vn4, mid41, NULL, 0);
896 }
897 }
898
899 // perform the reparents all at once at the end
900 if (level == 0)
901 {
902 for (int i = 0; i < reparents.Size(); i++)
903 {
904 const Triple<int, int, int> &tr = reparents[i];
905 ReparentNode(tr.one, tr.two, tr.three);
906 }
907 reparents.DeleteAll();
908 }
909 return;
910 }
911 }
912
913 // Also, this is the place where forced refinements begin. In the picture
914 // above, edges mid12-midf and midf-mid34 should actually exist in the
915 // neighboring elements, otherwise the mesh is inconsistent and needs to be
916 // fixed. Example: suppose an element is being refined isotropically (!)
917 // whose neighbors across some face look like this:
918 //
919 // *--------*--------*
920 // | d | e |
921 // *--------*--------*
922 // | c |
923 // *--------*--------*
924 // | | |
925 // | a | b |
926 // | | |
927 // *--------*--------*
928 //
929 // Element 'c' needs to be refined vertically for the mesh to remain valid.
930
931 if (level > 0)
932 {
933 ForceRefinement(vn1, vn2, vn3, vn4);
934 }
935}
936
937void NCMesh::CheckIsoFace(int vn1, int vn2, int vn3, int vn4,
938 int en1, int en2, int en3, int en4, int midf)
939{
940 if (!Iso)
941 {
942 /* If anisotropic refinements are present in the mesh, we need to check
943 isotropically split faces as well, see second comment in CheckAnisoFace
944 above. */
945
946 CheckAnisoFace(vn1, vn2, en2, en4, en1, midf);
947 CheckAnisoFace(en4, en2, vn3, vn4, midf, en3);
948 CheckAnisoFace(vn4, vn1, en1, en3, en4, midf);
949 CheckAnisoFace(en3, en1, vn2, vn3, midf, en2);
950 }
951}
952
953
954void NCMesh::RefineElement(int elem, char ref_type)
955{
956 if (!ref_type) { return; }
957
958 // handle elements that may have been (force-) refined already
959 Element &el = elements[elem];
960 if (el.ref_type)
961 {
962 char remaining = ref_type & ~el.ref_type;
963
964 // do the remaining splits on the children
965 for (int i = 0; i < MaxElemChildren; i++)
966 {
967 if (el.child[i] >= 0) { RefineElement(el.child[i], remaining); }
968 }
969 return;
970 }
971
972 /*mfem::out << "Refining element " << elem << " (" << el.node[0] << ", " <<
973 el.node[1] << ", " << el.node[2] << ", " << el.node[3] << ", " <<
974 el.node[4] << ", " << el.node[5] << ", " << el.node[6] << ", " <<
975 el.node[7] << "), " << "ref_type " << int(ref_type) << std::endl;*/
976
977 int* no = el.node;
978 int attr = el.attribute;
979
980 int child[MaxElemChildren];
981 for (int i = 0; i < MaxElemChildren; i++) { child[i] = -1; }
982
983 // get parent's face attributes
984 int fa[MaxElemFaces];
985 GeomInfo& gi = GI[el.Geom()];
986 for (int i = 0; i < gi.nf; i++)
987 {
988 const int* fv = gi.faces[i];
989 Face* face = faces.Find(no[fv[0]], no[fv[1]], no[fv[2]], no[fv[3]]);
990 fa[i] = face->attribute;
991 }
992
993 // create child elements
994 if (el.Geom() == Geometry::CUBE)
995 {
996 // Cube vertex numbering is assumed to be as follows:
997 //
998 // 7 6
999 // +-----------+ Faces: 0 bottom
1000 // /| /| 1 front
1001 // 4 / | 5 / | 2 right
1002 // +-----------+ | 3 back
1003 // | | | | 4 left
1004 // | +--------|--+ 5 top
1005 // | / 3 | / 2 Z Y
1006 // |/ |/ |/
1007 // +-----------+ *--X
1008 // 0 1
1009
1010 if (ref_type == Refinement::X) // split along X axis
1011 {
1012 int mid01 = GetMidEdgeNode(no[0], no[1]);
1013 int mid23 = GetMidEdgeNode(no[2], no[3]);
1014 int mid45 = GetMidEdgeNode(no[4], no[5]);
1015 int mid67 = GetMidEdgeNode(no[6], no[7]);
1016
1017 child[0] = NewHexahedron(no[0], mid01, mid23, no[3],
1018 no[4], mid45, mid67, no[7], attr,
1019 fa[0], fa[1], -1, fa[3], fa[4], fa[5]);
1020
1021 child[1] = NewHexahedron(mid01, no[1], no[2], mid23,
1022 mid45, no[5], no[6], mid67, attr,
1023 fa[0], fa[1], fa[2], fa[3], -1, fa[5]);
1024
1025 CheckAnisoFace(no[0], no[1], no[5], no[4], mid01, mid45);
1026 CheckAnisoFace(no[2], no[3], no[7], no[6], mid23, mid67);
1027 CheckAnisoFace(no[4], no[5], no[6], no[7], mid45, mid67);
1028 CheckAnisoFace(no[3], no[2], no[1], no[0], mid23, mid01);
1029 }
1030 else if (ref_type == Refinement::Y) // split along Y axis
1031 {
1032 int mid12 = GetMidEdgeNode(no[1], no[2]);
1033 int mid30 = GetMidEdgeNode(no[3], no[0]);
1034 int mid56 = GetMidEdgeNode(no[5], no[6]);
1035 int mid74 = GetMidEdgeNode(no[7], no[4]);
1036
1037 child[0] = NewHexahedron(no[0], no[1], mid12, mid30,
1038 no[4], no[5], mid56, mid74, attr,
1039 fa[0], fa[1], fa[2], -1, fa[4], fa[5]);
1040
1041 child[1] = NewHexahedron(mid30, mid12, no[2], no[3],
1042 mid74, mid56, no[6], no[7], attr,
1043 fa[0], -1, fa[2], fa[3], fa[4], fa[5]);
1044
1045 CheckAnisoFace(no[1], no[2], no[6], no[5], mid12, mid56);
1046 CheckAnisoFace(no[3], no[0], no[4], no[7], mid30, mid74);
1047 CheckAnisoFace(no[5], no[6], no[7], no[4], mid56, mid74);
1048 CheckAnisoFace(no[0], no[3], no[2], no[1], mid30, mid12);
1049 }
1050 else if (ref_type == Refinement::Z) // split along Z axis
1051 {
1052 int mid04 = GetMidEdgeNode(no[0], no[4]);
1053 int mid15 = GetMidEdgeNode(no[1], no[5]);
1054 int mid26 = GetMidEdgeNode(no[2], no[6]);
1055 int mid37 = GetMidEdgeNode(no[3], no[7]);
1056
1057 child[0] = NewHexahedron(no[0], no[1], no[2], no[3],
1058 mid04, mid15, mid26, mid37, attr,
1059 fa[0], fa[1], fa[2], fa[3], fa[4], -1);
1060
1061 child[1] = NewHexahedron(mid04, mid15, mid26, mid37,
1062 no[4], no[5], no[6], no[7], attr,
1063 -1, fa[1], fa[2], fa[3], fa[4], fa[5]);
1064
1065 CheckAnisoFace(no[4], no[0], no[1], no[5], mid04, mid15);
1066 CheckAnisoFace(no[5], no[1], no[2], no[6], mid15, mid26);
1067 CheckAnisoFace(no[6], no[2], no[3], no[7], mid26, mid37);
1068 CheckAnisoFace(no[7], no[3], no[0], no[4], mid37, mid04);
1069 }
1070 else if (ref_type == Refinement::XY) // XY split
1071 {
1072 int mid01 = GetMidEdgeNode(no[0], no[1]);
1073 int mid12 = GetMidEdgeNode(no[1], no[2]);
1074 int mid23 = GetMidEdgeNode(no[2], no[3]);
1075 int mid30 = GetMidEdgeNode(no[3], no[0]);
1076
1077 int mid45 = GetMidEdgeNode(no[4], no[5]);
1078 int mid56 = GetMidEdgeNode(no[5], no[6]);
1079 int mid67 = GetMidEdgeNode(no[6], no[7]);
1080 int mid74 = GetMidEdgeNode(no[7], no[4]);
1081
1082 int midf0 = GetMidFaceNode(mid23, mid12, mid01, mid30);
1083 int midf5 = GetMidFaceNode(mid45, mid56, mid67, mid74);
1084
1085 child[0] = NewHexahedron(no[0], mid01, midf0, mid30,
1086 no[4], mid45, midf5, mid74, attr,
1087 fa[0], fa[1], -1, -1, fa[4], fa[5]);
1088
1089 child[1] = NewHexahedron(mid01, no[1], mid12, midf0,
1090 mid45, no[5], mid56, midf5, attr,
1091 fa[0], fa[1], fa[2], -1, -1, fa[5]);
1092
1093 child[2] = NewHexahedron(midf0, mid12, no[2], mid23,
1094 midf5, mid56, no[6], mid67, attr,
1095 fa[0], -1, fa[2], fa[3], -1, fa[5]);
1096
1097 child[3] = NewHexahedron(mid30, midf0, mid23, no[3],
1098 mid74, midf5, mid67, no[7], attr,
1099 fa[0], -1, -1, fa[3], fa[4], fa[5]);
1100
1101 CheckAnisoFace(no[0], no[1], no[5], no[4], mid01, mid45);
1102 CheckAnisoFace(no[1], no[2], no[6], no[5], mid12, mid56);
1103 CheckAnisoFace(no[2], no[3], no[7], no[6], mid23, mid67);
1104 CheckAnisoFace(no[3], no[0], no[4], no[7], mid30, mid74);
1105
1106 CheckIsoFace(no[3], no[2], no[1], no[0], mid23, mid12, mid01, mid30, midf0);
1107 CheckIsoFace(no[4], no[5], no[6], no[7], mid45, mid56, mid67, mid74, midf5);
1108 }
1109 else if (ref_type == Refinement::XZ) // XZ split
1110 {
1111 int mid01 = GetMidEdgeNode(no[0], no[1]);
1112 int mid23 = GetMidEdgeNode(no[2], no[3]);
1113 int mid45 = GetMidEdgeNode(no[4], no[5]);
1114 int mid67 = GetMidEdgeNode(no[6], no[7]);
1115
1116 int mid04 = GetMidEdgeNode(no[0], no[4]);
1117 int mid15 = GetMidEdgeNode(no[1], no[5]);
1118 int mid26 = GetMidEdgeNode(no[2], no[6]);
1119 int mid37 = GetMidEdgeNode(no[3], no[7]);
1120
1121 int midf1 = GetMidFaceNode(mid01, mid15, mid45, mid04);
1122 int midf3 = GetMidFaceNode(mid23, mid37, mid67, mid26);
1123
1124 child[0] = NewHexahedron(no[0], mid01, mid23, no[3],
1125 mid04, midf1, midf3, mid37, attr,
1126 fa[0], fa[1], -1, fa[3], fa[4], -1);
1127
1128 child[1] = NewHexahedron(mid01, no[1], no[2], mid23,
1129 midf1, mid15, mid26, midf3, attr,
1130 fa[0], fa[1], fa[2], fa[3], -1, -1);
1131
1132 child[2] = NewHexahedron(midf1, mid15, mid26, midf3,
1133 mid45, no[5], no[6], mid67, attr,
1134 -1, fa[1], fa[2], fa[3], -1, fa[5]);
1135
1136 child[3] = NewHexahedron(mid04, midf1, midf3, mid37,
1137 no[4], mid45, mid67, no[7], attr,
1138 -1, fa[1], -1, fa[3], fa[4], fa[5]);
1139
1140 CheckAnisoFace(no[3], no[2], no[1], no[0], mid23, mid01);
1141 CheckAnisoFace(no[2], no[6], no[5], no[1], mid26, mid15);
1142 CheckAnisoFace(no[6], no[7], no[4], no[5], mid67, mid45);
1143 CheckAnisoFace(no[7], no[3], no[0], no[4], mid37, mid04);
1144
1145 CheckIsoFace(no[0], no[1], no[5], no[4], mid01, mid15, mid45, mid04, midf1);
1146 CheckIsoFace(no[2], no[3], no[7], no[6], mid23, mid37, mid67, mid26, midf3);
1147 }
1148 else if (ref_type == Refinement::YZ) // YZ split
1149 {
1150 int mid12 = GetMidEdgeNode(no[1], no[2]);
1151 int mid30 = GetMidEdgeNode(no[3], no[0]);
1152 int mid56 = GetMidEdgeNode(no[5], no[6]);
1153 int mid74 = GetMidEdgeNode(no[7], no[4]);
1154
1155 int mid04 = GetMidEdgeNode(no[0], no[4]);
1156 int mid15 = GetMidEdgeNode(no[1], no[5]);
1157 int mid26 = GetMidEdgeNode(no[2], no[6]);
1158 int mid37 = GetMidEdgeNode(no[3], no[7]);
1159
1160 int midf2 = GetMidFaceNode(mid12, mid26, mid56, mid15);
1161 int midf4 = GetMidFaceNode(mid30, mid04, mid74, mid37);
1162
1163 child[0] = NewHexahedron(no[0], no[1], mid12, mid30,
1164 mid04, mid15, midf2, midf4, attr,
1165 fa[0], fa[1], fa[2], -1, fa[4], -1);
1166
1167 child[1] = NewHexahedron(mid30, mid12, no[2], no[3],
1168 midf4, midf2, mid26, mid37, attr,
1169 fa[0], -1, fa[2], fa[3], fa[4], -1);
1170
1171 child[2] = NewHexahedron(mid04, mid15, midf2, midf4,
1172 no[4], no[5], mid56, mid74, attr,
1173 -1, fa[1], fa[2], -1, fa[4], fa[5]);
1174
1175 child[3] = NewHexahedron(midf4, midf2, mid26, mid37,
1176 mid74, mid56, no[6], no[7], attr,
1177 -1, -1, fa[2], fa[3], fa[4], fa[5]);
1178
1179 CheckAnisoFace(no[4], no[0], no[1], no[5], mid04, mid15);
1180 CheckAnisoFace(no[0], no[3], no[2], no[1], mid30, mid12);
1181 CheckAnisoFace(no[3], no[7], no[6], no[2], mid37, mid26);
1182 CheckAnisoFace(no[7], no[4], no[5], no[6], mid74, mid56);
1183
1184 CheckIsoFace(no[1], no[2], no[6], no[5], mid12, mid26, mid56, mid15, midf2);
1185 CheckIsoFace(no[3], no[0], no[4], no[7], mid30, mid04, mid74, mid37, midf4);
1186 }
1187 else if (ref_type == Refinement::XYZ) // full isotropic refinement
1188 {
1189 int mid01 = GetMidEdgeNode(no[0], no[1]);
1190 int mid12 = GetMidEdgeNode(no[1], no[2]);
1191 int mid23 = GetMidEdgeNode(no[2], no[3]);
1192 int mid30 = GetMidEdgeNode(no[3], no[0]);
1193
1194 int mid45 = GetMidEdgeNode(no[4], no[5]);
1195 int mid56 = GetMidEdgeNode(no[5], no[6]);
1196 int mid67 = GetMidEdgeNode(no[6], no[7]);
1197 int mid74 = GetMidEdgeNode(no[7], no[4]);
1198
1199 int mid04 = GetMidEdgeNode(no[0], no[4]);
1200 int mid15 = GetMidEdgeNode(no[1], no[5]);
1201 int mid26 = GetMidEdgeNode(no[2], no[6]);
1202 int mid37 = GetMidEdgeNode(no[3], no[7]);
1203
1204 int midf0 = GetMidFaceNode(mid23, mid12, mid01, mid30);
1205 int midf1 = GetMidFaceNode(mid01, mid15, mid45, mid04);
1206 int midf2 = GetMidFaceNode(mid12, mid26, mid56, mid15);
1207 int midf3 = GetMidFaceNode(mid23, mid37, mid67, mid26);
1208 int midf4 = GetMidFaceNode(mid30, mid04, mid74, mid37);
1209 int midf5 = GetMidFaceNode(mid45, mid56, mid67, mid74);
1210
1211 int midel = GetMidEdgeNode(midf1, midf3);
1212
1213 child[0] = NewHexahedron(no[0], mid01, midf0, mid30,
1214 mid04, midf1, midel, midf4, attr,
1215 fa[0], fa[1], -1, -1, fa[4], -1);
1216
1217 child[1] = NewHexahedron(mid01, no[1], mid12, midf0,
1218 midf1, mid15, midf2, midel, attr,
1219 fa[0], fa[1], fa[2], -1, -1, -1);
1220
1221 child[2] = NewHexahedron(midf0, mid12, no[2], mid23,
1222 midel, midf2, mid26, midf3, attr,
1223 fa[0], -1, fa[2], fa[3], -1, -1);
1224
1225 child[3] = NewHexahedron(mid30, midf0, mid23, no[3],
1226 midf4, midel, midf3, mid37, attr,
1227 fa[0], -1, -1, fa[3], fa[4], -1);
1228
1229 child[4] = NewHexahedron(mid04, midf1, midel, midf4,
1230 no[4], mid45, midf5, mid74, attr,
1231 -1, fa[1], -1, -1, fa[4], fa[5]);
1232
1233 child[5] = NewHexahedron(midf1, mid15, midf2, midel,
1234 mid45, no[5], mid56, midf5, attr,
1235 -1, fa[1], fa[2], -1, -1, fa[5]);
1236
1237 child[6] = NewHexahedron(midel, midf2, mid26, midf3,
1238 midf5, mid56, no[6], mid67, attr,
1239 -1, -1, fa[2], fa[3], -1, fa[5]);
1240
1241 child[7] = NewHexahedron(midf4, midel, midf3, mid37,
1242 mid74, midf5, mid67, no[7], attr,
1243 -1, -1, -1, fa[3], fa[4], fa[5]);
1244
1245 CheckIsoFace(no[3], no[2], no[1], no[0], mid23, mid12, mid01, mid30, midf0);
1246 CheckIsoFace(no[0], no[1], no[5], no[4], mid01, mid15, mid45, mid04, midf1);
1247 CheckIsoFace(no[1], no[2], no[6], no[5], mid12, mid26, mid56, mid15, midf2);
1248 CheckIsoFace(no[2], no[3], no[7], no[6], mid23, mid37, mid67, mid26, midf3);
1249 CheckIsoFace(no[3], no[0], no[4], no[7], mid30, mid04, mid74, mid37, midf4);
1250 CheckIsoFace(no[4], no[5], no[6], no[7], mid45, mid56, mid67, mid74, midf5);
1251 }
1252 else
1253 {
1254 MFEM_ABORT("invalid refinement type.");
1255 }
1256
1257 if (ref_type != Refinement::XYZ) { Iso = false; }
1258 }
1259 else if (el.Geom() == Geometry::PRISM)
1260 {
1261 // Wedge vertex numbering:
1262 //
1263 // 5
1264 // _+_
1265 // _/ | \_ Faces: 0 bottom
1266 // 3 / | \ 4 1 top
1267 // +---------+ 2 front
1268 // | | | 3 right (1 2 5 4)
1269 // | _+_ | 4 left (2 0 3 5)
1270 // | _/ 2 \_ | Z Y
1271 // |/ \| | /
1272 // +---------+ *--X
1273 // 0 1
1274
1275 if (ref_type < 4) // XY refinement (split in 4 wedges)
1276 {
1277 ref_type = Refinement::XY; // for consistence
1278
1279 int mid01 = GetMidEdgeNode(no[0], no[1]);
1280 int mid12 = GetMidEdgeNode(no[1], no[2]);
1281 int mid20 = GetMidEdgeNode(no[2], no[0]);
1282
1283 int mid34 = GetMidEdgeNode(no[3], no[4]);
1284 int mid45 = GetMidEdgeNode(no[4], no[5]);
1285 int mid53 = GetMidEdgeNode(no[5], no[3]);
1286
1287 child[0] = NewWedge(no[0], mid01, mid20,
1288 no[3], mid34, mid53, attr,
1289 fa[0], fa[1], fa[2], -1, fa[4]);
1290
1291 child[1] = NewWedge(mid01, no[1], mid12,
1292 mid34, no[4], mid45, attr,
1293 fa[0], fa[1], fa[2], fa[3], -1);
1294
1295 child[2] = NewWedge(mid20, mid12, no[2],
1296 mid53, mid45, no[5], attr,
1297 fa[0], fa[1], -1, fa[3], fa[4]);
1298
1299 child[3] = NewWedge(mid12, mid20, mid01,
1300 mid45, mid53, mid34, attr,
1301 fa[0], fa[1], -1, -1, -1);
1302
1303 CheckAnisoFace(no[0], no[1], no[4], no[3], mid01, mid34);
1304 CheckAnisoFace(no[1], no[2], no[5], no[4], mid12, mid45);
1305 CheckAnisoFace(no[2], no[0], no[3], no[5], mid20, mid53);
1306 }
1307 else if (ref_type == Refinement::Z) // Z refinement only (split in 2 wedges)
1308 {
1309 int mid03 = GetMidEdgeNode(no[0], no[3]);
1310 int mid14 = GetMidEdgeNode(no[1], no[4]);
1311 int mid25 = GetMidEdgeNode(no[2], no[5]);
1312
1313 child[0] = NewWedge(no[0], no[1], no[2],
1314 mid03, mid14, mid25, attr,
1315 fa[0], -1, fa[2], fa[3], fa[4]);
1316
1317 child[1] = NewWedge(mid03, mid14, mid25,
1318 no[3], no[4], no[5], attr,
1319 -1, fa[1], fa[2], fa[3], fa[4]);
1320
1321 CheckAnisoFace(no[3], no[0], no[1], no[4], mid03, mid14);
1322 CheckAnisoFace(no[4], no[1], no[2], no[5], mid14, mid25);
1323 CheckAnisoFace(no[5], no[2], no[0], no[3], mid25, mid03);
1324 }
1325 else // ref_type > 4, full isotropic refinement (split in 8 wedges)
1326 {
1327 ref_type = Refinement::XYZ; // for consistence
1328
1329 int mid01 = GetMidEdgeNode(no[0], no[1]);
1330 int mid12 = GetMidEdgeNode(no[1], no[2]);
1331 int mid20 = GetMidEdgeNode(no[2], no[0]);
1332
1333 int mid34 = GetMidEdgeNode(no[3], no[4]);
1334 int mid45 = GetMidEdgeNode(no[4], no[5]);
1335 int mid53 = GetMidEdgeNode(no[5], no[3]);
1336
1337 int mid03 = GetMidEdgeNode(no[0], no[3]);
1338 int mid14 = GetMidEdgeNode(no[1], no[4]);
1339 int mid25 = GetMidEdgeNode(no[2], no[5]);
1340
1341 int midf2 = GetMidFaceNode(mid01, mid14, mid34, mid03);
1342 int midf3 = GetMidFaceNode(mid12, mid25, mid45, mid14);
1343 int midf4 = GetMidFaceNode(mid20, mid03, mid53, mid25);
1344
1345 child[0] = NewWedge(no[0], mid01, mid20,
1346 mid03, midf2, midf4, attr,
1347 fa[0], -1, fa[2], -1, fa[4]);
1348
1349 child[1] = NewWedge(mid01, no[1], mid12,
1350 midf2, mid14, midf3, attr,
1351 fa[0], -1, fa[2], fa[3], -1);
1352
1353 child[2] = NewWedge(mid20, mid12, no[2],
1354 midf4, midf3, mid25, attr,
1355 fa[0], -1, -1, fa[3], fa[4]);
1356
1357 child[3] = NewWedge(mid12, mid20, mid01,
1358 midf3, midf4, midf2, attr,
1359 fa[0], -1, -1, -1, -1);
1360
1361 child[4] = NewWedge(mid03, midf2, midf4,
1362 no[3], mid34, mid53, attr,
1363 -1, fa[1], fa[2], -1, fa[4]);
1364
1365 child[5] = NewWedge(midf2, mid14, midf3,
1366 mid34, no[4], mid45, attr,
1367 -1, fa[1], fa[2], fa[3], -1);
1368
1369 child[6] = NewWedge(midf4, midf3, mid25,
1370 mid53, mid45, no[5], attr,
1371 -1, fa[1], -1, fa[3], fa[4]);
1372
1373 child[7] = NewWedge(midf3, midf4, midf2,
1374 mid45, mid53, mid34, attr,
1375 -1, fa[1], -1, -1, -1);
1376
1377 CheckIsoFace(no[0], no[1], no[4], no[3], mid01, mid14, mid34, mid03, midf2);
1378 CheckIsoFace(no[1], no[2], no[5], no[4], mid12, mid25, mid45, mid14, midf3);
1379 CheckIsoFace(no[2], no[0], no[3], no[5], mid20, mid03, mid53, mid25, midf4);
1380 }
1381
1382 if (ref_type != Refinement::XYZ) { Iso = false; }
1383 }
1384 else if (el.Geom() == Geometry::TETRAHEDRON)
1385 {
1386 // Tetrahedron vertex numbering:
1387 //
1388 // 3
1389 // + Faces: 0 back (1, 2, 3)
1390 // |\\_ 1 left (0, 3, 2)
1391 // || \_ 2 front (0, 1, 3)
1392 // | \ \_ 3 bottom (0, 1, 2)
1393 // | +__ \_
1394 // | /2 \__ \_ Z Y
1395 // |/ \__\ | /
1396 // +------------+ *--X
1397 // 0 1
1398
1399 ref_type = Refinement::XYZ; // for consistence
1400
1401 int mid01 = GetMidEdgeNode(no[0], no[1]);
1402 int mid12 = GetMidEdgeNode(no[1], no[2]);
1403 int mid02 = GetMidEdgeNode(no[2], no[0]);
1404
1405 int mid03 = GetMidEdgeNode(no[0], no[3]);
1406 int mid13 = GetMidEdgeNode(no[1], no[3]);
1407 int mid23 = GetMidEdgeNode(no[2], no[3]);
1408
1409 child[0] = NewTetrahedron(no[0], mid01, mid02, mid03, attr,
1410 -1, fa[1], fa[2], fa[3]);
1411
1412 child[1] = NewTetrahedron(mid01, no[1], mid12, mid13, attr,
1413 fa[0], -1, fa[2], fa[3]);
1414
1415 child[2] = NewTetrahedron(mid02, mid12, no[2], mid23, attr,
1416 fa[0], fa[1], -1, fa[3]);
1417
1418 child[3] = NewTetrahedron(mid03, mid13, mid23, no[3], attr,
1419 fa[0], fa[1], fa[2], -1);
1420
1421 // There are three ways to split the inner octahedron. A good strategy is
1422 // to use the shortest diagonal. At the moment we don't have the geometric
1423 // information in this class to determine which diagonal is the shortest,
1424 // but it seems that with reasonable shapes of the coarse tets and MFEM's
1425 // default tet orientation, always using tet_type == 0 produces stable
1426 // refinements. Types 1 and 2 are unused for now.
1427 el.tet_type = 0;
1428
1429 if (el.tet_type == 0) // shortest diagonal mid01--mid23
1430 {
1431 child[4] = NewTetrahedron(mid01, mid23, mid02, mid03, attr,
1432 fa[1], -1, -1, -1);
1433
1434 child[5] = NewTetrahedron(mid01, mid23, mid03, mid13, attr,
1435 -1, fa[2], -1, -1);
1436
1437 child[6] = NewTetrahedron(mid01, mid23, mid13, mid12, attr,
1438 fa[0], -1, -1, -1);
1439
1440 child[7] = NewTetrahedron(mid01, mid23, mid12, mid02, attr,
1441 -1, fa[3], -1, -1);
1442 }
1443 else if (el.tet_type == 1) // shortest diagonal mid12--mid03
1444 {
1445 child[4] = NewTetrahedron(mid03, mid01, mid02, mid12, attr,
1446 fa[3], -1, -1, -1);
1447
1448 child[5] = NewTetrahedron(mid03, mid02, mid23, mid12, attr,
1449 -1, -1, -1, fa[1]);
1450
1451 child[6] = NewTetrahedron(mid03, mid23, mid13, mid12, attr,
1452 fa[0], -1, -1, -1);
1453
1454 child[7] = NewTetrahedron(mid03, mid13, mid01, mid12, attr,
1455 -1, -1, -1, fa[2]);
1456 }
1457 else // el.tet_type == 2, shortest diagonal mid02--mid13
1458 {
1459 child[4] = NewTetrahedron(mid02, mid01, mid13, mid03, attr,
1460 fa[2], -1, -1, -1);
1461
1462 child[5] = NewTetrahedron(mid02, mid03, mid13, mid23, attr,
1463 -1, -1, fa[1], -1);
1464
1465 child[6] = NewTetrahedron(mid02, mid23, mid13, mid12, attr,
1466 fa[0], -1, -1, -1);
1467
1468 child[7] = NewTetrahedron(mid02, mid12, mid13, mid01, attr,
1469 -1, -1, fa[3], -1);
1470 }
1471 }
1472 else if (el.Geom() == Geometry::PYRAMID)
1473 {
1474 // Pyramid vertex numbering:
1475 //
1476 // 4
1477 // + \_ Faces: 0 bottom (3,2,1,0)
1478 // |\\_ \_ 1 front (0, 1, 4)
1479 // || \_ \__ 2 right (1, 2, 4)
1480 // | \ \_ \__ 3 back (2, 3, 4)
1481 // | +____\_ ____\ 4 left (3, 0, 4)
1482 // | /3 \_ 2 Z Y
1483 // |/ \ / | /
1484 // +------------+ *--X
1485 // 0 1
1486
1487 ref_type = Refinement::XYZ; // for consistence
1488
1489 int mid01 = GetMidEdgeNode(no[0], no[1]);
1490 int mid12 = GetMidEdgeNode(no[1], no[2]);
1491 int mid23 = GetMidEdgeNode(no[2], no[3]);
1492 int mid03 = GetMidEdgeNode(no[0], no[3]);
1493 int mid04 = GetMidEdgeNode(no[0], no[4]);
1494 int mid14 = GetMidEdgeNode(no[1], no[4]);
1495 int mid24 = GetMidEdgeNode(no[2], no[4]);
1496 int mid34 = GetMidEdgeNode(no[3], no[4]);
1497 int midf0 = GetMidFaceNode(mid23, mid12, mid01, mid03);
1498
1499 child[0] = NewPyramid(no[0], mid01, midf0, mid03, mid04,
1500 attr, fa[0], fa[1], -1, -1, fa[4]);
1501
1502 child[1] = NewPyramid(mid01, no[1], mid12, midf0, mid14,
1503 attr, fa[0], fa[1], fa[2], -1, -1);
1504
1505 child[2] = NewPyramid(midf0, mid12, no[2], mid23, mid24,
1506 attr, fa[0], -1, fa[2], fa[3], -1);
1507
1508 child[3] = NewPyramid(mid03, midf0, mid23, no[3], mid34,
1509 attr, fa[0], -1, -1, fa[3], fa[4]);
1510
1511 child[4] = NewPyramid(mid24, mid14, mid04, mid34, midf0,
1512 attr, -1, -1, -1, -1, -1);
1513
1514 child[5] = NewPyramid(mid04, mid14, mid24, mid34, no[4],
1515 attr, -1, fa[1], fa[2], fa[3], fa[4]);
1516
1517 child[6] = NewTetrahedron(mid01, midf0, mid04, mid14,
1518 attr, -1, -1, -1, fa[1]);
1519
1520 child[7] = NewTetrahedron(midf0, mid14, mid12, mid24,
1521 attr, -1, -1, fa[2], -1);
1522
1523 child[8] = NewTetrahedron(midf0, mid23, mid34, mid24,
1524 attr, -1, -1, fa[3], -1);
1525
1526 child[9] = NewTetrahedron(mid03, mid04, midf0, mid34,
1527 attr, -1, fa[4], -1, -1);
1528
1529 CheckIsoFace(no[3], no[2], no[1], no[0], mid23, mid12, mid01, mid03, midf0);
1530 }
1531 else if (el.Geom() == Geometry::SQUARE)
1532 {
1533 ref_type &= 0x3; // ignore Z bit
1534
1535 if (ref_type == Refinement::X) // X split
1536 {
1537 int mid01 = nodes.GetId(no[0], no[1]);
1538 int mid23 = nodes.GetId(no[2], no[3]);
1539
1540 child[0] = NewQuadrilateral(no[0], mid01, mid23, no[3],
1541 attr, fa[0], -1, fa[2], fa[3]);
1542
1543 child[1] = NewQuadrilateral(mid01, no[1], no[2], mid23,
1544 attr, fa[0], fa[1], fa[2], -1);
1545 }
1546 else if (ref_type == Refinement::Y) // Y split
1547 {
1548 int mid12 = nodes.GetId(no[1], no[2]);
1549 int mid30 = nodes.GetId(no[3], no[0]);
1550
1551 child[0] = NewQuadrilateral(no[0], no[1], mid12, mid30,
1552 attr, fa[0], fa[1], -1, fa[3]);
1553
1554 child[1] = NewQuadrilateral(mid30, mid12, no[2], no[3],
1555 attr, -1, fa[1], fa[2], fa[3]);
1556 }
1557 else if (ref_type == Refinement::XY) // iso split
1558 {
1559 int mid01 = nodes.GetId(no[0], no[1]);
1560 int mid12 = nodes.GetId(no[1], no[2]);
1561 int mid23 = nodes.GetId(no[2], no[3]);
1562 int mid30 = nodes.GetId(no[3], no[0]);
1563
1564 int midel = nodes.GetId(mid01, mid23);
1565
1566 child[0] = NewQuadrilateral(no[0], mid01, midel, mid30,
1567 attr, fa[0], -1, -1, fa[3]);
1568
1569 child[1] = NewQuadrilateral(mid01, no[1], mid12, midel,
1570 attr, fa[0], fa[1], -1, -1);
1571
1572 child[2] = NewQuadrilateral(midel, mid12, no[2], mid23,
1573 attr, -1, fa[1], fa[2], -1);
1574
1575 child[3] = NewQuadrilateral(mid30, midel, mid23, no[3],
1576 attr, -1, -1, fa[2], fa[3]);
1577 }
1578 else
1579 {
1580 MFEM_ABORT("Invalid refinement type.");
1581 }
1582
1583 if (ref_type != Refinement::XY) { Iso = false; }
1584 }
1585 else if (el.Geom() == Geometry::TRIANGLE)
1586 {
1587 ref_type = Refinement::XY; // for consistence
1588
1589 // isotropic split - the only ref_type available for triangles
1590 int mid01 = nodes.GetId(no[0], no[1]);
1591 int mid12 = nodes.GetId(no[1], no[2]);
1592 int mid20 = nodes.GetId(no[2], no[0]);
1593
1594 child[0] = NewTriangle(no[0], mid01, mid20, attr, fa[0], -1, fa[2]);
1595 child[1] = NewTriangle(mid01, no[1], mid12, attr, fa[0], fa[1], -1);
1596 child[2] = NewTriangle(mid20, mid12, no[2], attr, -1, fa[1], fa[2]);
1597 child[3] = NewTriangle(mid12, mid20, mid01, attr, -1, -1, -1);
1598 }
1599 else if (el.Geom() == Geometry::SEGMENT)
1600 {
1601 ref_type = Refinement::X; // for consistence
1602
1603 int mid = nodes.GetId(no[0], no[1]);
1604 child[0] = NewSegment(no[0], mid, attr, fa[0], -1);
1605 child[1] = NewSegment(mid, no[1], attr, -1, fa[1]);
1606 }
1607 else
1608 {
1609 MFEM_ABORT("Unsupported element geometry.");
1610 }
1611
1612 // start using the nodes of the children, create edges & faces
1613 for (int i = 0; i < MaxElemChildren && child[i] >= 0; i++)
1614 {
1615 ReferenceElement(child[i]);
1616 }
1617
1618 int buf[MaxElemFaces];
1619 Array<int> parentFaces(buf, MaxElemFaces);
1620 parentFaces.SetSize(0);
1621
1622 // sign off of all nodes of the parent, clean up unused nodes, but keep faces
1623 UnreferenceElement(elem, parentFaces);
1624
1625 // register the children in their faces
1626 for (int i = 0; i < MaxElemChildren && child[i] >= 0; i++)
1627 {
1628 RegisterFaces(child[i]);
1629 }
1630
1631 // clean up parent faces, if unused
1632 DeleteUnusedFaces(parentFaces);
1633
1634 // make the children inherit our rank; set the parent element
1635 for (int i = 0; i < MaxElemChildren && child[i] >= 0; i++)
1636 {
1637 Element &ch = elements[child[i]];
1638 ch.rank = el.rank;
1639 ch.parent = elem;
1640 }
1641
1642 // finish the refinement
1643 el.ref_type = ref_type;
1644 std::memcpy(el.child, child, sizeof(el.child));
1645}
1646
1647
1648void NCMesh::Refine(const Array<Refinement>& refinements)
1649{
1650 // push all refinements on the stack in reverse order
1651 ref_stack.Reserve(refinements.Size());
1652 for (int i = refinements.Size()-1; i >= 0; i--)
1653 {
1654 const Refinement& ref = refinements[i];
1656 }
1657
1658 // keep refining as long as the stack contains something
1659 int nforced = 0;
1660 while (ref_stack.Size())
1661 {
1662 Refinement ref = ref_stack.Last();
1663 ref_stack.DeleteLast();
1664
1665 int size = ref_stack.Size();
1666 RefineElement(ref.index, ref.ref_type);
1667 nforced += ref_stack.Size() - size;
1668 }
1669
1670 /* TODO: the current algorithm of forced refinements is not optimal. As
1671 forced refinements spread through the mesh, some may not be necessary in
1672 the end, since the affected elements may still be scheduled for refinement
1673 that could stop the propagation. We should introduce the member
1674 Element::ref_pending that would show the intended refinement in the batch.
1675 A forced refinement would be combined with ref_pending to (possibly) stop
1676 the propagation earlier.
1677
1678 Update: what about a FIFO instead of ref_stack? */
1679
1680#if defined(MFEM_DEBUG) && !defined(MFEM_USE_MPI)
1681 mfem::out << "Refined " << refinements.Size() << " + " << nforced
1682 << " elements" << std::endl;
1683#else
1684 MFEM_CONTRACT_VAR(nforced);
1685#endif
1686
1687 ref_stack.DeleteAll();
1688 shadow.DeleteAll();
1689
1690 Update();
1691}
1692
1693
1694//// Derefinement //////////////////////////////////////////////////////////////
1695
1697{
1698 if (!el.ref_type) { return el.node[index]; }
1699
1700 // need to retrieve node from a child element (there is always a child that
1701 // inherited the parent's corner under the same index)
1702 int ch;
1703 switch (el.Geom())
1704 {
1705 case Geometry::CUBE:
1706 ch = el.child[hex_deref_table[el.ref_type - 1][index]];
1707 break;
1708
1709 case Geometry::PRISM:
1710 ch = prism_deref_table[el.ref_type - 1][index];
1711 MFEM_ASSERT(ch != -1, "");
1712 ch = el.child[ch];
1713 break;
1714
1715 case Geometry::PYRAMID:
1716 ch = pyramid_deref_table[el.ref_type - 1][index];
1717 MFEM_ASSERT(ch != -1, "");
1718 ch = el.child[ch];
1719 break;
1720
1721 case Geometry::SQUARE:
1722 ch = el.child[quad_deref_table[el.ref_type - 1][index]];
1723 break;
1724
1726 case Geometry::TRIANGLE:
1727 ch = el.child[index];
1728 break;
1729
1730 default:
1731 ch = 0; // suppress compiler warning
1732 MFEM_ABORT("Unsupported element geometry.");
1733 }
1734 return RetrieveNode(elements[ch], index);
1735}
1736
1737
1739{
1740 Element &el = elements[elem];
1741 if (!el.ref_type) { return; }
1742
1743 int child[MaxElemChildren];
1744 std::memcpy(child, el.child, sizeof(child));
1745
1746 // first make sure that all children are leaves, derefine them if not
1747 for (int i = 0; i < MaxElemChildren && child[i] >= 0; i++)
1748 {
1749 if (elements[child[i]].ref_type)
1750 {
1751 DerefineElement(child[i]);
1752 }
1753 }
1754
1755 int faces_attribute[MaxElemFaces];
1756 int ref_type_key = el.ref_type - 1;
1757
1758 for (int i = 0; i < MaxElemNodes; i++) { el.node[i] = -1; }
1759
1760 // retrieve original corner nodes and face attributes from the children
1761 if (el.Geom() == Geometry::CUBE)
1762 {
1763 // Sets corner nodes from childs
1764 constexpr int nb_cube_childs = 8;
1765 for (int i = 0; i < nb_cube_childs; i++)
1766 {
1767 const int child_local_index = hex_deref_table[ref_type_key][i];
1768 const int child_global_index = child[child_local_index];
1769 Element &ch = elements[child_global_index];
1770 el.node[i] = ch.node[i];
1771 }
1772 // Sets faces attributes from childs' faces
1773 constexpr int nb_cube_faces = 6;
1774 for (int i = 0; i < nb_cube_faces; i++)
1775 {
1776 const int child_local_index = hex_deref_table[ref_type_key]
1777 [i + nb_cube_childs];
1778 const int child_global_index = child[child_local_index];
1779 Element &ch = elements[child_global_index];
1780 const int* fv = GI[el.Geom()].faces[i];
1781 faces_attribute[i] = faces.Find(ch.node[fv[0]], ch.node[fv[1]],
1782 ch.node[fv[2]], ch.node[fv[3]])
1783 ->attribute;
1784 }
1785 }
1786 else if (el.Geom() == Geometry::PRISM)
1787 {
1788 MFEM_ASSERT(prism_deref_table[ref_type_key][0] != -1,
1789 "invalid prism refinement");
1790 constexpr int nb_prism_childs = 6;
1791 for (int i = 0; i < nb_prism_childs; i++)
1792 {
1793 const int child_local_index = prism_deref_table[ref_type_key][i];
1794 const int child_global_index = child[child_local_index];
1795 Element &ch = elements[child_global_index];
1796 el.node[i] = ch.node[i];
1797 }
1798 el.node[6] = el.node[7] = -1;
1799
1800 constexpr int nb_prism_faces = 5;
1801 for (int i = 0; i < nb_prism_faces; i++)
1802 {
1803 const int child_local_index = prism_deref_table[ref_type_key]
1804 [i + nb_prism_childs];
1805 const int child_global_index = child[child_local_index];
1806 Element &ch = elements[child_global_index];
1807 const int* fv = GI[el.Geom()].faces[i];
1808 faces_attribute[i] = faces.Find(ch.node[fv[0]], ch.node[fv[1]],
1809 ch.node[fv[2]], ch.node[fv[3]])
1810 ->attribute;
1811 }
1812 }
1813 else if (el.Geom() == Geometry::PYRAMID)
1814 {
1815 MFEM_ASSERT(pyramid_deref_table[ref_type_key][0] != -1,
1816 "invalid pyramid refinement");
1817 constexpr int nb_pyramid_childs = 5;
1818 for (int i = 0; i < nb_pyramid_childs; i++)
1819 {
1820 const int child_local_index = pyramid_deref_table[ref_type_key][i];
1821 const int child_global_index = child[child_local_index];
1822 Element &ch = elements[child_global_index];
1823 el.node[i] = ch.node[i];
1824 }
1825 el.node[5] = el.node[6] = el.node[7] = -1;
1826
1827
1828 constexpr int nb_pyramid_faces = 5;
1829 for (int i = 0; i < nb_pyramid_faces; i++)
1830 {
1831 const int child_local_index = pyramid_deref_table[ref_type_key]
1832 [i + nb_pyramid_childs];
1833 const int child_global_index = child[child_local_index];
1834 Element &ch = elements[child_global_index];
1835 const int* fv = GI[el.Geom()].faces[i];
1836 faces_attribute[i] = faces.Find(ch.node[fv[0]], ch.node[fv[1]],
1837 ch.node[fv[2]], ch.node[fv[3]])
1838 ->attribute;
1839 }
1840 }
1841 else if (el.Geom() == Geometry::TETRAHEDRON)
1842 {
1843 for (int i = 0; i < 4; i++)
1844 {
1845 Element& ch1 = elements[child[i]];
1846 Element& ch2 = elements[child[(i+1) & 0x3]];
1847 el.node[i] = ch1.node[i];
1848 const int* fv = GI[el.Geom()].faces[i];
1849 faces_attribute[i] = faces.Find(ch2.node[fv[0]], ch2.node[fv[1]],
1850 ch2.node[fv[2]], ch2.node[fv[3]])
1851 ->attribute;
1852 }
1853 }
1854 else if (el.Geom() == Geometry::SQUARE)
1855 {
1856 constexpr int nb_square_childs = 4;
1857 for (int i = 0; i < nb_square_childs; i++)
1858 {
1859 const int child_local_index = quad_deref_table[ref_type_key][i];
1860 const int child_global_index = child[child_local_index];
1861 Element &ch = elements[child_global_index];
1862 el.node[i] = ch.node[i];
1863 }
1864 constexpr int nb_square_faces = 4;
1865 for (int i = 0; i < nb_square_faces; i++)
1866 {
1867 const int child_local_index = quad_deref_table[ref_type_key]
1868 [i + nb_square_childs];
1869 const int child_global_index = child[child_local_index];
1870 Element &ch = elements[child_global_index];
1871 const int* fv = GI[el.Geom()].faces[i];
1872 faces_attribute[i] = faces.Find(ch.node[fv[0]], ch.node[fv[1]],
1873 ch.node[fv[2]], ch.node[fv[3]])
1874 ->attribute;
1875 }
1876 }
1877 else if (el.Geom() == Geometry::TRIANGLE)
1878 {
1879 constexpr int nb_triangle_childs = 3;
1880 for (int i = 0; i < nb_triangle_childs; i++)
1881 {
1882 Element& ch = elements[child[i]];
1883 el.node[i] = ch.node[i];
1884 const int* fv = GI[el.Geom()].faces[i];
1885 faces_attribute[i] = faces.Find(ch.node[fv[0]], ch.node[fv[1]],
1886 ch.node[fv[2]], ch.node[fv[3]])
1887 ->attribute;
1888 }
1889 }
1890 else if (el.Geom() == Geometry::SEGMENT)
1891 {
1892 constexpr int nb_segment_childs = 2;
1893 for (int i = 0; i < nb_segment_childs; i++)
1894 {
1895 int ni = elements[child[i]].node[i];
1896 el.node[i] = ni;
1897 faces_attribute[i] = faces.Find(ni, ni, ni, ni)->attribute;
1898 }
1899 }
1900 else
1901 {
1902 MFEM_ABORT("Unsupported element geometry.");
1903 }
1904
1905 // sign in to all nodes
1906 ReferenceElement(elem);
1907
1909 Array<int> childFaces(buf, MaxElemChildren*MaxElemFaces);
1910 childFaces.SetSize(0);
1911
1912 // delete children, determine rank
1913 el.rank = std::numeric_limits<int>::max();
1914 for (int i = 0; i < MaxElemChildren && child[i] >= 0; i++)
1915 {
1916 el.rank = std::min(el.rank, elements[child[i]].rank);
1917 UnreferenceElement(child[i], childFaces);
1918 FreeElement(child[i]);
1919 }
1920
1921 RegisterFaces(elem, faces_attribute);
1922
1923 // delete unused faces
1924 childFaces.Sort();
1925 childFaces.Unique();
1926 DeleteUnusedFaces(childFaces);
1927
1928 el.ref_type = 0;
1929}
1930
1931
1933{
1934 Element &el = elements[elem];
1935 if (!el.ref_type) { return; }
1936
1937 int total = 0, ref = 0, ghost = 0;
1938 for (int i = 0; i < MaxElemChildren && el.child[i] >= 0; i++)
1939 {
1940 total++;
1941 Element &ch = elements[el.child[i]];
1942 if (ch.ref_type) { ref++; break; }
1943 if (IsGhost(ch)) { ghost++; }
1944 }
1945
1946 if (!ref && ghost < total)
1947 {
1948 // can be derefined, add to list
1949 int next_row = list.Size() ? (list.Last().from + 1) : 0;
1950 for (int i = 0; i < MaxElemChildren && el.child[i] >= 0; i++)
1951 {
1952 Element &ch = elements[el.child[i]];
1953 list.Append(Connection(next_row, ch.index));
1954 }
1955 }
1956 else
1957 {
1958 for (int i = 0; i < MaxElemChildren && el.child[i] >= 0; i++)
1959 {
1960 CollectDerefinements(el.child[i], list);
1961 }
1962 }
1963}
1964
1966{
1967 Array<Connection> list;
1968 list.Reserve(leaf_elements.Size());
1969
1970 for (int i = 0; i < root_state.Size(); i++)
1971 {
1972 CollectDerefinements(i, list);
1973 }
1974
1975 int size = list.Size() ? (list.Last().from + 1) : 0;
1976 derefinements.MakeFromList(size, list);
1977 return derefinements;
1978}
1979
1981 Array<int> &level_ok, int max_nc_level)
1982{
1983 level_ok.SetSize(deref_table.Size());
1984 for (int i = 0; i < deref_table.Size(); i++)
1985 {
1986 const int* fine = deref_table.GetRow(i), size = deref_table.RowSize(i);
1987 Element &parent = elements[elements[leaf_elements[fine[0]]].parent];
1988
1989 int ok = 1;
1990 for (int j = 0; j < size; j++)
1991 {
1992 int splits[3];
1993 CountSplits(leaf_elements[fine[j]], splits);
1994
1995 for (int k = 0; k < Dim; k++)
1996 {
1997 if ((parent.ref_type & (1 << k)) &&
1998 splits[k] >= max_nc_level)
1999 {
2000 ok = 0; break;
2001 }
2002 }
2003 if (!ok) { break; }
2004 }
2005 level_ok[i] = ok;
2006 }
2007}
2008
2009void NCMesh::Derefine(const Array<int> &derefs)
2010{
2011 MFEM_VERIFY(Dim < 3 || Iso,
2012 "derefinement of 3D anisotropic meshes not implemented yet.");
2013
2015
2016 Array<int> fine_coarse;
2017 leaf_elements.Copy(fine_coarse);
2018
2019 // perform the derefinements
2020 for (int i = 0; i < derefs.Size(); i++)
2021 {
2022 int row = derefs[i];
2023 MFEM_VERIFY(row >= 0 && row < derefinements.Size(),
2024 "invalid derefinement number.");
2025
2026 const int* fine = derefinements.GetRow(row);
2027 int parent = elements[leaf_elements[fine[0]]].parent;
2028
2029 // record the relation of the fine elements to their parent
2030 SetDerefMatrixCodes(parent, fine_coarse);
2031
2032 DerefineElement(parent);
2033 }
2034
2035 // update leaf_elements, Element::index etc.
2036 Update();
2037
2038 // link old fine elements to the new coarse elements
2039 for (int i = 0; i < fine_coarse.Size(); i++)
2040 {
2041 transforms.embeddings[i].parent = elements[fine_coarse[i]].index;
2042 }
2043}
2044
2046{
2047 int nfine = leaf_elements.Size();
2048
2049 // this will tell GetDerefinementTransforms that transforms are not finished
2050 transforms.Clear();
2051
2052 transforms.embeddings.SetSize(nfine);
2053 for (int i = 0; i < nfine; i++)
2054 {
2056 emb.parent = -1;
2057 emb.matrix = 0;
2058 Element &el = elements[leaf_elements[i]];
2059 emb.geom = el.Geom();
2060 emb.ghost = IsGhost(el);
2061 }
2062}
2063
2064void NCMesh::SetDerefMatrixCodes(int parent, Array<int> &fine_coarse)
2065{
2066 // encode the ref_type and child number for GetDerefinementTransforms()
2067 Element &prn = elements[parent];
2068 for (int i = 0; i < MaxElemChildren && prn.child[i] >= 0; i++)
2069 {
2070 Element &ch = elements[prn.child[i]];
2071 if (ch.index >= 0)
2072 {
2073 int code = (prn.ref_type << 4) | i;
2074 transforms.embeddings[ch.index].matrix = code;
2075 fine_coarse[ch.index] = parent;
2076 }
2077 }
2078}
2079
2080
2081//// Mesh Interface ////////////////////////////////////////////////////////////
2082
2083void NCMesh::CollectLeafElements(int elem, int state, Array<int> &ghosts,
2084 int &counter)
2085{
2086 Element &el = elements[elem];
2087 if (!el.ref_type)
2088 {
2089 if (el.rank >= 0) // skip elements beyond the ghost layer in parallel
2090 {
2091 if (!IsGhost(el))
2092 {
2093 leaf_elements.Append(elem);
2094 }
2095 else
2096 {
2097 // in parallel (or in serial loading a parallel file), collect
2098 // elements of neighboring ranks in a separate array
2099 ghosts.Append(elem);
2100 }
2101
2102 // assign the SFC index (temporarily, will be replaced by Mesh index)
2103 el.index = counter++;
2104 }
2105 else
2106 {
2107 // elements beyond the ghost layer are invalid and don't appear in
2108 // 'leaf_elements' (also for performance reasons)
2109 el.index = -1;
2110 }
2111 }
2112 else // Refined element
2113 {
2114 // in non-leaf elements, the 'rank' and 'index' members have no meaning
2115 el.rank = -1;
2116 el.index = -1;
2117
2118 // recurse to subtrees; try to order leaf elements along a space-filling
2119 // curve by changing the order the children are visited at each level
2120 if (el.Geom() == Geometry::SQUARE && el.ref_type == Refinement::XY)
2121 {
2122 for (int i = 0; i < 4; i++)
2123 {
2124 int ch = quad_hilbert_child_order[state][i];
2125 int st = quad_hilbert_child_state[state][i];
2126 CollectLeafElements(el.child[ch], st, ghosts, counter);
2127 }
2128 }
2129 else if (el.Geom() == Geometry::CUBE && el.ref_type == Refinement::XYZ)
2130 {
2131 for (int i = 0; i < 8; i++)
2132 {
2133 int ch = hex_hilbert_child_order[state][i];
2134 int st = hex_hilbert_child_state[state][i];
2135 CollectLeafElements(el.child[ch], st, ghosts, counter);
2136 }
2137 }
2138 else // no space filling curve tables yet for remaining cases
2139 {
2140 for (int i = 0; i < MaxElemChildren; i++)
2141 {
2142 if (el.child[i] >= 0)
2143 {
2144 CollectLeafElements(el.child[i], state, ghosts, counter);
2145 }
2146 }
2147 }
2148 }
2149}
2150
2152{
2153 Array<int> ghosts;
2154
2155 // collect leaf elements in leaf_elements and ghosts elements in ghosts from
2156 // all roots
2158 for (int i = 0, counter = 0; i < root_state.Size(); i++)
2159 {
2160 CollectLeafElements(i, root_state[i], ghosts, counter);
2161 }
2162
2164 NGhostElements = ghosts.Size();
2165
2166 // append ghost elements at the end of 'leaf_element' (if any) and assign the
2167 // final (Mesh) indices of leaves
2168 leaf_elements.Append(ghosts);
2170
2171 for (int i = 0; i < leaf_elements.Size(); i++)
2172 {
2173 Element &el = elements[leaf_elements[i]];
2174 leaf_sfc_index[i] = el.index;
2175 el.index = i;
2176 }
2177}
2178
2180{
2181#ifndef MFEM_NCMESH_OLD_VERTEX_ORDERING
2182 // This method assigns indices to vertices (Node::vert_index) that will be
2183 // seen by the Mesh class and the rest of MFEM. We must be careful to:
2184 //
2185 // 1. Stay compatible with the conforming code, which expects top-level
2186 // (original) vertices to be indexed first, otherwise GridFunctions
2187 // defined on a conforming mesh would no longer be valid when the mesh
2188 // is converted to an NC mesh.
2189 //
2190 // 2. Make sure serial NCMesh is compatible with the parallel ParNCMesh, so
2191 // it is possible to read parallel partial solutions in serial code
2192 // (e.g., serial GLVis). This means handling ghost elements, if present.
2193 //
2194 // 3. Assign vertices in a globally consistent order for parallel meshes:
2195 // if two vertices i,j are shared by two ranks r1,r2, and i<j on r1,
2196 // then i<j on r2 as well. This is true for top-level vertices but also
2197 // for the remaining shared vertices thanks to the globally consistent
2198 // SFC ordering of the leaf elements. This property reduces
2199 // communication and simplifies ParNCMesh.
2200
2201 // STEP 1: begin by splitting vertices into 4 classes:
2202 // - local top-level vertices (code -1)
2203 // - local non-top level vertices (code -2)
2204 // - ghost (non-local) vertices (code -3)
2205 // - vertices beyond the ghost layer (code -4)
2206
2207 for (auto & node : nodes)
2208 {
2209 node.vert_index = -4; // assume beyond ghost layer
2210 }
2211
2212 for (int i = 0; i < leaf_elements.Size(); i++)
2213 {
2214 Element &el = elements[leaf_elements[i]];
2215 for (int j = 0; j < GI[el.Geom()].nv; j++)
2216 {
2217 Node &nd = nodes[el.node[j]];
2218 if (el.rank == MyRank)
2219 {
2220 if (nd.p1 == nd.p2) // local top-level vertex
2221 {
2222 if (nd.vert_index < -1) { nd.vert_index = -1; }
2223 }
2224 else // local non-top-level vertex
2225 {
2226 if (nd.vert_index < -2) { nd.vert_index = -2; }
2227 }
2228 }
2229 else // ghost vertex
2230 {
2231 if (nd.vert_index < -3) { nd.vert_index = -3; }
2232 }
2233 }
2234 }
2235
2236 // STEP 2: assign indices of top-level local vertices, in original order
2237
2238 NVertices = 0;
2239 for (auto &node : nodes)
2240 {
2241 if (node.vert_index == -1)
2242 {
2243 node.vert_index = NVertices++;
2244 }
2245 }
2246
2247 // STEP 3: go over all elements (local and ghost) in SFC order and assign
2248 // remaining local vertices in that order.
2249
2250 Array<int> sfc_order(leaf_elements.Size());
2251 for (int i = 0; i < sfc_order.Size(); i++)
2252 {
2253 sfc_order[leaf_sfc_index[i]] = leaf_elements[i];
2254 }
2255
2256 for (int i = 0; i < sfc_order.Size(); i++)
2257 {
2258 const Element &el = elements[sfc_order[i]];
2259 for (int j = 0; j < GI[el.Geom()].nv; j++)
2260 {
2261 Node &nd = nodes[el.node[j]];
2262 if (nd.vert_index == -2) { nd.vert_index = NVertices++; }
2263 }
2264 }
2265
2266 // STEP 4: create the mapping from Mesh vertex index to NCMesh node index
2267
2269 for (auto node = nodes.begin(); node != nodes.end(); ++node)
2270 {
2271 if (node->HasVertex() && node->vert_index >= 0)
2272 {
2273 MFEM_ASSERT(node->vert_index < vertex_nodeId.Size(), "");
2274 vertex_nodeId[node->vert_index] = node.index();
2275 }
2276 }
2277
2278 // STEP 5: assign remaining ghost vertices, ignore vertices beyond the ghost
2279 // layer
2280
2281 NGhostVertices = 0;
2282 for (int i = 0; i < sfc_order.Size(); i++)
2283 {
2284 const Element &el = elements[sfc_order[i]];
2285 for (int j = 0; j < GI[el.Geom()].nv; j++)
2286 {
2287 Node &nd = nodes[el.node[j]];
2288 if (nd.vert_index == -3)
2289 {
2291 }
2292 }
2293 }
2294
2295#else // old ordering for debugging/testing only
2296 bool parallel = false;
2297#ifdef MFEM_USE_MPI
2298 if (dynamic_cast<ParNCMesh*>(this)) { parallel = true; }
2299#endif
2300
2301 if (!parallel)
2302 {
2303 NVertices = 0;
2304 for (auto node = nodes.begin(); node != nodes.end(); ++node)
2305 {
2306 if (node->HasVertex()) { node->vert_index = NVertices++; }
2307 }
2308
2310
2311 NVertices = 0;
2312 for (auto node = nodes.begin(); node != nodes.end(); ++node)
2313 {
2314 if (node->HasVertex()) { vertex_nodeId[NVertices++] = node.index(); }
2315 }
2316 }
2317 else
2318 {
2319 for (auto node = nodes.begin(); node != nodes.end(); ++node)
2320 {
2321 if (node->HasVertex()) { node->vert_index = -1; }
2322 }
2323
2324 NVertices = 0;
2325 for (int i = 0; i < leaf_elements.Size(); i++)
2326 {
2327 Element &el = elements[leaf_elements[i]];
2328 if (el.rank == MyRank)
2329 {
2330 for (int j = 0; j < GI[el.Geom()].nv; j++)
2331 {
2332 int &vindex = nodes[el.node[j]].vert_index;
2333 if (vindex < 0) { vindex = NVertices++; }
2334 }
2335 }
2336 }
2337
2339 for (auto &node : nodes)
2340 {
2341 if (node.HasVertex() && node.vert_index >= 0)
2342 {
2343 vertex_nodeId[node.vert_index] = node.index();
2344 }
2345 }
2346
2347 NGhostVertices = 0;
2348 for (auto &node : nodes)
2349 {
2350 if (node.HasVertex() && node.vert_index < 0)
2351 {
2352 node.vert_index = NVertices + (NGhostVertices++);
2353 }
2354 }
2355 }
2356#endif
2357}
2358
2359void NCMesh::InitRootState(int root_count)
2360{
2361 root_state.SetSize(root_count);
2362 root_state = 0;
2363
2364 char* node_order;
2365 int nch;
2366
2367 switch (elements[0].Geom()) // TODO: mixed meshes
2368 {
2369 case Geometry::SQUARE:
2370 nch = 4;
2371 node_order = (char*) quad_hilbert_child_order;
2372 break;
2373
2374 case Geometry::CUBE:
2375 nch = 8;
2376 node_order = (char*) hex_hilbert_child_order;
2377 break;
2378
2379 default:
2380 return; // do nothing, all states stay zero
2381 }
2382
2383 int entry_node = -2;
2384
2385 // process the root element sequence
2386 for (int i = 0; i < root_count; i++)
2387 {
2388 Element &el = elements[i];
2389
2390 int v_in = FindNodeExt(el, entry_node, false);
2391 if (v_in < 0) { v_in = 0; }
2392
2393 // determine which nodes are shared with the next element
2394 bool shared[MaxElemNodes];
2395 for (int ni = 0; ni < MaxElemNodes; ++ni) { shared[ni] = 0; }
2396 if (i+1 < root_count)
2397 {
2398 Element &next = elements[i+1];
2399 for (int j = 0; j < nch; j++)
2400 {
2401 int node = FindNodeExt(el, RetrieveNode(next, j), false);
2402 if (node >= 0) { shared[node] = true; }
2403 }
2404 }
2405
2406 // select orientation that starts in v_in and exits in shared node
2407 int state = Dim*v_in;
2408 for (int j = 0; j < Dim; j++)
2409 {
2410 if (shared[(int) node_order[nch*(state + j) + nch-1]])
2411 {
2412 state += j;
2413 break;
2414 }
2415 }
2416
2417 root_state[i] = state;
2418
2419 entry_node = RetrieveNode(el, node_order[nch*state + nch-1]);
2420 }
2421}
2422
2424{
2425 switch (geom)
2426 {
2427 case Geometry::CUBE: return new mfem::Hexahedron;
2428 case Geometry::PRISM: return new mfem::Wedge;
2429 case Geometry::PYRAMID: return new mfem::Pyramid;
2431 case Geometry::SQUARE: return new mfem::Quadrilateral;
2432 case Geometry::TRIANGLE: return new mfem::Triangle;
2433 }
2434 MFEM_ABORT("invalid geometry");
2435 return NULL;
2436}
2437
2438const real_t* NCMesh::CalcVertexPos(int node) const
2439{
2440 const Node &nd = nodes[node];
2441 if (nd.p1 == nd.p2) // top-level vertex
2442 {
2443 return &coordinates[3*nd.p1];
2444 }
2445
2446 TmpVertex &tv = tmp_vertex[node];
2447 if (tv.valid) { return tv.pos; }
2448
2449 MFEM_VERIFY(tv.visited == false, "cyclic vertex dependencies.");
2450 tv.visited = true;
2451
2452 const real_t* pos1 = CalcVertexPos(nd.p1);
2453 const real_t* pos2 = CalcVertexPos(nd.p2);
2454
2455 for (int i = 0; i < 3; i++)
2456 {
2457 tv.pos[i] = (pos1[i] + pos2[i]) * 0.5;
2458 }
2459 tv.valid = true;
2460 return tv.pos;
2461}
2462
2464{
2465 mesh.vertices.SetSize(vertex_nodeId.Size());
2466 if (coordinates.Size())
2467 {
2468 // calculate vertex positions from stored top-level vertex coordinates
2469 tmp_vertex = new TmpVertex[nodes.NumIds()];
2470 for (int i = 0; i < mesh.vertices.Size(); i++)
2471 {
2472 mesh.vertices[i].SetCoords(spaceDim, CalcVertexPos(vertex_nodeId[i]));
2473 }
2474 delete [] tmp_vertex;
2475 }
2476 // NOTE: if the mesh is curved ('coordinates' is empty), mesh.vertices are
2477 // left uninitialized here; they will be initialized later by the Mesh from
2478 // Nodes -- here we just make sure mesh.vertices has the correct size.
2479
2480 for (auto &elem : mesh.elements)
2481 {
2482 mesh.FreeElement(elem);
2483 }
2484 mesh.elements.SetSize(0);
2485
2486 for (auto &elem : mesh.boundary)
2487 {
2488 mesh.FreeElement(elem);
2489 }
2490 mesh.boundary.SetSize(0);
2491
2492 // Save off boundary face vertices to make boundary elements later.
2493 std::map<int, mfem::Array<int>> unique_boundary_faces;
2494
2495 // create an mfem::Element for each leaf Element
2496 for (int i = 0; i < NElements; i++)
2497 {
2498 const Element &nc_elem = elements[leaf_elements[i]];
2499
2500 const int* node = nc_elem.node;
2501 GeomInfo& gi = GI[(int) nc_elem.geom];
2502
2503 mfem::Element* elem = mesh.NewElement(nc_elem.geom);
2504 mesh.elements.Append(elem);
2505
2506 elem->SetAttribute(nc_elem.attribute);
2507 for (int j = 0; j < gi.nv; j++)
2508 {
2509 elem->GetVertices()[j] = nodes[node[j]].vert_index;
2510 }
2511
2512 // Loop over faces and collect those marked as boundaries
2513 for (int k = 0; k < gi.nf; ++k)
2514 {
2515 const int nfv = gi.nfv[k];
2516 const int * const fv = gi.faces[k];
2517 const auto id = faces.FindId(node[fv[0]], node[fv[1]], node[fv[2]],
2518 node[fv[3]]);
2519 if (id >= 0 && faces[id].Boundary())
2520 {
2521 const auto &face = faces[id];
2522 if (face.elem[0] >= 0 && face.elem[1] >= 0 &&
2523 nc_elem.rank != std::min(elements[face.elem[0]].rank,
2524 elements[face.elem[1]].rank))
2525 {
2526 // This is a conformal internal face, but this element is not the
2527 // lowest ranking attached processor, thus not the owner of the
2528 // face. Consequently, we do not add this face to avoid double
2529 // counting.
2530 continue;
2531 }
2532
2533 // Add in all boundary faces that are actual boundaries or not
2534 // masters of another face. The fv[2] in the edge split is on
2535 // purpose. A point cannot have a split level, thus do not check for
2536 // master/slave relation.
2537 if ((nfv == 4 &&
2538 !QuadFaceIsMaster(node[fv[0]], node[fv[1]], node[fv[2]], node[fv[3]]))
2539 || (nfv == 3 && !TriFaceIsMaster(node[fv[0]], node[fv[1]], node[fv[2]]))
2540 || (nfv == 2 && EdgeSplitLevel(node[fv[0]], node[fv[2]]) == 0) || (nfv == 1))
2541 {
2542 // This face has no split faces below, it is conformal or a
2543 // slave.
2544 unique_boundary_faces[id].SetSize(nfv);
2545 for (int v = 0; v < nfv; ++v)
2546 {
2547 // Using a map overwrites if a face is visited twice. The
2548 // nfv==2 is necessary because faces of 2D are storing the
2549 // second index in the 2 slot, not the 1 slot.
2550 unique_boundary_faces[id][v] = nodes[node[fv[(nfv==2) ? 2*v : v]]].vert_index;
2551 }
2552 }
2553 }
2554 }
2555 }
2556
2557 auto geom_from_nfv = [](int nfv)
2558 {
2559 switch (nfv)
2560 {
2561 case 1: return Geometry::POINT;
2562 case 2: return Geometry::SEGMENT;
2563 case 3: return Geometry::TRIANGLE;
2564 case 4: return Geometry::SQUARE;
2565 }
2566 return Geometry::INVALID;
2567 };
2568
2569 for (const auto &fv : unique_boundary_faces)
2570 {
2571 const auto f = fv.first;
2572 const auto &v = fv.second;
2573 const auto &face = faces.At(f);
2574
2575 auto geom = geom_from_nfv(v.Size());
2576
2577 MFEM_ASSERT(geom != Geometry::INVALID,
2578 "nfv: " << v.Size() <<
2579 " does not match a valid face geometry: Quad, Tri, Segment, Point");
2580
2581 // Add a new boundary element, with matching attribute and vertices
2582 mesh.boundary.Append(mesh.NewElement(geom));
2583 auto * const be = mesh.boundary.Last();
2584 be->SetAttribute(face.attribute);
2585 be->SetVertices(v);
2586 }
2587}
2588
2589
2591{
2592 //// PART 1: pull indices of regular edges/faces from the Mesh
2593
2594 NEdges = mesh->GetNEdges();
2595 NFaces = mesh->GetNumFaces();
2596 if (Dim < 2) { NFaces = 0; }
2597 // clear Node::edge_index and Face::index
2598 for (auto &node : nodes)
2599 {
2600 if (node.HasEdge()) { node.edge_index = -1; }
2601 }
2602 for (auto &face : faces)
2603 {
2604 face.index = -1;
2605 }
2606
2607 // get edge enumeration from the Mesh
2608 Table *edge_vertex = mesh->GetEdgeVertexTable();
2609 for (int i = 0; i < edge_vertex->Size(); i++)
2610 {
2611 const int *ev = edge_vertex->GetRow(i);
2612 Node* node = nodes.Find(vertex_nodeId[ev[0]], vertex_nodeId[ev[1]]);
2613
2614 MFEM_ASSERT(node && node->HasEdge(),
2615 "edge (" << ev[0] << "," << ev[1] << ") not found, "
2616 "node = " << node);
2617
2618 node->edge_index = i;
2619 }
2620
2621 // get face enumeration from the Mesh, initialize 'face_geom'
2623 for (int i = 0; i < NFaces; i++)
2624 {
2625 const int* fv = mesh->GetFace(i)->GetVertices();
2626 const int nfv = mesh->GetFace(i)->GetNVertices();
2627
2628 Face* face;
2629 if (Dim == 3)
2630 {
2631 if (nfv == 4)
2632 {
2634 face = faces.Find(vertex_nodeId[fv[0]], vertex_nodeId[fv[1]],
2635 vertex_nodeId[fv[2]], vertex_nodeId[fv[3]]);
2636 }
2637 else
2638 {
2639 MFEM_ASSERT(nfv == 3, "");
2641 face = faces.Find(vertex_nodeId[fv[0]], vertex_nodeId[fv[1]],
2642 vertex_nodeId[fv[2]]);
2643 }
2644 }
2645 else
2646 {
2647 MFEM_ASSERT(nfv == 2, "");
2649 int n0 = vertex_nodeId[fv[0]], n1 = vertex_nodeId[fv[1]];
2650 face = faces.Find(n0, n0, n1, n1); // look up degenerate face
2651
2652#ifdef MFEM_DEBUG
2653 // (non-ghost) edge and face numbers must match in 2D
2654 const int *ev = edge_vertex->GetRow(i);
2655 MFEM_ASSERT((ev[0] == fv[0] && ev[1] == fv[1]) ||
2656 (ev[1] == fv[0] && ev[0] == fv[1]), "");
2657#endif
2658 }
2659
2660 MFEM_VERIFY(face, "face not found.");
2661 face->index = i;
2662 }
2663
2664 //// PART 2: assign indices of ghost edges/faces, if any
2665
2666 // count ghost edges and assign their indices
2667 NGhostEdges = 0;
2668 for (auto &node : nodes)
2669 {
2670 if (node.HasEdge() && node.edge_index < 0)
2671 {
2672 node.edge_index = NEdges + (NGhostEdges++);
2673 }
2674 }
2675
2676 // count ghost faces
2677 NGhostFaces = 0;
2678 for (auto &face : faces)
2679 {
2680 if (face.index < 0) { NGhostFaces++; }
2681 }
2682
2683 if (Dim == 2)
2684 {
2685 // in 2D we have fake faces because of DG
2686 MFEM_ASSERT(NFaces == NEdges, "");
2687 MFEM_ASSERT(NGhostFaces == NGhostEdges, "");
2688 }
2689
2690 // resize face_geom (default_geom is for slave faces beyond the ghost layer)
2691 Geometry::Type default_geom = Geometry::SQUARE;
2692 face_geom.SetSize(NFaces + NGhostFaces, default_geom);
2693
2694 // update 'face_geom' for ghost faces, assign ghost face indices
2695 int nghosts = 0;
2696 for (int i = 0; i < NGhostElements; i++)
2697 {
2698 Element &el = elements[leaf_elements[NElements + i]]; // ghost element
2699 GeomInfo &gi = GI[el.Geom()];
2700
2701 for (int j = 0; j < gi.nf; j++)
2702 {
2703 const int *fv = gi.faces[j];
2704 const int fid = faces.FindId(el.node[fv[0]], el.node[fv[1]],
2705 el.node[fv[2]], el.node[fv[3]]);
2706 MFEM_ASSERT(fid >= 0, "face not found!");
2707 auto &face = faces[fid];
2708
2709 if (face.index < 0)
2710 {
2711 face.index = NFaces + (nghosts++);
2712
2713 // store the face geometry
2714 static const Geometry::Type types[5] =
2715 {
2718 };
2719 face_geom[face.index] = types[gi.nfv[j]];
2720 }
2721 }
2722 }
2723
2724 // assign valid indices also to faces beyond the ghost layer
2725 for (auto &face : faces)
2726 {
2727 if (face.index < 0) { face.index = NFaces + (nghosts++); }
2728 }
2729 MFEM_ASSERT(nghosts == NGhostFaces, "");
2730}
2731
2732
2733//// Face/edge lists ///////////////////////////////////////////////////////////
2734
2735int NCMesh::QuadFaceSplitType(int v1, int v2, int v3, int v4,
2736 int mid[5]) const
2737{
2738 MFEM_ASSERT(Dim >= 3, "");
2739
2740 // find edge nodes
2741 int e1 = FindMidEdgeNode(v1, v2);
2742 int e2 = FindMidEdgeNode(v2, v3);
2743 int e3 = (e1 >= 0 && nodes[e1].HasVertex()) ? FindMidEdgeNode(v3, v4) : -1;
2744 int e4 = (e2 >= 0 && nodes[e2].HasVertex()) ? FindMidEdgeNode(v4, v1) : -1;
2745
2746 // optional: return the mid-edge nodes if requested
2747 if (mid) { mid[0] = e1, mid[1] = e2, mid[2] = e3, mid[3] = e4; }
2748
2749 // try to get a mid-face node, either by (e1, e3) or by (e2, e4)
2750 int midf1 = -1, midf2 = -1;
2751 if (e1 >= 0 && e3 >= 0) { midf1 = FindMidEdgeNode(e1, e3); }
2752 if (e2 >= 0 && e4 >= 0) { midf2 = FindMidEdgeNode(e2, e4); }
2753
2754 // get proper node if shadow node exists
2755 if (midf1 >= 0 && midf1 == midf2)
2756 {
2757 const Node &nd = nodes[midf1];
2758 if (nd.p1 != e1 && nd.p2 != e1) { midf1 = -1; }
2759 if (nd.p1 != e2 && nd.p2 != e2) { midf2 = -1; }
2760 }
2761
2762 // only one way to access the mid-face node must always exist
2763 MFEM_ASSERT(!(midf1 >= 0 && midf2 >= 0), "incorrectly split face!");
2764
2765 if (midf1 < 0 && midf2 < 0) // face not split
2766 {
2767 if (mid) { mid[4] = -1; }
2768 return 0;
2769 }
2770 else if (midf1 >= 0) // face split "vertically"
2771 {
2772 if (mid) { mid[4] = midf1; }
2773 return 1;
2774 }
2775 else // face split "horizontally"
2776 {
2777 if (mid) { mid[4] = midf2; }
2778 return 2;
2779 }
2780}
2781
2782bool NCMesh::TriFaceSplit(int v1, int v2, int v3, int mid[3]) const
2783{
2784 int e1 = nodes.FindId(v1, v2);
2785 if (e1 < 0 || !nodes[e1].HasVertex()) { return false; }
2786
2787 int e2 = nodes.FindId(v2, v3);
2788 if (e2 < 0 || !nodes[e2].HasVertex()) { return false; }
2789
2790 int e3 = nodes.FindId(v3, v1);
2791 if (e3 < 0 || !nodes[e3].HasVertex()) { return false; }
2792
2793 if (mid) { mid[0] = e1, mid[1] = e2, mid[2] = e3; }
2794
2795 // This is necessary but not sufficient to determine if a face has been
2796 // split.
2797 return true;
2798}
2799
2800int NCMesh::find_node(const Element &el, int node)
2801{
2802 for (int i = 0; i < MaxElemNodes; i++)
2803 {
2804 if (el.node[i] == node) { return i; }
2805 }
2806 MFEM_ABORT("Node not found.");
2807 return -1;
2808}
2809
2810int NCMesh::FindNodeExt(const Element &el, int node, bool abort)
2811{
2812 for (int i = 0; i < GI[el.Geom()].nv; i++)
2813 {
2814 if (RetrieveNode(el, i) == node) { return i; }
2815 }
2816 if (abort) { MFEM_ABORT("Node not found."); }
2817 return -1;
2818}
2819
2820int NCMesh::find_element_edge(const Element &el, int vn0, int vn1, bool abort)
2821{
2822 MFEM_ASSERT(!el.ref_type, "");
2823
2824 GeomInfo &gi = GI[el.Geom()];
2825 for (int i = 0; i < gi.ne; i++)
2826 {
2827 const int* ev = gi.edges[i];
2828 int n0 = el.node[ev[0]];
2829 int n1 = el.node[ev[1]];
2830 if ((n0 == vn0 && n1 == vn1) ||
2831 (n0 == vn1 && n1 == vn0)) { return i; }
2832 }
2833
2834 if (abort) { MFEM_ABORT("Edge (" << vn0 << ", " << vn1 << ") not found"); }
2835 return -1;
2836}
2837
2838int NCMesh::find_local_face(int geom, int a, int b, int c)
2839{
2840 GeomInfo &gi = GI[geom];
2841 for (int i = 0; i < gi.nf; i++)
2842 {
2843 const int* fv = gi.faces[i];
2844 if ((a == fv[0] || a == fv[1] || a == fv[2] || a == fv[3]) &&
2845 (b == fv[0] || b == fv[1] || b == fv[2] || b == fv[3]) &&
2846 (c == fv[0] || c == fv[1] || c == fv[2] || c == fv[3]))
2847 {
2848 return i;
2849 }
2850 }
2851 MFEM_ABORT("Face not found.");
2852 return -1;
2853}
2854
2855namespace
2856{
2857template <typename T> struct IntHash;
2858template <> struct IntHash<float>
2859{
2860 using int_type = uint32_t;
2861 static constexpr int_type initial_value = 0xc4a016dd; // random value;
2862};
2863template <> struct IntHash<double>
2864{
2865 using int_type = uint64_t;
2866 static constexpr int_type initial_value = 0xf9ca9ba106acbba9; // random value
2867};
2868}
2869
2870/// Hash function for a PointMatrix, used in MatrixMap::map.
2871struct PointMatrixHash
2872{
2873 std::size_t operator()(const NCMesh::PointMatrix &pm) const
2874 {
2875 // This is a variation on "Hashing an array of floats" from here:
2876 // https://cs.stackexchange.com/questions/37952
2877
2878 // Make sure (at compile time) that the types have compatible sizes
2879 static_assert(sizeof(IntHash<real_t>::int_type) == sizeof(real_t), "");
2880 // Suppress maybe unused warnings
2881 MFEM_CONTRACT_VAR(IntHash<float>::initial_value);
2882 MFEM_CONTRACT_VAR(IntHash<double>::initial_value);
2883
2884 auto int_bit_cast = [](real_t val)
2885 {
2886 // std::memcpy is the proper way of doing type punning, see e.g.
2887 // https://gist.github.com/shafik/848ae25ee209f698763cffee272a58f8
2888 IntHash<real_t>::int_type int_val;
2889 std::memcpy(&int_val, &val, sizeof(real_t));
2890 return int_val;
2891 };
2892
2893 IntHash<real_t>::int_type hash = IntHash<real_t>::initial_value;
2894
2895 for (int i = 0; i < pm.np; i++)
2896 {
2897 for (int j = 0; j < pm.points[i].dim; j++)
2898 {
2899 // mix the doubles by adding their binary representations many times
2900 // over (note: 31 is 11111 in binary)
2901 real_t coord = pm.points[i].coord[j];
2902 hash = 31*hash + int_bit_cast(coord);
2903 }
2904 }
2905 return hash; // return the lowest bits of the huge sum
2906 }
2907};
2908
2909/** Helper container to keep track of point matrices encountered during
2910 * face/edge traversal and to assign unique indices to them.
2911 */
2912struct MatrixMap
2913{
2914 int GetIndex(const NCMesh::PointMatrix &pm)
2915 {
2916 int &index = map[pm];
2917 if (!index) { index = map.size(); }
2918 return index - 1;
2919 }
2920
2921 void ExportMatrices(Array<DenseMatrix*> &point_matrices) const
2922 {
2923 point_matrices.SetSize(map.size());
2924 for (const auto &pair : map)
2925 {
2926 DenseMatrix* mat = new DenseMatrix();
2927 pair.first.GetMatrix(*mat);
2928 point_matrices[pair.second - 1] = mat;
2929 }
2930 }
2931
2932 void DumpBucketSizes() const
2933 {
2934 for (unsigned i = 0; i < map.bucket_count(); i++)
2935 {
2936 mfem::out << map.bucket_size(i) << " ";
2937 }
2938 }
2939
2940private:
2941 std::unordered_map<NCMesh::PointMatrix, int, PointMatrixHash> map;
2942};
2943
2944
2945int NCMesh::ReorderFacePointMat(int v0, int v1, int v2, int v3,
2946 int elem, const PointMatrix &pm,
2947 PointMatrix &reordered) const
2948{
2949 const Element &el = elements[elem];
2950 int master[4] =
2951 {
2952 find_node(el, v0), find_node(el, v1), find_node(el, v2),
2953 (v3 >= 0) ? find_node(el, v3) : -1
2954 };
2955 int nfv = (v3 >= 0) ? 4 : 3;
2956
2957 int local = find_local_face(el.Geom(), master[0], master[1], master[2]);
2958 const int* fv = GI[el.Geom()].faces[local];
2959
2960 reordered.np = pm.np;
2961 for (int i = 0, j; i < nfv; i++)
2962 {
2963 for (j = 0; j < nfv; j++)
2964 {
2965 if (fv[i] == master[j])
2966 {
2967 reordered.points[i] = pm.points[j];
2968 break;
2969 }
2970 }
2971 MFEM_ASSERT(j != nfv, "node not found.");
2972 }
2973 return local;
2974}
2975
2976void NCMesh::TraverseQuadFace(int vn0, int vn1, int vn2, int vn3,
2977 const PointMatrix& pm, int level,
2978 Face* eface[4], MatrixMap &matrix_map)
2979{
2980 if (level > 0)
2981 {
2982 // check if we made it to a face that is not split further
2983 Face* fa = faces.Find(vn0, vn1, vn2, vn3);
2984 if (fa)
2985 {
2986 // we have a slave face, add it to the list
2987 int elem = fa->GetSingleElement();
2988 face_list.slaves.Append(
2989 Slave(fa->index, elem, -1, Geometry::SQUARE));
2990 Slave &sl = face_list.slaves.Last();
2991
2992 // reorder the point matrix according to slave face orientation
2993 PointMatrix pm_r;
2994 sl.local = ReorderFacePointMat(vn0, vn1, vn2, vn3, elem, pm, pm_r);
2995 sl.matrix = matrix_map.GetIndex(pm_r);
2996
2997 eface[0] = eface[2] = fa;
2998 eface[1] = eface[3] = fa;
2999
3000 return;
3001 }
3002 }
3003
3004 // we need to recurse deeper
3005 int mid[5];
3006 int split = QuadFaceSplitType(vn0, vn1, vn2, vn3, mid);
3007
3008 Face *ef[2][4];
3009 if (split == 1) // "X" split face
3010 {
3011 Point pmid0(pm(0), pm(1)), pmid2(pm(2), pm(3));
3012
3013 TraverseQuadFace(vn0, mid[0], mid[2], vn3,
3014 PointMatrix(pm(0), pmid0, pmid2, pm(3)),
3015 level+1, ef[0], matrix_map);
3016
3017 TraverseQuadFace(mid[0], vn1, vn2, mid[2],
3018 PointMatrix(pmid0, pm(1), pm(2), pmid2),
3019 level+1, ef[1], matrix_map);
3020
3021 eface[1] = ef[1][1];
3022 eface[3] = ef[0][3];
3023 eface[0] = eface[2] = NULL;
3024 }
3025 else if (split == 2) // "Y" split face
3026 {
3027 Point pmid1(pm(1), pm(2)), pmid3(pm(3), pm(0));
3028
3029 TraverseQuadFace(vn0, vn1, mid[1], mid[3],
3030 PointMatrix(pm(0), pm(1), pmid1, pmid3),
3031 level+1, ef[0], matrix_map);
3032
3033 TraverseQuadFace(mid[3], mid[1], vn2, vn3,
3034 PointMatrix(pmid3, pmid1, pm(2), pm(3)),
3035 level+1, ef[1], matrix_map);
3036
3037 eface[0] = ef[0][0];
3038 eface[2] = ef[1][2];
3039 eface[1] = eface[3] = NULL;
3040 }
3041
3042 // check for a prism edge constrained by the master face
3043 if (HavePrisms() && mid[4] >= 0)
3044 {
3045 Node& enode = nodes[mid[4]];
3046 if (enode.HasEdge())
3047 {
3048 // process the edge only if it's not shared by slave faces within this
3049 // master face (i.e. the edge is "hidden")
3050 const int fi[3][2] = {{0, 0}, {1, 3}, {2, 0}};
3051 if (!ef[0][fi[split][0]] && !ef[1][fi[split][1]])
3052 {
3053 MFEM_ASSERT(enode.edge_refc == 1, "");
3054
3055 MeshId buf[4];
3056 Array<MeshId> eid(buf, 4);
3057
3058 (split == 1) ? FindEdgeElements(mid[0], vn1, vn2, mid[2], eid)
3059 /* */ : FindEdgeElements(mid[3], vn0, vn1, mid[1], eid);
3060
3061 MFEM_ASSERT(eid.Size() > 0, "edge prism not found");
3062 MFEM_ASSERT(eid.Size() < 2, "non-unique edge prism");
3063
3064 // create a slave face record with a degenerate point matrix
3065 face_list.slaves.Append(
3066 Slave(-1 - enode.edge_index,
3067 eid[0].element, eid[0].local, Geometry::SQUARE));
3068 Slave &sl = face_list.slaves.Last();
3069
3070 if (split == 1)
3071 {
3072 Point mid0(pm(0), pm(1)), mid2(pm(2), pm(3));
3073 int v1 = nodes[mid[0]].vert_index;
3074 int v2 = nodes[mid[2]].vert_index;
3075 sl.matrix =
3076 matrix_map.GetIndex(
3077 (v1 < v2) ? PointMatrix(mid0, mid2, mid2, mid0) :
3078 /* */ PointMatrix(mid2, mid0, mid0, mid2));
3079 }
3080 else
3081 {
3082 Point mid1(pm(1), pm(2)), mid3(pm(3), pm(0));
3083 int v1 = nodes[mid[1]].vert_index;
3084 int v2 = nodes[mid[3]].vert_index;
3085 sl.matrix =
3086 matrix_map.GetIndex(
3087 (v1 < v2) ? PointMatrix(mid1, mid3, mid3, mid1) :
3088 /* */ PointMatrix(mid3, mid1, mid1, mid3));
3089 }
3090 }
3091 }
3092 }
3093}
3094
3095void NCMesh::TraverseTetEdge(int vn0, int vn1, const Point &p0, const Point &p1,
3096 MatrixMap &matrix_map)
3097{
3098 int mid = nodes.FindId(vn0, vn1);
3099 if (mid < 0) { return; }
3100
3101 const Node &nd = nodes[mid];
3102 if (nd.HasEdge())
3103 {
3104 // check if the edge is already a master in 'edge_list'
3105 const auto eid_and_type = edge_list.GetMeshIdAndType(nd.edge_index);
3106 if (eid_and_type.type == NCList::MeshIdType::MASTER
3107 || eid_and_type.type == NCList::MeshIdType::CONFORMING)
3108 {
3109 // in this case we need to add an edge-face constraint, because the
3110 // non-slave edge is really a (face-)slave itself.
3111 const MeshId &eid = *eid_and_type.id;
3112 face_list.slaves.Append(
3113 Slave(-1 - eid.index, eid.element, eid.local, Geometry::TRIANGLE));
3114
3115 int v0index = nodes[vn0].vert_index;
3116 int v1index = nodes[vn1].vert_index;
3117
3118 face_list.slaves.Last().matrix =
3119 matrix_map.GetIndex((v0index < v1index) ? PointMatrix(p0, p1, p0)
3120 /* */ : PointMatrix(p1, p0, p1));
3121
3122 return; // no need to continue deeper
3123 }
3124 }
3125
3126 // recurse deeper
3127 Point pmid(p0, p1);
3128 TraverseTetEdge(vn0, mid, p0, pmid, matrix_map);
3129 TraverseTetEdge(mid, vn1, pmid, p1, matrix_map);
3130}
3131
3133 int vn2,
3134 const PointMatrix& pm, int level,
3135 MatrixMap &matrix_map)
3136{
3137 if (level > 0)
3138 {
3139 // check if we made it to a face that is not split further
3140 Face* fa = faces.Find(vn0, vn1, vn2);
3141 if (fa)
3142 {
3143 // we have a slave face, add it to the list
3144 int elem = fa->GetSingleElement();
3145 face_list.slaves.Append(
3146 Slave(fa->index, elem, -1, Geometry::TRIANGLE));
3147 Slave &sl = face_list.slaves.Last();
3148
3149 // reorder the point matrix according to slave face orientation
3150 PointMatrix pm_r;
3151 sl.local = ReorderFacePointMat(vn0, vn1, vn2, -1, elem, pm, pm_r);
3152 sl.matrix = matrix_map.GetIndex(pm_r);
3153
3154 return {true, elements[elem].rank != MyRank};
3155 }
3156 }
3157
3158 int mid[3];
3159 if (TriFaceSplit(vn0, vn1, vn2, mid))
3160 {
3161 Point pmid0(pm(0), pm(1)), pmid1(pm(1), pm(2)), pmid2(pm(2), pm(0));
3163
3164 b[0] = TraverseTriFace(vn0, mid[0], mid[2],
3165 PointMatrix(pm(0), pmid0, pmid2),
3166 level+1, matrix_map);
3167
3168 b[1] = TraverseTriFace(mid[0], vn1, mid[1],
3169 PointMatrix(pmid0, pm(1), pmid1),
3170 level+1, matrix_map);
3171
3172 b[2] = TraverseTriFace(mid[2], mid[1], vn2,
3173 PointMatrix(pmid2, pmid1, pm(2)),
3174 level+1, matrix_map);
3175
3176 b[3] = TraverseTriFace(mid[1], mid[2], mid[0],
3177 PointMatrix(pmid1, pmid2, pmid0),
3178 level+1, matrix_map);
3179
3180 // Traverse possible tet edges constrained by the master face. This needs
3181 // to occur if none of these first NC level faces are split further, OR if
3182 // they are on different processors. The different processor constraint is
3183 // needed in the case of local elements constrained by this face via the
3184 // edge alone. Cannot know this a priori, so just constrain any edge
3185 // attached to two neighbors.
3186 if (HaveTets() && (!b[3].unsplit || b[3].ghost_neighbor))
3187 {
3188 // If the faces have no further splits, so would not be captured by
3189 // normal face relations, add possible edge constraints.
3190 if (!b[1].unsplit || b[1].ghost_neighbor) { TraverseTetEdge(mid[0],mid[1], pmid0,pmid1, matrix_map); }
3191 if (!b[2].unsplit || b[2].ghost_neighbor) { TraverseTetEdge(mid[1],mid[2], pmid1,pmid2, matrix_map); }
3192 if (!b[0].unsplit || b[0].ghost_neighbor) { TraverseTetEdge(mid[2],mid[0], pmid2,pmid0, matrix_map); }
3193 }
3194 }
3195 return {false, false};
3196}
3197
3199{
3200 face_list.Clear();
3201 if (Dim < 3) { return; }
3202
3203 if (HaveTets()) { GetEdgeList(); } // needed by TraverseTetEdge()
3204
3206
3207 Array<char> processed_faces(faces.NumIds());
3208 processed_faces = 0;
3209
3210 MatrixMap matrix_maps[Geometry::NumGeom];
3211
3212 // visit faces of leaf elements
3213 for (int i = 0; i < leaf_elements.Size(); i++)
3214 {
3215 int elem = leaf_elements[i];
3216 Element &el = elements[elem];
3217 MFEM_ASSERT(!el.ref_type, "not a leaf element.");
3218
3219 GeomInfo& gi = GI[el.Geom()];
3220 for (int j = 0; j < gi.nf; j++)
3221 {
3222 // get nodes for this face
3223 int node[4];
3224 for (int k = 0; k < 4; k++)
3225 {
3226 node[k] = el.node[gi.faces[j][k]];
3227 }
3228
3229 int face = faces.FindId(node[0], node[1], node[2], node[3]);
3230 MFEM_ASSERT(face >= 0, "face not found!");
3231
3232 // tell ParNCMesh about the face
3233 ElementSharesFace(elem, j, face);
3234
3235 // have we already processed this face? skip if yes
3236 if (processed_faces[face]) { continue; }
3237 processed_faces[face] = 1;
3238
3239 int fgeom = (node[3] >= 0) ? Geometry::SQUARE : Geometry::TRIANGLE;
3240
3241 Face &fa = faces[face];
3242 bool is_master = false;
3243 if (fa.elem[0] >= 0 && fa.elem[1] >= 0)
3244 {
3245 // this is a conforming face, add it to the list
3246 face_list.conforming.Append(MeshId(fa.index, elem, j, fgeom));
3247 }
3248 else
3249 {
3250 // this is either a master face or a slave face, but we can't tell
3251 // until we traverse the face refinement 'tree'...
3252 int sb = face_list.slaves.Size();
3253 if (fgeom == Geometry::SQUARE)
3254 {
3255 Face* dummy[4];
3256 TraverseQuadFace(node[0], node[1], node[2], node[3],
3257 pm_quad_identity, 0, dummy, matrix_maps[fgeom]);
3258 }
3259 else
3260 {
3261 TraverseTriFace(node[0], node[1], node[2],
3262 pm_tri_identity, 0, matrix_maps[fgeom]);
3263 }
3264
3265 int se = face_list.slaves.Size();
3266 if (sb < se)
3267 {
3268 // found slaves, so this is a master face; add it to the list
3269 is_master = true;
3270 face_list.masters.Append(
3271 Master(fa.index, elem, j, fgeom, sb, se));
3272
3273 // also, set the master index for the slaves
3274 for (int ii = sb; ii < se; ii++)
3275 {
3276 face_list.slaves[ii].master = fa.index;
3277 }
3278 }
3279 }
3280
3281 // To support internal boundaries can only insert non-master faces.
3282 if (fa.Boundary() && !is_master) { boundary_faces.Append(face); }
3283 }
3284 }
3285
3286 // export unique point matrices
3287 for (int i = 0; i < Geometry::NumGeom; i++)
3288 {
3289 matrix_maps[i].ExportMatrices(face_list.point_matrices[i]);
3290 }
3291}
3292
3293void NCMesh::TraverseEdge(int vn0, int vn1, real_t t0, real_t t1, int flags,
3294 int level, MatrixMap &matrix_map)
3295{
3296 int mid = nodes.FindId(vn0, vn1);
3297 if (mid < 0) { return; }
3298
3299 Node &nd = nodes[mid];
3300 if (nd.HasEdge() && level > 0)
3301 {
3302 // we have a slave edge, add it to the list
3303 edge_list.slaves.Append(Slave(nd.edge_index, -1, -1, Geometry::SEGMENT));
3304
3305 Slave &sl = edge_list.slaves.Last();
3306 sl.matrix = matrix_map.GetIndex(PointMatrix(Point(t0), Point(t1)));
3307
3308 // handle slave edge orientation
3309 sl.edge_flags = flags;
3310 int v0index = nodes[vn0].vert_index;
3311 int v1index = nodes[vn1].vert_index;
3312 if (v0index > v1index) { sl.edge_flags |= 2; }
3313 }
3314
3315 // recurse deeper
3316 real_t tmid = (t0 + t1) / 2;
3317 TraverseEdge(vn0, mid, t0, tmid, flags, level+1, matrix_map);
3318 TraverseEdge(mid, vn1, tmid, t1, flags, level+1, matrix_map);
3319}
3320
3322{
3323 edge_list.Clear();
3324 if (Dim < 3) { boundary_faces.SetSize(0); }
3325
3326 Array<char> processed_edges(nodes.NumIds());
3327 processed_edges = 0;
3328
3329 Array<int> edge_element(nodes.NumIds());
3330 Array<signed char> edge_local(nodes.NumIds());
3331 edge_local = -1;
3332
3333 MatrixMap matrix_map;
3334
3335 // visit edges of leaf elements
3336 for (int i = 0; i < leaf_elements.Size(); i++)
3337 {
3338 int elem = leaf_elements[i];
3339 Element &el = elements[elem];
3340 MFEM_ASSERT(!el.ref_type, "not a leaf element.");
3341
3342 GeomInfo& gi = GI[el.Geom()];
3343 for (int j = 0; j < gi.ne; j++)
3344 {
3345 // get nodes for this edge
3346 const int* ev = gi.edges[j];
3347 int node[2] = { el.node[ev[0]], el.node[ev[1]] };
3348
3349 int enode = nodes.FindId(node[0], node[1]);
3350 MFEM_ASSERT(enode >= 0, "edge node not found!");
3351
3352 Node &nd = nodes[enode];
3353 MFEM_ASSERT(nd.HasEdge(), "edge not found!");
3354
3355 // tell ParNCMesh about the edge
3356 ElementSharesEdge(elem, j, enode);
3357
3358 // store element/local for later
3359 edge_element[nd.edge_index] = elem;
3360 edge_local[nd.edge_index] = j;
3361
3362 // skip slave edges here, they will be reached from their masters
3363 if (GetEdgeMaster(enode) >= 0)
3364 {
3365 // (2D only, store internal boundary faces)
3366 if (Dim <= 2)
3367 {
3368 int face = faces.FindId(node[0], node[0], node[1], node[1]);
3369 MFEM_ASSERT(face >= 0, "face not found!");
3370 if (faces[face].Boundary()) { boundary_faces.Append(face); }
3371 }
3372 continue;
3373 }
3374
3375 // have we already processed this edge? skip if yes
3376 if (processed_edges[enode]) { continue; }
3377 processed_edges[enode] = 1;
3378
3379 // prepare edge interval for slave traversal, handle orientation
3380 real_t t0 = 0.0, t1 = 1.0;
3381 int v0index = nodes[node[0]].vert_index;
3382 int v1index = nodes[node[1]].vert_index;
3383 int flags = (v0index > v1index) ? 1 : 0;
3384
3385 // try traversing the edge to find slave edges
3386 int sb = edge_list.slaves.Size();
3387 TraverseEdge(node[0], node[1], t0, t1, flags, 0, matrix_map);
3388
3389 int se = edge_list.slaves.Size();
3390 if (sb < se)
3391 {
3392 // found slaves, this is a master face; add it to the list
3393 edge_list.masters.Append(
3394 Master(nd.edge_index, elem, j, Geometry::SEGMENT, sb, se));
3395
3396 // also, set the master index for the slaves
3397 for (int ii = sb; ii < se; ii++)
3398 {
3399 edge_list.slaves[ii].master = nd.edge_index;
3400 }
3401 }
3402 else
3403 {
3404 // no slaves, this is a conforming edge
3405 edge_list.conforming.Append(MeshId(nd.edge_index, elem, j));
3406 // (2D only, store boundary faces)
3407 if (Dim <= 2)
3408 {
3409 int face = faces.FindId(node[0], node[0], node[1], node[1]);
3410 MFEM_ASSERT(face >= 0, "face not found!");
3411 if (faces[face].Boundary()) { boundary_faces.Append(face); }
3412 }
3413 }
3414 }
3415 }
3416
3417 // fix up slave edge element/local
3418 for (int i = 0; i < edge_list.slaves.Size(); i++)
3419 {
3420 Slave &sl = edge_list.slaves[i];
3421 int local = edge_local[sl.index];
3422 if (local >= 0)
3423 {
3424 sl.local = local;
3425 sl.element = edge_element[sl.index];
3426 }
3427 }
3428
3429 // export unique point matrices
3430 matrix_map.ExportMatrices(edge_list.point_matrices[Geometry::SEGMENT]);
3431}
3432
3434{
3435 int total = NVertices + NGhostVertices;
3436
3438 vertex_list.conforming.Reserve(total);
3439
3440 Array<char> processed_vertices(total);
3441 processed_vertices = 0;
3442
3443 // analogously to above, visit vertices of leaf elements
3444 for (int i = 0; i < leaf_elements.Size(); i++)
3445 {
3446 int elem = leaf_elements[i];
3447 Element &el = elements[elem];
3448
3449 for (int j = 0; j < GI[el.Geom()].nv; j++)
3450 {
3451 int node = el.node[j];
3452 Node &nd = nodes[node];
3453
3454 int index = nd.vert_index;
3455 if (index >= 0)
3456 {
3457 ElementSharesVertex(elem, j, node);
3458
3459 if (processed_vertices[index]) { continue; }
3460 processed_vertices[index] = 1;
3461
3462 vertex_list.conforming.Append(MeshId(index, elem, j));
3463 }
3464 }
3465 }
3466}
3467
3469 DenseMatrix &oriented_matrix) const
3470{
3471 oriented_matrix = *(point_matrices[slave.Geom()][slave.matrix]);
3472
3473 if (slave.edge_flags)
3474 {
3475 MFEM_ASSERT(oriented_matrix.Height() == 1 &&
3476 oriented_matrix.Width() == 2, "not an edge point matrix");
3477
3478 if (slave.edge_flags & 1) // master inverted
3479 {
3480 oriented_matrix(0,0) = 1.0 - oriented_matrix(0,0);
3481 oriented_matrix(0,1) = 1.0 - oriented_matrix(0,1);
3482 }
3483 if (slave.edge_flags & 2) // slave inverted
3484 {
3485 std::swap(oriented_matrix(0,0), oriented_matrix(0,1));
3486 }
3487 }
3488}
3489
3491{
3492 conforming.DeleteAll();
3493 masters.DeleteAll();
3494 slaves.DeleteAll();
3495
3496 for (int i = 0; i < Geometry::NumGeom; i++)
3497 {
3498 for (int j = 0; j < point_matrices[i].Size(); j++)
3499 {
3500 delete point_matrices[i][j];
3501 }
3502 point_matrices[i].DeleteAll();
3503 }
3504
3505 inv_index.clear();
3506}
3507
3510{
3511 BuildIndex();
3512 const auto it = inv_index.find(index);
3513 auto ft = it != inv_index.end() ? it->second.first : MeshIdType::UNRECOGNIZED;
3514 switch (ft)
3515 {
3516 case MeshIdType::CONFORMING:
3517 return {&conforming[it->second.second], it->second.first};
3518 case MeshIdType::MASTER:
3519 return {&masters[it->second.second], it->second.first};
3520 case MeshIdType::SLAVE:
3521 return {&slaves[it->second.second], it->second.first};
3522 case MeshIdType::UNRECOGNIZED:
3523 default:
3524 return {nullptr, MeshIdType::UNRECOGNIZED};
3525 }
3526}
3527
3530{
3531 BuildIndex();
3532 auto it = inv_index.find(index);
3533 return (it != inv_index.end()) ? it->second.first : MeshIdType::UNRECOGNIZED;
3534}
3535
3536bool
3538{
3539 return GetMeshIdType(index) == ft;
3540}
3541
3542void
3543NCMesh::NCList::BuildIndex() const
3544{
3545 if (inv_index.size() == 0)
3546 {
3547 auto index_compare = [](const MeshId &a, const MeshId &b) { return a.index < b.index; };
3548 auto max_conforming = std::max_element(conforming.begin(), conforming.end(),
3549 index_compare);
3550 auto max_master = std::max_element(masters.begin(), masters.end(),
3551 index_compare);
3552 auto max_slave = std::max_element(slaves.begin(), slaves.end(), index_compare);
3553
3554 int max_conforming_index = max_conforming != nullptr ? max_conforming->index :
3555 -1;
3556 int max_master_index = max_master != nullptr ? max_master->index : -1;
3557 int max_slave_index = max_slave != nullptr ? max_slave->index : -1;
3558
3559 inv_index.reserve(std::max({max_conforming_index, max_master_index, max_slave_index}));
3560 for (int i = 0; i < conforming.Size(); i++)
3561 {
3562 inv_index.emplace(conforming[i].index, std::make_pair(MeshIdType::CONFORMING,
3563 i));
3564 }
3565 for (int i = 0; i < masters.Size(); i++)
3566 {
3567 inv_index.emplace(masters[i].index, std::make_pair(MeshIdType::MASTER, i));
3568 }
3569 for (int i = 0; i < slaves.Size(); i++)
3570 {
3571 inv_index.emplace(slaves[i].index, std::make_pair(MeshIdType::SLAVE, i));
3572 }
3573 }
3574 MFEM_ASSERT(inv_index.size() > 0,
3575 "Empty inverse index, member lists must be populated before BuildIndex is called!");
3576}
3577
3578//// Neighbors /////////////////////////////////////////////////////////////////
3579
3580void NCMesh::CollectEdgeVertices(int v0, int v1, Array<int> &indices)
3581{
3582 int mid = nodes.FindId(v0, v1);
3583 if (mid >= 0 && nodes[mid].HasVertex())
3584 {
3585 indices.Append(mid);
3586
3587 CollectEdgeVertices(v0, mid, indices);
3588 CollectEdgeVertices(mid, v1, indices);
3589 }
3590}
3591
3592void NCMesh::CollectTriFaceVertices(int v0, int v1, int v2, Array<int> &indices)
3593{
3594 int mid[3];
3595 if (TriFaceSplit(v0, v1, v2, mid))
3596 {
3597 for (int i = 0; i < 3; i++)
3598 {
3599 indices.Append(mid[i]);
3600 }
3601
3602 CollectTriFaceVertices(v0, mid[0], mid[2], indices);
3603 CollectTriFaceVertices(mid[0], v1, mid[1], indices);
3604 CollectTriFaceVertices(mid[2], mid[1], v2, indices);
3605 CollectTriFaceVertices(mid[0], mid[1], mid[2], indices);
3606
3607 if (HaveTets()) // possible edge-face contact
3608 {
3609 CollectEdgeVertices(mid[0], mid[1], indices);
3610 CollectEdgeVertices(mid[1], mid[2], indices);
3611 CollectEdgeVertices(mid[2], mid[0], indices);
3612 }
3613 }
3614}
3615
3616void NCMesh::CollectQuadFaceVertices(int v0, int v1, int v2, int v3,
3617 Array<int> &indices)
3618{
3619 int mid[5];
3620 switch (QuadFaceSplitType(v0, v1, v2, v3, mid))
3621 {
3622 case 1:
3623 indices.Append(mid[0]);
3624 indices.Append(mid[2]);
3625
3626 CollectQuadFaceVertices(v0, mid[0], mid[2], v3, indices);
3627 CollectQuadFaceVertices(mid[0], v1, v2, mid[2], indices);
3628
3629 if (HavePrisms()) // possible edge-face contact
3630 {
3631 CollectEdgeVertices(mid[0], mid[2], indices);
3632 }
3633 break;
3634
3635 case 2:
3636 indices.Append(mid[1]);
3637 indices.Append(mid[3]);
3638
3639 CollectQuadFaceVertices(v0, v1, mid[1], mid[3], indices);
3640 CollectQuadFaceVertices(mid[3], mid[1], v2, v3, indices);
3641
3642 if (HavePrisms()) // possible edge-face contact
3643 {
3644 CollectEdgeVertices(mid[1], mid[3], indices);
3645 }
3646 break;
3647 }
3648}
3649
3651{
3652 int nrows = leaf_elements.Size();
3653 int* I = Memory<int>(nrows + 1);
3654 int** JJ = new int*[nrows];
3655
3656 Array<int> indices;
3657 indices.Reserve(128);
3658
3659 // collect vertices coinciding with each element, including hanging vertices
3660 for (int i = 0; i < leaf_elements.Size(); i++)
3661 {
3662 int elem = leaf_elements[i];
3663 Element &el = elements[elem];
3664 MFEM_ASSERT(!el.ref_type, "not a leaf element.");
3665
3666 GeomInfo& gi = GI[el.Geom()];
3667 int* node = el.node;
3668
3669 indices.SetSize(0);
3670 for (int j = 0; j < gi.ne; j++)
3671 {
3672 const int* ev = gi.edges[j];
3673 CollectEdgeVertices(node[ev[0]], node[ev[1]], indices);
3674 }
3675
3676 if (Dim >= 3)
3677 {
3678 for (int j = 0; j < gi.nf; j++)
3679 {
3680 const int* fv = gi.faces[j];
3681 if (gi.nfv[j] == 4)
3682 {
3683 CollectQuadFaceVertices(node[fv[0]], node[fv[1]],
3684 node[fv[2]], node[fv[3]], indices);
3685 }
3686 else
3687 {
3688 CollectTriFaceVertices(node[fv[0]], node[fv[1]], node[fv[2]],
3689 indices);
3690 }
3691 }
3692 }
3693
3694 // temporarily store one row of the table
3695 indices.Sort();
3696 indices.Unique();
3697 int size = indices.Size();
3698 I[i] = size;
3699 JJ[i] = new int[size];
3700 std::memcpy(JJ[i], indices.GetData(), size * sizeof(int));
3701 }
3702
3703 // finalize the I array of the table
3704 int nnz = 0;
3705 for (int i = 0; i < nrows; i++)
3706 {
3707 int cnt = I[i];
3708 I[i] = nnz;
3709 nnz += cnt;
3710 }
3711 I[nrows] = nnz;
3712
3713 // copy the temporarily stored rows into one J array
3714 int *J = Memory<int>(nnz);
3715 nnz = 0;
3716 for (int i = 0; i < nrows; i++)
3717 {
3718 int cnt = I[i+1] - I[i];
3719 std::memcpy(J+nnz, JJ[i], cnt * sizeof(int));
3720 delete [] JJ[i];
3721 nnz += cnt;
3722 }
3723
3724 element_vertex.SetIJ(I, J, nrows);
3725
3726 delete [] JJ;
3727}
3728
3729
3731 Array<int> *neighbors,
3732 Array<char> *neighbor_set)
3733{
3734 // If A is the element-to-vertex table (see 'element_vertex') listing all
3735 // vertices touching each element, including hanging vertices, then A*A^T is
3736 // the element-to-neighbor table. Multiplying the element set with A*A^T
3737 // gives the neighbor set. To save memory, this function only computes the
3738 // action of A*A^T, the product itself is not stored anywhere.
3739
3740 // Optimization: the 'element_vertex' table does not store the obvious corner
3741 // nodes in it. The table is therefore empty for conforming meshes.
3742
3744
3745 int nleaves = leaf_elements.Size();
3746 MFEM_VERIFY(elem_set.Size() == nleaves, "");
3747 MFEM_ASSERT(element_vertex.Size() == nleaves, "");
3748
3749 // step 1: vertices = A^T * elem_set, i.e, find all vertices touching the
3750 // element set
3751
3752 Array<char> vmark(nodes.NumIds());
3753 vmark = 0;
3754
3755 for (int i = 0; i < nleaves; i++)
3756 {
3757 if (elem_set[i])
3758 {
3759 int *v = element_vertex.GetRow(i);
3760 int nv = element_vertex.RowSize(i);
3761 for (int j = 0; j < nv; j++)
3762 {
3763 vmark[v[j]] = 1;
3764 }
3765
3766 Element &el = elements[leaf_elements[i]];
3767 nv = GI[el.Geom()].nv;
3768 for (int j = 0; j < nv; j++)
3769 {
3770 vmark[el.node[j]] = 1;
3771 }
3772 }
3773 }
3774
3775 // step 2: neighbors = A * vertices, i.e., find all elements coinciding with
3776 // vertices from step 1; NOTE: in the result we don't include elements from
3777 // the original set
3778
3779 if (neighbor_set)
3780 {
3781 neighbor_set->SetSize(nleaves);
3782 *neighbor_set = 0;
3783 }
3784
3785 for (int i = 0; i < nleaves; i++)
3786 {
3787 if (!elem_set[i])
3788 {
3789 bool hit = false;
3790
3791 int *v = element_vertex.GetRow(i);
3792 int nv = element_vertex.RowSize(i);
3793 for (int j = 0; j < nv; j++)
3794 {
3795 if (vmark[v[j]]) { hit = true; break; }
3796 }
3797
3798 if (!hit)
3799 {
3800 Element &el = elements[leaf_elements[i]];
3801 nv = GI[el.Geom()].nv;
3802 for (int j = 0; j < nv; j++)
3803 {
3804 if (vmark[el.node[j]]) { hit = true; break; }
3805 }
3806 }
3807
3808 if (hit)
3809 {
3810 if (neighbors) { neighbors->Append(leaf_elements[i]); }
3811 if (neighbor_set) { (*neighbor_set)[i] = 1; }
3812 }
3813 }
3814 }
3815}
3816
3817static bool sorted_lists_intersect(const int* a, const int* b, int na, int nb)
3818{
3819 // pointers to "end" sentinel, not last entry. Not for dereferencing.
3820 const int * const a_end = a + na;
3821 const int * const b_end = b + nb;
3822 while (a != a_end && b != b_end)
3823 {
3824 if (*a < *b)
3825 {
3826 ++a;
3827 }
3828 else if (*b < *a)
3829 {
3830 ++b;
3831 }
3832 else
3833 {
3834 return true; // neither *a < *b nor *b < *a thus a == b
3835 }
3836 }
3837 return false; // no common element found
3838}
3839
3840
3841void NCMesh::FindNeighbors(int elem, Array<int> &neighbors,
3842 const Array<int> *search_set)
3843{
3844 // TODO future: this function is inefficient. For a single element, an octree
3845 // neighbor search algorithm would be better. However, the octree neighbor
3846 // algorithm is hard to get right in the multi-octree case due to the
3847 // different orientations of the octrees (i.e., the root elements).
3848
3850
3851 // sorted list of all vertex nodes touching 'elem'
3852 Array<int> vert;
3853 vert.Reserve(128);
3854
3855 // support for non-leaf 'elem', collect vertices of all children
3856 Array<int> stack;
3857 stack.Reserve(64);
3858 stack.Append(elem);
3859
3860 while (stack.Size())
3861 {
3862 Element &el = elements[stack.Last()];
3863 stack.DeleteLast();
3864
3865 if (!el.ref_type)
3866 {
3867 int *v = element_vertex.GetRow(el.index);
3868 int nv = element_vertex.RowSize(el.index);
3869 for (int i = 0; i < nv; i++)
3870 {
3871 vert.Append(v[i]);
3872 }
3873
3874 nv = GI[el.Geom()].nv;
3875 for (int i = 0; i < nv; i++)
3876 {
3877 vert.Append(el.node[i]);
3878 }
3879 }
3880 else
3881 {
3882 for (int i = 0; i < MaxElemChildren && el.child[i] >= 0; i++)
3883 {
3884 stack.Append(el.child[i]);
3885 }
3886 }
3887 }
3888
3889 vert.Sort();
3890 vert.Unique();
3891
3892 int *v1 = vert.GetData();
3893 int nv1 = vert.Size();
3894
3895 if (!search_set) { search_set = &leaf_elements; }
3896
3897 // test *all* potential neighbors from the search set
3898 for (int i = 0; i < search_set->Size(); i++)
3899 {
3900 int testme = (*search_set)[i];
3901 if (testme != elem)
3902 {
3903 Element &el = elements[testme];
3904 int *v2 = element_vertex.GetRow(el.index);
3905 int nv2 = element_vertex.RowSize(el.index);
3906
3907 bool hit = sorted_lists_intersect(v1, v2, nv1, nv2);
3908
3909 if (!hit)
3910 {
3911 int nv = GI[el.Geom()].nv;
3912 for (int j = 0; j < nv; j++)
3913 {
3914 hit = sorted_lists_intersect(&el.node[j], v1, 1, nv1);
3915 if (hit) { break; }
3916 }
3917 }
3918
3919 if (hit) { neighbors.Append(testme); }
3920 }
3921 }
3922}
3923
3925 Array<int> &expanded,
3926 const Array<int> *search_set)
3927{
3929
3930 Array<char> vmark(nodes.NumIds());
3931 vmark = 0;
3932
3933 for (int i = 0; i < elems.Size(); i++)
3934 {
3935 Element &el = elements[elems[i]];
3936
3937 int *v = element_vertex.GetRow(el.index);
3938 int nv = element_vertex.RowSize(el.index);
3939 for (int j = 0; j < nv; j++)
3940 {
3941 vmark[v[j]] = 1;
3942 }
3943
3944 nv = GI[el.Geom()].nv;
3945 for (int j = 0; j < nv; j++)
3946 {
3947 vmark[el.node[j]] = 1;
3948 }
3949 }
3950
3951 if (!search_set)
3952 {
3953 search_set = &leaf_elements;
3954 }
3955
3956 expanded.SetSize(0);
3957 for (int i = 0; i < search_set->Size(); i++)
3958 {
3959 int testme = (*search_set)[i];
3960 Element &el = elements[testme];
3961 bool hit = false;
3962
3963 int *v = element_vertex.GetRow(el.index);
3964 int nv = element_vertex.RowSize(el.index);
3965 for (int j = 0; j < nv; j++)
3966 {
3967 if (vmark[v[j]]) { hit = true; break; }
3968 }
3969
3970 if (!hit)
3971 {
3972 nv = GI[el.Geom()].nv;
3973 for (int j = 0; j < nv; j++)
3974 {
3975 if (vmark[el.node[j]]) { hit = true; break; }
3976 }
3977 }
3978
3979 if (hit) { expanded.Append(testme); }
3980 }
3981}
3982
3983int NCMesh::GetVertexRootCoord(int elem, RefCoord coord[3]) const
3984{
3985 while (1)
3986 {
3987 const Element &el = elements[elem];
3988 if (el.parent < 0) { return elem; }
3989
3990 const Element &pa = elements[el.parent];
3991 MFEM_ASSERT(pa.ref_type, "internal error");
3992
3993 int ch = 0;
3994 while (ch < MaxElemChildren && pa.child[ch] != elem) { ch++; }
3995 MFEM_ASSERT(ch < MaxElemChildren, "internal error");
3996
3997 MFEM_ASSERT(geom_parent[el.Geom()], "unsupported geometry");
3998 const RefTrf &tr = geom_parent[el.Geom()][(int) pa.ref_type][ch];
3999 tr.Apply(coord, coord);
4000
4001 elem = el.parent;
4002 }
4003}
4004
4005static bool RefPointInside(Geometry::Type geom, const RefCoord pt[3])
4006{
4007 switch (geom)
4008 {
4009 case Geometry::SQUARE:
4010 return (pt[0] >= 0) && (pt[0] <= T_ONE) &&
4011 (pt[1] >= 0) && (pt[1] <= T_ONE);
4012
4013 case Geometry::CUBE:
4014 return (pt[0] >= 0) && (pt[0] <= T_ONE) &&
4015 (pt[1] >= 0) && (pt[1] <= T_ONE) &&
4016 (pt[2] >= 0) && (pt[2] <= T_ONE);
4017
4018 case Geometry::TRIANGLE:
4019 return (pt[0] >= 0) && (pt[1] >= 0) && (pt[0] + pt[1] <= T_ONE);
4020
4021 case Geometry::PRISM:
4022 return (pt[0] >= 0) && (pt[1] >= 0) && (pt[0] + pt[1] <= T_ONE) &&
4023 (pt[2] >= 0) && (pt[2] <= T_ONE);
4024
4025 case Geometry::PYRAMID:
4026 return (pt[0] >= 0) && (pt[1] >= 0) && (pt[2] >= 0.0) &&
4027 (pt[0] + pt[2] <= T_ONE) && (pt[1] + pt[2] <= T_ONE) &&
4028 (pt[2] <= T_ONE);
4029
4030 default:
4031 MFEM_ABORT("unsupported geometry");
4032 return false;
4033 }
4034}
4035
4036void NCMesh::CollectIncidentElements(int elem, const RefCoord coord[3],
4037 Array<int> &list) const
4038{
4039 const Element &el = elements[elem];
4040 if (!el.ref_type)
4041 {
4042 list.Append(elem);
4043 return;
4044 }
4045
4046 RefCoord tcoord[3];
4047 for (int ch = 0; ch < MaxElemChildren && el.child[ch] >= 0; ch++)
4048 {
4049 const RefTrf &tr = geom_child[el.Geom()][(int) el.ref_type][ch];
4050 tr.Apply(coord, tcoord);
4051
4052 if (RefPointInside(el.Geom(), tcoord))
4053 {
4054 CollectIncidentElements(el.child[ch], tcoord, list);
4055 }
4056 }
4057}
4058
4059void NCMesh::FindVertexCousins(int elem, int local, Array<int> &cousins) const
4060{
4061 const Element &el = elements[elem];
4062
4063 RefCoord coord[3];
4064 MFEM_ASSERT(geom_corners[el.Geom()], "unsupported geometry");
4065 std::memcpy(coord, geom_corners[el.Geom()][local], sizeof(coord));
4066
4067 int root = GetVertexRootCoord(elem, coord);
4068
4069 cousins.SetSize(0);
4070 CollectIncidentElements(root, coord, cousins);
4071}
4072
4073
4074//// Coarse/fine transformations ///////////////////////////////////////////////
4075
4077{
4078 MFEM_ASSERT(np == pm.np, "");
4079 for (int i = 0; i < np; i++)
4080 {
4081 MFEM_ASSERT(points[i].dim == pm.points[i].dim, "");
4082 for (int j = 0; j < points[i].dim; j++)
4083 {
4084 if (points[i].coord[j] != pm.points[i].coord[j]) { return false; }
4085 }
4086 }
4087 return true;
4088}
4089
4091{
4092 point_matrix.SetSize(points[0].dim, np);
4093 for (int i = 0; i < np; i++)
4094 {
4095 for (int j = 0; j < points[0].dim; j++)
4096 {
4097 point_matrix(j, i) = points[i].coord[j];
4098 }
4099 }
4100}
4101
4103 Point(0), Point(1)
4104);
4106 Point(0, 0), Point(1, 0), Point(0, 1)
4107);
4109 Point(0, 0), Point(1, 0), Point(1, 1), Point(0, 1)
4110);
4112 Point(0, 0, 0), Point(1, 0, 0), Point(0, 1, 0), Point(0, 0, 1)
4113);
4115 Point(0, 0, 0), Point(1, 0, 0), Point(0, 1, 0),
4116 Point(0, 0, 1), Point(1, 0, 1), Point(0, 1, 1)
4117);
4119 Point(0, 0, 0), Point(1, 0, 0), Point(1, 1, 0),
4120 Point(0, 1, 0), Point(0, 0, 1)
4121);
4123 Point(0, 0, 0), Point(1, 0, 0), Point(1, 1, 0), Point(0, 1, 0),
4124 Point(0, 0, 1), Point(1, 0, 1), Point(1, 1, 1), Point(0, 1, 1)
4125);
4126
4128{
4129 switch (geom)
4130 {
4137 case Geometry::CUBE: return pm_hex_identity;
4138 default:
4139 MFEM_ABORT("unsupported geometry " << geom);
4140 return pm_tri_identity;
4141 }
4142}
4143
4144void NCMesh::GetPointMatrix(Geometry::Type geom, const char* ref_path,
4145 DenseMatrix& matrix) const
4146{
4147 PointMatrix pm = GetGeomIdentity(geom);
4148
4149 while (*ref_path)
4150 {
4151 int ref_type = *ref_path++;
4152 int child = *ref_path++;
4153
4154 // TODO: do this with the new child transform tables
4155
4156 if (geom == Geometry::CUBE)
4157 {
4158 if (ref_type == 1) // split along X axis
4159 {
4160 Point mid01(pm(0), pm(1)), mid23(pm(2), pm(3));
4161 Point mid67(pm(6), pm(7)), mid45(pm(4), pm(5));
4162
4163 if (child == 0)
4164 {
4165 pm = PointMatrix(pm(0), mid01, mid23, pm(3),
4166 pm(4), mid45, mid67, pm(7));
4167 }
4168 else if (child == 1)
4169 {
4170 pm = PointMatrix(mid01, pm(1), pm(2), mid23,
4171 mid45, pm(5), pm(6), mid67);
4172 }
4173 }
4174 else if (ref_type == 2) // split along Y axis
4175 {
4176 Point mid12(pm(1), pm(2)), mid30(pm(3), pm(0));
4177 Point mid56(pm(5), pm(6)), mid74(pm(7), pm(4));
4178
4179 if (child == 0)
4180 {
4181 pm = PointMatrix(pm(0), pm(1), mid12, mid30,
4182 pm(4), pm(5), mid56, mid74);
4183 }
4184 else if (child == 1)
4185 {
4186 pm = PointMatrix(mid30, mid12, pm(2), pm(3),
4187 mid74, mid56, pm(6), pm(7));
4188 }
4189 }
4190 else if (ref_type == 4) // split along Z axis
4191 {
4192 Point mid04(pm(0), pm(4)), mid15(pm(1), pm(5));
4193 Point mid26(pm(2), pm(6)), mid37(pm(3), pm(7));
4194
4195 if (child == 0)
4196 {
4197 pm = PointMatrix(pm(0), pm(1), pm(2), pm(3),
4198 mid04, mid15, mid26, mid37);
4199 }
4200 else if (child == 1)
4201 {
4202 pm = PointMatrix(mid04, mid15, mid26, mid37,
4203 pm(4), pm(5), pm(6), pm(7));
4204 }
4205 }
4206 else if (ref_type == 3) // XY split
4207 {
4208 Point mid01(pm(0), pm(1)), mid12(pm(1), pm(2));
4209 Point mid23(pm(2), pm(3)), mid30(pm(3), pm(0));
4210 Point mid45(pm(4), pm(5)), mid56(pm(5), pm(6));
4211 Point mid67(pm(6), pm(7)), mid74(pm(7), pm(4));
4212
4213 Point midf0(mid23, mid12, mid01, mid30);
4214 Point midf5(mid45, mid56, mid67, mid74);
4215
4216 if (child == 0)
4217 {
4218 pm = PointMatrix(pm(0), mid01, midf0, mid30,
4219 pm(4), mid45, midf5, mid74);
4220 }
4221 else if (child == 1)
4222 {
4223 pm = PointMatrix(mid01, pm(1), mid12, midf0,
4224 mid45, pm(5), mid56, midf5);
4225 }
4226 else if (child == 2)
4227 {
4228 pm = PointMatrix(midf0, mid12, pm(2), mid23,
4229 midf5, mid56, pm(6), mid67);
4230 }
4231 else if (child == 3)
4232 {
4233 pm = PointMatrix(mid30, midf0, mid23, pm(3),
4234 mid74, midf5, mid67, pm(7));
4235 }
4236 }
4237 else if (ref_type == 5) // XZ split
4238 {
4239 Point mid01(pm(0), pm(1)), mid23(pm(2), pm(3));
4240 Point mid45(pm(4), pm(5)), mid67(pm(6), pm(7));
4241 Point mid04(pm(0), pm(4)), mid15(pm(1), pm(5));
4242 Point mid26(pm(2), pm(6)), mid37(pm(3), pm(7));
4243
4244 Point midf1(mid01, mid15, mid45, mid04);
4245 Point midf3(mid23, mid37, mid67, mid26);
4246
4247 if (child == 0)
4248 {
4249 pm = PointMatrix(pm(0), mid01, mid23, pm(3),
4250 mid04, midf1, midf3, mid37);
4251 }
4252 else if (child == 1)
4253 {
4254 pm = PointMatrix(mid01, pm(1), pm(2), mid23,
4255 midf1, mid15, mid26, midf3);
4256 }
4257 else if (child == 2)
4258 {
4259 pm = PointMatrix(midf1, mid15, mid26, midf3,
4260 mid45, pm(5), pm(6), mid67);
4261 }
4262 else if (child == 3)
4263 {
4264 pm = PointMatrix(mid04, midf1, midf3, mid37,
4265 pm(4), mid45, mid67, pm(7));
4266 }
4267 }
4268 else if (ref_type == 6) // YZ split
4269 {
4270 Point mid12(pm(1), pm(2)), mid30(pm(3), pm(0));
4271 Point mid56(pm(5), pm(6)), mid74(pm(7), pm(4));
4272 Point mid04(pm(0), pm(4)), mid15(pm(1), pm(5));
4273 Point mid26(pm(2), pm(6)), mid37(pm(3), pm(7));
4274
4275 Point midf2(mid12, mid26, mid56, mid15);
4276 Point midf4(mid30, mid04, mid74, mid37);
4277
4278 if (child == 0)
4279 {
4280 pm = PointMatrix(pm(0), pm(1), mid12, mid30,
4281 mid04, mid15, midf2, midf4);
4282 }
4283 else if (child == 1)
4284 {
4285 pm = PointMatrix(mid30, mid12, pm(2), pm(3),
4286 midf4, midf2, mid26, mid37);
4287 }
4288 else if (child == 2)
4289 {
4290 pm = PointMatrix(mid04, mid15, midf2, midf4,
4291 pm(4), pm(5), mid56, mid74);
4292 }
4293 else if (child == 3)
4294 {
4295 pm = PointMatrix(midf4, midf2, mid26, mid37,
4296 mid74, mid56, pm(6), pm(7));
4297 }
4298 }
4299 else if (ref_type == 7) // full isotropic refinement
4300 {
4301 Point mid01(pm(0), pm(1)), mid12(pm(1), pm(2));
4302 Point mid23(pm(2), pm(3)), mid30(pm(3), pm(0));
4303 Point mid45(pm(4), pm(5)), mid56(pm(5), pm(6));
4304 Point mid67(pm(6), pm(7)), mid74(pm(7), pm(4));
4305 Point mid04(pm(0), pm(4)), mid15(pm(1), pm(5));
4306 Point mid26(pm(2), pm(6)), mid37(pm(3), pm(7));
4307
4308 Point midf0(mid23, mid12, mid01, mid30);
4309 Point midf1(mid01, mid15, mid45, mid04);
4310 Point midf2(mid12, mid26, mid56, mid15);
4311 Point midf3(mid23, mid37, mid67, mid26);
4312 Point midf4(mid30, mid04, mid74, mid37);
4313 Point midf5(mid45, mid56, mid67, mid74);
4314
4315 Point midel(midf1, midf3);
4316
4317 if (child == 0)
4318 {
4319 pm = PointMatrix(pm(0), mid01, midf0, mid30,
4320 mid04, midf1, midel, midf4);
4321 }
4322 else if (child == 1)
4323 {
4324 pm = PointMatrix(mid01, pm(1), mid12, midf0,
4325 midf1, mid15, midf2, midel);
4326 }
4327 else if (child == 2)
4328 {
4329 pm = PointMatrix(midf0, mid12, pm(2), mid23,
4330 midel, midf2, mid26, midf3);
4331 }
4332 else if (child == 3)
4333 {
4334 pm = PointMatrix(mid30, midf0, mid23, pm(3),
4335 midf4, midel, midf3, mid37);
4336 }
4337 else if (child == 4)
4338 {
4339 pm = PointMatrix(mid04, midf1, midel, midf4,
4340 pm(4), mid45, midf5, mid74);
4341 }
4342 else if (child == 5)
4343 {
4344 pm = PointMatrix(midf1, mid15, midf2, midel,
4345 mid45, pm(5), mid56, midf5);
4346 }
4347 else if (child == 6)
4348 {
4349 pm = PointMatrix(midel, midf2, mid26, midf3,
4350 midf5, mid56, pm(6), mid67);
4351 }
4352 else if (child == 7)
4353 {
4354 pm = PointMatrix(midf4, midel, midf3, mid37,
4355 mid74, midf5, mid67, pm(7));
4356 }
4357 }
4358 }
4359 else if (geom == Geometry::PRISM)
4360 {
4361 if (ref_type < 4) // XY split
4362 {
4363 Point mid01(pm(0), pm(1)), mid12(pm(1), pm(2));
4364 Point mid20(pm(2), pm(0)), mid34(pm(3), pm(4));
4365 Point mid45(pm(4), pm(5)), mid53(pm(5), pm(3));
4366
4367 if (child == 0)
4368 {
4369 pm = PointMatrix(pm(0), mid01, mid20, pm(3), mid34, mid53);
4370 }
4371 else if (child == 1)
4372 {
4373 pm = PointMatrix(mid01, pm(1), mid12, mid34, pm(4), mid45);
4374 }
4375 else if (child == 2)
4376 {
4377 pm = PointMatrix(mid20, mid12, pm(2), mid53, mid45, pm(5));
4378 }
4379 else if (child == 3)
4380 {
4381 pm = PointMatrix(mid12, mid20, mid01, mid45, mid53, mid34);
4382 }
4383 }
4384 else if (ref_type == 4) // Z split
4385 {
4386 Point mid03(pm(0), pm(3)), mid14(pm(1), pm(4)), mid25(pm(2), pm(5));
4387
4388 if (child == 0)
4389 {
4390 pm = PointMatrix(pm(0), pm(1), pm(2), mid03, mid14, mid25);
4391 }
4392 else if (child == 1)
4393 {
4394 pm = PointMatrix(mid03, mid14, mid25, pm(3), pm(4), pm(5));
4395 }
4396 }
4397 else // ref_type > 4, iso split
4398 {
4399 Point mid01(pm(0), pm(1)), mid12(pm(1), pm(2)), mid20(pm(2), pm(0));
4400 Point mid34(pm(3), pm(4)), mid45(pm(4), pm(5)), mid53(pm(5), pm(3));
4401 Point mid03(pm(0), pm(3)), mid14(pm(1), pm(4)), mid25(pm(2), pm(5));
4402
4403 Point midf2(mid01, mid14, mid34, mid03);
4404 Point midf3(mid12, mid25, mid45, mid14);
4405 Point midf4(mid20, mid03, mid53, mid25);
4406
4407 if (child == 0)
4408 {
4409 pm = PointMatrix(pm(0), mid01, mid20, mid03, midf2, midf4);
4410 }
4411 else if (child == 1)
4412 {
4413 pm = PointMatrix(mid01, pm(1), mid12, midf2, mid14, midf3);
4414 }
4415 else if (child == 2)
4416 {
4417 pm = PointMatrix(mid20, mid12, pm(2), midf4, midf3, mid25);
4418 }
4419 else if (child == 3)
4420 {
4421 pm = PointMatrix(mid12, mid20, mid01, midf3, midf4, midf2);
4422 }
4423 else if (child == 4)
4424 {
4425 pm = PointMatrix(mid03, midf2, midf4, pm(3), mid34, mid53);
4426 }
4427 else if (child == 5)
4428 {
4429 pm = PointMatrix(midf2, mid14, midf3, mid34, pm(4), mid45);
4430 }
4431 else if (child == 6)
4432 {
4433 pm = PointMatrix(midf4, midf3, mid25, mid53, mid45, pm(5));
4434 }
4435 else if (child == 7)
4436 {
4437 pm = PointMatrix(midf3, midf4, midf2, mid45, mid53, mid34);
4438 }
4439 }
4440 }
4441 else if (geom == Geometry::PYRAMID)
4442 {
4443 Point mid01(pm(0), pm(1)), mid23(pm(2), pm(3));
4444 Point mid03(pm(0), pm(3)), mid12(pm(1), pm(2));
4445 Point mid04(pm(0), pm(4)), mid14(pm(1), pm(4));
4446 Point mid24(pm(2), pm(4)), mid34(pm(3), pm(4));
4447 Point midf0(mid23, mid12, mid01, mid03);
4448
4449 if (child == 0) // Pyramid
4450 {
4451 pm = PointMatrix(pm(0), mid01, midf0, mid03, mid04);
4452 }
4453 else if (child == 1) // Pyramid
4454 {
4455 pm = PointMatrix(mid01, pm(1), mid12, midf0, mid14);
4456 }
4457 else if (child == 2) // Pyramid
4458 {
4459 pm = PointMatrix(midf0, mid12, pm(2), mid23, mid24);
4460 }
4461 else if (child == 3) // Pyramid
4462 {
4463 pm = PointMatrix(mid03, midf0, mid23, pm(3), mid34);
4464 }
4465 else if (child == 4) // Pyramid
4466 {
4467 pm = PointMatrix(mid24, mid14, mid04, mid34, midf0);
4468 }
4469 else if (child == 5) // Pyramid
4470 {
4471 pm = PointMatrix(mid04, mid14, mid24, mid34, pm(4));
4472 }
4473 else if (child == 6) // Tet
4474 {
4475 pm = PointMatrix(mid01, midf0, mid04, mid14);
4476 }
4477 else if (child == 7) // Tet
4478 {
4479 pm = PointMatrix(midf0, mid14, mid12, mid24);
4480 }
4481 else if (child == 8) // Tet
4482 {
4483 pm = PointMatrix(midf0, mid23, mid34, mid24);
4484 }
4485 else if (child == 9) // Tet
4486 {
4487 pm = PointMatrix(mid03, mid04, midf0, mid34);
4488 }
4489 }
4490 else if (geom == Geometry::TETRAHEDRON)
4491 {
4492 Point mid01(pm(0), pm(1)), mid12(pm(1), pm(2)), mid02(pm(2), pm(0));
4493 Point mid03(pm(0), pm(3)), mid13(pm(1), pm(3)), mid23(pm(2), pm(3));
4494
4495 if (child == 0)
4496 {
4497 pm = PointMatrix(pm(0), mid01, mid02, mid03);
4498 }
4499 else if (child == 1)
4500 {
4501 pm = PointMatrix(mid01, pm(1), mid12, mid13);
4502 }
4503 else if (child == 2)
4504 {
4505 pm = PointMatrix(mid02, mid12, pm(2), mid23);
4506 }
4507 else if (child == 3)
4508 {
4509 pm = PointMatrix(mid03, mid13, mid23, pm(3));
4510 }
4511 else if (child == 4)
4512 {
4513 pm = PointMatrix(mid01, mid23, mid02, mid03);
4514 }
4515 else if (child == 5)
4516 {
4517 pm = PointMatrix(mid01, mid23, mid03, mid13);
4518 }
4519 else if (child == 6)
4520 {
4521 pm = PointMatrix(mid01, mid23, mid13, mid12);
4522 }
4523 else if (child == 7)
4524 {
4525 pm = PointMatrix(mid01, mid23, mid12, mid02);
4526 }
4527 }
4528 else if (geom == Geometry::SQUARE)
4529 {
4530 if (ref_type == 1) // X split
4531 {
4532 Point mid01(pm(0), pm(1)), mid23(pm(2), pm(3));
4533
4534 if (child == 0)
4535 {
4536 pm = PointMatrix(pm(0), mid01, mid23, pm(3));
4537 }
4538 else if (child == 1)
4539 {
4540 pm = PointMatrix(mid01, pm(1), pm(2), mid23);
4541 }
4542 }
4543 else if (ref_type == 2) // Y split
4544 {
4545 Point mid12(pm(1), pm(2)), mid30(pm(3), pm(0));
4546
4547 if (child == 0)
4548 {
4549 pm = PointMatrix(pm(0), pm(1), mid12, mid30);
4550 }
4551 else if (child == 1)
4552 {
4553 pm = PointMatrix(mid30, mid12, pm(2), pm(3));
4554 }
4555 }
4556 else if (ref_type == 3) // iso split
4557 {
4558 Point mid01(pm(0), pm(1)), mid12(pm(1), pm(2));
4559 Point mid23(pm(2), pm(3)), mid30(pm(3), pm(0));
4560 Point midel(mid01, mid23);
4561
4562 if (child == 0)
4563 {
4564 pm = PointMatrix(pm(0), mid01, midel, mid30);
4565 }
4566 else if (child == 1)
4567 {
4568 pm = PointMatrix(mid01, pm(1), mid12, midel);
4569 }
4570 else if (child == 2)
4571 {
4572 pm = PointMatrix(midel, mid12, pm(2), mid23);
4573 }
4574 else if (child == 3)
4575 {
4576 pm = PointMatrix(mid30, midel, mid23, pm(3));
4577 }
4578 }
4579 }
4580 else if (geom == Geometry::TRIANGLE)
4581 {
4582 Point mid01(pm(0), pm(1)), mid12(pm(1), pm(2)), mid20(pm(2), pm(0));
4583
4584 if (child == 0)
4585 {
4586 pm = PointMatrix(pm(0), mid01, mid20);
4587 }
4588 else if (child == 1)
4589 {
4590 pm = PointMatrix(mid01, pm(1), mid12);
4591 }
4592 else if (child == 2)
4593 {
4594 pm = PointMatrix(mid20, mid12, pm(2));
4595 }
4596 else if (child == 3)
4597 {
4598 pm = PointMatrix(mid12, mid20, mid01);
4599 }
4600 }
4601 else if (geom == Geometry::SEGMENT)
4602 {
4603 Point mid01(pm(0), pm(1));
4604
4605 if (child == 0)
4606 {
4607 pm = PointMatrix(pm(0), mid01);
4608 }
4609 else if (child == 1)
4610 {
4611 pm = PointMatrix(mid01, pm(1));
4612 }
4613 }
4614 }
4615
4616 // write the points to the matrix
4617 for (int i = 0; i < pm.np; i++)
4618 {
4619 for (int j = 0; j < pm(i).dim; j++)
4620 {
4621 matrix(j, i) = pm(i).coord[j];
4622 }
4623 }
4624}
4625
4627{
4630
4631 for (int i = 0; i < leaf_elements.Size(); i++)
4632 {
4633 int elem = leaf_elements[i];
4634 if (!IsGhost(elements[elem])) { coarse_elements.Append(elem); }
4635 }
4636
4637 transforms.embeddings.DeleteAll();
4638}
4639
4640void NCMesh::TraverseRefinements(int elem, int coarse_index,
4641 std::string &ref_path, RefPathMap &map) const
4642{
4643 const Element &el = elements[elem];
4644 if (!el.ref_type)
4645 {
4646 int &matrix = map[ref_path];
4647 if (!matrix) { matrix = map.size(); }
4648
4650 emb.parent = coarse_index;
4651 emb.matrix = matrix - 1;
4652 emb.geom = el.Geom();
4653 emb.ghost = IsGhost(el);
4654 }
4655 else
4656 {
4657 MFEM_ASSERT(el.tet_type == 0, "not implemented");
4658
4659 ref_path.push_back(el.ref_type);
4660 ref_path.push_back(0);
4661
4662 for (int i = 0; i < MaxElemChildren; i++)
4663 {
4664 if (el.child[i] >= 0)
4665 {
4666 ref_path[ref_path.length()-1] = i;
4667 TraverseRefinements(el.child[i], coarse_index, ref_path, map);
4668 }
4669 }
4670 ref_path.resize(ref_path.length()-2);
4671 }
4672}
4673
4675{
4676 MFEM_VERIFY(coarse_elements.Size() || !leaf_elements.Size(),
4677 "GetRefinementTransforms() must be preceded by MarkCoarseLevel()"
4678 " and Refine().");
4679
4680 if (!transforms.embeddings.Size())
4681 {
4682 transforms.Clear();
4684
4685 std::string ref_path;
4686 ref_path.reserve(100);
4687
4688 RefPathMap path_map[Geometry::NumGeom];
4689 for (int g = 0; g < Geometry::NumGeom; g++)
4690 {
4691 path_map[g][ref_path] = 1; // empty path == identity
4692 }
4693
4694 int used_geoms = 0;
4695 for (int i = 0; i < coarse_elements.Size(); i++)
4696 {
4697 int geom = elements[coarse_elements[i]].geom;
4698 TraverseRefinements(coarse_elements[i], i, ref_path, path_map[geom]);
4699 used_geoms |= (1 << geom);
4700 }
4701
4702 for (int g = 0; g < Geometry::NumGeom; g++)
4703 {
4704 if (used_geoms & (1 << g))
4705 {
4707 const PointMatrix &identity = GetGeomIdentity(geom);
4708
4710 .SetSize(Dim, identity.np, path_map[g].size());
4711
4712 // calculate the point matrices
4713 RefPathMap::iterator it;
4714 for (it = path_map[g].begin(); it != path_map[g].end(); ++it)
4715 {
4716 GetPointMatrix(geom, it->first.c_str(),
4717 transforms.point_matrices[g](it->second-1));
4718 }
4719 }
4720 }
4721 }
4722 return transforms;
4723}
4724
4726{
4727 MFEM_VERIFY(transforms.embeddings.Size() || !leaf_elements.Size(),
4728 "GetDerefinementTransforms() must be preceded by Derefine().");
4729
4731 {
4732 std::map<int, int> mat_no[Geometry::NumGeom];
4733 for (int g = 0; g < Geometry::NumGeom; g++)
4734 {
4735 mat_no[g][0] = 1; // 0 == identity
4736 }
4737
4738 // assign numbers to the different matrices used
4739 for (int i = 0; i < transforms.embeddings.Size(); i++)
4740 {
4742 int code = emb.matrix; // see SetDerefMatrixCodes()
4743 if (code)
4744 {
4745 int &matrix = mat_no[emb.geom][code];
4746 if (!matrix) { matrix = mat_no[emb.geom].size(); }
4747
4748 emb.matrix = matrix - 1;
4749 }
4750 }
4751
4752 for (int g = 0; g < Geometry::NumGeom; g++)
4753 {
4754 if (Geoms & (1 << g))
4755 {
4757 const PointMatrix &identity = GetGeomIdentity(geom);
4758
4760 .SetSize(Dim, identity.np, mat_no[geom].size());
4761
4762 // calculate point matrices
4763 for (auto it = mat_no[geom].begin(); it != mat_no[geom].end(); ++it)
4764 {
4765 char path[3] = { 0, 0, 0 };
4766
4767 int code = it->first;
4768 if (code)
4769 {
4770 path[0] = code >> 4; // ref_type (see SetDerefMatrixCodes())
4771 path[1] = code & 0xf; // child
4772 }
4773
4774 GetPointMatrix(geom, path,
4775 transforms.point_matrices[geom](it->second-1));
4776 }
4777 }
4778 }
4779 }
4780 return transforms;
4781}
4782
4784 bool want_ghosts) const
4785{
4786 Array<Connection> conn;
4787 conn.Reserve(embeddings.Size());
4788
4789 int max_parent = -1;
4790 for (int i = 0; i < embeddings.Size(); i++)
4791 {
4792 const Embedding &emb = embeddings[i];
4793 if ((emb.parent >= 0) &&
4794 (!emb.ghost || want_ghosts))
4795 {
4796 conn.Append(Connection(emb.parent, i));
4797 max_parent = std::max(emb.parent, max_parent);
4798 }
4799 }
4800
4801 conn.Sort(); // NOTE: unique is not necessary
4802 coarse_to_fine.MakeFromList(max_parent+1, conn);
4803}
4804
4810
4812{
4813 for (int i = 0; i < Geometry::NumGeom; i++)
4814 {
4815 point_matrices[i].SetSize(0, 0, 0);
4816 }
4817 embeddings.DeleteAll();
4818}
4819
4821{
4822 // return true if point matrices are present for any geometry
4823 for (int i = 0; i < Geometry::NumGeom; i++)
4824 {
4825 if (point_matrices[i].SizeK()) { return true; }
4826 }
4827 return false;
4828}
4829
4831{
4832 for (int g = 0; g < Geometry::NumGeom; ++g)
4833 {
4834 a.point_matrices[g].Swap(b.point_matrices[g]);
4835 }
4836 Swap(a.embeddings, b.embeddings);
4837}
4838
4839
4840//// SFC Ordering //////////////////////////////////////////////////////////////
4841
4842static int sgn(int x)
4843{
4844 return (x < 0) ? -1 : (x > 0) ? 1 : 0;
4845}
4846
4847static void HilbertSfc2D(int x, int y, int ax, int ay, int bx, int by,
4848 Array<int> &coords)
4849{
4850 int w = std::abs(ax + ay);
4851 int h = std::abs(bx + by);
4852
4853 int dax = sgn(ax), day = sgn(ay); // unit major direction ("right")
4854 int dbx = sgn(bx), dby = sgn(by); // unit orthogonal direction ("up")
4855
4856 if (h == 1) // trivial row fill
4857 {
4858 for (int i = 0; i < w; i++, x += dax, y += day)
4859 {
4860 coords.Append(x);
4861 coords.Append(y);
4862 }
4863 return;
4864 }
4865 if (w == 1) // trivial column fill
4866 {
4867 for (int i = 0; i < h; i++, x += dbx, y += dby)
4868 {
4869 coords.Append(x);
4870 coords.Append(y);
4871 }
4872 return;
4873 }
4874
4875 int ax2 = ax/2, ay2 = ay/2;
4876 int bx2 = bx/2, by2 = by/2;
4877
4878 int w2 = std::abs(ax2 + ay2);
4879 int h2 = std::abs(bx2 + by2);
4880
4881 if (2*w > 3*h) // long case: split in two parts only
4882 {
4883 if ((w2 & 0x1) && (w > 2))
4884 {
4885 ax2 += dax, ay2 += day; // prefer even steps
4886 }
4887
4888 HilbertSfc2D(x, y, ax2, ay2, bx, by, coords);
4889 HilbertSfc2D(x+ax2, y+ay2, ax-ax2, ay-ay2, bx, by, coords);
4890 }
4891 else // standard case: one step up, one long horizontal step, one step down
4892 {
4893 if ((h2 & 0x1) && (h > 2))
4894 {
4895 bx2 += dbx, by2 += dby; // prefer even steps
4896 }
4897
4898 HilbertSfc2D(x, y, bx2, by2, ax2, ay2, coords);
4899 HilbertSfc2D(x+bx2, y+by2, ax, ay, bx-bx2, by-by2, coords);
4900 HilbertSfc2D(x+(ax-dax)+(bx2-dbx), y+(ay-day)+(by2-dby),
4901 -bx2, -by2, -(ax-ax2), -(ay-ay2), coords);
4902 }
4903}
4904
4905static void HilbertSfc3D(int x, int y, int z,
4906 int ax, int ay, int az,
4907 int bx, int by, int bz,
4908 int cx, int cy, int cz,
4909 Array<int> &coords)
4910{
4911 int w = std::abs(ax + ay + az);
4912 int h = std::abs(bx + by + bz);
4913 int d = std::abs(cx + cy + cz);
4914
4915 int dax = sgn(ax), day = sgn(ay), daz = sgn(az); // unit major dir ("right")
4916 int dbx = sgn(bx), dby = sgn(by), dbz = sgn(bz); // unit ortho dir ("forward")
4917 int dcx = sgn(cx), dcy = sgn(cy), dcz = sgn(cz); // unit ortho dir ("up")
4918
4919 // trivial row/column fills
4920 if (h == 1 && d == 1)
4921 {
4922 for (int i = 0; i < w; i++, x += dax, y += day, z += daz)
4923 {
4924 coords.Append(x);
4925 coords.Append(y);
4926 coords.Append(z);
4927 }
4928 return;
4929 }
4930 if (w == 1 && d == 1)
4931 {
4932 for (int i = 0; i < h; i++, x += dbx, y += dby, z += dbz)
4933 {
4934 coords.Append(x);
4935 coords.Append(y);
4936 coords.Append(z);
4937 }
4938 return;
4939 }
4940 if (w == 1 && h == 1)
4941 {
4942 for (int i = 0; i < d; i++, x += dcx, y += dcy, z += dcz)
4943 {
4944 coords.Append(x);
4945 coords.Append(y);
4946 coords.Append(z);
4947 }
4948 return;
4949 }
4950
4951 int ax2 = ax/2, ay2 = ay/2, az2 = az/2;
4952 int bx2 = bx/2, by2 = by/2, bz2 = bz/2;
4953 int cx2 = cx/2, cy2 = cy/2, cz2 = cz/2;
4954
4955 int w2 = std::abs(ax2 + ay2 + az2);
4956 int h2 = std::abs(bx2 + by2 + bz2);
4957 int d2 = std::abs(cx2 + cy2 + cz2);
4958
4959 // prefer even steps
4960 if ((w2 & 0x1) && (w > 2))
4961 {
4962 ax2 += dax, ay2 += day, az2 += daz;
4963 }
4964 if ((h2 & 0x1) && (h > 2))
4965 {
4966 bx2 += dbx, by2 += dby, bz2 += dbz;
4967 }
4968 if ((d2 & 0x1) && (d > 2))
4969 {
4970 cx2 += dcx, cy2 += dcy, cz2 += dcz;
4971 }
4972
4973 // wide case, split in w only
4974 if ((2*w > 3*h) && (2*w > 3*d))
4975 {
4976 HilbertSfc3D(x, y, z,
4977 ax2, ay2, az2,
4978 bx, by, bz,
4979 cx, cy, cz, coords);
4980
4981 HilbertSfc3D(x+ax2, y+ay2, z+az2,
4982 ax-ax2, ay-ay2, az-az2,
4983 bx, by, bz,
4984 cx, cy, cz, coords);
4985 }
4986 // do not split in d
4987 else if (3*h > 4*d)
4988 {
4989 HilbertSfc3D(x, y, z,
4990 bx2, by2, bz2,
4991 cx, cy, cz,
4992 ax2, ay2, az2, coords);
4993
4994 HilbertSfc3D(x+bx2, y+by2, z+bz2,
4995 ax, ay, az,
4996 bx-bx2, by-by2, bz-bz2,
4997 cx, cy, cz, coords);
4998
4999 HilbertSfc3D(x+(ax-dax)+(bx2-dbx),
5000 y+(ay-day)+(by2-dby),
5001 z+(az-daz)+(bz2-dbz),
5002 -bx2, -by2, -bz2,
5003 cx, cy, cz,
5004 -(ax-ax2), -(ay-ay2), -(az-az2), coords);
5005 }
5006 // do not split in h
5007 else if (3*d > 4*h)
5008 {
5009 HilbertSfc3D(x, y, z,
5010 cx2, cy2, cz2,
5011 ax2, ay2, az2,
5012 bx, by, bz, coords);
5013
5014 HilbertSfc3D(x+cx2, y+cy2, z+cz2,
5015 ax, ay, az,
5016 bx, by, bz,
5017 cx-cx2, cy-cy2, cz-cz2, coords);
5018
5019 HilbertSfc3D(x+(ax-dax)+(cx2-dcx),
5020 y+(ay-day)+(cy2-dcy),
5021 z+(az-daz)+(cz2-dcz),
5022 -cx2, -cy2, -cz2,
5023 -(ax-ax2), -(ay-ay2), -(az-az2),
5024 bx, by, bz, coords);
5025 }
5026 // regular case, split in all w/h/d
5027 else
5028 {
5029 HilbertSfc3D(x, y, z,
5030 bx2, by2, bz2,
5031 cx2, cy2, cz2,
5032 ax2, ay2, az2, coords);
5033
5034 HilbertSfc3D(x+bx2, y+by2, z+bz2,
5035 cx, cy, cz,
5036 ax2, ay2, az2,
5037 bx-bx2, by-by2, bz-bz2, coords);
5038
5039 HilbertSfc3D(x+(bx2-dbx)+(cx-dcx),
5040 y+(by2-dby)+(cy-dcy),
5041 z+(bz2-dbz)+(cz-dcz),
5042 ax, ay, az,
5043 -bx2, -by2, -bz2,
5044 -(cx-cx2), -(cy-cy2), -(cz-cz2), coords);
5045
5046 HilbertSfc3D(x+(ax-dax)+bx2+(cx-dcx),
5047 y+(ay-day)+by2+(cy-dcy),
5048 z+(az-daz)+bz2+(cz-dcz),
5049 -cx, -cy, -cz,
5050 -(ax-ax2), -(ay-ay2), -(az-az2),
5051 bx-bx2, by-by2, bz-bz2, coords);
5052
5053 HilbertSfc3D(x+(ax-dax)+(bx2-dbx),
5054 y+(ay-day)+(by2-dby),
5055 z+(az-daz)+(bz2-dbz),
5056 -bx2, -by2, -bz2,
5057 cx2, cy2, cz2,
5058 -(ax-ax2), -(ay-ay2), -(az-az2), coords);
5059 }
5060}
5061
5062void NCMesh::GridSfcOrdering2D(int width, int height, Array<int> &coords)
5063{
5064 coords.SetSize(0);
5065 coords.Reserve(2*width*height);
5066
5067 if (width >= height)
5068 {
5069 HilbertSfc2D(0, 0, width, 0, 0, height, coords);
5070 }
5071 else
5072 {
5073 HilbertSfc2D(0, 0, 0, height, width, 0, coords);
5074 }
5075}
5076
5077void NCMesh::GridSfcOrdering3D(int width, int height, int depth,
5078 Array<int> &coords)
5079{
5080 coords.SetSize(0);
5081 coords.Reserve(3*width*height*depth);
5082
5083 if (width >= height && width >= depth)
5084 {
5085 HilbertSfc3D(0, 0, 0,
5086 width, 0, 0,
5087 0, height, 0,
5088 0, 0, depth, coords);
5089 }
5090 else if (height >= width && height >= depth)
5091 {
5092 HilbertSfc3D(0, 0, 0,
5093 0, height, 0,
5094 width, 0, 0,
5095 0, 0, depth, coords);
5096 }
5097 else // depth >= width && depth >= height
5098 {
5099 HilbertSfc3D(0, 0, 0,
5100 0, 0, depth,
5101 width, 0, 0,
5102 0, height, 0, coords);
5103 }
5104}
5105
5106
5107//// Utility ///////////////////////////////////////////////////////////////////
5108
5109void NCMesh::GetEdgeVertices(const MeshId &edge_id, int vert_index[2],
5110 bool oriented) const
5111{
5112 const Element &el = elements[edge_id.element];
5113 const GeomInfo& gi = GI[el.Geom()];
5114 const int* ev = gi.edges[(int) edge_id.local];
5115
5116 int n0 = el.node[ev[0]], n1 = el.node[ev[1]];
5117 if (n0 > n1) { std::swap(n0, n1); }
5118
5119 vert_index[0] = nodes[n0].vert_index;
5120 vert_index[1] = nodes[n1].vert_index;
5121
5122 if (oriented && vert_index[0] > vert_index[1])
5123 {
5124 std::swap(vert_index[0], vert_index[1]);
5125 }
5126}
5127
5129{
5130 const Element &el = elements[edge_id.element];
5131 const GeomInfo& gi = GI[el.Geom()];
5132 const int* ev = gi.edges[(int) edge_id.local];
5133
5134 int v0 = nodes[el.node[ev[0]]].vert_index;
5135 int v1 = nodes[el.node[ev[1]]].vert_index;
5136
5137 return ((v0 < v1 && ev[0] > ev[1]) || (v0 > v1 && ev[0] < ev[1])) ? -1 : 1;
5138}
5139
5141 int vert_index[4], int edge_index[4],
5142 int edge_orientation[4]) const
5143{
5144 MFEM_ASSERT(Dim >= 3, "");
5145
5146 const Element &el = elements[face_id.element];
5147 const GeomInfo& gi = GI[el.Geom()];
5148
5149 const int *fv = gi.faces[(int) face_id.local];
5150 const int nfv = gi.nfv[(int) face_id.local];
5151
5152 vert_index[3] = edge_index[3] = -1;
5153 edge_orientation[3] = 0;
5154
5155 for (int i = 0; i < nfv; i++)
5156 {
5157 vert_index[i] = nodes[el.node[fv[i]]].vert_index;
5158 }
5159
5160 for (int i = 0; i < nfv; i++)
5161 {
5162 int j = i+1;
5163 if (j >= nfv) { j = 0; }
5164
5165 int n1 = el.node[fv[i]];
5166 int n2 = el.node[fv[j]];
5167
5168 const Node* en = nodes.Find(n1, n2);
5169 MFEM_ASSERT(en != NULL, "edge not found.");
5170
5171 edge_index[i] = en->edge_index;
5172 edge_orientation[i] = (vert_index[i] < vert_index[j]) ? 1 : -1;
5173 }
5174
5175 return nfv;
5176}
5177
5178int NCMesh::GetEdgeMaster(int node) const
5179{
5180 MFEM_ASSERT(node >= 0, "edge node not found.");
5181 const Node &nd = nodes[node];
5182
5183 int p1 = nd.p1, p2 = nd.p2;
5184 MFEM_ASSERT(p1 != p2, "invalid edge node.");
5185
5186 const Node &n1 = nodes[p1], &n2 = nodes[p2];
5187
5188 int n1p1 = n1.p1, n1p2 = n1.p2;
5189 int n2p1 = n2.p1, n2p2 = n2.p2;
5190
5191 if ((n2p1 != n2p2) && (p1 == n2p1 || p1 == n2p2))
5192 {
5193 // n1 is parent of n2: (n1)--(nd)--(n2)------(*)
5194 if (n2.HasEdge()) { return p2; }
5195 else { return GetEdgeMaster(p2); }
5196 }
5197
5198 if ((n1p1 != n1p2) && (p2 == n1p1 || p2 == n1p2))
5199 {
5200 // n2 is parent of n1: (n2)--(nd)--(n1)------(*)
5201 if (n1.HasEdge()) { return p1; }
5202 else { return GetEdgeMaster(p1); }
5203 }
5204
5205 return -1;
5206}
5207
5208int NCMesh::GetEdgeMaster(int v1, int v2) const
5209{
5210 int node = nodes.FindId(vertex_nodeId[v1], vertex_nodeId[v2]);
5211 MFEM_ASSERT(node >= 0 && nodes[node].HasEdge(), "(v1, v2) is not an edge.");
5212
5213 int master = GetEdgeMaster(node);
5214 return (master >= 0) ? nodes[master].edge_index : -1;
5215}
5216
5218{
5219 int elem = leaf_elements[i];
5220 int depth = 0, parent;
5221 while ((parent = elements[elem].parent) != -1)
5222 {
5223 elem = parent;
5224 depth++;
5225 }
5226 return depth;
5227}
5228
5230{
5231 int elem = leaf_elements[i];
5232 int parent, reduction = 1;
5233 while ((parent = elements[elem].parent) != -1)
5234 {
5235 if (elements[parent].ref_type & 1) { reduction *= 2; }
5236 if (elements[parent].ref_type & 2) { reduction *= 2; }
5237 if (elements[parent].ref_type & 4) { reduction *= 2; }
5238 elem = parent;
5239 }
5240 return reduction;
5241}
5242
5244 Array<int> &face_indices,
5245 Array<int> &face_attribs) const
5246{
5247 const Element &el = elements[leaf_elements[leaf_elem]];
5248 const GeomInfo& gi = GI[el.Geom()];
5249
5250 face_indices.SetSize(gi.nf);
5251 face_attribs.SetSize(gi.nf);
5252
5253 for (int i = 0; i < gi.nf; i++)
5254 {
5255 const int* fv = gi.faces[i];
5256 const Face *face = faces.Find(el.node[fv[0]], el.node[fv[1]],
5257 el.node[fv[2]], el.node[fv[3]]);
5258 MFEM_ASSERT(face, "face not found");
5259 face_indices[i] = face->index;
5260 face_attribs[i] = face->attribute;
5261 }
5262}
5263
5264void NCMesh::FindFaceNodes(int face, int node[4]) const
5265{
5266 // Obtain face nodes from one of its elements (note that face->p1, p2, p3
5267 // cannot be used directly since they are not in order and p4 is missing).
5268 const Face &fa = faces[face];
5269 int elem = fa.elem[0];
5270 if (elem < 0) { elem = fa.elem[1]; }
5271 MFEM_ASSERT(elem >= 0, "Face has no elements?");
5272
5273 const Element &el = elements[elem];
5274 int f = find_local_face(el.Geom(),
5275 find_node(el, fa.p1),
5276 find_node(el, fa.p2),
5277 find_node(el, fa.p3));
5278
5279 const int* fv = GI[el.Geom()].faces[f];
5280 for (int i = 0; i < 4; i++)
5281 {
5282 node[i] = el.node[fv[i]];
5283 }
5284}
5285
5286void NCMesh::GetBoundaryClosure(const Array<int> &bdr_attr_is_ess,
5287 Array<int> &bdr_vertices, Array<int> &bdr_edges,
5288 Array<int> &bdr_faces)
5289{
5290 bdr_vertices.SetSize(0);
5291 bdr_edges.SetSize(0);
5292 bdr_faces.SetSize(0);
5293
5294 if (Dim == 3)
5295 {
5296 GetFaceList(); // make sure 'boundary_faces' is up to date
5297
5298 for (int f : boundary_faces)
5299 {
5300 if (bdr_attr_is_ess[faces[f].attribute - 1])
5301 {
5302 int node[4];
5303 FindFaceNodes(f, node);
5304 int nfv = (node[3] < 0) ? 3 : 4;
5305
5306 for (int j = 0; j < nfv; j++)
5307 {
5308 bdr_vertices.Append(nodes[node[j]].vert_index);
5309
5310 int enode = nodes.FindId(node[j], node[(j+1) % nfv]);
5311 MFEM_ASSERT(enode >= 0 && nodes[enode].HasEdge(), "Edge not found.");
5312 bdr_edges.Append(nodes[enode].edge_index);
5313
5314 while ((enode = GetEdgeMaster(enode)) >= 0)
5315 {
5316 // append master edges that may not be accessible from any
5317 // boundary element, this happens in 3D in re-entrant corners
5318 bdr_edges.Append(nodes[enode].edge_index);
5319 }
5320 }
5321
5322 // If the face is a slave face, collect its non-ghost master face
5323 const Face &face = faces[f];
5324
5325 const auto id_and_type = GetFaceList().GetMeshIdAndType(face.index);
5326 if (id_and_type.type == NCList::MeshIdType::SLAVE)
5327 {
5328 // A slave face must mark its masters
5329 const auto &slave_face_id = static_cast<const Slave&>(*id_and_type.id);
5330 bdr_faces.Append(slave_face_id.master);
5331 }
5332 }
5333 }
5334 }
5335 else if (Dim == 2)
5336 {
5337 GetEdgeList(); // make sure 'boundary_faces' is up to date
5338
5339 for (int f : boundary_faces)
5340 {
5341 Face &face = faces[f];
5342 if (bdr_attr_is_ess[face.attribute - 1])
5343 {
5344 bdr_vertices.Append(nodes[face.p1].vert_index);
5345 bdr_vertices.Append(nodes[face.p3].vert_index);
5346 }
5347
5348 const auto id_and_type = GetEdgeList().GetMeshIdAndType(face.index);
5349 if (id_and_type.type == NCList::MeshIdType::SLAVE)
5350 {
5351 // A slave face must mark its masters
5352 const auto &slave_edge_id = static_cast<const Slave&>(*id_and_type.id);
5353 bdr_edges.Append(slave_edge_id.master);
5354 }
5355 }
5356 }
5357
5358 // Filter, sort and unique an array, so it contains only local unique values.
5359 auto FilterSortUnique = [](Array<int> &v, int N)
5360 {
5361 // Perform the O(N) filter before the O(NlogN) sort. begin -> it is only
5362 // entries < N.
5363 auto it = std::remove_if(v.begin(), v.end(), [N](int i) { return i >= N; });
5364 std::sort(v.begin(), it);
5365 v.SetSize(std::distance(v.begin(), std::unique(v.begin(), it)));
5366 };
5367
5368 FilterSortUnique(bdr_vertices, NVertices);
5369 FilterSortUnique(bdr_edges, NEdges);