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