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);
5370 FilterSortUnique(bdr_faces, NFaces);
5371}
5372
5373int NCMesh::EdgeSplitLevel(int vn1, int vn2) const
5374{
5375 int mid = nodes.FindId(vn1, vn2);
5376 if (mid < 0 || !nodes[mid].HasVertex()) { return 0; }
5377 return 1 + std::max(EdgeSplitLevel(vn1, mid), EdgeSplitLevel(mid, vn2));
5378}
5379
5380int NCMesh::TriFaceSplitLevel(int vn1, int vn2, int vn3) const
5381{
5382 int mid[3];
5383 if (TriFaceSplit(vn1, vn2, vn3, mid) &&
5384 faces.FindId(vn1, vn2, vn3) < 0)
5385 {
5386 return 1 + max(TriFaceSplitLevel(vn1, mid[0], mid[2]),
5387 TriFaceSplitLevel(mid[0], vn2, mid[1]),
5388 TriFaceSplitLevel(mid[2], mid[1], vn3),
5389 TriFaceSplitLevel(mid[0], mid[1], mid[2]));
5390 }
5391
5392 return 0; // not split
5393}
5394
5395void NCMesh::QuadFaceSplitLevel(int vn1, int vn2, int vn3, int vn4,
5396 int& h_level, int& v_level) const
5397{
5398 int hl1, hl2, vl1, vl2;
5399 int mid[5];
5400
5401 switch (QuadFaceSplitType(vn1, vn2, vn3, vn4, mid))
5402 {
5403 case 0: // not split
5404 h_level = v_level = 0;
5405 break;
5406
5407 case 1: // vertical
5408 QuadFaceSplitLevel(vn1, mid[0], mid[2], vn4, hl1, vl1);
5409 QuadFaceSplitLevel(mid[0], vn2, vn3, mid[2], hl2, vl2);
5410 h_level = std::max(hl1, hl2);
5411 v_level = std::max(vl1, vl2) + 1;
5412 break;
5413
5414 default: // horizontal
5415 QuadFaceSplitLevel(vn1, vn2, mid[1], mid[3], hl1, vl1);
5416 QuadFaceSplitLevel(mid[3], mid[1], vn3, vn4, hl2, vl2);
5417 h_level = std::max(hl1, hl2) + 1;
5418 v_level = std::max(vl1, vl2);
5419 }
5420}
5421
5422int NCMesh::QuadFaceSplitLevel(int vn1, int vn2, int vn3, int vn4) const
5423{
5424 int h_level, v_level;
5425 QuadFaceSplitLevel(vn1, vn2, vn3, vn4, h_level, v_level);
5426 return h_level + v_level;
5427}
5428
5429void NCMesh::CountSplits(int elem, int splits[3]) const
5430{
5431 const Element &el = elements[elem];
5432 const int* node = el.node;
5433 GeomInfo& gi = GI[el.Geom()];
5434
5435 int elevel[MaxElemEdges];
5436 for (int i = 0; i < gi.ne; i++)
5437 {
5438 const int* ev = gi.edges[i];
5439 elevel[i] = EdgeSplitLevel(node[ev[0]], node[ev[1]]);
5440 }
5441
5442 int flevel[MaxElemFaces][2];
5443 if (Dim >= 3)
5444 {
5445 for (int i = 0; i < gi.nf; i++)
5446 {
5447 const int* fv = gi.faces[i];
5448 if (gi.nfv[i] == 4)
5449 {
5450 QuadFaceSplitLevel(node[fv[0]], node[fv[1]],
5451 node[fv[2]], node[fv[3]],
5452 flevel[i][1], flevel[i][0]);
5453 }
5454 else
5455 {
5456 flevel[i][1] = 0;
5457 flevel[i][0] =
5458 TriFaceSplitLevel(node[fv[0]], node[fv[1]], node[fv[2]]);
5459 }
5460 }
5461 }
5462
5463 if (el.Geom() == Geometry::CUBE)
5464 {
5465 splits[0] = max(flevel[0][0], flevel[1][0], flevel[3][0], flevel[5][0],
5466 elevel[0], elevel[2], elevel[4], elevel[6]);
5467
5468 splits[1] = max(flevel[0][1], flevel[2][0], flevel[4][0], flevel[5][1],
5469 elevel[1], elevel[3], elevel[5], elevel[7]);
5470
5471 splits[2] = max(flevel[1][1], flevel[2][1], flevel[3][1], flevel[4][1],
5472 elevel[8], elevel[9], elevel[10], elevel[11]);
5473 }
5474 else if (el.Geom() == Geometry::PRISM)
5475 {
5476 splits[0] = splits[1] = max(flevel[0][0], flevel[1][0], 0,
5477 flevel[2][0], flevel[3][0], flevel[4][0],
5478 elevel[0], elevel[1], elevel[2],
5479 elevel[3], elevel[4], elevel[5]);
5480
5481 splits[2] = max(flevel[2][1], flevel[3][1], flevel[4][1],
5482 elevel[6], elevel[7], elevel[8]);
5483 }
5484 else if (el.Geom() == Geometry::PYRAMID)
5485 {
5486 splits[0] = max(flevel[0][0], flevel[1][0], 0,
5487 flevel[2][0], flevel[3][0], flevel[4][0],
5488 elevel[0], elevel[1], elevel[2],
5489 elevel[3], elevel[4], elevel[5],
5490 elevel[6], elevel[7]);
5491
5492 splits[1] = splits[0];
5493 splits[2] = splits[0];
5494 }
5495 else if (el.Geom() == Geometry::TETRAHEDRON)
5496 {
5497 splits[0] = max(flevel[0][0], flevel[1][0], flevel[2][0], flevel[3][0],
5498 elevel[0], elevel[1], elevel[2], elevel[3], elevel[4], elevel[5]);
5499
5500 splits[1] = splits[0];
5501 splits[2] = splits[0];
5502 }
5503 else if (el.Geom() == Geometry::SQUARE)
5504 {
5505 splits[0] = max(elevel[0], elevel[2]);
5506 splits[1] = max(elevel[1], elevel[3]);
5507 }
5508 else if (el.Geom() == Geometry::TRIANGLE)
5509 {
5510 splits[0] = max(elevel[0], elevel[1], elevel[2]);
5511 splits[1] = splits[0];
5512 }
5513 else
5514 {
5515 MFEM_ABORT("Unsupported element geometry.");
5516 }
5517}
5518
5519void NCMesh::GetLimitRefinements(Array<Refinement> &refinements, int max_level)
5520{
5521 for (int i = 0; i < leaf_elements.Size(); i++)
5522 {
5523 if (IsGhost(elements[leaf_elements[i]])) { break; } // TODO: NElements
5524
5525 int splits[3];
5526 CountSplits(leaf_elements[i], splits);
5527
5528 char ref_type = 0;
5529 for (int k = 0; k < Dim; k++)
5530 {
5531 if (splits[k] > max_level)
5532 {
5533 ref_type |= (1 << k);
5534 }
5535 }
5536
5537 if (ref_type)
5538 {
5539 if (Iso)
5540 {
5541 // iso meshes should only be modified by iso refinements
5542 ref_type = 7;
5543 }
5544 refinements.Append(Refinement(i, ref_type));
5545 }
5546 }
5547}
5548
5549void NCMesh::LimitNCLevel(int max_nc_level)
5550{
5551 MFEM_VERIFY(max_nc_level >= 1, "'max_nc_level' must be 1 or greater.");
5552
5553 while (1)
5554 {
5555 Array<Refinement> refinements;
5556 GetLimitRefinements(refinements, max_nc_level);
5557
5558 if (!refinements.Size()) { break; }
5559
5560 Refine(refinements);
5561 }
5562}
5563
5564
5565//// I/O ///////////////////////////////////////////////////////////////////////
5566
5567int NCMesh::PrintVertexParents(std::ostream *os) const
5568{
5569 if (!os)
5570 {
5571 // count vertex nodes with parents
5572 int nv = 0;
5573 for (auto node = nodes.cbegin(); node != nodes.cend(); ++node)
5574 {
5575 if (node->HasVertex() && node->p1 != node->p2) { nv++; }
5576 }
5577 return nv;
5578 }
5579 else
5580 {
5581 // print the relations
5582 for (auto node = nodes.cbegin(); node != nodes.cend(); ++node)
5583 {
5584 if (node->HasVertex() && node->p1 != node->p2)
5585 {
5586 MFEM_ASSERT(nodes[node->p1].HasVertex(), "");
5587 MFEM_ASSERT(nodes[node->p2].HasVertex(), "");
5588
5589 (*os) << node.index() << " " << node->p1 << " " << node->p2 << "\n";
5590 }
5591 }
5592 return 0;
5593 }
5594}
5595
5596void NCMesh::LoadVertexParents(std::istream &input)
5597{
5598 int nv;
5599 input >> nv;
5600 while (nv--)
5601 {
5602 int id, p1, p2;
5603 input >> id >> p1 >> p2;
5604 MFEM_VERIFY(input, "problem reading vertex parents.");
5605
5606 MFEM_VERIFY(nodes.IdExists(id), "vertex " << id << " not found.");
5607 MFEM_VERIFY(nodes.IdExists(p1), "parent " << p1 << " not found.");
5608 MFEM_VERIFY(nodes.IdExists(p2), "parent " << p2 << " not found.");
5609
5610 int check = nodes.FindId(p1, p2);
5611 MFEM_VERIFY(check < 0, "parents (" << p1 << ", " << p2 << ") already "
5612 "assigned to node " << check);
5613
5614 // assign new parents for the node
5615 nodes.Reparent(id, p1, p2);
5616 }
5617}
5618
5619int NCMesh::PrintBoundary(std::ostream *os) const
5620{
5621 static const int nfv2geom[5] =
5622 {
5625 };
5626 int deg = (Dim == 2) ? 2 : 1; // for degenerate faces in 2D
5627
5628 int count = 0;
5629 for (int i = 0; i < elements.Size(); i++)
5630 {
5631 const Element &el = elements[i];
5632 if (!el.IsLeaf()) { continue; }
5633
5634 GeomInfo& gi = GI[el.Geom()];
5635 for (int k = 0; k < gi.nf; k++)
5636 {
5637 const int* fv = gi.faces[k];
5638 const int nfv = gi.nfv[k];
5639 const Face* face = faces.Find(el.node[fv[0]], el.node[fv[1]],
5640 el.node[fv[2]], el.node[fv[3]]);
5641 MFEM_ASSERT(face != NULL, "face not found");
5642 if (face->Boundary())
5643 {
5644 if (!os) { count++; continue; }
5645
5646 (*os) << face->attribute << " " << nfv2geom[nfv];
5647 for (int j = 0; j < nfv; j++)
5648 {
5649 (*os) << " " << el.node[fv[j*deg]];
5650 }
5651 (*os) << "\n";
5652 }
5653 }
5654 }
5655 return count;
5656}
5657
5658void NCMesh::LoadBoundary(std::istream &input)
5659{
5660 int nb, attr, geom;
5661 input >> nb;
5662 for (int i = 0; i < nb; i++)
5663 {
5664 input >> attr >> geom;
5665
5666 int v1, v2, v3, v4;
5667 if (geom == Geometry::SQUARE)
5668 {
5669 input >> v1 >> v2 >> v3 >> v4;
5670 Face* face = faces.Get(v1, v2, v3, v4);
5671 face->attribute = attr;
5672 }
5673 else if (geom == Geometry::TRIANGLE)
5674 {
5675 input >> v1 >> v2 >> v3;
5676 Face* face = faces.Get(v1, v2, v3);
5677 face->attribute = attr;
5678 }
5679 else if (geom == Geometry::SEGMENT)
5680 {
5681 input >> v1 >> v2;
5682 Face* face = faces.Get(v1, v1, v2, v2);
5683 face->attribute = attr;
5684 }
5685 else if (geom == Geometry::POINT)
5686 {
5687 input >> v1;
5688 Face* face = faces.Get(v1, v1, v1, v1);
5689 face->attribute = attr;
5690 }
5691 else
5692 {
5693 MFEM_ABORT("unsupported boundary element geometry: " << geom);
5694 }
5695 }
5696}
5697
5698void NCMesh::PrintCoordinates(std::ostream &os) const
5699{
5700 int nv = coordinates.Size()/3;
5701 os << nv << "\n";
5702 if (!nv) { return; }
5703
5704 os << spaceDim << "\n";
5705 for (int i = 0; i < nv; i++)
5706 {
5707 os << coordinates[3*i];
5708 for (int j = 1; j < spaceDim; j++)
5709 {
5710 os << " " << coordinates[3*i + j];
5711 }
5712 os << "\n";
5713 }
5714}
5715
5716void NCMesh::LoadCoordinates(std::istream &input)
5717{
5718 int nv;
5719 input >> nv;
5720 if (!nv) { return; }
5721
5722 input >> spaceDim;
5723
5724 coordinates.SetSize(nv * 3);
5725 coordinates = 0.0;
5726
5727 for (int i = 0; i < nv; i++)
5728 {
5729 for (int j = 0; j < spaceDim; j++)
5730 {
5731 input >> coordinates[3*i + j];
5732 MFEM_VERIFY(input.good(), "unexpected EOF");
5733 }
5734 }
5735}
5736
5738{
5739 for (int i = 0; i < root_state.Size(); i++)
5740 {
5741 if (root_state[i]) { return false; }
5742 }
5743 return true;
5744}
5745
5746void NCMesh::Print(std::ostream &os, const std::string &comments) const
5747{
5748 os << "MFEM NC mesh v1.0\n\n";
5749
5750 if (!comments.empty()) { os << comments << "\n\n"; }
5751
5752 os <<
5753 "# NCMesh supported geometry types:\n"
5754 "# SEGMENT = 1\n"
5755 "# TRIANGLE = 2\n"
5756 "# SQUARE = 3\n"
5757 "# TETRAHEDRON = 4\n"
5758 "# CUBE = 5\n"
5759 "# PRISM = 6\n"
5760 "# PYRAMID = 7\n";
5761
5762 os << "\ndimension\n" << Dim << "\n";
5763
5764#ifndef MFEM_USE_MPI
5765 if (MyRank != 0) // don't print this section in serial: default rank is 0
5766#endif
5767 {
5768 os << "\nrank\n" << MyRank << "\n";
5769 }
5770
5771 os << "\n# rank attr geom ref_type nodes/children";
5772 os << "\nelements\n" << elements.Size() << "\n";
5773
5774 for (int i = 0; i < elements.Size(); i++)
5775 {
5776 const Element &el = elements[i];
5777 os << el.rank << " " << el.attribute << " ";
5778 if (el.parent == -2) { os << "-1\n"; continue; } // unused element
5779
5780 os << int(el.geom) << " " << int(el.ref_type);
5781 for (int j = 0; j < MaxElemNodes && el.node[j] >= 0; j++)
5782 {
5783 os << " " << el.node[j];
5784 }
5785 os << "\n";
5786 }
5787
5788 int nb = PrintBoundary(NULL);
5789 if (nb)
5790 {
5791 os << "\n# attr geom nodes";
5792 os << "\nboundary\n" << nb << "\n";
5793
5794 PrintBoundary(&os);
5795 }
5796
5797 int nvp = PrintVertexParents(NULL);
5798 if (nvp)
5799 {
5800 os << "\n# vert_id p1 p2";
5801 os << "\nvertex_parents\n" << nvp << "\n";
5802
5803 PrintVertexParents(&os);
5804 }
5805
5806 if (!ZeroRootStates()) // root_state section is optional
5807 {
5808 os << "\n# root element orientation";
5809 os << "\nroot_state\n" << root_state.Size() << "\n";
5810
5811 for (int i = 0; i < root_state.Size(); i++)
5812 {
5813 os << root_state[i] << "\n";
5814 }
5815 }
5816
5817 if (coordinates.Size())
5818 {
5819 os << "\n# top-level node coordinates";
5820 os << "\ncoordinates\n";
5821
5822 PrintCoordinates(os);
5823 }
5824 else
5825 {
5826 // 'nodes' will be printed one level up by Mesh::Printer()
5827 }
5828}
5829
5831{
5832 // initialize Element::parent
5833 for (int i = 0; i < elements.Size(); i++)
5834 {
5835 Element &el = elements[i];
5836 if (el.ref_type)
5837 {
5838 for (int j = 0; j < MaxElemChildren && el.child[j] >= 0; j++)
5839 {
5840 int child = el.child[j];
5841 MFEM_VERIFY(child < elements.Size(), "invalid mesh file: "
5842 "element " << i << " references invalid child " << child);
5843 elements[child].parent = i;
5844 }
5845 }
5846 }
5847
5848 // count the root elements
5849 int nroots = 0;
5850 while (nroots < elements.Size() &&
5851 elements[nroots].parent == -1)
5852 {
5853 nroots++;
5854 }
5855 MFEM_VERIFY(nroots, "invalid mesh file: no root elements found.");
5856
5857 // check that only the first 'nroot' elements are roots (have no parent)
5858 for (int i = nroots; i < elements.Size(); i++)
5859 {
5860 MFEM_VERIFY(elements[i].parent != -1,
5861 "invalid mesh file: only the first M elements can be roots. "
5862 "Found element " << i << " with no parent.");
5863 }
5864
5865 // default root state is zero
5866 root_state.SetSize(nroots);
5867 root_state = 0;
5868}
5869
5871{
5872 int ntop = 0;
5873 for (auto node = nodes.cbegin(); node != nodes.cend(); ++node)
5874 {
5875 if (node->p1 == node->p2) { ntop = node.index() + 1; }
5876 }
5877 return ntop;
5878}
5879
5880NCMesh::NCMesh(std::istream &input, int version, int &curved, int &is_nc)
5881 : spaceDim(0), MyRank(0), Iso(true), Legacy(false)
5882{
5883 is_nc = 1;
5884 if (version == 1) // old MFEM mesh v1.1 format
5885 {
5886 LoadLegacyFormat(input, curved, is_nc);
5887 Legacy = true;
5888 return;
5889 }
5890
5891 MFEM_ASSERT(version == 10, "");
5892 std::string ident;
5893 int count;
5894
5895 // load dimension
5896 skip_comment_lines(input, '#');
5897 input >> ident;
5898 MFEM_VERIFY(ident == "dimension", "Invalid mesh file: " << ident);
5899 input >> Dim;
5900
5901 // load rank, if present
5902 skip_comment_lines(input, '#');
5903 input >> ident;
5904 if (ident == "rank")
5905 {
5906 input >> MyRank;
5907 MFEM_VERIFY(MyRank >= 0, "Invalid rank");
5908
5909 skip_comment_lines(input, '#');
5910 input >> ident;
5911 }
5912
5913 // load file SFC version, if present (for future changes to SFC ordering)
5914 if (ident == "sfc_version")
5915 {
5916 int sfc_version; // TODO future: store as class member
5917 input >> sfc_version;
5918 MFEM_VERIFY(sfc_version == 0,
5919 "Unsupported mesh file SFC version (" << sfc_version << "). "
5920 "Please update MFEM.");
5921
5922 skip_comment_lines(input, '#');
5923 input >> ident;
5924 }
5925
5926 // load elements
5927 MFEM_VERIFY(ident == "elements", "Invalid mesh file: " << ident);
5928 input >> count;
5929 for (int i = 0; i < count; i++)
5930 {
5931 int rank, attr, geom, ref_type;
5932 input >> rank >> attr >> geom;
5933
5934 Geometry::Type type = Geometry::Type(geom);
5935 elements.Append(Element(type, attr));
5936
5937 MFEM_ASSERT(elements.Size() == i+1, "");
5938 Element &el = elements[i];
5939 el.rank = rank;
5940
5941 if (geom >= 0)
5942 {
5943 CheckSupportedGeom(type);
5944 GI[geom].InitGeom(type);
5945
5946 input >> ref_type;
5947 MFEM_VERIFY(ref_type >= 0 && ref_type < 8, "");
5948 el.ref_type = ref_type;
5949
5950 if (ref_type) // refined element
5951 {
5952 for (int j = 0; j < ref_type_num_children[ref_type]; j++)
5953 {
5954 input >> el.child[j];
5955 }
5956 if (Dim == 3 && ref_type != 7) { Iso = false; }
5957 }
5958 else // leaf element
5959 {
5960 for (int j = 0; j < GI[geom].nv; j++)
5961 {
5962 int id;
5963 input >> id;
5964 el.node[j] = id;
5965 nodes.Alloc(id, id, id);
5966 // NOTE: nodes that won't get parents assigned will stay hashed
5967 // with p1 == p2 == id (top-level nodes)
5968 }
5969 }
5970 }
5971 else
5972 {
5973 el.parent = -2; // mark as unused
5975 }
5976 }
5977
5979 InitGeomFlags();
5980
5981 skip_comment_lines(input, '#');
5982 input >> ident;
5983
5984 // load boundary
5985 if (ident == "boundary")
5986 {
5987 LoadBoundary(input);
5988
5989 skip_comment_lines(input, '#');
5990 input >> ident;
5991 }
5992
5993 // load vertex hierarchy
5994 if (ident == "vertex_parents")
5995 {
5996 LoadVertexParents(input);
5997
5998 skip_comment_lines(input, '#');
5999 input >> ident;
6000 }
6001
6002 // load root states
6003 if (ident == "root_state")
6004 {
6005 input >> count;
6006 MFEM_VERIFY(count <= root_state.Size(), "Too many root states");
6007 for (int i = 0; i < count; i++)
6008 {
6009 input >> root_state[i];
6010 }
6011
6012 skip_comment_lines(input, '#');
6013 input >> ident;
6014 }
6015
6016 // load coordinates or nodes
6017 if (ident == "coordinates")
6018 {
6019 LoadCoordinates(input);
6020
6021 MFEM_VERIFY(coordinates.Size()/3 >= CountTopLevelNodes(),
6022 "Invalid mesh file: not all top-level nodes are covered by "
6023 "the 'coordinates' section of the mesh file.");
6024 curved = 0;
6025 }
6026 else if (ident == "nodes")
6027 {
6028 coordinates.SetSize(0); // this means the mesh is curved
6029
6030 // prepare to read the nodes
6031 input >> std::ws;
6032 curved = 1;
6033 }
6034 else
6035 {
6036 MFEM_ABORT("Invalid mesh file: either 'coordinates' or "
6037 "'nodes' must be present");
6038 }
6039
6040 // create edge nodes and faces
6041 nodes.UpdateUnused();
6042 for (int i = 0; i < elements.Size(); i++)
6043 {
6044 if (elements[i].IsLeaf())
6045 {
6047 RegisterFaces(i);
6048 }
6049 }
6050
6051 Update();
6052}
6053
6055 const BlockArray<Element> &tmp_elements)
6056{
6057 Element &el = elements[elem];
6058 if (el.ref_type)
6059 {
6060 for (int i = 0; i < MaxElemChildren && el.child[i] >= 0; i++)
6061 {
6062 int old_id = el.child[i];
6063 // here we know 'free_element_ids' is empty
6064 int new_id = elements.Append(tmp_elements[old_id]);
6065 el.child[i] = new_id;
6066 elements[new_id].parent = elem;
6067 CopyElements(new_id, tmp_elements);
6068 }
6069 }
6070}
6071
6072void NCMesh::LoadCoarseElements(std::istream &input)
6073{
6074 int ne;
6075 input >> ne;
6076
6077 bool iso = true;
6078
6079 // load the coarse elements
6080 while (ne--)
6081 {
6082 int ref_type;
6083 input >> ref_type;
6084
6085 int elem = AddElement(Element(Geometry::INVALID, 0));
6086 Element &el = elements[elem];
6087 el.ref_type = ref_type;
6088
6089 if (Dim == 3 && ref_type != 7) { iso = false; }
6090
6091 // load child IDs and make parent-child links
6092 int nch = ref_type_num_children[ref_type];
6093 for (int i = 0, id; i < nch; i++)
6094 {
6095 input >> id;
6096 MFEM_VERIFY(id >= 0, "");
6097 MFEM_VERIFY(id < elements.Size(),
6098 "coarse element cannot be referenced before it is "
6099 "defined (id=" << id << ").");
6100
6101 Element &child = elements[id];
6102 MFEM_VERIFY(child.parent == -1,
6103 "element " << id << " cannot have two parents.");
6104
6105 el.child[i] = id;
6106 child.parent = elem;
6107
6108 if (!i) // copy geom and attribute from first child
6109 {
6110 el.geom = child.geom;
6111 el.attribute = child.attribute;
6112 }
6113 }
6114 }
6115
6116 // prepare for reordering the elements
6117 BlockArray<Element> tmp_elements;
6118 elements.Swap(tmp_elements);
6119
6120 // copy roots, they need to be at the beginning of 'elements'
6121 int root_count = 0;
6122 for (auto el = tmp_elements.begin(); el != tmp_elements.end(); ++el)
6123 {
6124 if (el->parent == -1)
6125 {
6126 elements.Append(*el); // same as AddElement()
6127 root_count++;
6128 }
6129 }
6130
6131 // copy the rest of the hierarchy
6132 for (int i = 0; i < root_count; i++)
6133 {
6134 CopyElements(i, tmp_elements);
6135 }
6136
6137 // set the Iso flag (must be false if there are 3D aniso refinements)
6138 Iso = iso;
6139
6140 InitRootState(root_count);
6141}
6142
6143void NCMesh::LoadLegacyFormat(std::istream &input, int &curved, int &is_nc)
6144{
6145 MFEM_ASSERT(elements.Size() == 0, "");
6146 MFEM_ASSERT(nodes.Size() == 0, "");
6147 MFEM_ASSERT(free_element_ids.Size() == 0, "");
6148
6149 std::string ident;
6150 int count, attr, geom;
6151
6152 // load dimension
6153 skip_comment_lines(input, '#');
6154 input >> ident;
6155 MFEM_VERIFY(ident == "dimension", "invalid mesh file");
6156 input >> Dim;
6157
6158 // load elements
6159 skip_comment_lines(input, '#');
6160 input >> ident;
6161 MFEM_VERIFY(ident == "elements", "invalid mesh file");
6162
6163 input >> count;
6164 for (int i = 0; i < count; i++)
6165 {
6166 input >> attr >> geom;
6167
6168 Geometry::Type type = Geometry::Type(geom);
6169 CheckSupportedGeom(type);
6170 GI[geom].InitGeom(type);
6171
6172 int eid = AddElement(Element(type, attr));
6173 MFEM_ASSERT(eid == i, "");
6174
6175 Element &el = elements[eid];
6176 for (int j = 0; j < GI[geom].nv; j++)
6177 {
6178 int id;
6179 input >> id;
6180 el.node[j] = id;
6181 nodes.Alloc(id, id, id); // see comment in NCMesh::NCMesh
6182 }
6183 el.index = i; // needed for file leaf order below
6184 }
6185
6186 // load boundary
6187 skip_comment_lines(input, '#');
6188 input >> ident;
6189 MFEM_VERIFY(ident == "boundary", "invalid mesh file");
6190
6191 LoadBoundary(input);
6192
6193 // load vertex hierarchy
6194 skip_comment_lines(input, '#');
6195 input >> ident;
6196 if (ident == "vertex_parents")
6197 {
6198 LoadVertexParents(input);
6199 is_nc = 1;
6200
6201 skip_comment_lines(input, '#');
6202 input >> ident;
6203 }
6204 else
6205 {
6206 // no "vertex_parents" section: this file needs to be treated as a
6207 // conforming mesh for complete backward compatibility with MFEM 4.2
6208 is_nc = 0;
6209 }
6210
6211 // load element hierarchy
6212 if (ident == "coarse_elements")
6213 {
6214 LoadCoarseElements(input);
6215
6216 skip_comment_lines(input, '#');
6217 input >> ident;
6218 }
6219 else
6220 {
6221 // no element hierarchy -> all elements are roots
6222 InitRootState(elements.Size());
6223 }
6224 InitGeomFlags();
6225
6226 // load vertices
6227 MFEM_VERIFY(ident == "vertices", "invalid mesh file");
6228 int nvert;
6229 input >> nvert;
6230 input >> std::ws >> ident;
6231 if (ident != "nodes")
6232 {
6233 spaceDim = atoi(ident.c_str());
6234
6235 coordinates.SetSize(3*nvert);
6236 coordinates = 0.0;
6237
6238 for (int i = 0; i < nvert; i++)
6239 {
6240 for (int j = 0; j < spaceDim; j++)
6241 {
6242 input >> coordinates[3*i + j];
6243 MFEM_VERIFY(input.good(), "unexpected EOF");
6244 }
6245 }
6246
6247 // truncate extra coordinates (legacy vertices section is longer)
6248 int ntop = CountTopLevelNodes();
6249 if (3*ntop < coordinates.Size())
6250 {
6251 coordinates.SetSize(3*ntop);
6252 }
6253 }
6254 else
6255 {
6257
6258 // prepare to read the nodes
6259 input >> std::ws;
6260 curved = 1;
6261 }
6262
6263 // create edge nodes and faces
6264 nodes.UpdateUnused();
6265 int leaf_count = 0;
6266 for (int i = 0; i < elements.Size(); i++)
6267 {
6268 if (elements[i].IsLeaf())
6269 {
6271 RegisterFaces(i);
6272 leaf_count++;
6273 }
6274 }
6275
6276 // v1.1 honors file leaf order on load, prepare legacy 'leaf_elements'
6277 Array<int> file_leaf_elements(leaf_count);
6278 file_leaf_elements = -1;
6279 for (int i = 0; i < elements.Size(); i++)
6280 {
6281 if (elements[i].IsLeaf())
6282 {
6283 file_leaf_elements[elements[i].index] = i;
6284 }
6285 }
6286 MFEM_ASSERT(file_leaf_elements.Min() >= 0, "");
6287
6288 Update();
6289
6290 // force file leaf order
6291 Swap(leaf_elements, file_leaf_elements);
6292
6293 // make sure Mesh::NVertices is equal to "nvert" from the file (in case of
6294 // unused vertices), see also GetMeshComponents
6295 if (nvert > vertex_nodeId.Size())
6296 {
6297 vertex_nodeId.SetSize(nvert, -1);
6298 }
6299}
6300
6302{
6303 order.SetSize(NVertices);
6304 order = -1;
6305
6306 int count = 0;
6307 for (auto node = nodes.cbegin(); node != nodes.cend(); ++node)
6308 {
6309 if (node->HasVertex())
6310 {
6311 MFEM_ASSERT(node.index() >= 0, "");
6312 MFEM_ASSERT(node.index() < order.Size(), "");
6313 MFEM_ASSERT(order[node.index()] == -1, "");
6314
6315 order[node.index()] = node->vert_index;
6316 count++;
6317 }
6318 }
6319 MFEM_ASSERT(count == order.Size(), "");
6320 MFEM_CONTRACT_VAR(count);
6321}
6322
6323
6324////////////////////////////////////////////////////////////////////////////////
6325
6327{
6329 face_list.Clear();
6330 edge_list.Clear();
6331
6334
6336
6337 // TODO future: consider trimming unused blocks at the end of 'elements' and
6338 // maybe also of 'nodes' and 'faces'.
6339}
6340
6342{
6343 int pm_size = 0;
6344 for (int i = 0; i < Geometry::NumGeom; i++)
6345 {
6346 for (int j = 0; j < point_matrices[i].Size(); i++)
6347 {
6348 pm_size += point_matrices[i][j]->MemoryUsage();
6349 }
6350 pm_size += point_matrices[i].MemoryUsage();
6351 }
6352
6353 return conforming.MemoryUsage() +
6354 masters.MemoryUsage() +
6355 slaves.MemoryUsage() +
6356 pm_size;
6357}
6358
6360{
6361 long mem = embeddings.MemoryUsage();
6362 for (int i = 0; i < Geometry::NumGeom; i++)
6363 {
6364 mem += point_matrices[i].MemoryUsage();
6365 }
6366 return mem;
6367}
6368
6370{
6371 return nodes.MemoryUsage() +
6372 faces.MemoryUsage() +
6373 elements.MemoryUsage() +
6385 ref_stack.MemoryUsage() +
6389 sizeof(*this);
6390}
6391
6393{
6394 nodes.PrintMemoryDetail(); mfem::out << " nodes\n";
6395 faces.PrintMemoryDetail(); mfem::out << " faces\n";
6396
6397 mfem::out << elements.MemoryUsage() << " elements\n"
6398 << free_element_ids.MemoryUsage() << " free_element_ids\n"
6399 << root_state.MemoryUsage() << " root_state\n"
6400 << coordinates.MemoryUsage() << " top_vertex_pos\n"
6401 << leaf_elements.MemoryUsage() << " leaf_elements\n"
6402 << leaf_sfc_index.MemoryUsage() << " leaf_sfc_index\n"
6403 << vertex_nodeId.MemoryUsage() << " vertex_nodeId\n"
6404 << face_list.MemoryUsage() << " face_list\n"
6405 << edge_list.MemoryUsage() << " edge_list\n"
6406 << vertex_list.MemoryUsage() << " vertex_list\n"
6407 << boundary_faces.MemoryUsage() << " boundary_faces\n"
6408 << element_vertex.MemoryUsage() << " element_vertex\n"
6409 << ref_stack.MemoryUsage() << " ref_stack\n"
6410 << derefinements.MemoryUsage() << " derefinements\n"
6411 << transforms.MemoryUsage() << " transforms\n"
6412 << coarse_elements.MemoryUsage() << " coarse_elements\n"
6413 << sizeof(*this) << " NCMesh"
6414 << std::endl;
6415
6416 return elements.Size() - free_element_ids.Size();
6417}
6418
6419#ifdef MFEM_DEBUG
6420void NCMesh::DebugLeafOrder(std::ostream &os) const
6421{
6422 tmp_vertex = new TmpVertex[nodes.NumIds()];
6423 for (int i = 0; i < leaf_elements.Size(); i++)
6424 {
6425 const Element* elem = &elements[leaf_elements[i]];
6426 for (int j = 0; j < Dim; j++)
6427 {
6428 real_t sum = 0.0;
6429 int count = 0;
6430 for (int k = 0; k < MaxElemNodes; k++)
6431 {
6432 if (elem->node[k] >= 0)
6433 {
6434 sum += CalcVertexPos(elem->node[k])[j];
6435 count++;
6436 }
6437 }
6438 os << sum / count << " ";
6439 }
6440 os << "\n";
6441 }
6442 delete [] tmp_vertex;
6443}
6444
6445void NCMesh::DebugDump(std::ostream &os) const
6446{
6447 // dump nodes
6448 tmp_vertex = new TmpVertex[nodes.NumIds()];
6449 os << nodes.Size() << "\n";
6450 for (auto node = nodes.cbegin(); node != nodes.cend(); ++node)
6451 {
6452 const real_t *pos = CalcVertexPos(node.index());
6453 os << node.index() << " "
6454 << pos[0] << " " << pos[1] << " " << pos[2] << " "
6455 << node->p1 << " " << node->p2 << " "
6456 << node->vert_index << " " << node->edge_index << " "
6457 << 0 << "\n";
6458 }
6459 delete [] tmp_vertex;
6460 os << "\n";
6461
6462 // dump elements
6463 int nleaves = 0;
6464 for (int i = 0; i < elements.Size(); i++)
6465 {
6466 if (elements[i].IsLeaf()) { nleaves++; }
6467 }
6468 os << nleaves << "\n";
6469 for (int i = 0; i < elements.Size(); i++)
6470 {
6471 const Element &el = elements[i];
6472 if (el.IsLeaf())
6473 {
6474 const GeomInfo& gi = GI[el.Geom()];
6475 os << gi.nv << " ";
6476 for (int j = 0; j < gi.nv; j++)
6477 {
6478 os << el.node[j] << " ";
6479 }
6480 os << el.attribute << " " << el.rank << " " << i << "\n";
6481 }
6482 }
6483 os << "\n";
6484
6485 // dump faces
6486 os << faces.Size() << "\n";
6487 for (const auto &face : faces)
6488 {
6489 int elem = face.elem[0];
6490 if (elem < 0) { elem = face.elem[1]; }
6491 MFEM_ASSERT(elem >= 0, "");
6492 const Element &el = elements[elem];
6493
6494 int lf = find_local_face(el.Geom(),
6495 find_node(el, face.p1),
6496 find_node(el, face.p2),
6497 find_node(el, face.p3));
6498
6499 const int* fv = GI[el.Geom()].faces[lf];
6500 const int nfv = GI[el.Geom()].nfv[lf];
6501
6502 os << nfv;
6503 for (int i = 0; i < nfv; i++)
6504 {
6505 os << " " << el.node[fv[i]];
6506 }
6507 //os << " # face " << face.index() << ", index " << face.index << "\n";
6508 os << "\n";
6509 }
6510}
6511#endif
6512
6513} // namespace mfem
void Sort()
Sorts the array in ascending order. This requires operator< to be defined for T.
Definition array.hpp:261
void Reserve(int capacity)
Ensures that the allocated size is at least the given size.
Definition array.hpp:162
void SetSize(int nsize)
Change the logical size of the array, keep existing entries.
Definition array.hpp:697
T Min() const
Find the minimal element in the array, using the comparison operator < for class T.
Definition array.cpp:85
int Size() const
Return the logical size of the array.
Definition array.hpp:144
void DeleteAll()
Delete the whole array.
Definition array.hpp:864
int Append(const T &el)
Append element 'el' to array, resize if necessary.
Definition array.hpp:769
T * GetData()
Returns the data.
Definition array.hpp:118
void Copy(Array &copy) const
Create a copy of the internal array to the provided copy.
Definition array.hpp:874
void Unique()
Removes duplicities from a sorted array. This requires operator== to be defined for T.
Definition array.hpp:269
T * end()
STL-like end. Returns pointer after the last element of the array.
Definition array.hpp:305
T * begin()
STL-like begin. Returns pointer to the first element of the array.
Definition array.hpp:302
std::size_t MemoryUsage() const
Returns the number of bytes allocated for the array including any reserve.
Definition array.hpp:314
iterator begin()
Definition array.hpp:614
void Swap(BlockArray< T > &other)
Definition array.hpp:1062
iterator end()
Definition array.hpp:615
Data type dense matrix using column-major storage.
Definition densemat.hpp:24
void SetSize(int s)
Change the size of the DenseMatrix to s x s.
Definition densemat.hpp:105
void SetSize(int i, int j, int k, MemoryType mt_=MemoryType::PRESERVE)
Abstract data type element.
Definition element.hpp:29
virtual MFEM_DEPRECATED int GetNFaces(int &nFaceVertices) const =0
virtual const int * GetFaceVertices(int fi) const =0
Geometry::Type GetGeometryType() const
Definition element.hpp:52
virtual void GetVertices(Array< int > &v) const =0
Get the indices defining the vertices.
virtual int GetNFaceVertices(int fi) const =0
void SetAttribute(const int attr)
Set element's attribute.
Definition element.hpp:58
virtual Type GetType() const =0
Returns element's type.
virtual int GetNEdges() const =0
virtual const int * GetEdgeVertices(int) const =0
int GetAttribute() const
Return element's attribute.
Definition element.hpp:55
virtual int GetNVertices() const =0
static const int NumGeom
Definition geom.hpp:42
Data type hexahedron element.
Class used by MFEM to store pointers to host and/or device memory.
Mesh data type.
Definition mesh.hpp:56
Array< Vertex > vertices
Definition mesh.hpp:97
Element * NewElement(int geom)
Definition mesh.cpp:4401
int GetNEdges() const
Return the number of edges.
Definition mesh.hpp:1232
int GetNumFaces() const
Return the number of faces (3D), edges (2D) or vertices (1D).
Definition mesh.cpp:6250
const Element * GetElement(int i) const
Return pointer to the i'th element object.
Definition mesh.hpp:1283
int GetNE() const
Returns number of elements.
Definition mesh.hpp:1226
const Element * GetFace(int i) const
Return pointer to the i'th face element object.
Definition mesh.hpp:1310
int Dimension() const
Dimension of the reference space used within the elements.
Definition mesh.hpp:1160
const Element * GetBdrElement(int i) const
Return pointer to the i'th boundary element object.
Definition mesh.hpp:1298
int SpaceDimension() const
Dimension of the physical space containing the mesh.
Definition mesh.hpp:1163
int GetNV() const
Returns number of vertices. Vertices are only at the corners of elements, where you would expect them...
Definition mesh.hpp:1223
GridFunction * Nodes
Definition mesh.hpp:252
Array< Triple< int, int, int > > tmp_vertex_parents
Definition mesh.hpp:266
int GetNBE() const
Returns number of boundary elements.
Definition mesh.hpp:1229
void FreeElement(Element *E)
Definition mesh.cpp:13056
Array< Element * > boundary
Definition mesh.hpp:98
Table * GetEdgeVertexTable() const
Definition mesh.cpp:7106
Array< Element * > elements
Definition mesh.hpp:92
const real_t * GetVertex(int i) const
Return pointer to vertex i's coordinates.
Definition mesh.hpp:1265
A class for non-conforming AMR. The class is not used directly by the user, rather it is an extension...
Definition ncmesh.hpp:123
int GetEdgeMaster(int v1, int v2) const
Definition ncmesh.cpp:5208
static PointMatrix pm_tet_identity
Definition ncmesh.hpp:1047
void OnMeshUpdated(Mesh *mesh)
Definition ncmesh.cpp:2590
void GetElementFacesAttributes(int i, Array< int > &faces, Array< int > &fattr) const
Return the faces and face attributes of leaf element 'i'.
Definition ncmesh.cpp:5243
static GeomInfo GI[Geometry::NumGeom]
Definition ncmesh.hpp:1191
virtual void Update()
Definition ncmesh.cpp:268
void FindNeighbors(int elem, Array< int > &neighbors, const Array< int > *search_set=NULL)
Definition ncmesh.cpp:3841
void FreeElement(int id)
Definition ncmesh.hpp:700
virtual void Trim()
Save memory by releasing all non-essential and cached data.
Definition ncmesh.cpp:6326
virtual void ElementSharesVertex(int elem, int local, int vnode)
Definition ncmesh.hpp:881
HashTable< Node > shadow
temporary storage for reparented nodes
Definition ncmesh.hpp:674
void SetDerefMatrixCodes(int parent, Array< int > &fine_coarse)
Definition ncmesh.cpp:2064
void CopyElements(int elem, const BlockArray< Element > &tmp_elements)
Definition ncmesh.cpp:6054
Array< Refinement > ref_stack
stack of scheduled refinements (temporary)
Definition ncmesh.hpp:673
int QuadFaceSplitType(int n1, int n2, int n3, int n4, int mid[5]=NULL) const
Given a quad face defined by four vertices, establish which edges of this face have been split,...
Definition ncmesh.cpp:2735
mfem::Element * NewMeshElement(int geom) const
Definition ncmesh.cpp:2423
int NewTriangle(int n0, int n1, int n2, int attr, int eattr0, int eattr1, int eattr2)
Definition ncmesh.cpp:646
void GetMeshComponents(Mesh &mesh) const
Fill Mesh::{vertices,elements,boundary} for the current finest level.
Definition ncmesh.cpp:2463
int NGhostElements
Definition ncmesh.hpp:602
void LoadCoarseElements(std::istream &input)
Load the element refinement hierarchy from a legacy mesh file.
Definition ncmesh.cpp:6072
void NeighborExpand(const Array< int > &elems, Array< int > &expanded, const Array< int > *search_set=NULL)
Definition ncmesh.cpp:3924
HashTable< Node > nodes
Definition ncmesh.hpp:572
int PrintVertexParents(std::ostream *out) const
Print the "vertex_parents" section of the mesh file.
Definition ncmesh.cpp:5567
static PointMatrix pm_prism_identity
Definition ncmesh.hpp:1048
int NewSegment(int n0, int n1, int attr, int vattr1, int vattr2)
Definition ncmesh.cpp:672
static int find_node(const Element &el, int node)
Definition ncmesh.cpp:2800
void DeleteUnusedFaces(const Array< int > &elemFaces)
Definition ncmesh.cpp:440
const CoarseFineTransformations & GetRefinementTransforms() const
Definition ncmesh.cpp:4674
void BuildElementToVertexTable()
Definition ncmesh.cpp:3650
void TraverseEdge(int vn0, int vn1, real_t t0, real_t t1, int flags, int level, MatrixMap &matrix_map)
Definition ncmesh.cpp:3293
static int find_element_edge(const Element &el, int vn0, int vn1, bool abort=true)
Definition ncmesh.cpp:2820
Array< int > free_element_ids
Definition ncmesh.hpp:576
void LoadBoundary(std::istream &input)
Load the "boundary" section of the mesh file.
Definition ncmesh.cpp:5658
virtual void ElementSharesFace(int elem, int local, int face)
Definition ncmesh.hpp:879
int PrintMemoryDetail() const
Definition ncmesh.cpp:6392
int NewHexahedron(int n0, int n1, int n2, int n3, int n4, int n5, int n6, int n7, int attr, int fattr0, int fattr1, int fattr2, int fattr3, int fattr4, int fattr5)
Definition ncmesh.cpp:503
HashTable< Face > faces
Definition ncmesh.hpp:573
bool HaveTets() const
Return true if the mesh contains tetrahedral elements.
Definition ncmesh.hpp:665
static const int MaxElemChildren
Number of children of an element can have.
Definition ncmesh.hpp:494
void GetEdgeVertices(const MeshId &edge_id, int vert_index[2], bool oriented=true) const
Return Mesh vertex indices of an edge identified by 'edge_id'.
Definition ncmesh.cpp:5109
static PointMatrix pm_seg_identity
Definition ncmesh.hpp:1044
static const int MaxElemEdges
Number of edges of an element can have.
Definition ncmesh.hpp:490
bool QuadFaceIsMaster(int n1, int n2, int n3, int n4) const
Determine if a Quad face is a master face.
Definition ncmesh.hpp:803
Array< int > boundary_faces
subset of all faces, set by BuildFaceList
Definition ncmesh.hpp:612
friend struct MatrixMap
Definition ncmesh.hpp:1200
static PointMatrix pm_quad_identity
Definition ncmesh.hpp:1046
void ReferenceElement(int elem)
Definition ncmesh.cpp:352
void UpdateElementToVertexTable()
Definition ncmesh.hpp:918
void ReparentNode(int node, int new_p1, int new_p2)
Definition ncmesh.cpp:305
BlockArray< Element > elements
Definition ncmesh.hpp:575
Array< int > coarse_elements
state of leaf_elements before Refine(), set by MarkCoarseLevel()
Definition ncmesh.hpp:1066
const CoarseFineTransformations & GetDerefinementTransforms() const
Definition ncmesh.cpp:4725
Table derefinements
possible derefinements, see GetDerefinementTable
Definition ncmesh.hpp:677
int FindNodeExt(const Element &el, int node, bool abort=true)
Extended version of find_node: works if 'el' is refined.
Definition ncmesh.cpp:2810
int ReorderFacePointMat(int v0, int v1, int v2, int v3, int elem, const PointMatrix &pm, PointMatrix &reordered) const
Definition ncmesh.cpp:2945
int FindMidEdgeNode(int node1, int node2) const
Definition ncmesh.cpp:321
friend struct PointMatrixHash
Definition ncmesh.hpp:1201
Array< char > face_geom
face geometry by face index, set by OnMeshUpdated
Definition ncmesh.hpp:613
int GetEdgeNCOrientation(const MeshId &edge_id) const
Definition ncmesh.cpp:5128
Table element_vertex
leaf-element to vertex table, see FindSetNeighbors
Definition ncmesh.hpp:615
Array< Triple< int, int, int > > reparents
scheduled node reparents (tmp)
Definition ncmesh.hpp:675
virtual void BuildFaceList()
Definition ncmesh.cpp:3198
void CheckAnisoPrism(int vn1, int vn2, int vn3, int vn4, const Refinement *refs, int nref)
Definition ncmesh.cpp:817
void LoadVertexParents(std::istream &input)
Load the vertex parent hierarchy from a mesh file.
Definition ncmesh.cpp:5596
TmpVertex * tmp_vertex
Definition ncmesh.hpp:1079
static void GridSfcOrdering3D(int width, int height, int depth, Array< int > &coords)
Definition ncmesh.cpp:5077
Array< int > leaf_elements
finest elements, in Mesh ordering (+ ghosts)
Definition ncmesh.hpp:604
const real_t * CalcVertexPos(int node) const
Definition ncmesh.cpp:2438
void CollectLeafElements(int elem, int state, Array< int > &ghosts, int &counter)
Definition ncmesh.cpp:2083
int CountTopLevelNodes() const
Return the index of the last top-level node plus one.
Definition ncmesh.cpp:5870
void QuadFaceSplitLevel(int vn1, int vn2, int vn3, int vn4, int &h_level, int &v_level) const
Computes the number of horizontal and vertical splits of this quad that have occurred in the NCMesh....
Definition ncmesh.cpp:5395
int Geoms
bit mask of element geometries present, see InitGeomFlags()
Definition ncmesh.hpp:485
void InitDerefTransforms()
Definition ncmesh.cpp:2045
int GetElementSizeReduction(int i) const
Definition ncmesh.cpp:5229
virtual void LimitNCLevel(int max_nc_level)
Definition ncmesh.cpp:5549
int NGhostVertices
Definition ncmesh.hpp:602
bool ZeroRootStates() const
Return true if all root_states are zero.
Definition ncmesh.cpp:5737
NCMesh(const Mesh *mesh)
Definition ncmesh.cpp:135
Array< int > vertex_nodeId
vertex-index to node-id map, see UpdateVertices
Definition ncmesh.hpp:606
int RetrieveNode(const Element &el, int index)
Return el.node[index] correctly, even if the element is refined.
Definition ncmesh.cpp:1696
void FindEdgeElements(int vn1, int vn2, int vn3, int vn4, Array< MeshId > &prisms) const
Definition ncmesh.cpp:770
virtual ~NCMesh()
Definition ncmesh.cpp:280
void CollectDerefinements(int elem, Array< Connection > &list)
Definition ncmesh.cpp:1932
static const int MaxElemFaces
Number of faces of an element can have.
Definition ncmesh.hpp:492
virtual void ElementSharesEdge(int elem, int local, int enode)
Definition ncmesh.hpp:880
int GetFaceVerticesEdges(const MeshId &face_id, int vert_index[4], int edge_index[4], int edge_orientation[4]) const
Definition ncmesh.cpp:5140
void RefineElement(int elem, char ref_type)
Definition ncmesh.cpp:954
void InitRootState(int root_count)
Definition ncmesh.cpp:2359
int NewQuadrilateral(int n0, int n1, int n2, int n3, int attr, int eattr0, int eattr1, int eattr2, int eattr3)
Definition ncmesh.cpp:620
Array< int > leaf_sfc_index
natural tree ordering of leaf elements
Definition ncmesh.hpp:605
void ForceRefinement(int vn1, int vn2, int vn3, int vn4)
Definition ncmesh.cpp:712
void CollectEdgeVertices(int v0, int v1, Array< int > &indices)
Definition ncmesh.cpp:3580
NCList edge_list
lazy-initialized list of edges, see GetEdgeList
Definition ncmesh.hpp:609
static PointMatrix pm_pyramid_identity
Definition ncmesh.hpp:1049
Array< int > root_state
Definition ncmesh.hpp:581
const NCList & GetFaceList()
Return the current list of conforming and nonconforming faces.
Definition ncmesh.hpp:298
void CollectTriFaceVertices(int v0, int v1, int v2, Array< int > &indices)
Definition ncmesh.cpp:3592
void InitRootElements()
Count root elements and initialize root_state.
Definition ncmesh.cpp:5830
int GetMidEdgeNode(int node1, int node2)
Definition ncmesh.cpp:337
virtual void BuildEdgeList()
Definition ncmesh.cpp:3321
void DebugDump(std::ostream &out) const
Definition ncmesh.cpp:6445
void DebugLeafOrder(std::ostream &out) const
Definition ncmesh.cpp:6420
int GetElementDepth(int i) const
Return the distance of leaf 'i' from the root.
Definition ncmesh.cpp:5217
void TraverseRefinements(int elem, int coarse_index, std::string &ref_path, RefPathMap &map) const
Definition ncmesh.cpp:4640
void CheckIsoFace(int vn1, int vn2, int vn3, int vn4, int en1, int en2, int en3, int en4, int midf)
Definition ncmesh.cpp:937
void GetLimitRefinements(Array< Refinement > &refinements, int max_level)
Definition ncmesh.cpp:5519
int NewPyramid(int n0, int n1, int n2, int n3, int n4, int attr, int fattr0, int fattr1, int fattr2, int fattr3, int fattr4)
Definition ncmesh.cpp:590
virtual void Derefine(const Array< int > &derefs)
Definition ncmesh.cpp:2009
bool Iso
true if the mesh only contains isotropic refinements
Definition ncmesh.hpp:484
virtual void GetBoundaryClosure(const Array< int > &bdr_attr_is_ess, Array< int > &bdr_vertices, Array< int > &bdr_edges, Array< int > &bdr_faces)
Get a list of vertices (2D/3D), edges (3D) and faces (3D) that coincide with boundary elements with t...
Definition ncmesh.cpp:5286
bool TriFaceSplit(int n1, int n2, int n3, int mid[3]=NULL) const
Given a tri face defined by three vertices, establish whether the edges that make up this face have b...
Definition ncmesh.cpp:2782
bool Legacy
true if the mesh was loaded from the legacy v1.1 format
Definition ncmesh.hpp:486
virtual void BuildVertexList()
Definition ncmesh.cpp:3433
static const PointMatrix & GetGeomIdentity(Geometry::Type geom)
Definition ncmesh.cpp:4127
void CheckAnisoFace(int vn1, int vn2, int vn3, int vn4, int mid12, int mid34, int level=0)
Definition ncmesh.cpp:842
int NewWedge(int n0, int n1, int n2, int n3, int n4, int n5, int attr, int fattr0, int fattr1, int fattr2, int fattr3, int fattr4)
Definition ncmesh.cpp:533
int GetVertexRootCoord(int elem, RefCoord coord[3]) const
Definition ncmesh.cpp:3983
Array< real_t > coordinates
Definition ncmesh.hpp:585
void FindVertexCousins(int elem, int local, Array< int > &cousins) const
Definition ncmesh.cpp:4059
Face * GetFace(Element &elem, int face_no)
Definition ncmesh.cpp:465
std::map< std::string, int > RefPathMap
Definition ncmesh.hpp:1057
bool IsGhost(const Element &el) const
Return true if the Element el is a ghost element.
Definition ncmesh.hpp:668
const NCList & GetEdgeList()
Return the current list of conforming and nonconforming edges.
Definition ncmesh.hpp:305
bool TriFaceIsMaster(int n1, int n2, int n3) const
Determine if a Triangle face is a master face.
Definition ncmesh.hpp:783
int MyRank
used in parallel, or when loading a parallel file in serial
Definition ncmesh.hpp:483
void Print(std::ostream &out, const std::string &comments="") const
Definition ncmesh.cpp:5746
int AddElement(const Element &el)
Definition ncmesh.hpp:687
void CountSplits(int elem, int splits[3]) const
Definition ncmesh.cpp:5429
int spaceDim
dimensions of the elements and the vertex coordinates
Definition ncmesh.hpp:482
void UnreferenceElement(int elem, Array< int > &elemFaces)
Definition ncmesh.cpp:383
void LegacyToNewVertexOrdering(Array< int > &order) const
I/O: Return a map from old (v1.1) vertex indices to new vertex indices.
Definition ncmesh.cpp:6301
int NGhostEdges
Definition ncmesh.hpp:602
void CollectIncidentElements(int elem, const RefCoord coord[3], Array< int > &list) const
Definition ncmesh.cpp:4036
void TraverseTetEdge(int vn0, int vn1, const Point &p0, const Point &p1, MatrixMap &matrix_map)
Definition ncmesh.cpp:3095
void RegisterFaces(int elem, int *fattr=NULL)
Definition ncmesh.cpp:426
int PrintBoundary(std::ostream *out) const
Definition ncmesh.cpp:5619
static const int MaxElemNodes
Number of nodes of an element can have.
Definition ncmesh.hpp:488
CoarseFineTransformations transforms
storage for data returned by Get[De]RefinementTransforms()
Definition ncmesh.hpp:1063
void MarkCoarseLevel()
Definition ncmesh.cpp:4626
int TriFaceSplitLevel(int vn1, int vn2, int vn3) const
Return the number of splits of this triangle that have occurred in the NCMesh. If zero,...
Definition ncmesh.cpp:5380
virtual void CheckDerefinementNCLevel(const Table &deref_table, Array< int > &level_ok, int max_nc_level)
Definition ncmesh.cpp:1980
void UpdateVertices()
This method assigns indices to vertices (Node::vert_index) that will be seen by the Mesh class and th...
Definition ncmesh.cpp:2179
NCList vertex_list
lazy-initialized list of vertices, see GetVertexList
Definition ncmesh.hpp:610
void InitGeomFlags()
Definition ncmesh.cpp:259
int NewTetrahedron(int n0, int n1, int n2, int n3, int attr, int fattr0, int fattr1, int fattr2, int fattr3)
Definition ncmesh.cpp:565
void LoadCoordinates(std::istream &input)
Load the "coordinates" section of the mesh file.
Definition ncmesh.cpp:5716
void GetPointMatrix(Geometry::Type geom, const char *ref_path, DenseMatrix &matrix) const
Definition ncmesh.cpp:4144
void TraverseQuadFace(int vn0, int vn1, int vn2, int vn3, const PointMatrix &pm, int level, Face *eface[4], MatrixMap &matrix_map)
Definition ncmesh.cpp:2976
const Table & GetDerefinementTable()
Definition ncmesh.cpp:1965
void FindFaceNodes(int face, int node[4]) const
Definition ncmesh.cpp:5264
void ClearTransforms()
Free all internal data created by the above three functions.
Definition ncmesh.cpp:4805
void FindSetNeighbors(const Array< char > &elem_set, Array< int > *neighbors, Array< char > *neighbor_set=NULL)
Definition ncmesh.cpp:3730
void CollectQuadFaceVertices(int v0, int v1, int v2, int v3, Array< int > &indices)
Definition ncmesh.cpp:3616
void PrintCoordinates(std::ostream &out) const
Print the "coordinates" section of the mesh file.
Definition ncmesh.cpp:5698
TriFaceTraverseResults TraverseTriFace(int vn0, int vn1, int vn2, const PointMatrix &pm, int level, MatrixMap &matrix_map)
Definition ncmesh.cpp:3132
int GetMidFaceNode(int en1, int en2, int en3, int en4)
Definition ncmesh.cpp:344
bool HavePrisms() const
Return true if the mesh contains prism elements.
Definition ncmesh.hpp:659
long MemoryUsage() const
Return total number of bytes allocated.
Definition ncmesh.cpp:6369
void DerefineElement(int elem)
Derefine the element elem, does nothing on leaf elements.
Definition ncmesh.cpp:1738
NCList face_list
lazy-initialized list of faces, see GetFaceList
Definition ncmesh.hpp:608
virtual void Refine(const Array< Refinement > &refinements)
Definition ncmesh.cpp:1648
std::int64_t RefCoord
Definition ncmesh.hpp:462
static PointMatrix pm_tri_identity
Definition ncmesh.hpp:1045
int NGhostFaces
Definition ncmesh.hpp:602
static PointMatrix pm_hex_identity
Definition ncmesh.hpp:1050
void UpdateLeafElements()
Update the leaf elements indices in leaf_elements.
Definition ncmesh.cpp:2151
void LoadLegacyFormat(std::istream &input, int &curved, int &is_nc)
Load the deprecated MFEM mesh v1.1 format for backward compatibility.
Definition ncmesh.cpp:6143
int EdgeSplitLevel(int vn1, int vn2) const
Return the number of splits of this edge that have occurred in the NCMesh. If zero,...
Definition ncmesh.cpp:5373
static void GridSfcOrdering2D(int width, int height, Array< int > &coords)
Definition ncmesh.cpp:5062
static int find_local_face(int geom, int a, int b, int c)
Definition ncmesh.cpp:2838
int Height() const
Get the height (size of output) of the Operator. Synonym with NumRows().
Definition operator.hpp:66
int Width() const
Get the width (size of input) of the Operator. Synonym with NumCols().
Definition operator.hpp:72
A parallel extension of the NCMesh class.
Definition pncmesh.hpp:65
Data type Pyramid element.
Definition pyramid.hpp:23
Data type quadrilateral element.
Data type line segment element.
Definition segment.hpp:23
int RowSize(int i) const
Definition table.hpp:108
void Clear()
Definition table.cpp:381
void GetRow(int i, Array< int > &row) const
Return row i in array row (the Table must be finalized)
Definition table.cpp:187
void SetIJ(int *newI, int *newJ, int newsize=-1)
Replace the I and J arrays with the given newI and newJ arrays.
Definition table.cpp:207
int Size() const
Returns the number of TYPE I elements.
Definition table.hpp:92
std::size_t MemoryUsage() const
Definition table.cpp:402
void MakeFromList(int nrows, const Array< Connection > &list)
Definition table.cpp:280
Data type tetrahedron element.
Data type triangle element.
Definition triangle.hpp:24
A triple of objects.
Data type Wedge element.
Definition wedge.hpp:23
int dim
Definition ex24.cpp:53
int index(int i, int j, int nx, int ny)
Definition life.cpp:235
real_t b
Definition lissajous.cpp:42
real_t a
Definition lissajous.cpp:41
bool PrismFaceBottom(int node, int *n)
Definition ncmesh.cpp:705
void Swap(Array< T > &, Array< T > &)
Definition array.hpp:648
bool PrismFaceTop(int node, int *n)
Definition ncmesh.cpp:708
OutStream out(std::cout)
Global stream used by the library for standard output. Initially it uses the same std::streambuf as s...
Definition globals.hpp:66
bool CubeFaceBottom(int node, int *n)
Definition ncmesh.cpp:699
bool CubeFaceBack(int node, int *n)
Definition ncmesh.cpp:696
bool CubeFaceLeft(int node, int *n)
Definition ncmesh.cpp:687
bool CubeFaceRight(int node, int *n)
Definition ncmesh.cpp:690
bool CubeFaceFront(int node, int *n)
Definition ncmesh.cpp:693
float real_t
Definition config.hpp:43
std::function< real_t(const Vector &)> f(real_t mass_coeff)
Definition lor_mms.hpp:30
void skip_comment_lines(std::istream &is, const char comment_char)
Check if the stream starts with comment_char. If so skip it.
Definition text.hpp:31
bool CubeFaceTop(int node, int *n)
Definition ncmesh.cpp:702
Defines the coarse-fine transformations of all fine elements.
Definition ncmesh.hpp:72
Array< Embedding > embeddings
Fine element positions in their parents.
Definition ncmesh.hpp:74
void MakeCoarseToFineTable(Table &coarse_to_fine, bool want_ghosts=false) const
Definition ncmesh.cpp:4783
DenseTensor point_matrices[Geometry::NumGeom]
Definition ncmesh.hpp:78
Helper struct for defining a connectivity table, see Table::MakeFromList.
Definition table.hpp:28
Defines the position of a fine element within a coarse element.
Definition ncmesh.hpp:51
unsigned geom
Definition ncmesh.hpp:58
unsigned ghost
For internal use: 0 if regular fine element, 1 if parallel ghost element.
Definition ncmesh.hpp:62
int parent
Coarse Element index in the coarse mesh.
Definition ncmesh.hpp:53
unsigned matrix
Definition ncmesh.hpp:59
int rank
processor number (ParNCMesh), -1 if undefined/unknown
Definition ncmesh.hpp:554
bool IsLeaf() const
Definition ncmesh.hpp:566
int child[MaxElemChildren]
2-10 children (if ref_type != 0)
Definition ncmesh.hpp:559
char tet_type
tetrahedron split type, currently always 0
Definition ncmesh.hpp:551
Element(Geometry::Type geom, int attr)
Definition ncmesh.cpp:490
int node[MaxElemNodes]
element corners (if ref_type == 0)
Definition ncmesh.hpp:558
char ref_type
bit mask of X,Y,Z refinements (bits 0,1,2 respectively)
Definition ncmesh.hpp:550
char geom
Geometry::Type of the element (char for storage only)
Definition ncmesh.hpp:549
int index
element number in the Mesh, -1 if refined
Definition ncmesh.hpp:553
int parent
parent element, -1 if this is a root element, -2 if free'd
Definition ncmesh.hpp:561
Geometry::Type Geom() const
Definition ncmesh.hpp:565
void ForgetElement(int e)
Definition ncmesh.cpp:458
int elem[2]
up to 2 elements sharing the face
Definition ncmesh.hpp:529
void RegisterElement(int e)
Definition ncmesh.cpp:451
bool Boundary() const
Definition ncmesh.hpp:533
int index
face number in the Mesh
Definition ncmesh.hpp:528
int GetSingleElement() const
Return one of elem[0] or elem[1] and make sure the other is -1.
Definition ncmesh.cpp:473
int attribute
boundary element attribute, -1 if internal face
Definition ncmesh.hpp:527
This holds in one place the constants about the geometries we support.
Definition ncmesh.hpp:1180
int nfv[MaxElemFaces]
Definition ncmesh.hpp:1184
int faces[MaxElemFaces][4]
Definition ncmesh.hpp:1183
int edges[MaxElemEdges][2]
Definition ncmesh.hpp:1182
void InitGeom(Geometry::Type geom)
Definition ncmesh.cpp:57
Identifies a vertex/edge/face in both Mesh and NCMesh.
Definition ncmesh.hpp:189
int element
NCMesh::Element containing this vertex/edge/face.
Definition ncmesh.hpp:191
int index
Mesh number.
Definition ncmesh.hpp:190
Geometry::Type Geom() const
Definition ncmesh.hpp:195
signed char local
local number within 'element'
Definition ncmesh.hpp:192
MeshIdType GetMeshIdType(int index) const
Return a face type for a given nc index.
Definition ncmesh.cpp:3529
Array< MeshId > conforming
All MeshIds corresponding to conformal faces.
Definition ncmesh.hpp:231
long MemoryUsage() const
Definition ncmesh.cpp:6341
Array< Slave > slaves
All MeshIds corresponding to slave faces.
Definition ncmesh.hpp:233
bool CheckMeshIdType(int index, MeshIdType type) const
Given an index, check if this is a certain face type.
Definition ncmesh.cpp:3537
void Clear()
Erase the contents of the conforming, master and slave arrays.
Definition ncmesh.cpp:3490
void OrientedPointMatrix(const Slave &slave, DenseMatrix &oriented_matrix) const
Definition ncmesh.cpp:3468
Array< Master > masters
All MeshIds corresponding to master faces.
Definition ncmesh.hpp:232
Array< DenseMatrix * > point_matrices[Geometry::NumGeom]
List of unique point matrices for each slave geometry.
Definition ncmesh.hpp:236
MeshIdAndType GetMeshIdAndType(int index) const
Return a mesh id and type for a given nc index.
Definition ncmesh.cpp:3509
bool HasEdge() const
Definition ncmesh.hpp:514
The PointMatrix stores the coordinates of the slave face using the master face coordinate as referenc...
Definition ncmesh.hpp:998
void GetMatrix(DenseMatrix &point_matrix) const
Definition ncmesh.cpp:4090
Point points[MaxElemNodes]
Definition ncmesh.hpp:1000
bool operator==(const PointMatrix &pm) const
Definition ncmesh.cpp:4076
Nonconforming edge/face within a bigger edge/face.
Definition ncmesh.hpp:216
unsigned edge_flags
orientation flags, see OrientedPointMatrix
Definition ncmesh.hpp:219
unsigned matrix
index into NCList::point_matrices[geom]
Definition ncmesh.hpp:218
char ref_type
refinement XYZ bit mask (7 = full isotropic)
Definition ncmesh.hpp:40
int index
Mesh element number.
Definition ncmesh.hpp:39