1 // Copyright (c) 2010-2021, 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 // Implementation of data type mesh
13
14 #include "mesh_headers.hpp"
15 #include "../fem/fem.hpp"
16 #include "../general/sort_pairs.hpp"
17 #include "../general/binaryio.hpp"
18 #include "../general/text.hpp"
19 #include "../general/device.hpp"
20 #include "../general/tic_toc.hpp"
21 #include "../general/gecko.hpp"
22 #include "../fem/quadinterpolator.hpp"
23
24 #include <iostream>
25 #include <sstream>
26 #include <fstream>
27 #include <limits>
28 #include <cmath>
29 #include <cstring>
30 #include <ctime>
31 #include <functional>
32 #include <map>
33 #include <set>
34
35 // Include the METIS header, if using version 5. If using METIS 4, the needed
36 // declarations are inlined below, i.e. no header is needed.
37 #if defined(MFEM_USE_METIS) && defined(MFEM_USE_METIS_5)
38 #include "metis.h"
39 #endif
40
41 // METIS 4 prototypes
42 #if defined(MFEM_USE_METIS) && !defined(MFEM_USE_METIS_5)
43 typedef int idx_t;
44 typedef int idxtype;
45 extern "C" {
46 void METIS_PartGraphRecursive(int*, idxtype*, idxtype*, idxtype*, idxtype*,
47 int*, int*, int*, int*, int*, idxtype*);
48 void METIS_PartGraphKway(int*, idxtype*, idxtype*, idxtype*, idxtype*,
49 int*, int*, int*, int*, int*, idxtype*);
50 void METIS_PartGraphVKway(int*, idxtype*, idxtype*, idxtype*, idxtype*,
51 int*, int*, int*, int*, int*, idxtype*);
52 }
53 #endif
54
55 using namespace std;
56
57 namespace mfem
58 {
59
GetElementJacobian(int i,DenseMatrix & J)60 void Mesh::GetElementJacobian(int i, DenseMatrix &J)
61 {
62 Geometry::Type geom = GetElementBaseGeometry(i);
63 ElementTransformation *eltransf = GetElementTransformation(i);
64 eltransf->SetIntPoint(&Geometries.GetCenter(geom));
65 Geometries.JacToPerfJac(geom, eltransf->Jacobian(), J);
66 }
67
GetElementCenter(int i,Vector & center)68 void Mesh::GetElementCenter(int i, Vector ¢er)
69 {
70 center.SetSize(spaceDim);
71 int geom = GetElementBaseGeometry(i);
72 ElementTransformation *eltransf = GetElementTransformation(i);
73 eltransf->Transform(Geometries.GetCenter(geom), center);
74 }
75
GetElementSize(ElementTransformation * T,int type)76 double Mesh::GetElementSize(ElementTransformation *T, int type)
77 {
78 DenseMatrix J(Dim);
79
80 Geometry::Type geom = T->GetGeometryType();
81 T->SetIntPoint(&Geometries.GetCenter(geom));
82 Geometries.JacToPerfJac(geom, T->Jacobian(), J);
83
84 if (type == 0)
85 {
86 return pow(fabs(J.Det()), 1./Dim);
87 }
88 else if (type == 1)
89 {
90 return J.CalcSingularvalue(Dim-1); // h_min
91 }
92 else
93 {
94 return J.CalcSingularvalue(0); // h_max
95 }
96 }
97
GetElementSize(int i,int type)98 double Mesh::GetElementSize(int i, int type)
99 {
100 return GetElementSize(GetElementTransformation(i), type);
101 }
102
GetElementSize(int i,const Vector & dir)103 double Mesh::GetElementSize(int i, const Vector &dir)
104 {
105 DenseMatrix J(Dim);
106 Vector d_hat(Dim);
107 GetElementJacobian(i, J);
108 J.MultTranspose(dir, d_hat);
109 return sqrt((d_hat * d_hat) / (dir * dir));
110 }
111
GetElementVolume(int i)112 double Mesh::GetElementVolume(int i)
113 {
114 ElementTransformation *et = GetElementTransformation(i);
115 const IntegrationRule &ir = IntRules.Get(GetElementBaseGeometry(i),
116 et->OrderJ());
117 double volume = 0.0;
118 for (int j = 0; j < ir.GetNPoints(); j++)
119 {
120 const IntegrationPoint &ip = ir.IntPoint(j);
121 et->SetIntPoint(&ip);
122 volume += ip.weight * et->Weight();
123 }
124
125 return volume;
126 }
127
128 // Similar to VisualizationSceneSolution3d::FindNewBox in GLVis
GetBoundingBox(Vector & min,Vector & max,int ref)129 void Mesh::GetBoundingBox(Vector &min, Vector &max, int ref)
130 {
131 min.SetSize(spaceDim);
132 max.SetSize(spaceDim);
133
134 for (int d = 0; d < spaceDim; d++)
135 {
136 min(d) = infinity();
137 max(d) = -infinity();
138 }
139
140 if (Nodes == NULL)
141 {
142 double *coord;
143 for (int i = 0; i < NumOfVertices; i++)
144 {
145 coord = GetVertex(i);
146 for (int d = 0; d < spaceDim; d++)
147 {
148 if (coord[d] < min(d)) { min(d) = coord[d]; }
149 if (coord[d] > max(d)) { max(d) = coord[d]; }
150 }
151 }
152 }
153 else
154 {
155 const bool use_boundary = false; // make this a parameter?
156 int ne = use_boundary ? GetNBE() : GetNE();
157 int fn, fo;
158 DenseMatrix pointmat;
159 RefinedGeometry *RefG;
160 IntegrationRule eir;
161 FaceElementTransformations *Tr;
162 ElementTransformation *T;
163
164 for (int i = 0; i < ne; i++)
165 {
166 if (use_boundary)
167 {
168 GetBdrElementFace(i, &fn, &fo);
169 RefG = GlobGeometryRefiner.Refine(GetFaceBaseGeometry(fn), ref);
170 Tr = GetFaceElementTransformations(fn, 5);
171 eir.SetSize(RefG->RefPts.GetNPoints());
172 Tr->Loc1.Transform(RefG->RefPts, eir);
173 Tr->Elem1->Transform(eir, pointmat);
174 }
175 else
176 {
177 T = GetElementTransformation(i);
178 RefG = GlobGeometryRefiner.Refine(GetElementBaseGeometry(i), ref);
179 T->Transform(RefG->RefPts, pointmat);
180 }
181 for (int j = 0; j < pointmat.Width(); j++)
182 {
183 for (int d = 0; d < pointmat.Height(); d++)
184 {
185 if (pointmat(d,j) < min(d)) { min(d) = pointmat(d,j); }
186 if (pointmat(d,j) > max(d)) { max(d) = pointmat(d,j); }
187 }
188 }
189 }
190 }
191 }
192
GetCharacteristics(double & h_min,double & h_max,double & kappa_min,double & kappa_max,Vector * Vh,Vector * Vk)193 void Mesh::GetCharacteristics(double &h_min, double &h_max,
194 double &kappa_min, double &kappa_max,
195 Vector *Vh, Vector *Vk)
196 {
197 int i, dim, sdim;
198 DenseMatrix J;
199 double h, kappa;
200
201 dim = Dimension();
202 sdim = SpaceDimension();
203
204 if (Vh) { Vh->SetSize(NumOfElements); }
205 if (Vk) { Vk->SetSize(NumOfElements); }
206
207 h_min = kappa_min = infinity();
208 h_max = kappa_max = -h_min;
209 if (dim == 0) { if (Vh) { *Vh = 1.0; } if (Vk) {*Vk = 1.0; } return; }
210 J.SetSize(sdim, dim);
211 for (i = 0; i < NumOfElements; i++)
212 {
213 GetElementJacobian(i, J);
214 h = pow(fabs(J.Weight()), 1.0/double(dim));
215 kappa = (dim == sdim) ?
216 J.CalcSingularvalue(0) / J.CalcSingularvalue(dim-1) : -1.0;
217 if (Vh) { (*Vh)(i) = h; }
218 if (Vk) { (*Vk)(i) = kappa; }
219
220 if (h < h_min) { h_min = h; }
221 if (h > h_max) { h_max = h; }
222 if (kappa < kappa_min) { kappa_min = kappa; }
223 if (kappa > kappa_max) { kappa_max = kappa; }
224 }
225 }
226
227 // static method
PrintElementsByGeometry(int dim,const Array<int> & num_elems_by_geom,std::ostream & out)228 void Mesh::PrintElementsByGeometry(int dim,
229 const Array<int> &num_elems_by_geom,
230 std::ostream &out)
231 {
232 for (int g = Geometry::DimStart[dim], first = 1;
233 g < Geometry::DimStart[dim+1]; g++)
234 {
235 if (!num_elems_by_geom[g]) { continue; }
236 if (!first) { out << " + "; }
237 else { first = 0; }
238 out << num_elems_by_geom[g] << ' ' << Geometry::Name[g] << "(s)";
239 }
240 }
241
PrintCharacteristics(Vector * Vh,Vector * Vk,std::ostream & out)242 void Mesh::PrintCharacteristics(Vector *Vh, Vector *Vk, std::ostream &out)
243 {
244 double h_min, h_max, kappa_min, kappa_max;
245
246 out << "Mesh Characteristics:";
247
248 this->GetCharacteristics(h_min, h_max, kappa_min, kappa_max, Vh, Vk);
249
250 Array<int> num_elems_by_geom(Geometry::NumGeom);
251 num_elems_by_geom = 0;
252 for (int i = 0; i < GetNE(); i++)
253 {
254 num_elems_by_geom[GetElementBaseGeometry(i)]++;
255 }
256
257 out << '\n'
258 << "Dimension : " << Dimension() << '\n'
259 << "Space dimension : " << SpaceDimension();
260 if (Dim == 0)
261 {
262 out << '\n'
263 << "Number of vertices : " << GetNV() << '\n'
264 << "Number of elements : " << GetNE() << '\n'
265 << "Number of bdr elem : " << GetNBE() << '\n';
266 }
267 else if (Dim == 1)
268 {
269 out << '\n'
270 << "Number of vertices : " << GetNV() << '\n'
271 << "Number of elements : " << GetNE() << '\n'
272 << "Number of bdr elem : " << GetNBE() << '\n'
273 << "h_min : " << h_min << '\n'
274 << "h_max : " << h_max << '\n';
275 }
276 else if (Dim == 2)
277 {
278 out << '\n'
279 << "Number of vertices : " << GetNV() << '\n'
280 << "Number of edges : " << GetNEdges() << '\n'
281 << "Number of elements : " << GetNE() << " -- ";
282 PrintElementsByGeometry(2, num_elems_by_geom, out);
283 out << '\n'
284 << "Number of bdr elem : " << GetNBE() << '\n'
285 << "Euler Number : " << EulerNumber2D() << '\n'
286 << "h_min : " << h_min << '\n'
287 << "h_max : " << h_max << '\n'
288 << "kappa_min : " << kappa_min << '\n'
289 << "kappa_max : " << kappa_max << '\n';
290 }
291 else
292 {
293 Array<int> num_bdr_elems_by_geom(Geometry::NumGeom);
294 num_bdr_elems_by_geom = 0;
295 for (int i = 0; i < GetNBE(); i++)
296 {
297 num_bdr_elems_by_geom[GetBdrElementBaseGeometry(i)]++;
298 }
299 Array<int> num_faces_by_geom(Geometry::NumGeom);
300 num_faces_by_geom = 0;
301 for (int i = 0; i < GetNFaces(); i++)
302 {
303 num_faces_by_geom[GetFaceBaseGeometry(i)]++;
304 }
305
306 out << '\n'
307 << "Number of vertices : " << GetNV() << '\n'
308 << "Number of edges : " << GetNEdges() << '\n'
309 << "Number of faces : " << GetNFaces() << " -- ";
310 PrintElementsByGeometry(Dim-1, num_faces_by_geom, out);
311 out << '\n'
312 << "Number of elements : " << GetNE() << " -- ";
313 PrintElementsByGeometry(Dim, num_elems_by_geom, out);
314 out << '\n'
315 << "Number of bdr elem : " << GetNBE() << " -- ";
316 PrintElementsByGeometry(Dim-1, num_bdr_elems_by_geom, out);
317 out << '\n'
318 << "Euler Number : " << EulerNumber() << '\n'
319 << "h_min : " << h_min << '\n'
320 << "h_max : " << h_max << '\n'
321 << "kappa_min : " << kappa_min << '\n'
322 << "kappa_max : " << kappa_max << '\n';
323 }
324 out << '\n' << std::flush;
325 }
326
GetTransformationFEforElementType(Element::Type ElemType)327 FiniteElement *Mesh::GetTransformationFEforElementType(Element::Type ElemType)
328 {
329 switch (ElemType)
330 {
331 case Element::POINT : return &PointFE;
332 case Element::SEGMENT : return &SegmentFE;
333 case Element::TRIANGLE : return &TriangleFE;
334 case Element::QUADRILATERAL : return &QuadrilateralFE;
335 case Element::TETRAHEDRON : return &TetrahedronFE;
336 case Element::HEXAHEDRON : return &HexahedronFE;
337 case Element::WEDGE : return &WedgeFE;
338 default:
339 MFEM_ABORT("Unknown element type \"" << ElemType << "\"");
340 break;
341 }
342 MFEM_ABORT("Unknown element type");
343 return NULL;
344 }
345
346
GetElementTransformation(int i,IsoparametricTransformation * ElTr)347 void Mesh::GetElementTransformation(int i, IsoparametricTransformation *ElTr)
348 {
349 ElTr->Attribute = GetAttribute(i);
350 ElTr->ElementNo = i;
351 ElTr->ElementType = ElementTransformation::ELEMENT;
352 ElTr->Reset();
353 if (Nodes == NULL)
354 {
355 GetPointMatrix(i, ElTr->GetPointMat());
356 ElTr->SetFE(GetTransformationFEforElementType(GetElementType(i)));
357 }
358 else
359 {
360 DenseMatrix &pm = ElTr->GetPointMat();
361 Array<int> vdofs;
362 Nodes->FESpace()->GetElementVDofs(i, vdofs);
363 Nodes->HostRead();
364 const GridFunction &nodes = *Nodes;
365 int n = vdofs.Size()/spaceDim;
366 pm.SetSize(spaceDim, n);
367 for (int k = 0; k < spaceDim; k++)
368 {
369 for (int j = 0; j < n; j++)
370 {
371 pm(k,j) = nodes(vdofs[n*k+j]);
372 }
373 }
374 ElTr->SetFE(Nodes->FESpace()->GetFE(i));
375 }
376 }
377
GetElementTransformation(int i,const Vector & nodes,IsoparametricTransformation * ElTr)378 void Mesh::GetElementTransformation(int i, const Vector &nodes,
379 IsoparametricTransformation *ElTr)
380 {
381 ElTr->Attribute = GetAttribute(i);
382 ElTr->ElementNo = i;
383 ElTr->ElementType = ElementTransformation::ELEMENT;
384 DenseMatrix &pm = ElTr->GetPointMat();
385 ElTr->Reset();
386 nodes.HostRead();
387 if (Nodes == NULL)
388 {
389 MFEM_ASSERT(nodes.Size() == spaceDim*GetNV(), "");
390 int nv = elements[i]->GetNVertices();
391 const int *v = elements[i]->GetVertices();
392 int n = vertices.Size();
393 pm.SetSize(spaceDim, nv);
394 for (int k = 0; k < spaceDim; k++)
395 {
396 for (int j = 0; j < nv; j++)
397 {
398 pm(k, j) = nodes(k*n+v[j]);
399 }
400 }
401 ElTr->SetFE(GetTransformationFEforElementType(GetElementType(i)));
402 }
403 else
404 {
405 MFEM_ASSERT(nodes.Size() == Nodes->Size(), "");
406 Array<int> vdofs;
407 Nodes->FESpace()->GetElementVDofs(i, vdofs);
408 int n = vdofs.Size()/spaceDim;
409 pm.SetSize(spaceDim, n);
410 for (int k = 0; k < spaceDim; k++)
411 {
412 for (int j = 0; j < n; j++)
413 {
414 pm(k,j) = nodes(vdofs[n*k+j]);
415 }
416 }
417 ElTr->SetFE(Nodes->FESpace()->GetFE(i));
418 }
419 }
420
GetElementTransformation(int i)421 ElementTransformation *Mesh::GetElementTransformation(int i)
422 {
423 GetElementTransformation(i, &Transformation);
424
425 return &Transformation;
426 }
427
GetBdrElementTransformation(int i)428 ElementTransformation *Mesh::GetBdrElementTransformation(int i)
429 {
430 GetBdrElementTransformation(i, &BdrTransformation);
431 return &BdrTransformation;
432 }
433
GetBdrElementTransformation(int i,IsoparametricTransformation * ElTr)434 void Mesh::GetBdrElementTransformation(int i, IsoparametricTransformation* ElTr)
435 {
436 ElTr->Attribute = GetBdrAttribute(i);
437 ElTr->ElementNo = i; // boundary element number
438 ElTr->ElementType = ElementTransformation::BDR_ELEMENT;
439 DenseMatrix &pm = ElTr->GetPointMat();
440 ElTr->Reset();
441 if (Nodes == NULL)
442 {
443 GetBdrPointMatrix(i, pm);
444 ElTr->SetFE(GetTransformationFEforElementType(GetBdrElementType(i)));
445 }
446 else
447 {
448 const FiniteElement *bdr_el = Nodes->FESpace()->GetBE(i);
449 Nodes->HostRead();
450 const GridFunction &nodes = *Nodes;
451 if (bdr_el)
452 {
453 Array<int> vdofs;
454 Nodes->FESpace()->GetBdrElementVDofs(i, vdofs);
455 int n = vdofs.Size()/spaceDim;
456 pm.SetSize(spaceDim, n);
457 for (int k = 0; k < spaceDim; k++)
458 {
459 for (int j = 0; j < n; j++)
460 {
461 pm(k,j) = nodes(vdofs[n*k+j]);
462 }
463 }
464 ElTr->SetFE(bdr_el);
465 }
466 else // L2 Nodes (e.g., periodic mesh)
467 {
468 int elem_id, face_info;
469 GetBdrElementAdjacentElement(i, elem_id, face_info);
470
471 GetLocalFaceTransformation(GetBdrElementType(i),
472 GetElementType(elem_id),
473 FaceElemTr.Loc1.Transf, face_info);
474 // NOTE: FaceElemTr.Loc1 is overwritten here -- used as a temporary
475
476 Geometry::Type face_geom = GetBdrElementBaseGeometry(i);
477 const FiniteElement *face_el =
478 Nodes->FESpace()->GetTraceElement(elem_id, face_geom);
479 MFEM_VERIFY(dynamic_cast<const NodalFiniteElement*>(face_el),
480 "Mesh requires nodal Finite Element.");
481 IntegrationRule eir(face_el->GetDof());
482 FaceElemTr.Loc1.Transf.ElementNo = elem_id;
483 FaceElemTr.Loc1.Transf.ElementType = ElementTransformation::ELEMENT;
484 FaceElemTr.Loc1.Transform(face_el->GetNodes(), eir);
485 Nodes->GetVectorValues(FaceElemTr.Loc1.Transf, eir, pm);
486
487 ElTr->SetFE(face_el);
488 }
489 }
490 }
491
GetFaceTransformation(int FaceNo,IsoparametricTransformation * FTr)492 void Mesh::GetFaceTransformation(int FaceNo, IsoparametricTransformation *FTr)
493 {
494 FTr->Attribute = (Dim == 1) ? 1 : faces[FaceNo]->GetAttribute();
495 FTr->ElementNo = FaceNo;
496 FTr->ElementType = ElementTransformation::FACE;
497 DenseMatrix &pm = FTr->GetPointMat();
498 FTr->Reset();
499 if (Nodes == NULL)
500 {
501 const int *v = (Dim == 1) ? &FaceNo : faces[FaceNo]->GetVertices();
502 const int nv = (Dim == 1) ? 1 : faces[FaceNo]->GetNVertices();
503 pm.SetSize(spaceDim, nv);
504 for (int i = 0; i < spaceDim; i++)
505 {
506 for (int j = 0; j < nv; j++)
507 {
508 pm(i, j) = vertices[v[j]](i);
509 }
510 }
511 FTr->SetFE(GetTransformationFEforElementType(GetFaceElementType(FaceNo)));
512 }
513 else // curved mesh
514 {
515 const FiniteElement *face_el = Nodes->FESpace()->GetFaceElement(FaceNo);
516 Nodes->HostRead();
517 const GridFunction &nodes = *Nodes;
518 if (face_el)
519 {
520 Array<int> vdofs;
521 Nodes->FESpace()->GetFaceVDofs(FaceNo, vdofs);
522 int n = vdofs.Size()/spaceDim;
523 pm.SetSize(spaceDim, n);
524 for (int i = 0; i < spaceDim; i++)
525 {
526 for (int j = 0; j < n; j++)
527 {
528 pm(i, j) = nodes(vdofs[n*i+j]);
529 }
530 }
531 FTr->SetFE(face_el);
532 }
533 else // L2 Nodes (e.g., periodic mesh), go through the volume of Elem1
534 {
535 FaceInfo &face_info = faces_info[FaceNo];
536
537 Geometry::Type face_geom = GetFaceGeometryType(FaceNo);
538 Element::Type face_type = GetFaceElementType(FaceNo);
539
540 GetLocalFaceTransformation(face_type,
541 GetElementType(face_info.Elem1No),
542 FaceElemTr.Loc1.Transf, face_info.Elem1Inf);
543 // NOTE: FaceElemTr.Loc1 is overwritten here -- used as a temporary
544
545 face_el = Nodes->FESpace()->GetTraceElement(face_info.Elem1No,
546 face_geom);
547 MFEM_VERIFY(dynamic_cast<const NodalFiniteElement*>(face_el),
548 "Mesh requires nodal Finite Element.");
549
550 IntegrationRule eir(face_el->GetDof());
551 FaceElemTr.Loc1.Transf.ElementNo = face_info.Elem1No;
552 FaceElemTr.Loc1.Transf.ElementType = ElementTransformation::ELEMENT;
553 FaceElemTr.Loc1.Transform(face_el->GetNodes(), eir);
554 Nodes->GetVectorValues(FaceElemTr.Loc1.Transf, eir, pm);
555
556 FTr->SetFE(face_el);
557 }
558 }
559 }
560
GetFaceTransformation(int FaceNo)561 ElementTransformation *Mesh::GetFaceTransformation(int FaceNo)
562 {
563 GetFaceTransformation(FaceNo, &FaceTransformation);
564 return &FaceTransformation;
565 }
566
GetEdgeTransformation(int EdgeNo,IsoparametricTransformation * EdTr)567 void Mesh::GetEdgeTransformation(int EdgeNo, IsoparametricTransformation *EdTr)
568 {
569 if (Dim == 2)
570 {
571 GetFaceTransformation(EdgeNo, EdTr);
572 return;
573 }
574 if (Dim == 1)
575 {
576 mfem_error("Mesh::GetEdgeTransformation not defined in 1D \n");
577 }
578
579 EdTr->Attribute = 1;
580 EdTr->ElementNo = EdgeNo;
581 EdTr->ElementType = ElementTransformation::EDGE;
582 DenseMatrix &pm = EdTr->GetPointMat();
583 EdTr->Reset();
584 if (Nodes == NULL)
585 {
586 Array<int> v;
587 GetEdgeVertices(EdgeNo, v);
588 const int nv = 2;
589 pm.SetSize(spaceDim, nv);
590 for (int i = 0; i < spaceDim; i++)
591 {
592 for (int j = 0; j < nv; j++)
593 {
594 pm(i, j) = vertices[v[j]](i);
595 }
596 }
597 EdTr->SetFE(GetTransformationFEforElementType(Element::SEGMENT));
598 }
599 else
600 {
601 const FiniteElement *edge_el = Nodes->FESpace()->GetEdgeElement(EdgeNo);
602 Nodes->HostRead();
603 const GridFunction &nodes = *Nodes;
604 if (edge_el)
605 {
606 Array<int> vdofs;
607 Nodes->FESpace()->GetEdgeVDofs(EdgeNo, vdofs);
608 int n = vdofs.Size()/spaceDim;
609 pm.SetSize(spaceDim, n);
610 for (int i = 0; i < spaceDim; i++)
611 {
612 for (int j = 0; j < n; j++)
613 {
614 pm(i, j) = nodes(vdofs[n*i+j]);
615 }
616 }
617 EdTr->SetFE(edge_el);
618 }
619 else
620 {
621 MFEM_ABORT("Not implemented.");
622 }
623 }
624 }
625
GetEdgeTransformation(int EdgeNo)626 ElementTransformation *Mesh::GetEdgeTransformation(int EdgeNo)
627 {
628 GetEdgeTransformation(EdgeNo, &EdgeTransformation);
629 return &EdgeTransformation;
630 }
631
632
GetLocalPtToSegTransformation(IsoparametricTransformation & Transf,int i)633 void Mesh::GetLocalPtToSegTransformation(
634 IsoparametricTransformation &Transf, int i)
635 {
636 const IntegrationRule *SegVert;
637 DenseMatrix &locpm = Transf.GetPointMat();
638 Transf.Reset();
639
640 Transf.SetFE(&PointFE);
641 SegVert = Geometries.GetVertices(Geometry::SEGMENT);
642 locpm.SetSize(1, 1);
643 locpm(0, 0) = SegVert->IntPoint(i/64).x;
644 // (i/64) is the local face no. in the segment
645 // (i%64) is the orientation of the point (not used)
646 }
647
GetLocalSegToTriTransformation(IsoparametricTransformation & Transf,int i)648 void Mesh::GetLocalSegToTriTransformation(
649 IsoparametricTransformation &Transf, int i)
650 {
651 const int *tv, *so;
652 const IntegrationRule *TriVert;
653 DenseMatrix &locpm = Transf.GetPointMat();
654 Transf.Reset();
655
656 Transf.SetFE(&SegmentFE);
657 tv = tri_t::Edges[i/64]; // (i/64) is the local face no. in the triangle
658 so = seg_t::Orient[i%64]; // (i%64) is the orientation of the segment
659 TriVert = Geometries.GetVertices(Geometry::TRIANGLE);
660 locpm.SetSize(2, 2);
661 for (int j = 0; j < 2; j++)
662 {
663 locpm(0, so[j]) = TriVert->IntPoint(tv[j]).x;
664 locpm(1, so[j]) = TriVert->IntPoint(tv[j]).y;
665 }
666 }
667
GetLocalSegToQuadTransformation(IsoparametricTransformation & Transf,int i)668 void Mesh::GetLocalSegToQuadTransformation(
669 IsoparametricTransformation &Transf, int i)
670 {
671 const int *qv, *so;
672 const IntegrationRule *QuadVert;
673 DenseMatrix &locpm = Transf.GetPointMat();
674 Transf.Reset();
675
676 Transf.SetFE(&SegmentFE);
677 qv = quad_t::Edges[i/64]; // (i/64) is the local face no. in the quad
678 so = seg_t::Orient[i%64]; // (i%64) is the orientation of the segment
679 QuadVert = Geometries.GetVertices(Geometry::SQUARE);
680 locpm.SetSize(2, 2);
681 for (int j = 0; j < 2; j++)
682 {
683 locpm(0, so[j]) = QuadVert->IntPoint(qv[j]).x;
684 locpm(1, so[j]) = QuadVert->IntPoint(qv[j]).y;
685 }
686 }
687
GetLocalTriToTetTransformation(IsoparametricTransformation & Transf,int i)688 void Mesh::GetLocalTriToTetTransformation(
689 IsoparametricTransformation &Transf, int i)
690 {
691 DenseMatrix &locpm = Transf.GetPointMat();
692 Transf.Reset();
693
694 Transf.SetFE(&TriangleFE);
695 // (i/64) is the local face no. in the tet
696 const int *tv = tet_t::FaceVert[i/64];
697 // (i%64) is the orientation of the tetrahedron face
698 // w.r.t. the face element
699 const int *to = tri_t::Orient[i%64];
700 const IntegrationRule *TetVert =
701 Geometries.GetVertices(Geometry::TETRAHEDRON);
702 locpm.SetSize(3, 3);
703 for (int j = 0; j < 3; j++)
704 {
705 const IntegrationPoint &vert = TetVert->IntPoint(tv[to[j]]);
706 locpm(0, j) = vert.x;
707 locpm(1, j) = vert.y;
708 locpm(2, j) = vert.z;
709 }
710 }
711
GetLocalTriToWdgTransformation(IsoparametricTransformation & Transf,int i)712 void Mesh::GetLocalTriToWdgTransformation(
713 IsoparametricTransformation &Transf, int i)
714 {
715 DenseMatrix &locpm = Transf.GetPointMat();
716 Transf.Reset();
717
718 Transf.SetFE(&TriangleFE);
719 // (i/64) is the local face no. in the pri
720 MFEM_VERIFY(i < 128, "Local face index " << i/64
721 << " is not a triangular face of a wedge.");
722 const int *pv = pri_t::FaceVert[i/64];
723 // (i%64) is the orientation of the wedge face
724 // w.r.t. the face element
725 const int *to = tri_t::Orient[i%64];
726 const IntegrationRule *PriVert =
727 Geometries.GetVertices(Geometry::PRISM);
728 locpm.SetSize(3, 3);
729 for (int j = 0; j < 3; j++)
730 {
731 const IntegrationPoint &vert = PriVert->IntPoint(pv[to[j]]);
732 locpm(0, j) = vert.x;
733 locpm(1, j) = vert.y;
734 locpm(2, j) = vert.z;
735 }
736 }
737
GetLocalQuadToHexTransformation(IsoparametricTransformation & Transf,int i)738 void Mesh::GetLocalQuadToHexTransformation(
739 IsoparametricTransformation &Transf, int i)
740 {
741 DenseMatrix &locpm = Transf.GetPointMat();
742 Transf.Reset();
743
744 Transf.SetFE(&QuadrilateralFE);
745 // (i/64) is the local face no. in the hex
746 const int *hv = hex_t::FaceVert[i/64];
747 // (i%64) is the orientation of the quad
748 const int *qo = quad_t::Orient[i%64];
749 const IntegrationRule *HexVert = Geometries.GetVertices(Geometry::CUBE);
750 locpm.SetSize(3, 4);
751 for (int j = 0; j < 4; j++)
752 {
753 const IntegrationPoint &vert = HexVert->IntPoint(hv[qo[j]]);
754 locpm(0, j) = vert.x;
755 locpm(1, j) = vert.y;
756 locpm(2, j) = vert.z;
757 }
758 }
759
GetLocalQuadToWdgTransformation(IsoparametricTransformation & Transf,int i)760 void Mesh::GetLocalQuadToWdgTransformation(
761 IsoparametricTransformation &Transf, int i)
762 {
763 DenseMatrix &locpm = Transf.GetPointMat();
764 Transf.Reset();
765
766 Transf.SetFE(&QuadrilateralFE);
767 // (i/64) is the local face no. in the pri
768 MFEM_VERIFY(i >= 128, "Local face index " << i/64
769 << " is not a quadrilateral face of a wedge.");
770 const int *pv = pri_t::FaceVert[i/64];
771 // (i%64) is the orientation of the quad
772 const int *qo = quad_t::Orient[i%64];
773 const IntegrationRule *PriVert = Geometries.GetVertices(Geometry::PRISM);
774 locpm.SetSize(3, 4);
775 for (int j = 0; j < 4; j++)
776 {
777 const IntegrationPoint &vert = PriVert->IntPoint(pv[qo[j]]);
778 locpm(0, j) = vert.x;
779 locpm(1, j) = vert.y;
780 locpm(2, j) = vert.z;
781 }
782 }
783
GetGeometricFactors(const IntegrationRule & ir,const int flags,MemoryType d_mt)784 const GeometricFactors* Mesh::GetGeometricFactors(const IntegrationRule& ir,
785 const int flags,
786 MemoryType d_mt)
787 {
788 for (int i = 0; i < geom_factors.Size(); i++)
789 {
790 GeometricFactors *gf = geom_factors[i];
791 if (gf->IntRule == &ir && (gf->computed_factors & flags) == flags)
792 {
793 return gf;
794 }
795 }
796
797 this->EnsureNodes();
798
799 GeometricFactors *gf = new GeometricFactors(this, ir, flags, d_mt);
800 geom_factors.Append(gf);
801 return gf;
802 }
803
GetFaceGeometricFactors(const IntegrationRule & ir,const int flags,FaceType type)804 const FaceGeometricFactors* Mesh::GetFaceGeometricFactors(
805 const IntegrationRule& ir,
806 const int flags, FaceType type)
807 {
808 for (int i = 0; i < face_geom_factors.Size(); i++)
809 {
810 FaceGeometricFactors *gf = face_geom_factors[i];
811 if (gf->IntRule == &ir && (gf->computed_factors & flags) == flags &&
812 gf->type==type)
813 {
814 return gf;
815 }
816 }
817
818 this->EnsureNodes();
819
820 FaceGeometricFactors *gf = new FaceGeometricFactors(this, ir, flags, type);
821 face_geom_factors.Append(gf);
822 return gf;
823 }
824
DeleteGeometricFactors()825 void Mesh::DeleteGeometricFactors()
826 {
827 for (int i = 0; i < geom_factors.Size(); i++)
828 {
829 delete geom_factors[i];
830 }
831 geom_factors.SetSize(0);
832 for (int i = 0; i < face_geom_factors.Size(); i++)
833 {
834 delete face_geom_factors[i];
835 }
836 face_geom_factors.SetSize(0);
837 }
838
GetLocalFaceTransformation(int face_type,int elem_type,IsoparametricTransformation & Transf,int info)839 void Mesh::GetLocalFaceTransformation(
840 int face_type, int elem_type, IsoparametricTransformation &Transf, int info)
841 {
842 switch (face_type)
843 {
844 case Element::POINT:
845 GetLocalPtToSegTransformation(Transf, info);
846 break;
847
848 case Element::SEGMENT:
849 if (elem_type == Element::TRIANGLE)
850 {
851 GetLocalSegToTriTransformation(Transf, info);
852 }
853 else
854 {
855 MFEM_ASSERT(elem_type == Element::QUADRILATERAL, "");
856 GetLocalSegToQuadTransformation(Transf, info);
857 }
858 break;
859
860 case Element::TRIANGLE:
861 if (elem_type == Element::TETRAHEDRON)
862 {
863 GetLocalTriToTetTransformation(Transf, info);
864 }
865 else
866 {
867 MFEM_ASSERT(elem_type == Element::WEDGE, "");
868 GetLocalTriToWdgTransformation(Transf, info);
869 }
870 break;
871
872 case Element::QUADRILATERAL:
873 if (elem_type == Element::HEXAHEDRON)
874 {
875 GetLocalQuadToHexTransformation(Transf, info);
876 }
877 else
878 {
879 MFEM_ASSERT(elem_type == Element::WEDGE, "");
880 GetLocalQuadToWdgTransformation(Transf, info);
881 }
882 break;
883 }
884 }
885
GetFaceElementTransformations(int FaceNo,int mask)886 FaceElementTransformations *Mesh::GetFaceElementTransformations(int FaceNo,
887 int mask)
888 {
889 FaceInfo &face_info = faces_info[FaceNo];
890
891 int cmask = 0;
892 FaceElemTr.SetConfigurationMask(cmask);
893 FaceElemTr.Elem1 = NULL;
894 FaceElemTr.Elem2 = NULL;
895
896 // setup the transformation for the first element
897 FaceElemTr.Elem1No = face_info.Elem1No;
898 if (mask & FaceElementTransformations::HAVE_ELEM1)
899 {
900 GetElementTransformation(FaceElemTr.Elem1No, &Transformation);
901 FaceElemTr.Elem1 = &Transformation;
902 cmask |= 1;
903 }
904
905 // setup the transformation for the second element
906 // return NULL in the Elem2 field if there's no second element, i.e.
907 // the face is on the "boundary"
908 FaceElemTr.Elem2No = face_info.Elem2No;
909 if ((mask & FaceElementTransformations::HAVE_ELEM2) &&
910 FaceElemTr.Elem2No >= 0)
911 {
912 #ifdef MFEM_DEBUG
913 if (NURBSext && (mask & FaceElementTransformations::HAVE_ELEM1))
914 { MFEM_ABORT("NURBS mesh not supported!"); }
915 #endif
916 GetElementTransformation(FaceElemTr.Elem2No, &Transformation2);
917 FaceElemTr.Elem2 = &Transformation2;
918 cmask |= 2;
919 }
920
921 // setup the face transformation
922 if (mask & FaceElementTransformations::HAVE_FACE)
923 {
924 GetFaceTransformation(FaceNo, &FaceElemTr);
925 cmask |= 16;
926 }
927 else
928 {
929 FaceElemTr.SetGeometryType(GetFaceGeometryType(FaceNo));
930 }
931
932 // setup Loc1 & Loc2
933 int face_type = GetFaceElementType(FaceNo);
934 if (mask & FaceElementTransformations::HAVE_LOC1)
935 {
936 int elem_type = GetElementType(face_info.Elem1No);
937 GetLocalFaceTransformation(face_type, elem_type,
938 FaceElemTr.Loc1.Transf, face_info.Elem1Inf);
939 cmask |= 4;
940 }
941 if ((mask & FaceElementTransformations::HAVE_LOC2) &&
942 FaceElemTr.Elem2No >= 0)
943 {
944 int elem_type = GetElementType(face_info.Elem2No);
945 GetLocalFaceTransformation(face_type, elem_type,
946 FaceElemTr.Loc2.Transf, face_info.Elem2Inf);
947
948 // NC meshes: prepend slave edge/face transformation to Loc2
949 if (Nonconforming() && IsSlaveFace(face_info))
950 {
951 ApplyLocalSlaveTransformation(FaceElemTr, face_info, false);
952 }
953 cmask |= 8;
954 }
955
956 FaceElemTr.SetConfigurationMask(cmask);
957
958 // This check can be useful for internal debugging, however it will fail on
959 // periodic boundary faces, so we keep it disabled in general.
960 #if 0
961 #ifdef MFEM_DEBUG
962 double dist = FaceElemTr.CheckConsistency();
963 if (dist >= 1e-12)
964 {
965 mfem::out << "\nInternal error: face id = " << FaceNo
966 << ", dist = " << dist << '\n';
967 FaceElemTr.CheckConsistency(1); // print coordinates
968 MFEM_ABORT("internal error");
969 }
970 #endif
971 #endif
972
973 return &FaceElemTr;
974 }
975
IsSlaveFace(const FaceInfo & fi) const976 bool Mesh::IsSlaveFace(const FaceInfo &fi) const
977 {
978 return fi.NCFace >= 0 && nc_faces_info[fi.NCFace].Slave;
979 }
980
ApplyLocalSlaveTransformation(FaceElementTransformations & FT,const FaceInfo & fi,bool is_ghost)981 void Mesh::ApplyLocalSlaveTransformation(FaceElementTransformations &FT,
982 const FaceInfo &fi, bool is_ghost)
983 {
984 #ifdef MFEM_THREAD_SAFE
985 DenseMatrix composition;
986 #else
987 static DenseMatrix composition;
988 #endif
989 MFEM_ASSERT(fi.NCFace >= 0, "");
990 MFEM_ASSERT(nc_faces_info[fi.NCFace].Slave, "internal error");
991 if (!is_ghost)
992 {
993 // side 1 -> child side, side 2 -> parent side
994 IsoparametricTransformation < = FT.Loc2.Transf;
995 LT.Transform(*nc_faces_info[fi.NCFace].PointMatrix, composition);
996 // In 2D, we need to flip the point matrix since it is aligned with the
997 // parent side.
998 if (Dim == 2)
999 {
1000 // swap points (columns) 0 and 1
1001 std::swap(composition(0,0), composition(0,1));
1002 std::swap(composition(1,0), composition(1,1));
1003 }
1004 LT.SetPointMat(composition);
1005 }
1006 else // is_ghost == true
1007 {
1008 // side 1 -> parent side, side 2 -> child side
1009 IsoparametricTransformation < = FT.Loc1.Transf;
1010 LT.Transform(*nc_faces_info[fi.NCFace].PointMatrix, composition);
1011 // In 2D, there is no need to flip the point matrix since it is already
1012 // aligned with the parent side, see also ParNCMesh::GetFaceNeighbors.
1013 // In 3D the point matrix was flipped during construction in
1014 // ParNCMesh::GetFaceNeighbors and due to that it is already aligned with
1015 // the parent side.
1016 LT.SetPointMat(composition);
1017 }
1018 }
1019
GetBdrFaceTransformations(int BdrElemNo)1020 FaceElementTransformations *Mesh::GetBdrFaceTransformations(int BdrElemNo)
1021 {
1022 FaceElementTransformations *tr;
1023 int fn = GetBdrFace(BdrElemNo);
1024
1025 // Check if the face is interior, shared, or non-conforming.
1026 if (FaceIsTrueInterior(fn) || faces_info[fn].NCFace >= 0)
1027 {
1028 return NULL;
1029 }
1030 tr = GetFaceElementTransformations(fn, 21);
1031 tr->Attribute = boundary[BdrElemNo]->GetAttribute();
1032 tr->ElementNo = BdrElemNo;
1033 tr->ElementType = ElementTransformation::BDR_FACE;
1034 return tr;
1035 }
1036
GetBdrFace(int BdrElemNo) const1037 int Mesh::GetBdrFace(int BdrElemNo) const
1038 {
1039 int fn;
1040 if (Dim == 3)
1041 {
1042 fn = be_to_face[BdrElemNo];
1043 }
1044 else if (Dim == 2)
1045 {
1046 fn = be_to_edge[BdrElemNo];
1047 }
1048 else
1049 {
1050 fn = boundary[BdrElemNo]->GetVertices()[0];
1051 }
1052 return fn;
1053 }
1054
GetFaceElements(int Face,int * Elem1,int * Elem2) const1055 void Mesh::GetFaceElements(int Face, int *Elem1, int *Elem2) const
1056 {
1057 *Elem1 = faces_info[Face].Elem1No;
1058 *Elem2 = faces_info[Face].Elem2No;
1059 }
1060
GetFaceInfos(int Face,int * Inf1,int * Inf2) const1061 void Mesh::GetFaceInfos(int Face, int *Inf1, int *Inf2) const
1062 {
1063 *Inf1 = faces_info[Face].Elem1Inf;
1064 *Inf2 = faces_info[Face].Elem2Inf;
1065 }
1066
GetFaceInfos(int Face,int * Inf1,int * Inf2,int * NCFace) const1067 void Mesh::GetFaceInfos(int Face, int *Inf1, int *Inf2, int *NCFace) const
1068 {
1069 *Inf1 = faces_info[Face].Elem1Inf;
1070 *Inf2 = faces_info[Face].Elem2Inf;
1071 *NCFace = faces_info[Face].NCFace;
1072 }
1073
GetFaceGeometryType(int Face) const1074 Geometry::Type Mesh::GetFaceGeometryType(int Face) const
1075 {
1076 switch (Dim)
1077 {
1078 case 1: return Geometry::POINT;
1079 case 2: return Geometry::SEGMENT;
1080 case 3:
1081 if (Face < NumOfFaces) // local (non-ghost) face
1082 {
1083 return faces[Face]->GetGeometryType();
1084 }
1085 // ghost face
1086 const int nc_face_id = faces_info[Face].NCFace;
1087 MFEM_ASSERT(nc_face_id >= 0, "parent ghost faces are not supported");
1088 return faces[nc_faces_info[nc_face_id].MasterFace]->GetGeometryType();
1089 }
1090 return Geometry::INVALID;
1091 }
1092
GetFaceElementType(int Face) const1093 Element::Type Mesh::GetFaceElementType(int Face) const
1094 {
1095 return (Dim == 1) ? Element::POINT : faces[Face]->GetType();
1096 }
1097
Init()1098 void Mesh::Init()
1099 {
1100 // in order of declaration:
1101 Dim = spaceDim = 0;
1102 NumOfVertices = -1;
1103 NumOfElements = NumOfBdrElements = 0;
1104 NumOfEdges = NumOfFaces = 0;
1105 nbInteriorFaces = -1;
1106 nbBoundaryFaces = -1;
1107 meshgen = mesh_geoms = 0;
1108 sequence = 0;
1109 Nodes = NULL;
1110 own_nodes = 1;
1111 NURBSext = NULL;
1112 ncmesh = NULL;
1113 last_operation = Mesh::NONE;
1114 }
1115
InitTables()1116 void Mesh::InitTables()
1117 {
1118 el_to_edge =
1119 el_to_face = el_to_el = bel_to_edge = face_edge = edge_vertex = NULL;
1120 }
1121
SetEmpty()1122 void Mesh::SetEmpty()
1123 {
1124 Init();
1125 InitTables();
1126 }
1127
DestroyTables()1128 void Mesh::DestroyTables()
1129 {
1130 delete el_to_edge;
1131 delete el_to_face;
1132 delete el_to_el;
1133 DeleteGeometricFactors();
1134
1135 if (Dim == 3)
1136 {
1137 delete bel_to_edge;
1138 }
1139
1140 delete face_edge;
1141 delete edge_vertex;
1142 }
1143
DestroyPointers()1144 void Mesh::DestroyPointers()
1145 {
1146 if (own_nodes) { delete Nodes; }
1147
1148 delete ncmesh;
1149
1150 delete NURBSext;
1151
1152 for (int i = 0; i < NumOfElements; i++)
1153 {
1154 FreeElement(elements[i]);
1155 }
1156
1157 for (int i = 0; i < NumOfBdrElements; i++)
1158 {
1159 FreeElement(boundary[i]);
1160 }
1161
1162 for (int i = 0; i < faces.Size(); i++)
1163 {
1164 FreeElement(faces[i]);
1165 }
1166
1167 DestroyTables();
1168 }
1169
Destroy()1170 void Mesh::Destroy()
1171 {
1172 DestroyPointers();
1173
1174 elements.DeleteAll();
1175 vertices.DeleteAll();
1176 boundary.DeleteAll();
1177 faces.DeleteAll();
1178 faces_info.DeleteAll();
1179 nc_faces_info.DeleteAll();
1180 be_to_edge.DeleteAll();
1181 be_to_face.DeleteAll();
1182
1183 // TODO:
1184 // IsoparametricTransformations
1185 // Transformation, Transformation2, BdrTransformation, FaceTransformation,
1186 // EdgeTransformation;
1187 // FaceElementTransformations FaceElemTr;
1188
1189 CoarseFineTr.Clear();
1190
1191 #ifdef MFEM_USE_MEMALLOC
1192 TetMemory.Clear();
1193 #endif
1194
1195 attributes.DeleteAll();
1196 bdr_attributes.DeleteAll();
1197 }
1198
ResetLazyData()1199 void Mesh::ResetLazyData()
1200 {
1201 delete el_to_el; el_to_el = NULL;
1202 delete face_edge; face_edge = NULL;
1203 delete edge_vertex; edge_vertex = NULL;
1204 DeleteGeometricFactors();
1205 nbInteriorFaces = -1;
1206 nbBoundaryFaces = -1;
1207 }
1208
SetAttributes()1209 void Mesh::SetAttributes()
1210 {
1211 Array<int> attribs;
1212
1213 attribs.SetSize(GetNBE());
1214 for (int i = 0; i < attribs.Size(); i++)
1215 {
1216 attribs[i] = GetBdrAttribute(i);
1217 }
1218 attribs.Sort();
1219 attribs.Unique();
1220 attribs.Copy(bdr_attributes);
1221 if (bdr_attributes.Size() > 0 && bdr_attributes[0] <= 0)
1222 {
1223 MFEM_WARNING("Non-positive attributes on the boundary!");
1224 }
1225
1226 attribs.SetSize(GetNE());
1227 for (int i = 0; i < attribs.Size(); i++)
1228 {
1229 attribs[i] = GetAttribute(i);
1230 }
1231 attribs.Sort();
1232 attribs.Unique();
1233 attribs.Copy(attributes);
1234 if (attributes.Size() > 0 && attributes[0] <= 0)
1235 {
1236 MFEM_WARNING("Non-positive attributes in the domain!");
1237 }
1238 }
1239
InitMesh(int Dim_,int spaceDim_,int NVert,int NElem,int NBdrElem)1240 void Mesh::InitMesh(int Dim_, int spaceDim_, int NVert, int NElem, int NBdrElem)
1241 {
1242 SetEmpty();
1243
1244 Dim = Dim_;
1245 spaceDim = spaceDim_;
1246
1247 NumOfVertices = 0;
1248 vertices.SetSize(NVert); // just allocate space for vertices
1249
1250 NumOfElements = 0;
1251 elements.SetSize(NElem); // just allocate space for Element *
1252
1253 NumOfBdrElements = 0;
1254 boundary.SetSize(NBdrElem); // just allocate space for Element *
1255 }
1256
1257 template<typename T>
CheckEnlarge(Array<T> & array,int size)1258 static void CheckEnlarge(Array<T> &array, int size)
1259 {
1260 if (size >= array.Size()) { array.SetSize(size + 1); }
1261 }
1262
AddVertex(double x,double y,double z)1263 int Mesh::AddVertex(double x, double y, double z)
1264 {
1265 CheckEnlarge(vertices, NumOfVertices);
1266 double *v = vertices[NumOfVertices]();
1267 v[0] = x;
1268 v[1] = y;
1269 v[2] = z;
1270 return NumOfVertices++;
1271 }
1272
AddVertex(const double * coords)1273 int Mesh::AddVertex(const double *coords)
1274 {
1275 CheckEnlarge(vertices, NumOfVertices);
1276 vertices[NumOfVertices].SetCoords(spaceDim, coords);
1277 return NumOfVertices++;
1278 }
1279
AddVertexParents(int i,int p1,int p2)1280 void Mesh::AddVertexParents(int i, int p1, int p2)
1281 {
1282 tmp_vertex_parents.Append(Triple<int, int, int>(i, p1, p2));
1283
1284 // if vertex coordinates are defined, make sure the hanging vertex has the
1285 // correct position
1286 if (i < vertices.Size())
1287 {
1288 double *vi = vertices[i](), *vp1 = vertices[p1](), *vp2 = vertices[p2]();
1289 for (int j = 0; j < 3; j++)
1290 {
1291 vi[j] = (vp1[j] + vp2[j]) * 0.5;
1292 }
1293 }
1294 }
1295
AddSegment(int v1,int v2,int attr)1296 int Mesh::AddSegment(int v1, int v2, int attr)
1297 {
1298 CheckEnlarge(elements, NumOfElements);
1299 elements[NumOfElements] = new Segment(v1, v2, attr);
1300 return NumOfElements++;
1301 }
1302
AddSegment(const int * vi,int attr)1303 int Mesh::AddSegment(const int *vi, int attr)
1304 {
1305 CheckEnlarge(elements, NumOfElements);
1306 elements[NumOfElements] = new Segment(vi, attr);
1307 return NumOfElements++;
1308 }
1309
AddTriangle(int v1,int v2,int v3,int attr)1310 int Mesh::AddTriangle(int v1, int v2, int v3, int attr)
1311 {
1312 CheckEnlarge(elements, NumOfElements);
1313 elements[NumOfElements] = new Triangle(v1, v2, v3, attr);
1314 return NumOfElements++;
1315 }
1316
AddTriangle(const int * vi,int attr)1317 int Mesh::AddTriangle(const int *vi, int attr)
1318 {
1319 CheckEnlarge(elements, NumOfElements);
1320 elements[NumOfElements] = new Triangle(vi, attr);
1321 return NumOfElements++;
1322 }
1323
AddQuad(int v1,int v2,int v3,int v4,int attr)1324 int Mesh::AddQuad(int v1, int v2, int v3, int v4, int attr)
1325 {
1326 CheckEnlarge(elements, NumOfElements);
1327 elements[NumOfElements] = new Quadrilateral(v1, v2, v3, v4, attr);
1328 return NumOfElements++;
1329 }
1330
AddQuad(const int * vi,int attr)1331 int Mesh::AddQuad(const int *vi, int attr)
1332 {
1333 CheckEnlarge(elements, NumOfElements);
1334 elements[NumOfElements] = new Quadrilateral(vi, attr);
1335 return NumOfElements++;
1336 }
1337
AddTet(int v1,int v2,int v3,int v4,int attr)1338 int Mesh::AddTet(int v1, int v2, int v3, int v4, int attr)
1339 {
1340 int vi[4] = {v1, v2, v3, v4};
1341 return AddTet(vi, attr);
1342 }
1343
AddTet(const int * vi,int attr)1344 int Mesh::AddTet(const int *vi, int attr)
1345 {
1346 CheckEnlarge(elements, NumOfElements);
1347 #ifdef MFEM_USE_MEMALLOC
1348 Tetrahedron *tet;
1349 tet = TetMemory.Alloc();
1350 tet->SetVertices(vi);
1351 tet->SetAttribute(attr);
1352 elements[NumOfElements] = tet;
1353 #else
1354 elements[NumOfElements] = new Tetrahedron(vi, attr);
1355 #endif
1356 return NumOfElements++;
1357 }
1358
AddWedge(int v1,int v2,int v3,int v4,int v5,int v6,int attr)1359 int Mesh::AddWedge(int v1, int v2, int v3, int v4, int v5, int v6, int attr)
1360 {
1361 CheckEnlarge(elements, NumOfElements);
1362 elements[NumOfElements] = new Wedge(v1, v2, v3, v4, v5, v6, attr);
1363 return NumOfElements++;
1364 }
1365
AddWedge(const int * vi,int attr)1366 int Mesh::AddWedge(const int *vi, int attr)
1367 {
1368 CheckEnlarge(elements, NumOfElements);
1369 elements[NumOfElements] = new Wedge(vi, attr);
1370 return NumOfElements++;
1371 }
1372
AddHex(int v1,int v2,int v3,int v4,int v5,int v6,int v7,int v8,int attr)1373 int Mesh::AddHex(int v1, int v2, int v3, int v4, int v5, int v6, int v7, int v8,
1374 int attr)
1375 {
1376 CheckEnlarge(elements, NumOfElements);
1377 elements[NumOfElements] =
1378 new Hexahedron(v1, v2, v3, v4, v5, v6, v7, v8, attr);
1379 return NumOfElements++;
1380 }
1381
AddHex(const int * vi,int attr)1382 int Mesh::AddHex(const int *vi, int attr)
1383 {
1384 CheckEnlarge(elements, NumOfElements);
1385 elements[NumOfElements] = new Hexahedron(vi, attr);
1386 return NumOfElements++;
1387 }
1388
AddHexAsTets(const int * vi,int attr)1389 void Mesh::AddHexAsTets(const int *vi, int attr)
1390 {
1391 static const int hex_to_tet[6][4] =
1392 {
1393 { 0, 1, 2, 6 }, { 0, 5, 1, 6 }, { 0, 4, 5, 6 },
1394 { 0, 2, 3, 6 }, { 0, 3, 7, 6 }, { 0, 7, 4, 6 }
1395 };
1396 int ti[4];
1397
1398 for (int i = 0; i < 6; i++)
1399 {
1400 for (int j = 0; j < 4; j++)
1401 {
1402 ti[j] = vi[hex_to_tet[i][j]];
1403 }
1404 AddTet(ti, attr);
1405 }
1406 }
1407
AddHexAsWedges(const int * vi,int attr)1408 void Mesh::AddHexAsWedges(const int *vi, int attr)
1409 {
1410 static const int hex_to_wdg[2][6] =
1411 {
1412 { 0, 1, 2, 4, 5, 6 }, { 0, 2, 3, 4, 6, 7 }
1413 };
1414 int ti[6];
1415
1416 for (int i = 0; i < 2; i++)
1417 {
1418 for (int j = 0; j < 6; j++)
1419 {
1420 ti[j] = vi[hex_to_wdg[i][j]];
1421 }
1422 AddWedge(ti, attr);
1423 }
1424 }
1425
AddElement(Element * elem)1426 int Mesh::AddElement(Element *elem)
1427 {
1428 CheckEnlarge(elements, NumOfElements);
1429 elements[NumOfElements] = elem;
1430 return NumOfElements++;
1431 }
1432
AddBdrElement(Element * elem)1433 int Mesh::AddBdrElement(Element *elem)
1434 {
1435 CheckEnlarge(boundary, NumOfBdrElements);
1436 boundary[NumOfBdrElements] = elem;
1437 return NumOfBdrElements++;
1438 }
1439
AddBdrSegment(int v1,int v2,int attr)1440 int Mesh::AddBdrSegment(int v1, int v2, int attr)
1441 {
1442 CheckEnlarge(boundary, NumOfBdrElements);
1443 boundary[NumOfBdrElements] = new Segment(v1, v2, attr);
1444 return NumOfBdrElements++;
1445 }
1446
AddBdrSegment(const int * vi,int attr)1447 int Mesh::AddBdrSegment(const int *vi, int attr)
1448 {
1449 CheckEnlarge(boundary, NumOfBdrElements);
1450 boundary[NumOfBdrElements] = new Segment(vi, attr);
1451 return NumOfBdrElements++;
1452 }
1453
AddBdrTriangle(int v1,int v2,int v3,int attr)1454 int Mesh::AddBdrTriangle(int v1, int v2, int v3, int attr)
1455 {
1456 CheckEnlarge(boundary, NumOfBdrElements);
1457 boundary[NumOfBdrElements] = new Triangle(v1, v2, v3, attr);
1458 return NumOfBdrElements++;
1459 }
1460
AddBdrTriangle(const int * vi,int attr)1461 int Mesh::AddBdrTriangle(const int *vi, int attr)
1462 {
1463 CheckEnlarge(boundary, NumOfBdrElements);
1464 boundary[NumOfBdrElements] = new Triangle(vi, attr);
1465 return NumOfBdrElements++;
1466 }
1467
AddBdrQuad(int v1,int v2,int v3,int v4,int attr)1468 int Mesh::AddBdrQuad(int v1, int v2, int v3, int v4, int attr)
1469 {
1470 CheckEnlarge(boundary, NumOfBdrElements);
1471 boundary[NumOfBdrElements] = new Quadrilateral(v1, v2, v3, v4, attr);
1472 return NumOfBdrElements++;
1473 }
1474
AddBdrQuad(const int * vi,int attr)1475 int Mesh::AddBdrQuad(const int *vi, int attr)
1476 {
1477 CheckEnlarge(boundary, NumOfBdrElements);
1478 boundary[NumOfBdrElements] = new Quadrilateral(vi, attr);
1479 return NumOfBdrElements++;
1480 }
1481
AddBdrQuadAsTriangles(const int * vi,int attr)1482 void Mesh::AddBdrQuadAsTriangles(const int *vi, int attr)
1483 {
1484 static const int quad_to_tri[2][3] = { { 0, 1, 2 }, { 0, 2, 3 } };
1485 int ti[3];
1486
1487 for (int i = 0; i < 2; i++)
1488 {
1489 for (int j = 0; j < 3; j++)
1490 {
1491 ti[j] = vi[quad_to_tri[i][j]];
1492 }
1493 AddBdrTriangle(ti, attr);
1494 }
1495 }
1496
AddBdrPoint(int v,int attr)1497 int Mesh::AddBdrPoint(int v, int attr)
1498 {
1499 CheckEnlarge(boundary, NumOfBdrElements);
1500 boundary[NumOfBdrElements] = new Point(&v, attr);
1501 return NumOfBdrElements++;
1502 }
1503
GenerateBoundaryElements()1504 void Mesh::GenerateBoundaryElements()
1505 {
1506 int i, j;
1507 Array<int> &be2face = (Dim == 2) ? be_to_edge : be_to_face;
1508
1509 // GenerateFaces();
1510
1511 for (i = 0; i < boundary.Size(); i++)
1512 {
1513 FreeElement(boundary[i]);
1514 }
1515
1516 if (Dim == 3)
1517 {
1518 delete bel_to_edge;
1519 bel_to_edge = NULL;
1520 }
1521
1522 // count the 'NumOfBdrElements'
1523 NumOfBdrElements = 0;
1524 for (i = 0; i < faces_info.Size(); i++)
1525 {
1526 if (faces_info[i].Elem2No < 0) { NumOfBdrElements++; }
1527 }
1528
1529 boundary.SetSize(NumOfBdrElements);
1530 be2face.SetSize(NumOfBdrElements);
1531 for (j = i = 0; i < faces_info.Size(); i++)
1532 {
1533 if (faces_info[i].Elem2No < 0)
1534 {
1535 boundary[j] = faces[i]->Duplicate(this);
1536 be2face[j++] = i;
1537 }
1538 }
1539 // In 3D, 'bel_to_edge' is destroyed but it's not updated.
1540 }
1541
FinalizeCheck()1542 void Mesh::FinalizeCheck()
1543 {
1544 MFEM_VERIFY(vertices.Size() == NumOfVertices ||
1545 vertices.Size() == 0,
1546 "incorrect number of vertices: preallocated: " << vertices.Size()
1547 << ", actually added: " << NumOfVertices);
1548 MFEM_VERIFY(elements.Size() == NumOfElements,
1549 "incorrect number of elements: preallocated: " << elements.Size()
1550 << ", actually added: " << NumOfElements);
1551 MFEM_VERIFY(boundary.Size() == NumOfBdrElements,
1552 "incorrect number of boundary elements: preallocated: "
1553 << boundary.Size() << ", actually added: " << NumOfBdrElements);
1554 }
1555
FinalizeTriMesh(int generate_edges,int refine,bool fix_orientation)1556 void Mesh::FinalizeTriMesh(int generate_edges, int refine, bool fix_orientation)
1557 {
1558 FinalizeCheck();
1559 CheckElementOrientation(fix_orientation);
1560
1561 if (refine)
1562 {
1563 MarkTriMeshForRefinement();
1564 }
1565
1566 if (generate_edges)
1567 {
1568 el_to_edge = new Table;
1569 NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
1570 GenerateFaces();
1571 CheckBdrElementOrientation();
1572 }
1573 else
1574 {
1575 NumOfEdges = 0;
1576 }
1577
1578 NumOfFaces = 0;
1579
1580 SetAttributes();
1581
1582 SetMeshGen();
1583 }
1584
FinalizeQuadMesh(int generate_edges,int refine,bool fix_orientation)1585 void Mesh::FinalizeQuadMesh(int generate_edges, int refine,
1586 bool fix_orientation)
1587 {
1588 FinalizeCheck();
1589 if (fix_orientation)
1590 {
1591 CheckElementOrientation(fix_orientation);
1592 }
1593
1594 if (generate_edges)
1595 {
1596 el_to_edge = new Table;
1597 NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
1598 GenerateFaces();
1599 CheckBdrElementOrientation();
1600 }
1601 else
1602 {
1603 NumOfEdges = 0;
1604 }
1605
1606 NumOfFaces = 0;
1607
1608 SetAttributes();
1609
1610 SetMeshGen();
1611 }
1612
1613
1614 class GeckoProgress : public Gecko::Progress
1615 {
1616 double limit;
1617 mutable StopWatch sw;
1618 public:
GeckoProgress(double limit)1619 GeckoProgress(double limit) : limit(limit) { sw.Start(); }
quit() const1620 virtual bool quit() const { return limit > 0 && sw.UserTime() > limit; }
1621 };
1622
1623 class GeckoVerboseProgress : public GeckoProgress
1624 {
1625 using Float = Gecko::Float;
1626 using Graph = Gecko::Graph;
1627 using uint = Gecko::uint;
1628 public:
GeckoVerboseProgress(double limit)1629 GeckoVerboseProgress(double limit) : GeckoProgress(limit) {}
1630
beginorder(const Graph * graph,Float cost) const1631 virtual void beginorder(const Graph* graph, Float cost) const
1632 { mfem::out << "Begin Gecko ordering, cost = " << cost << std::endl; }
endorder(const Graph * graph,Float cost) const1633 virtual void endorder(const Graph* graph, Float cost) const
1634 { mfem::out << "End ordering, cost = " << cost << std::endl; }
1635
beginiter(const Graph * graph,uint iter,uint maxiter,uint window) const1636 virtual void beginiter(const Graph* graph,
1637 uint iter, uint maxiter, uint window) const
1638 {
1639 mfem::out << "Iteration " << iter << "/" << maxiter << ", window "
1640 << window << std::flush;
1641 }
enditer(const Graph * graph,Float mincost,Float cost) const1642 virtual void enditer(const Graph* graph, Float mincost, Float cost) const
1643 { mfem::out << ", cost = " << cost << endl; }
1644 };
1645
1646
GetGeckoElementOrdering(Array<int> & ordering,int iterations,int window,int period,int seed,bool verbose,double time_limit)1647 double Mesh::GetGeckoElementOrdering(Array<int> &ordering,
1648 int iterations, int window,
1649 int period, int seed, bool verbose,
1650 double time_limit)
1651 {
1652 Gecko::Graph graph;
1653 Gecko::FunctionalGeometric functional; // edge product cost
1654
1655 GeckoProgress progress(time_limit);
1656 GeckoVerboseProgress vprogress(time_limit);
1657
1658 // insert elements as nodes in the graph
1659 for (int elemid = 0; elemid < GetNE(); ++elemid)
1660 {
1661 graph.insert_node();
1662 }
1663
1664 // insert graph edges for element neighbors
1665 // NOTE: indices in Gecko are 1 based hence the +1 on insertion
1666 const Table &my_el_to_el = ElementToElementTable();
1667 for (int elemid = 0; elemid < GetNE(); ++elemid)
1668 {
1669 const int *neighid = my_el_to_el.GetRow(elemid);
1670 for (int i = 0; i < my_el_to_el.RowSize(elemid); ++i)
1671 {
1672 graph.insert_arc(elemid + 1, neighid[i] + 1);
1673 }
1674 }
1675
1676 // get the ordering from Gecko and copy it into the Array<int>
1677 graph.order(&functional, iterations, window, period, seed,
1678 verbose ? &vprogress : &progress);
1679
1680 ordering.SetSize(GetNE());
1681 Gecko::Node::Index NE = GetNE();
1682 for (Gecko::Node::Index gnodeid = 1; gnodeid <= NE; ++gnodeid)
1683 {
1684 ordering[gnodeid - 1] = graph.rank(gnodeid);
1685 }
1686
1687 return graph.cost();
1688 }
1689
1690
1691 struct HilbertCmp
1692 {
1693 int coord;
1694 bool dir;
1695 const Array<double> &points;
1696 double mid;
1697
HilbertCmpmfem::HilbertCmp1698 HilbertCmp(int coord, bool dir, const Array<double> &points, double mid)
1699 : coord(coord), dir(dir), points(points), mid(mid) {}
1700
operator ()mfem::HilbertCmp1701 bool operator()(int i) const
1702 {
1703 return (points[3*i + coord] < mid) != dir;
1704 }
1705 };
1706
HilbertSort2D(int coord1,bool dir1,bool dir2,const Array<double> & points,int * beg,int * end,double xmin,double ymin,double xmax,double ymax)1707 static void HilbertSort2D(int coord1, // major coordinate to sort points by
1708 bool dir1, // sort coord1 ascending/descending?
1709 bool dir2, // sort coord2 ascending/descending?
1710 const Array<double> &points, int *beg, int *end,
1711 double xmin, double ymin, double xmax, double ymax)
1712 {
1713 if (end - beg <= 1) { return; }
1714
1715 double xmid = (xmin + xmax)*0.5;
1716 double ymid = (ymin + ymax)*0.5;
1717
1718 int coord2 = (coord1 + 1) % 2; // the 'other' coordinate
1719
1720 // sort (partition) points into four quadrants
1721 int *p0 = beg, *p4 = end;
1722 int *p2 = std::partition(p0, p4, HilbertCmp(coord1, dir1, points, xmid));
1723 int *p1 = std::partition(p0, p2, HilbertCmp(coord2, dir2, points, ymid));
1724 int *p3 = std::partition(p2, p4, HilbertCmp(coord2, !dir2, points, ymid));
1725
1726 if (p1 != p4)
1727 {
1728 HilbertSort2D(coord2, dir2, dir1, points, p0, p1,
1729 ymin, xmin, ymid, xmid);
1730 }
1731 if (p1 != p0 || p2 != p4)
1732 {
1733 HilbertSort2D(coord1, dir1, dir2, points, p1, p2,
1734 xmin, ymid, xmid, ymax);
1735 }
1736 if (p2 != p0 || p3 != p4)
1737 {
1738 HilbertSort2D(coord1, dir1, dir2, points, p2, p3,
1739 xmid, ymid, xmax, ymax);
1740 }
1741 if (p3 != p0)
1742 {
1743 HilbertSort2D(coord2, !dir2, !dir1, points, p3, p4,
1744 ymid, xmax, ymin, xmid);
1745 }
1746 }
1747
HilbertSort3D(int coord1,bool dir1,bool dir2,bool dir3,const Array<double> & points,int * beg,int * end,double xmin,double ymin,double zmin,double xmax,double ymax,double zmax)1748 static void HilbertSort3D(int coord1, bool dir1, bool dir2, bool dir3,
1749 const Array<double> &points, int *beg, int *end,
1750 double xmin, double ymin, double zmin,
1751 double xmax, double ymax, double zmax)
1752 {
1753 if (end - beg <= 1) { return; }
1754
1755 double xmid = (xmin + xmax)*0.5;
1756 double ymid = (ymin + ymax)*0.5;
1757 double zmid = (zmin + zmax)*0.5;
1758
1759 int coord2 = (coord1 + 1) % 3;
1760 int coord3 = (coord1 + 2) % 3;
1761
1762 // sort (partition) points into eight octants
1763 int *p0 = beg, *p8 = end;
1764 int *p4 = std::partition(p0, p8, HilbertCmp(coord1, dir1, points, xmid));
1765 int *p2 = std::partition(p0, p4, HilbertCmp(coord2, dir2, points, ymid));
1766 int *p6 = std::partition(p4, p8, HilbertCmp(coord2, !dir2, points, ymid));
1767 int *p1 = std::partition(p0, p2, HilbertCmp(coord3, dir3, points, zmid));
1768 int *p3 = std::partition(p2, p4, HilbertCmp(coord3, !dir3, points, zmid));
1769 int *p5 = std::partition(p4, p6, HilbertCmp(coord3, dir3, points, zmid));
1770 int *p7 = std::partition(p6, p8, HilbertCmp(coord3, !dir3, points, zmid));
1771
1772 if (p1 != p8)
1773 {
1774 HilbertSort3D(coord3, dir3, dir1, dir2, points, p0, p1,
1775 zmin, xmin, ymin, zmid, xmid, ymid);
1776 }
1777 if (p1 != p0 || p2 != p8)
1778 {
1779 HilbertSort3D(coord2, dir2, dir3, dir1, points, p1, p2,
1780 ymin, zmid, xmin, ymid, zmax, xmid);
1781 }
1782 if (p2 != p0 || p3 != p8)
1783 {
1784 HilbertSort3D(coord2, dir2, dir3, dir1, points, p2, p3,
1785 ymid, zmid, xmin, ymax, zmax, xmid);
1786 }
1787 if (p3 != p0 || p4 != p8)
1788 {
1789 HilbertSort3D(coord1, dir1, !dir2, !dir3, points, p3, p4,
1790 xmin, ymax, zmid, xmid, ymid, zmin);
1791 }
1792 if (p4 != p0 || p5 != p8)
1793 {
1794 HilbertSort3D(coord1, dir1, !dir2, !dir3, points, p4, p5,
1795 xmid, ymax, zmid, xmax, ymid, zmin);
1796 }
1797 if (p5 != p0 || p6 != p8)
1798 {
1799 HilbertSort3D(coord2, !dir2, dir3, !dir1, points, p5, p6,
1800 ymax, zmid, xmax, ymid, zmax, xmid);
1801 }
1802 if (p6 != p0 || p7 != p8)
1803 {
1804 HilbertSort3D(coord2, !dir2, dir3, !dir1, points, p6, p7,
1805 ymid, zmid, xmax, ymin, zmax, xmid);
1806 }
1807 if (p7 != p0)
1808 {
1809 HilbertSort3D(coord3, !dir3, !dir1, dir2, points, p7, p8,
1810 zmid, xmax, ymin, zmin, xmid, ymid);
1811 }
1812 }
1813
GetHilbertElementOrdering(Array<int> & ordering)1814 void Mesh::GetHilbertElementOrdering(Array<int> &ordering)
1815 {
1816 MFEM_VERIFY(spaceDim <= 3, "");
1817
1818 Vector min, max, center;
1819 GetBoundingBox(min, max);
1820
1821 Array<int> indices(GetNE());
1822 Array<double> points(3*GetNE());
1823
1824 if (spaceDim < 3) { points = 0.0; }
1825
1826 // calculate element centers
1827 for (int i = 0; i < GetNE(); i++)
1828 {
1829 GetElementCenter(i, center);
1830 for (int j = 0; j < spaceDim; j++)
1831 {
1832 points[3*i + j] = center(j);
1833 }
1834 indices[i] = i;
1835 }
1836
1837 if (spaceDim == 1)
1838 {
1839 indices.Sort([&](int a, int b)
1840 { return points[3*a] < points[3*b]; });
1841 }
1842 else if (spaceDim == 2)
1843 {
1844 // recursively partition the points in 2D
1845 HilbertSort2D(0, false, false,
1846 points, indices.begin(), indices.end(),
1847 min(0), min(1), max(0), max(1));
1848 }
1849 else
1850 {
1851 // recursively partition the points in 3D
1852 HilbertSort3D(0, false, false, false,
1853 points, indices.begin(), indices.end(),
1854 min(0), min(1), min(2), max(0), max(1), max(2));
1855 }
1856
1857 // return ordering in the format required by ReorderElements
1858 ordering.SetSize(GetNE());
1859 for (int i = 0; i < GetNE(); i++)
1860 {
1861 ordering[indices[i]] = i;
1862 }
1863 }
1864
1865
ReorderElements(const Array<int> & ordering,bool reorder_vertices)1866 void Mesh::ReorderElements(const Array<int> &ordering, bool reorder_vertices)
1867 {
1868 if (NURBSext)
1869 {
1870 MFEM_WARNING("element reordering of NURBS meshes is not supported.");
1871 return;
1872 }
1873 if (ncmesh)
1874 {
1875 MFEM_WARNING("element reordering of non-conforming meshes is not"
1876 " supported.");
1877 return;
1878 }
1879 MFEM_VERIFY(ordering.Size() == GetNE(), "invalid reordering array.")
1880
1881 // Data members that need to be updated:
1882
1883 // - elements - reorder of the pointers and the vertex ids if reordering
1884 // the vertices
1885 // - vertices - if reordering the vertices
1886 // - boundary - update the vertex ids, if reordering the vertices
1887 // - faces - regenerate
1888 // - faces_info - regenerate
1889
1890 // Deleted by DeleteTables():
1891 // - el_to_edge - rebuild in 2D and 3D only
1892 // - el_to_face - rebuild in 3D only
1893 // - bel_to_edge - rebuild in 3D only
1894 // - el_to_el - no need to rebuild
1895 // - face_edge - no need to rebuild
1896 // - edge_vertex - no need to rebuild
1897 // - geom_factors - no need to rebuild
1898
1899 // - be_to_edge - 2D only
1900 // - be_to_face - 3D only
1901
1902 // - Nodes
1903
1904 // Save the locations of the Nodes so we can rebuild them later
1905 Array<Vector*> old_elem_node_vals;
1906 FiniteElementSpace *nodes_fes = NULL;
1907 if (Nodes)
1908 {
1909 old_elem_node_vals.SetSize(GetNE());
1910 nodes_fes = Nodes->FESpace();
1911 Array<int> old_dofs;
1912 Vector vals;
1913 for (int old_elid = 0; old_elid < GetNE(); ++old_elid)
1914 {
1915 nodes_fes->GetElementVDofs(old_elid, old_dofs);
1916 Nodes->GetSubVector(old_dofs, vals);
1917 old_elem_node_vals[old_elid] = new Vector(vals);
1918 }
1919 }
1920
1921 // Get the newly ordered elements
1922 Array<Element *> new_elements(GetNE());
1923 for (int old_elid = 0; old_elid < ordering.Size(); ++old_elid)
1924 {
1925 int new_elid = ordering[old_elid];
1926 new_elements[new_elid] = elements[old_elid];
1927 }
1928 mfem::Swap(elements, new_elements);
1929 new_elements.DeleteAll();
1930
1931 if (reorder_vertices)
1932 {
1933 // Get the new vertex ordering permutation vectors and fill the new
1934 // vertices
1935 Array<int> vertex_ordering(GetNV());
1936 vertex_ordering = -1;
1937 Array<Vertex> new_vertices(GetNV());
1938 int new_vertex_ind = 0;
1939 for (int new_elid = 0; new_elid < GetNE(); ++new_elid)
1940 {
1941 int *elem_vert = elements[new_elid]->GetVertices();
1942 int nv = elements[new_elid]->GetNVertices();
1943 for (int vi = 0; vi < nv; ++vi)
1944 {
1945 int old_vertex_ind = elem_vert[vi];
1946 if (vertex_ordering[old_vertex_ind] == -1)
1947 {
1948 vertex_ordering[old_vertex_ind] = new_vertex_ind;
1949 new_vertices[new_vertex_ind] = vertices[old_vertex_ind];
1950 new_vertex_ind++;
1951 }
1952 }
1953 }
1954 mfem::Swap(vertices, new_vertices);
1955 new_vertices.DeleteAll();
1956
1957 // Replace the vertex ids in the elements with the reordered vertex
1958 // numbers
1959 for (int new_elid = 0; new_elid < GetNE(); ++new_elid)
1960 {
1961 int *elem_vert = elements[new_elid]->GetVertices();
1962 int nv = elements[new_elid]->GetNVertices();
1963 for (int vi = 0; vi < nv; ++vi)
1964 {
1965 elem_vert[vi] = vertex_ordering[elem_vert[vi]];
1966 }
1967 }
1968
1969 // Replace the vertex ids in the boundary with reordered vertex numbers
1970 for (int belid = 0; belid < GetNBE(); ++belid)
1971 {
1972 int *be_vert = boundary[belid]->GetVertices();
1973 int nv = boundary[belid]->GetNVertices();
1974 for (int vi = 0; vi < nv; ++vi)
1975 {
1976 be_vert[vi] = vertex_ordering[be_vert[vi]];
1977 }
1978 }
1979 }
1980
1981 // Destroy tables that need to be rebuild
1982 DeleteTables();
1983
1984 if (Dim > 1)
1985 {
1986 // generate el_to_edge, be_to_edge (2D), bel_to_edge (3D)
1987 el_to_edge = new Table;
1988 NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
1989 }
1990 if (Dim > 2)
1991 {
1992 // generate el_to_face, be_to_face
1993 GetElementToFaceTable();
1994 }
1995 // Update faces and faces_info
1996 GenerateFaces();
1997
1998 // Build the nodes from the saved locations if they were around before
1999 if (Nodes)
2000 {
2001 // To force FE space update, we need to increase 'sequence':
2002 sequence++;
2003 last_operation = Mesh::NONE;
2004 nodes_fes->Update(false); // want_transform = false
2005 Nodes->Update(); // just needed to update Nodes->sequence
2006 Array<int> new_dofs;
2007 for (int old_elid = 0; old_elid < GetNE(); ++old_elid)
2008 {
2009 int new_elid = ordering[old_elid];
2010 nodes_fes->GetElementVDofs(new_elid, new_dofs);
2011 Nodes->SetSubVector(new_dofs, *(old_elem_node_vals[old_elid]));
2012 delete old_elem_node_vals[old_elid];
2013 }
2014 }
2015 }
2016
2017
MarkForRefinement()2018 void Mesh::MarkForRefinement()
2019 {
2020 if (meshgen & 1)
2021 {
2022 if (Dim == 2)
2023 {
2024 MarkTriMeshForRefinement();
2025 }
2026 else if (Dim == 3)
2027 {
2028 DSTable v_to_v(NumOfVertices);
2029 GetVertexToVertexTable(v_to_v);
2030 MarkTetMeshForRefinement(v_to_v);
2031 }
2032 }
2033 }
2034
MarkTriMeshForRefinement()2035 void Mesh::MarkTriMeshForRefinement()
2036 {
2037 // Mark the longest triangle edge by rotating the indeces so that
2038 // vertex 0 - vertex 1 is the longest edge in the triangle.
2039 DenseMatrix pmat;
2040 for (int i = 0; i < NumOfElements; i++)
2041 {
2042 if (elements[i]->GetType() == Element::TRIANGLE)
2043 {
2044 GetPointMatrix(i, pmat);
2045 static_cast<Triangle*>(elements[i])->MarkEdge(pmat);
2046 }
2047 }
2048 }
2049
GetEdgeOrdering(DSTable & v_to_v,Array<int> & order)2050 void Mesh::GetEdgeOrdering(DSTable &v_to_v, Array<int> &order)
2051 {
2052 NumOfEdges = v_to_v.NumberOfEntries();
2053 order.SetSize(NumOfEdges);
2054 Array<Pair<double, int> > length_idx(NumOfEdges);
2055
2056 for (int i = 0; i < NumOfVertices; i++)
2057 {
2058 for (DSTable::RowIterator it(v_to_v, i); !it; ++it)
2059 {
2060 int j = it.Index();
2061 length_idx[j].one = GetLength(i, it.Column());
2062 length_idx[j].two = j;
2063 }
2064 }
2065
2066 // Sort by increasing edge-length.
2067 length_idx.Sort();
2068
2069 for (int i = 0; i < NumOfEdges; i++)
2070 {
2071 order[length_idx[i].two] = i;
2072 }
2073 }
2074
MarkTetMeshForRefinement(DSTable & v_to_v)2075 void Mesh::MarkTetMeshForRefinement(DSTable &v_to_v)
2076 {
2077 // Mark the longest tetrahedral edge by rotating the indices so that
2078 // vertex 0 - vertex 1 is the longest edge in the element.
2079 Array<int> order;
2080 GetEdgeOrdering(v_to_v, order);
2081
2082 for (int i = 0; i < NumOfElements; i++)
2083 {
2084 if (elements[i]->GetType() == Element::TETRAHEDRON)
2085 {
2086 elements[i]->MarkEdge(v_to_v, order);
2087 }
2088 }
2089 for (int i = 0; i < NumOfBdrElements; i++)
2090 {
2091 if (boundary[i]->GetType() == Element::TRIANGLE)
2092 {
2093 boundary[i]->MarkEdge(v_to_v, order);
2094 }
2095 }
2096 }
2097
PrepareNodeReorder(DSTable ** old_v_to_v,Table ** old_elem_vert)2098 void Mesh::PrepareNodeReorder(DSTable **old_v_to_v, Table **old_elem_vert)
2099 {
2100 if (*old_v_to_v && *old_elem_vert)
2101 {
2102 return;
2103 }
2104
2105 FiniteElementSpace *fes = Nodes->FESpace();
2106
2107 if (*old_v_to_v == NULL)
2108 {
2109 bool need_v_to_v = false;
2110 Array<int> dofs;
2111 for (int i = 0; i < GetNEdges(); i++)
2112 {
2113 // Since edge indices may change, we need to permute edge interior dofs
2114 // any time an edge index changes and there is at least one dof on that
2115 // edge.
2116 fes->GetEdgeInteriorDofs(i, dofs);
2117 if (dofs.Size() > 0)
2118 {
2119 need_v_to_v = true;
2120 break;
2121 }
2122 }
2123 if (need_v_to_v)
2124 {
2125 *old_v_to_v = new DSTable(NumOfVertices);
2126 GetVertexToVertexTable(*(*old_v_to_v));
2127 }
2128 }
2129 if (*old_elem_vert == NULL)
2130 {
2131 bool need_elem_vert = false;
2132 Array<int> dofs;
2133 for (int i = 0; i < GetNE(); i++)
2134 {
2135 // Since element indices do not change, we need to permute element
2136 // interior dofs only when there are at least 2 interior dofs in an
2137 // element (assuming the nodal dofs are non-directional).
2138 fes->GetElementInteriorDofs(i, dofs);
2139 if (dofs.Size() > 1)
2140 {
2141 need_elem_vert = true;
2142 break;
2143 }
2144 }
2145 if (need_elem_vert)
2146 {
2147 *old_elem_vert = new Table;
2148 (*old_elem_vert)->MakeI(GetNE());
2149 for (int i = 0; i < GetNE(); i++)
2150 {
2151 (*old_elem_vert)->AddColumnsInRow(i, elements[i]->GetNVertices());
2152 }
2153 (*old_elem_vert)->MakeJ();
2154 for (int i = 0; i < GetNE(); i++)
2155 {
2156 (*old_elem_vert)->AddConnections(i, elements[i]->GetVertices(),
2157 elements[i]->GetNVertices());
2158 }
2159 (*old_elem_vert)->ShiftUpI();
2160 }
2161 }
2162 }
2163
DoNodeReorder(DSTable * old_v_to_v,Table * old_elem_vert)2164 void Mesh::DoNodeReorder(DSTable *old_v_to_v, Table *old_elem_vert)
2165 {
2166 FiniteElementSpace *fes = Nodes->FESpace();
2167 const FiniteElementCollection *fec = fes->FEColl();
2168 Array<int> old_dofs, new_dofs;
2169
2170 // assuming that all edges have the same number of dofs
2171 if (NumOfEdges) { fes->GetEdgeInteriorDofs(0, old_dofs); }
2172 const int num_edge_dofs = old_dofs.Size();
2173
2174 // Save the original nodes
2175 const Vector onodes = *Nodes;
2176
2177 // vertex dofs do not need to be moved
2178 fes->GetVertexDofs(0, old_dofs);
2179 int offset = NumOfVertices * old_dofs.Size();
2180
2181 // edge dofs:
2182 // edge enumeration may be different but edge orientation is the same
2183 if (num_edge_dofs > 0)
2184 {
2185 DSTable new_v_to_v(NumOfVertices);
2186 GetVertexToVertexTable(new_v_to_v);
2187
2188 for (int i = 0; i < NumOfVertices; i++)
2189 {
2190 for (DSTable::RowIterator it(new_v_to_v, i); !it; ++it)
2191 {
2192 const int old_i = (*old_v_to_v)(i, it.Column());
2193 const int new_i = it.Index();
2194 if (new_i == old_i) { continue; }
2195
2196 old_dofs.SetSize(num_edge_dofs);
2197 new_dofs.SetSize(num_edge_dofs);
2198 for (int j = 0; j < num_edge_dofs; j++)
2199 {
2200 old_dofs[j] = offset + old_i * num_edge_dofs + j;
2201 new_dofs[j] = offset + new_i * num_edge_dofs + j;
2202 }
2203 fes->DofsToVDofs(old_dofs);
2204 fes->DofsToVDofs(new_dofs);
2205 for (int j = 0; j < old_dofs.Size(); j++)
2206 {
2207 (*Nodes)(new_dofs[j]) = onodes(old_dofs[j]);
2208 }
2209 }
2210 }
2211 offset += NumOfEdges * num_edge_dofs;
2212 }
2213
2214 // face dofs:
2215 // both enumeration and orientation of the faces may be different
2216 if (fes->GetNFDofs() > 0)
2217 {
2218 // generate the old face-vertex table using the unmodified 'faces'
2219 Table old_face_vertex;
2220 old_face_vertex.MakeI(NumOfFaces);
2221 for (int i = 0; i < NumOfFaces; i++)
2222 {
2223 old_face_vertex.AddColumnsInRow(i, faces[i]->GetNVertices());
2224 }
2225 old_face_vertex.MakeJ();
2226 for (int i = 0; i < NumOfFaces; i++)
2227 old_face_vertex.AddConnections(i, faces[i]->GetVertices(),
2228 faces[i]->GetNVertices());
2229 old_face_vertex.ShiftUpI();
2230
2231 // update 'el_to_face', 'be_to_face', 'faces', and 'faces_info'
2232 STable3D *faces_tbl = GetElementToFaceTable(1);
2233 GenerateFaces();
2234
2235 // compute the new face dof offsets
2236 Array<int> new_fdofs(NumOfFaces+1);
2237 new_fdofs[0] = 0;
2238 for (int i = 0; i < NumOfFaces; i++) // i = old face index
2239 {
2240 const int *old_v = old_face_vertex.GetRow(i);
2241 int new_i; // new face index
2242 switch (old_face_vertex.RowSize(i))
2243 {
2244 case 3:
2245 new_i = (*faces_tbl)(old_v[0], old_v[1], old_v[2]);
2246 break;
2247 case 4:
2248 default:
2249 new_i = (*faces_tbl)(old_v[0], old_v[1], old_v[2], old_v[3]);
2250 break;
2251 }
2252 fes->GetFaceInteriorDofs(i, old_dofs);
2253 new_fdofs[new_i+1] = old_dofs.Size();
2254 }
2255 new_fdofs.PartialSum();
2256
2257 // loop over the old face numbers
2258 for (int i = 0; i < NumOfFaces; i++)
2259 {
2260 const int *old_v = old_face_vertex.GetRow(i), *new_v;
2261 const int *dof_ord;
2262 int new_i, new_or;
2263 switch (old_face_vertex.RowSize(i))
2264 {
2265 case 3:
2266 new_i = (*faces_tbl)(old_v[0], old_v[1], old_v[2]);
2267 new_v = faces[new_i]->GetVertices();
2268 new_or = GetTriOrientation(old_v, new_v);
2269 dof_ord = fec->DofOrderForOrientation(Geometry::TRIANGLE, new_or);
2270 break;
2271 case 4:
2272 default:
2273 new_i = (*faces_tbl)(old_v[0], old_v[1], old_v[2], old_v[3]);
2274 new_v = faces[new_i]->GetVertices();
2275 new_or = GetQuadOrientation(old_v, new_v);
2276 dof_ord = fec->DofOrderForOrientation(Geometry::SQUARE, new_or);
2277 break;
2278 }
2279
2280 fes->GetFaceInteriorDofs(i, old_dofs);
2281 new_dofs.SetSize(old_dofs.Size());
2282 for (int j = 0; j < old_dofs.Size(); j++)
2283 {
2284 // we assume the dofs are non-directional, i.e. dof_ord[j] is >= 0
2285 const int old_j = dof_ord[j];
2286 new_dofs[old_j] = offset + new_fdofs[new_i] + j;
2287 }
2288 fes->DofsToVDofs(old_dofs);
2289 fes->DofsToVDofs(new_dofs);
2290 for (int j = 0; j < old_dofs.Size(); j++)
2291 {
2292 (*Nodes)(new_dofs[j]) = onodes(old_dofs[j]);
2293 }
2294 }
2295
2296 offset += fes->GetNFDofs();
2297 delete faces_tbl;
2298 }
2299
2300 // element dofs:
2301 // element orientation may be different
2302 if (old_elem_vert) // have elements with 2 or more dofs
2303 {
2304 // matters when the 'fec' is
2305 // (this code is executed only for triangles/tets)
2306 // - Pk on triangles, k >= 4
2307 // - Qk on quads, k >= 3
2308 // - Pk on tets, k >= 5
2309 // - Qk on hexes, k >= 3
2310 // - DG spaces
2311 // - ...
2312
2313 // loop over all elements
2314 for (int i = 0; i < GetNE(); i++)
2315 {
2316 const int *old_v = old_elem_vert->GetRow(i);
2317 const int *new_v = elements[i]->GetVertices();
2318 const int *dof_ord;
2319 int new_or;
2320 const Geometry::Type geom = elements[i]->GetGeometryType();
2321 switch (geom)
2322 {
2323 case Geometry::SEGMENT:
2324 new_or = (old_v[0] == new_v[0]) ? +1 : -1;
2325 break;
2326 case Geometry::TRIANGLE:
2327 new_or = GetTriOrientation(old_v, new_v);
2328 break;
2329 case Geometry::SQUARE:
2330 new_or = GetQuadOrientation(old_v, new_v);
2331 break;
2332 case Geometry::TETRAHEDRON:
2333 new_or = GetTetOrientation(old_v, new_v);
2334 break;
2335 default:
2336 new_or = 0;
2337 MFEM_ABORT(Geometry::Name[geom] << " elements (" << fec->Name()
2338 << " FE collection) are not supported yet!");
2339 break;
2340 }
2341 dof_ord = fec->DofOrderForOrientation(geom, new_or);
2342 MFEM_VERIFY(dof_ord != NULL,
2343 "FE collection '" << fec->Name()
2344 << "' does not define reordering for "
2345 << Geometry::Name[geom] << " elements!");
2346 fes->GetElementInteriorDofs(i, old_dofs);
2347 new_dofs.SetSize(old_dofs.Size());
2348 for (int j = 0; j < new_dofs.Size(); j++)
2349 {
2350 // we assume the dofs are non-directional, i.e. dof_ord[j] is >= 0
2351 const int old_j = dof_ord[j];
2352 new_dofs[old_j] = offset + j;
2353 }
2354 offset += new_dofs.Size();
2355 fes->DofsToVDofs(old_dofs);
2356 fes->DofsToVDofs(new_dofs);
2357 for (int j = 0; j < old_dofs.Size(); j++)
2358 {
2359 (*Nodes)(new_dofs[j]) = onodes(old_dofs[j]);
2360 }
2361 }
2362 }
2363
2364 // Update Tables, faces, etc
2365 if (Dim > 2)
2366 {
2367 if (fes->GetNFDofs() == 0)
2368 {
2369 // needed for FE spaces that have face dofs, even if
2370 // the 'Nodes' do not have face dofs.
2371 GetElementToFaceTable();
2372 GenerateFaces();
2373 }
2374 CheckBdrElementOrientation();
2375 }
2376 if (el_to_edge)
2377 {
2378 // update 'el_to_edge', 'be_to_edge' (2D), 'bel_to_edge' (3D)
2379 NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
2380 if (Dim == 2)
2381 {
2382 // update 'faces' and 'faces_info'
2383 GenerateFaces();
2384 CheckBdrElementOrientation();
2385 }
2386 }
2387 // To force FE space update, we need to increase 'sequence':
2388 sequence++;
2389 last_operation = Mesh::NONE;
2390 fes->Update(false); // want_transform = false
2391 Nodes->Update(); // just needed to update Nodes->sequence
2392 }
2393
FinalizeTetMesh(int generate_edges,int refine,bool fix_orientation)2394 void Mesh::FinalizeTetMesh(int generate_edges, int refine, bool fix_orientation)
2395 {
2396 FinalizeCheck();
2397 CheckElementOrientation(fix_orientation);
2398
2399 if (NumOfBdrElements == 0)
2400 {
2401 GetElementToFaceTable();
2402 GenerateFaces();
2403 GenerateBoundaryElements();
2404 }
2405
2406 if (refine)
2407 {
2408 DSTable v_to_v(NumOfVertices);
2409 GetVertexToVertexTable(v_to_v);
2410 MarkTetMeshForRefinement(v_to_v);
2411 }
2412
2413 GetElementToFaceTable();
2414 GenerateFaces();
2415
2416 CheckBdrElementOrientation();
2417
2418 if (generate_edges == 1)
2419 {
2420 el_to_edge = new Table;
2421 NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
2422 }
2423 else
2424 {
2425 el_to_edge = NULL; // Not really necessary -- InitTables was called
2426 bel_to_edge = NULL;
2427 NumOfEdges = 0;
2428 }
2429
2430 SetAttributes();
2431
2432 SetMeshGen();
2433 }
2434
FinalizeWedgeMesh(int generate_edges,int refine,bool fix_orientation)2435 void Mesh::FinalizeWedgeMesh(int generate_edges, int refine,
2436 bool fix_orientation)
2437 {
2438 FinalizeCheck();
2439 CheckElementOrientation(fix_orientation);
2440
2441 if (NumOfBdrElements == 0)
2442 {
2443 GetElementToFaceTable();
2444 GenerateFaces();
2445 GenerateBoundaryElements();
2446 }
2447
2448 GetElementToFaceTable();
2449 GenerateFaces();
2450
2451 CheckBdrElementOrientation();
2452
2453 if (generate_edges == 1)
2454 {
2455 el_to_edge = new Table;
2456 NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
2457 }
2458 else
2459 {
2460 el_to_edge = NULL; // Not really necessary -- InitTables was called
2461 bel_to_edge = NULL;
2462 NumOfEdges = 0;
2463 }
2464
2465 SetAttributes();
2466
2467 SetMeshGen();
2468 }
2469
FinalizeHexMesh(int generate_edges,int refine,bool fix_orientation)2470 void Mesh::FinalizeHexMesh(int generate_edges, int refine, bool fix_orientation)
2471 {
2472 FinalizeCheck();
2473 CheckElementOrientation(fix_orientation);
2474
2475 GetElementToFaceTable();
2476 GenerateFaces();
2477
2478 if (NumOfBdrElements == 0)
2479 {
2480 GenerateBoundaryElements();
2481 }
2482
2483 CheckBdrElementOrientation();
2484
2485 if (generate_edges)
2486 {
2487 el_to_edge = new Table;
2488 NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
2489 }
2490 else
2491 {
2492 NumOfEdges = 0;
2493 }
2494
2495 SetAttributes();
2496
2497 SetMeshGen();
2498 }
2499
FinalizeMesh(int refine,bool fix_orientation)2500 void Mesh::FinalizeMesh(int refine, bool fix_orientation)
2501 {
2502 FinalizeTopology();
2503
2504 Finalize(refine, fix_orientation);
2505 }
2506
FinalizeTopology(bool generate_bdr)2507 void Mesh::FinalizeTopology(bool generate_bdr)
2508 {
2509 // Requirements: the following should be defined:
2510 // 1) Dim
2511 // 2) NumOfElements, elements
2512 // 3) NumOfBdrElements, boundary
2513 // 4) NumOfVertices
2514 // Optional:
2515 // 2) ncmesh may be defined
2516 // 3) el_to_edge may be allocated (it will be re-computed)
2517
2518 FinalizeCheck();
2519 bool generate_edges = true;
2520
2521 if (spaceDim == 0) { spaceDim = Dim; }
2522 if (ncmesh) { ncmesh->spaceDim = spaceDim; }
2523
2524 // if the user defined any hanging nodes (see AddVertexParent),
2525 // we're initializing a non-conforming mesh
2526 if (tmp_vertex_parents.Size())
2527 {
2528 MFEM_VERIFY(ncmesh == NULL, "");
2529 ncmesh = new NCMesh(this);
2530
2531 // we need to recreate the Mesh because NCMesh reorders the vertices
2532 // (see NCMesh::UpdateVertices())
2533 InitFromNCMesh(*ncmesh);
2534 ncmesh->OnMeshUpdated(this);
2535 GenerateNCFaceInfo();
2536
2537 SetAttributes();
2538
2539 tmp_vertex_parents.DeleteAll();
2540 return;
2541 }
2542
2543 // set the mesh type: 'meshgen', ...
2544 SetMeshGen();
2545
2546 // generate the faces
2547 if (Dim > 2)
2548 {
2549 GetElementToFaceTable();
2550 GenerateFaces();
2551 if (NumOfBdrElements == 0 && generate_bdr)
2552 {
2553 GenerateBoundaryElements();
2554 GetElementToFaceTable(); // update be_to_face
2555 }
2556 }
2557 else
2558 {
2559 NumOfFaces = 0;
2560 }
2561
2562 // generate edges if requested
2563 if (Dim > 1 && generate_edges)
2564 {
2565 // el_to_edge may already be allocated (P2 VTK meshes)
2566 if (!el_to_edge) { el_to_edge = new Table; }
2567 NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
2568 if (Dim == 2)
2569 {
2570 GenerateFaces(); // 'Faces' in 2D refers to the edges
2571 if (NumOfBdrElements == 0 && generate_bdr)
2572 {
2573 GenerateBoundaryElements();
2574 }
2575 }
2576 }
2577 else
2578 {
2579 NumOfEdges = 0;
2580 }
2581
2582 if (Dim == 1)
2583 {
2584 GenerateFaces();
2585 }
2586
2587 if (ncmesh)
2588 {
2589 // tell NCMesh the numbering of edges/faces
2590 ncmesh->OnMeshUpdated(this);
2591
2592 // update faces_info with NC relations
2593 GenerateNCFaceInfo();
2594 }
2595
2596 // generate the arrays 'attributes' and 'bdr_attributes'
2597 SetAttributes();
2598 }
2599
Finalize(bool refine,bool fix_orientation)2600 void Mesh::Finalize(bool refine, bool fix_orientation)
2601 {
2602 if (NURBSext || ncmesh)
2603 {
2604 MFEM_ASSERT(CheckElementOrientation(false) == 0, "");
2605 MFEM_ASSERT(CheckBdrElementOrientation() == 0, "");
2606 return;
2607 }
2608
2609 // Requirements:
2610 // 1) FinalizeTopology() or equivalent was called
2611 // 2) if (Nodes == NULL), vertices must be defined
2612 // 3) if (Nodes != NULL), Nodes must be defined
2613
2614 const bool check_orientation = true; // for regular elements, not boundary
2615 const bool curved = (Nodes != NULL);
2616 const bool may_change_topology =
2617 ( refine && (Dim > 1 && (meshgen & 1)) ) ||
2618 ( check_orientation && fix_orientation &&
2619 (Dim == 2 || (Dim == 3 && (meshgen & 1))) );
2620
2621 DSTable *old_v_to_v = NULL;
2622 Table *old_elem_vert = NULL;
2623
2624 if (curved && may_change_topology)
2625 {
2626 PrepareNodeReorder(&old_v_to_v, &old_elem_vert);
2627 }
2628
2629 if (check_orientation)
2630 {
2631 // check and optionally fix element orientation
2632 CheckElementOrientation(fix_orientation);
2633 }
2634 if (refine)
2635 {
2636 MarkForRefinement(); // may change topology!
2637 }
2638
2639 if (may_change_topology)
2640 {
2641 if (curved)
2642 {
2643 DoNodeReorder(old_v_to_v, old_elem_vert); // updates the mesh topology
2644 delete old_elem_vert;
2645 delete old_v_to_v;
2646 }
2647 else
2648 {
2649 FinalizeTopology(); // Re-computes some data unnecessarily.
2650 }
2651
2652 // TODO: maybe introduce Mesh::NODE_REORDER operation and FESpace::
2653 // NodeReorderMatrix and do Nodes->Update() instead of DoNodeReorder?
2654 }
2655
2656 // check and fix boundary element orientation
2657 CheckBdrElementOrientation();
2658
2659 #ifdef MFEM_DEBUG
2660 // For non-orientable surfaces/manifolds, the check below will fail, so we
2661 // only perform it when Dim == spaceDim.
2662 if (Dim >= 2 && Dim == spaceDim)
2663 {
2664 const int num_faces = GetNumFaces();
2665 for (int i = 0; i < num_faces; i++)
2666 {
2667 MFEM_VERIFY(faces_info[i].Elem2No < 0 ||
2668 faces_info[i].Elem2Inf%2 != 0, "Invalid mesh topology."
2669 " Interior face with incompatible orientations.");
2670 }
2671 }
2672 #endif
2673 }
2674
Make3D(int nx,int ny,int nz,Element::Type type,double sx,double sy,double sz,bool sfc_ordering)2675 void Mesh::Make3D(int nx, int ny, int nz, Element::Type type,
2676 double sx, double sy, double sz, bool sfc_ordering)
2677 {
2678 int x, y, z;
2679
2680 int NVert, NElem, NBdrElem;
2681
2682 NVert = (nx+1) * (ny+1) * (nz+1);
2683 NElem = nx * ny * nz;
2684 NBdrElem = 2*(nx*ny+nx*nz+ny*nz);
2685 if (type == Element::TETRAHEDRON)
2686 {
2687 NElem *= 6;
2688 NBdrElem *= 2;
2689 }
2690 else if (type == Element::WEDGE)
2691 {
2692 NElem *= 2;
2693 NBdrElem += 2*nx*ny;
2694 }
2695
2696 InitMesh(3, 3, NVert, NElem, NBdrElem);
2697
2698 double coord[3];
2699 int ind[8];
2700
2701 // Sets vertices and the corresponding coordinates
2702 for (z = 0; z <= nz; z++)
2703 {
2704 coord[2] = ((double) z / nz) * sz;
2705 for (y = 0; y <= ny; y++)
2706 {
2707 coord[1] = ((double) y / ny) * sy;
2708 for (x = 0; x <= nx; x++)
2709 {
2710 coord[0] = ((double) x / nx) * sx;
2711 AddVertex(coord);
2712 }
2713 }
2714 }
2715
2716 #define VTX(XC, YC, ZC) ((XC)+((YC)+(ZC)*(ny+1))*(nx+1))
2717
2718 // Sets elements and the corresponding indices of vertices
2719 if (sfc_ordering && type == Element::HEXAHEDRON)
2720 {
2721 Array<int> sfc;
2722 NCMesh::GridSfcOrdering3D(nx, ny, nz, sfc);
2723 MFEM_VERIFY(sfc.Size() == 3*nx*ny*nz, "");
2724
2725 for (int k = 0; k < nx*ny*nz; k++)
2726 {
2727 x = sfc[3*k + 0];
2728 y = sfc[3*k + 1];
2729 z = sfc[3*k + 2];
2730
2731 ind[0] = VTX(x , y , z );
2732 ind[1] = VTX(x+1, y , z );
2733 ind[2] = VTX(x+1, y+1, z );
2734 ind[3] = VTX(x , y+1, z );
2735 ind[4] = VTX(x , y , z+1);
2736 ind[5] = VTX(x+1, y , z+1);
2737 ind[6] = VTX(x+1, y+1, z+1);
2738 ind[7] = VTX(x , y+1, z+1);
2739
2740 AddHex(ind, 1);
2741 }
2742 }
2743 else
2744 {
2745 for (z = 0; z < nz; z++)
2746 {
2747 for (y = 0; y < ny; y++)
2748 {
2749 for (x = 0; x < nx; x++)
2750 {
2751 ind[0] = VTX(x , y , z );
2752 ind[1] = VTX(x+1, y , z );
2753 ind[2] = VTX(x+1, y+1, z );
2754 ind[3] = VTX(x , y+1, z );
2755 ind[4] = VTX(x , y , z+1);
2756 ind[5] = VTX(x+1, y , z+1);
2757 ind[6] = VTX(x+1, y+1, z+1);
2758 ind[7] = VTX( x, y+1, z+1);
2759 if (type == Element::TETRAHEDRON)
2760 {
2761 AddHexAsTets(ind, 1);
2762 }
2763 else if (type == Element::WEDGE)
2764 {
2765 AddHexAsWedges(ind, 1);
2766 }
2767 else
2768 {
2769 AddHex(ind, 1);
2770 }
2771 }
2772 }
2773 }
2774 }
2775
2776 // Sets boundary elements and the corresponding indices of vertices
2777 // bottom, bdr. attribute 1
2778 for (y = 0; y < ny; y++)
2779 {
2780 for (x = 0; x < nx; x++)
2781 {
2782 ind[0] = VTX(x , y , 0);
2783 ind[1] = VTX(x , y+1, 0);
2784 ind[2] = VTX(x+1, y+1, 0);
2785 ind[3] = VTX(x+1, y , 0);
2786 if (type == Element::TETRAHEDRON)
2787 {
2788 AddBdrQuadAsTriangles(ind, 1);
2789 }
2790 else if (type == Element::WEDGE)
2791 {
2792 AddBdrQuadAsTriangles(ind, 1);
2793 }
2794 else
2795 {
2796 AddBdrQuad(ind, 1);
2797 }
2798 }
2799 }
2800 // top, bdr. attribute 6
2801 for (y = 0; y < ny; y++)
2802 {
2803 for (x = 0; x < nx; x++)
2804 {
2805 ind[0] = VTX(x , y , nz);
2806 ind[1] = VTX(x+1, y , nz);
2807 ind[2] = VTX(x+1, y+1, nz);
2808 ind[3] = VTX(x , y+1, nz);
2809 if (type == Element::TETRAHEDRON)
2810 {
2811 AddBdrQuadAsTriangles(ind, 6);
2812 }
2813 else if (type == Element::WEDGE)
2814 {
2815 AddBdrQuadAsTriangles(ind, 1);
2816 }
2817 else
2818 {
2819 AddBdrQuad(ind, 6);
2820 }
2821 }
2822 }
2823 // left, bdr. attribute 5
2824 for (z = 0; z < nz; z++)
2825 {
2826 for (y = 0; y < ny; y++)
2827 {
2828 ind[0] = VTX(0 , y , z );
2829 ind[1] = VTX(0 , y , z+1);
2830 ind[2] = VTX(0 , y+1, z+1);
2831 ind[3] = VTX(0 , y+1, z );
2832 if (type == Element::TETRAHEDRON)
2833 {
2834 AddBdrQuadAsTriangles(ind, 5);
2835 }
2836 else
2837 {
2838 AddBdrQuad(ind, 5);
2839 }
2840 }
2841 }
2842 // right, bdr. attribute 3
2843 for (z = 0; z < nz; z++)
2844 {
2845 for (y = 0; y < ny; y++)
2846 {
2847 ind[0] = VTX(nx, y , z );
2848 ind[1] = VTX(nx, y+1, z );
2849 ind[2] = VTX(nx, y+1, z+1);
2850 ind[3] = VTX(nx, y , z+1);
2851 if (type == Element::TETRAHEDRON)
2852 {
2853 AddBdrQuadAsTriangles(ind, 3);
2854 }
2855 else
2856 {
2857 AddBdrQuad(ind, 3);
2858 }
2859 }
2860 }
2861 // front, bdr. attribute 2
2862 for (x = 0; x < nx; x++)
2863 {
2864 for (z = 0; z < nz; z++)
2865 {
2866 ind[0] = VTX(x , 0, z );
2867 ind[1] = VTX(x+1, 0, z );
2868 ind[2] = VTX(x+1, 0, z+1);
2869 ind[3] = VTX(x , 0, z+1);
2870 if (type == Element::TETRAHEDRON)
2871 {
2872 AddBdrQuadAsTriangles(ind, 2);
2873 }
2874 else
2875 {
2876 AddBdrQuad(ind, 2);
2877 }
2878 }
2879 }
2880 // back, bdr. attribute 4
2881 for (x = 0; x < nx; x++)
2882 {
2883 for (z = 0; z < nz; z++)
2884 {
2885 ind[0] = VTX(x , ny, z );
2886 ind[1] = VTX(x , ny, z+1);
2887 ind[2] = VTX(x+1, ny, z+1);
2888 ind[3] = VTX(x+1, ny, z );
2889 if (type == Element::TETRAHEDRON)
2890 {
2891 AddBdrQuadAsTriangles(ind, 4);
2892 }
2893 else
2894 {
2895 AddBdrQuad(ind, 4);
2896 }
2897 }
2898 }
2899
2900 #undef VTX
2901
2902 #if 0
2903 ofstream test_stream("debug.mesh");
2904 Print(test_stream);
2905 test_stream.close();
2906 #endif
2907
2908 FinalizeTopology();
2909
2910 // Finalize(...) can be called after this method, if needed
2911 }
2912
Make2D(int nx,int ny,Element::Type type,double sx,double sy,bool generate_edges,bool sfc_ordering)2913 void Mesh::Make2D(int nx, int ny, Element::Type type,
2914 double sx, double sy,
2915 bool generate_edges, bool sfc_ordering)
2916 {
2917 int i, j, k;
2918
2919 SetEmpty();
2920
2921 Dim = spaceDim = 2;
2922
2923 // Creates quadrilateral mesh
2924 if (type == Element::QUADRILATERAL)
2925 {
2926 NumOfVertices = (nx+1) * (ny+1);
2927 NumOfElements = nx * ny;
2928 NumOfBdrElements = 2 * nx + 2 * ny;
2929
2930 vertices.SetSize(NumOfVertices);
2931 elements.SetSize(NumOfElements);
2932 boundary.SetSize(NumOfBdrElements);
2933
2934 double cx, cy;
2935 int ind[4];
2936
2937 // Sets vertices and the corresponding coordinates
2938 k = 0;
2939 for (j = 0; j < ny+1; j++)
2940 {
2941 cy = ((double) j / ny) * sy;
2942 for (i = 0; i < nx+1; i++)
2943 {
2944 cx = ((double) i / nx) * sx;
2945 vertices[k](0) = cx;
2946 vertices[k](1) = cy;
2947 k++;
2948 }
2949 }
2950
2951 // Sets elements and the corresponding indices of vertices
2952 if (sfc_ordering)
2953 {
2954 Array<int> sfc;
2955 NCMesh::GridSfcOrdering2D(nx, ny, sfc);
2956 MFEM_VERIFY(sfc.Size() == 2*nx*ny, "");
2957
2958 for (k = 0; k < nx*ny; k++)
2959 {
2960 i = sfc[2*k + 0];
2961 j = sfc[2*k + 1];
2962 ind[0] = i + j*(nx+1);
2963 ind[1] = i + 1 +j*(nx+1);
2964 ind[2] = i + 1 + (j+1)*(nx+1);
2965 ind[3] = i + (j+1)*(nx+1);
2966 elements[k] = new Quadrilateral(ind);
2967 }
2968 }
2969 else
2970 {
2971 k = 0;
2972 for (j = 0; j < ny; j++)
2973 {
2974 for (i = 0; i < nx; i++)
2975 {
2976 ind[0] = i + j*(nx+1);
2977 ind[1] = i + 1 +j*(nx+1);
2978 ind[2] = i + 1 + (j+1)*(nx+1);
2979 ind[3] = i + (j+1)*(nx+1);
2980 elements[k] = new Quadrilateral(ind);
2981 k++;
2982 }
2983 }
2984 }
2985
2986 // Sets boundary elements and the corresponding indices of vertices
2987 int m = (nx+1)*ny;
2988 for (i = 0; i < nx; i++)
2989 {
2990 boundary[i] = new Segment(i, i+1, 1);
2991 boundary[nx+i] = new Segment(m+i+1, m+i, 3);
2992 }
2993 m = nx+1;
2994 for (j = 0; j < ny; j++)
2995 {
2996 boundary[2*nx+j] = new Segment((j+1)*m, j*m, 4);
2997 boundary[2*nx+ny+j] = new Segment(j*m+nx, (j+1)*m+nx, 2);
2998 }
2999 }
3000 // Creates triangular mesh
3001 else if (type == Element::TRIANGLE)
3002 {
3003 NumOfVertices = (nx+1) * (ny+1);
3004 NumOfElements = 2 * nx * ny;
3005 NumOfBdrElements = 2 * nx + 2 * ny;
3006
3007 vertices.SetSize(NumOfVertices);
3008 elements.SetSize(NumOfElements);
3009 boundary.SetSize(NumOfBdrElements);
3010
3011 double cx, cy;
3012 int ind[3];
3013
3014 // Sets vertices and the corresponding coordinates
3015 k = 0;
3016 for (j = 0; j < ny+1; j++)
3017 {
3018 cy = ((double) j / ny) * sy;
3019 for (i = 0; i < nx+1; i++)
3020 {
3021 cx = ((double) i / nx) * sx;
3022 vertices[k](0) = cx;
3023 vertices[k](1) = cy;
3024 k++;
3025 }
3026 }
3027
3028 // Sets the elements and the corresponding indices of vertices
3029 k = 0;
3030 for (j = 0; j < ny; j++)
3031 {
3032 for (i = 0; i < nx; i++)
3033 {
3034 ind[0] = i + j*(nx+1);
3035 ind[1] = i + 1 + (j+1)*(nx+1);
3036 ind[2] = i + (j+1)*(nx+1);
3037 elements[k] = new Triangle(ind);
3038 k++;
3039 ind[1] = i + 1 + j*(nx+1);
3040 ind[2] = i + 1 + (j+1)*(nx+1);
3041 elements[k] = new Triangle(ind);
3042 k++;
3043 }
3044 }
3045
3046 // Sets boundary elements and the corresponding indices of vertices
3047 int m = (nx+1)*ny;
3048 for (i = 0; i < nx; i++)
3049 {
3050 boundary[i] = new Segment(i, i+1, 1);
3051 boundary[nx+i] = new Segment(m+i+1, m+i, 3);
3052 }
3053 m = nx+1;
3054 for (j = 0; j < ny; j++)
3055 {
3056 boundary[2*nx+j] = new Segment((j+1)*m, j*m, 4);
3057 boundary[2*nx+ny+j] = new Segment(j*m+nx, (j+1)*m+nx, 2);
3058 }
3059
3060 // MarkTriMeshForRefinement(); // done in Finalize(...)
3061 }
3062 else
3063 {
3064 MFEM_ABORT("Unsupported element type.");
3065 }
3066
3067 SetMeshGen();
3068 CheckElementOrientation();
3069
3070 if (generate_edges == 1)
3071 {
3072 el_to_edge = new Table;
3073 NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
3074 GenerateFaces();
3075 CheckBdrElementOrientation();
3076 }
3077 else
3078 {
3079 NumOfEdges = 0;
3080 }
3081
3082 NumOfFaces = 0;
3083
3084 attributes.Append(1);
3085 bdr_attributes.Append(1); bdr_attributes.Append(2);
3086 bdr_attributes.Append(3); bdr_attributes.Append(4);
3087
3088 // Finalize(...) can be called after this method, if needed
3089 }
3090
Make1D(int n,double sx)3091 void Mesh::Make1D(int n, double sx)
3092 {
3093 int j, ind[1];
3094
3095 SetEmpty();
3096
3097 Dim = 1;
3098 spaceDim = 1;
3099
3100 NumOfVertices = n + 1;
3101 NumOfElements = n;
3102 NumOfBdrElements = 2;
3103 vertices.SetSize(NumOfVertices);
3104 elements.SetSize(NumOfElements);
3105 boundary.SetSize(NumOfBdrElements);
3106
3107 // Sets vertices and the corresponding coordinates
3108 for (j = 0; j < n+1; j++)
3109 {
3110 vertices[j](0) = ((double) j / n) * sx;
3111 }
3112
3113 // Sets elements and the corresponding indices of vertices
3114 for (j = 0; j < n; j++)
3115 {
3116 elements[j] = new Segment(j, j+1, 1);
3117 }
3118
3119 // Sets the boundary elements
3120 ind[0] = 0;
3121 boundary[0] = new Point(ind, 1);
3122 ind[0] = n;
3123 boundary[1] = new Point(ind, 2);
3124
3125 NumOfEdges = 0;
3126 NumOfFaces = 0;
3127
3128 SetMeshGen();
3129 GenerateFaces();
3130
3131 attributes.Append(1);
3132 bdr_attributes.Append(1); bdr_attributes.Append(2);
3133 }
3134
Mesh(const Mesh & mesh,bool copy_nodes)3135 Mesh::Mesh(const Mesh &mesh, bool copy_nodes)
3136 {
3137 Dim = mesh.Dim;
3138 spaceDim = mesh.spaceDim;
3139
3140 NumOfVertices = mesh.NumOfVertices;
3141 NumOfElements = mesh.NumOfElements;
3142 NumOfBdrElements = mesh.NumOfBdrElements;
3143 NumOfEdges = mesh.NumOfEdges;
3144 NumOfFaces = mesh.NumOfFaces;
3145 nbInteriorFaces = mesh.nbInteriorFaces;
3146 nbBoundaryFaces = mesh.nbBoundaryFaces;
3147
3148 meshgen = mesh.meshgen;
3149 mesh_geoms = mesh.mesh_geoms;
3150
3151 // Create the new Mesh instance without a record of its refinement history
3152 sequence = 0;
3153 last_operation = Mesh::NONE;
3154
3155 // Duplicate the elements
3156 elements.SetSize(NumOfElements);
3157 for (int i = 0; i < NumOfElements; i++)
3158 {
3159 elements[i] = mesh.elements[i]->Duplicate(this);
3160 }
3161
3162 // Copy the vertices
3163 mesh.vertices.Copy(vertices);
3164
3165 // Duplicate the boundary
3166 boundary.SetSize(NumOfBdrElements);
3167 for (int i = 0; i < NumOfBdrElements; i++)
3168 {
3169 boundary[i] = mesh.boundary[i]->Duplicate(this);
3170 }
3171
3172 // Copy the element-to-face Table, el_to_face
3173 el_to_face = (mesh.el_to_face) ? new Table(*mesh.el_to_face) : NULL;
3174
3175 // Copy the boundary-to-face Array, be_to_face.
3176 mesh.be_to_face.Copy(be_to_face);
3177
3178 // Copy the element-to-edge Table, el_to_edge
3179 el_to_edge = (mesh.el_to_edge) ? new Table(*mesh.el_to_edge) : NULL;
3180
3181 // Copy the boundary-to-edge Table, bel_to_edge (3D)
3182 bel_to_edge = (mesh.bel_to_edge) ? new Table(*mesh.bel_to_edge) : NULL;
3183
3184 // Copy the boundary-to-edge Array, be_to_edge (2D)
3185 mesh.be_to_edge.Copy(be_to_edge);
3186
3187 // Duplicate the faces and faces_info.
3188 faces.SetSize(mesh.faces.Size());
3189 for (int i = 0; i < faces.Size(); i++)
3190 {
3191 Element *face = mesh.faces[i]; // in 1D the faces are NULL
3192 faces[i] = (face) ? face->Duplicate(this) : NULL;
3193 }
3194 mesh.faces_info.Copy(faces_info);
3195 mesh.nc_faces_info.Copy(nc_faces_info);
3196
3197 // Do NOT copy the element-to-element Table, el_to_el
3198 el_to_el = NULL;
3199
3200 // Do NOT copy the face-to-edge Table, face_edge
3201 face_edge = NULL;
3202
3203 // Copy the edge-to-vertex Table, edge_vertex
3204 edge_vertex = (mesh.edge_vertex) ? new Table(*mesh.edge_vertex) : NULL;
3205
3206 // Copy the attributes and bdr_attributes
3207 mesh.attributes.Copy(attributes);
3208 mesh.bdr_attributes.Copy(bdr_attributes);
3209
3210 // Deep copy the NURBSExtension.
3211 #ifdef MFEM_USE_MPI
3212 ParNURBSExtension *pNURBSext =
3213 dynamic_cast<ParNURBSExtension *>(mesh.NURBSext);
3214 if (pNURBSext)
3215 {
3216 NURBSext = new ParNURBSExtension(*pNURBSext);
3217 }
3218 else
3219 #endif
3220 {
3221 NURBSext = mesh.NURBSext ? new NURBSExtension(*mesh.NURBSext) : NULL;
3222 }
3223
3224 // Deep copy the NCMesh.
3225 #ifdef MFEM_USE_MPI
3226 if (dynamic_cast<const ParMesh*>(&mesh))
3227 {
3228 ncmesh = NULL; // skip; will be done in ParMesh copy ctor
3229 }
3230 else
3231 #endif
3232 {
3233 ncmesh = mesh.ncmesh ? new NCMesh(*mesh.ncmesh) : NULL;
3234 }
3235
3236 // Duplicate the Nodes, including the FiniteElementCollection and the
3237 // FiniteElementSpace
3238 if (mesh.Nodes && copy_nodes)
3239 {
3240 FiniteElementSpace *fes = mesh.Nodes->FESpace();
3241 const FiniteElementCollection *fec = fes->FEColl();
3242 FiniteElementCollection *fec_copy =
3243 FiniteElementCollection::New(fec->Name());
3244 FiniteElementSpace *fes_copy =
3245 new FiniteElementSpace(*fes, this, fec_copy);
3246 Nodes = new GridFunction(fes_copy);
3247 Nodes->MakeOwner(fec_copy);
3248 *Nodes = *mesh.Nodes;
3249 own_nodes = 1;
3250 }
3251 else
3252 {
3253 Nodes = mesh.Nodes;
3254 own_nodes = 0;
3255 }
3256 }
3257
Mesh(Mesh && mesh)3258 Mesh::Mesh(Mesh &&mesh) : Mesh()
3259 {
3260 Swap(mesh, true);
3261 }
3262
operator =(Mesh && mesh)3263 Mesh& Mesh::operator=(Mesh &&mesh)
3264 {
3265 Swap(mesh, true);
3266 return *this;
3267 }
3268
LoadFromFile(const char * filename,int generate_edges,int refine,bool fix_orientation)3269 Mesh Mesh::LoadFromFile(const char *filename, int generate_edges, int refine,
3270 bool fix_orientation)
3271 {
3272 Mesh mesh;
3273 named_ifgzstream imesh(filename);
3274 if (!imesh) { MFEM_ABORT("Mesh file not found: " << filename << '\n'); }
3275 else { mesh.Load(imesh, generate_edges, refine, fix_orientation); }
3276 return mesh;
3277 }
3278
MakeCartesian1D(int n,double sx)3279 Mesh Mesh::MakeCartesian1D(int n, double sx)
3280 {
3281 Mesh mesh;
3282 mesh.Make1D(n, sx);
3283 // mesh.Finalize(); not needed in this case
3284 return mesh;
3285 }
3286
MakeCartesian2D(int nx,int ny,Element::Type type,bool generate_edges,double sx,double sy,bool sfc_ordering)3287 Mesh Mesh::MakeCartesian2D(
3288 int nx, int ny, Element::Type type, bool generate_edges,
3289 double sx, double sy, bool sfc_ordering)
3290 {
3291 Mesh mesh;
3292 mesh.Make2D(nx, ny, type, sx, sy, generate_edges, sfc_ordering);
3293 mesh.Finalize(true); // refine = true
3294 return mesh;
3295 }
3296
MakeCartesian3D(int nx,int ny,int nz,Element::Type type,double sx,double sy,double sz,bool sfc_ordering)3297 Mesh Mesh::MakeCartesian3D(
3298 int nx, int ny, int nz, Element::Type type,
3299 double sx, double sy, double sz, bool sfc_ordering)
3300 {
3301 Mesh mesh;
3302 mesh.Make3D(nx, ny, nz, type, sx, sy, sz, sfc_ordering);
3303 mesh.Finalize(true); // refine = true
3304 return mesh;
3305 }
3306
MakeRefined(Mesh & orig_mesh,int ref_factor,int ref_type)3307 Mesh Mesh::MakeRefined(Mesh &orig_mesh, int ref_factor, int ref_type)
3308 {
3309 Mesh mesh;
3310 Array<int> ref_factors(orig_mesh.GetNE());
3311 ref_factors = ref_factor;
3312 mesh.MakeRefined_(orig_mesh, ref_factors, ref_type);
3313 return mesh;
3314 }
3315
MakeRefined(Mesh & orig_mesh,const Array<int> & ref_factors,int ref_type)3316 Mesh Mesh::MakeRefined(Mesh &orig_mesh, const Array<int> &ref_factors,
3317 int ref_type)
3318 {
3319 Mesh mesh;
3320 mesh.MakeRefined_(orig_mesh, ref_factors, ref_type);
3321 return mesh;
3322 }
3323
Mesh(const char * filename,int generate_edges,int refine,bool fix_orientation)3324 Mesh::Mesh(const char *filename, int generate_edges, int refine,
3325 bool fix_orientation)
3326 {
3327 // Initialization as in the default constructor
3328 SetEmpty();
3329
3330 named_ifgzstream imesh(filename);
3331 if (!imesh)
3332 {
3333 // Abort with an error message.
3334 MFEM_ABORT("Mesh file not found: " << filename << '\n');
3335 }
3336 else
3337 {
3338 Load(imesh, generate_edges, refine, fix_orientation);
3339 }
3340 }
3341
Mesh(std::istream & input,int generate_edges,int refine,bool fix_orientation)3342 Mesh::Mesh(std::istream &input, int generate_edges, int refine,
3343 bool fix_orientation)
3344 {
3345 SetEmpty();
3346 Load(input, generate_edges, refine, fix_orientation);
3347 }
3348
ChangeVertexDataOwnership(double * vertex_data,int len_vertex_data,bool zerocopy)3349 void Mesh::ChangeVertexDataOwnership(double *vertex_data, int len_vertex_data,
3350 bool zerocopy)
3351 {
3352 // A dimension of 3 is now required since we use mfem::Vertex objects as PODs
3353 // and these object have a hardcoded double[3] entry
3354 MFEM_VERIFY(len_vertex_data >= NumOfVertices * 3,
3355 "Not enough vertices in external array : "
3356 "len_vertex_data = "<< len_vertex_data << ", "
3357 "NumOfVertices * 3 = " << NumOfVertices * 3);
3358 // Allow multiple calls to this method with the same vertex_data
3359 if (vertex_data == (double *)(vertices.GetData()))
3360 {
3361 MFEM_ASSERT(!vertices.OwnsData(), "invalid ownership");
3362 return;
3363 }
3364 if (!zerocopy)
3365 {
3366 memcpy(vertex_data, vertices.GetData(),
3367 NumOfVertices * 3 * sizeof(double));
3368 }
3369 // Vertex is POD double[3]
3370 vertices.MakeRef(reinterpret_cast<Vertex*>(vertex_data), NumOfVertices);
3371 }
3372
Mesh(double * vertices_,int num_vertices,int * element_indices,Geometry::Type element_type,int * element_attributes,int num_elements,int * boundary_indices,Geometry::Type boundary_type,int * boundary_attributes,int num_boundary_elements,int dimension,int space_dimension)3373 Mesh::Mesh(double *vertices_, int num_vertices,
3374 int *element_indices, Geometry::Type element_type,
3375 int *element_attributes, int num_elements,
3376 int *boundary_indices, Geometry::Type boundary_type,
3377 int *boundary_attributes, int num_boundary_elements,
3378 int dimension, int space_dimension)
3379 {
3380 if (space_dimension == -1)
3381 {
3382 space_dimension = dimension;
3383 }
3384
3385 InitMesh(dimension, space_dimension, /*num_vertices*/ 0, num_elements,
3386 num_boundary_elements);
3387
3388 int element_index_stride = Geometry::NumVerts[element_type];
3389 int boundary_index_stride = num_boundary_elements > 0 ?
3390 Geometry::NumVerts[boundary_type] : 0;
3391
3392 // assuming Vertex is POD
3393 vertices.MakeRef(reinterpret_cast<Vertex*>(vertices_), num_vertices);
3394 NumOfVertices = num_vertices;
3395
3396 for (int i = 0; i < num_elements; i++)
3397 {
3398 elements[i] = NewElement(element_type);
3399 elements[i]->SetVertices(element_indices + i * element_index_stride);
3400 elements[i]->SetAttribute(element_attributes[i]);
3401 }
3402 NumOfElements = num_elements;
3403
3404 for (int i = 0; i < num_boundary_elements; i++)
3405 {
3406 boundary[i] = NewElement(boundary_type);
3407 boundary[i]->SetVertices(boundary_indices + i * boundary_index_stride);
3408 boundary[i]->SetAttribute(boundary_attributes[i]);
3409 }
3410 NumOfBdrElements = num_boundary_elements;
3411
3412 FinalizeTopology();
3413 }
3414
NewElement(int geom)3415 Element *Mesh::NewElement(int geom)
3416 {
3417 switch (geom)
3418 {
3419 case Geometry::POINT: return (new Point);
3420 case Geometry::SEGMENT: return (new Segment);
3421 case Geometry::TRIANGLE: return (new Triangle);
3422 case Geometry::SQUARE: return (new Quadrilateral);
3423 case Geometry::TETRAHEDRON:
3424 #ifdef MFEM_USE_MEMALLOC
3425 return TetMemory.Alloc();
3426 #else
3427 return (new Tetrahedron);
3428 #endif
3429 case Geometry::CUBE: return (new Hexahedron);
3430 case Geometry::PRISM: return (new Wedge);
3431 default:
3432 MFEM_ABORT("invalid Geometry::Type, geom = " << geom);
3433 }
3434
3435 return NULL;
3436 }
3437
ReadElementWithoutAttr(std::istream & input)3438 Element *Mesh::ReadElementWithoutAttr(std::istream &input)
3439 {
3440 int geom, nv, *v;
3441 Element *el;
3442
3443 input >> geom;
3444 el = NewElement(geom);
3445 MFEM_VERIFY(el, "Unsupported element type: " << geom);
3446 nv = el->GetNVertices();
3447 v = el->GetVertices();
3448 for (int i = 0; i < nv; i++)
3449 {
3450 input >> v[i];
3451 }
3452
3453 return el;
3454 }
3455
PrintElementWithoutAttr(const Element * el,std::ostream & out)3456 void Mesh::PrintElementWithoutAttr(const Element *el, std::ostream &out)
3457 {
3458 out << el->GetGeometryType();
3459 const int nv = el->GetNVertices();
3460 const int *v = el->GetVertices();
3461 for (int j = 0; j < nv; j++)
3462 {
3463 out << ' ' << v[j];
3464 }
3465 out << '\n';
3466 }
3467
ReadElement(std::istream & input)3468 Element *Mesh::ReadElement(std::istream &input)
3469 {
3470 int attr;
3471 Element *el;
3472
3473 input >> attr;
3474 el = ReadElementWithoutAttr(input);
3475 el->SetAttribute(attr);
3476
3477 return el;
3478 }
3479
PrintElement(const Element * el,std::ostream & out)3480 void Mesh::PrintElement(const Element *el, std::ostream &out)
3481 {
3482 out << el->GetAttribute() << ' ';
3483 PrintElementWithoutAttr(el, out);
3484 }
3485
SetMeshGen()3486 void Mesh::SetMeshGen()
3487 {
3488 meshgen = mesh_geoms = 0;
3489 for (int i = 0; i < NumOfElements; i++)
3490 {
3491 const Element::Type type = GetElement(i)->GetType();
3492 switch (type)
3493 {
3494 case Element::TETRAHEDRON:
3495 mesh_geoms |= (1 << Geometry::TETRAHEDRON);
3496 case Element::TRIANGLE:
3497 mesh_geoms |= (1 << Geometry::TRIANGLE);
3498 case Element::SEGMENT:
3499 mesh_geoms |= (1 << Geometry::SEGMENT);
3500 case Element::POINT:
3501 mesh_geoms |= (1 << Geometry::POINT);
3502 meshgen |= 1;
3503 break;
3504
3505 case Element::HEXAHEDRON:
3506 mesh_geoms |= (1 << Geometry::CUBE);
3507 case Element::QUADRILATERAL:
3508 mesh_geoms |= (1 << Geometry::SQUARE);
3509 mesh_geoms |= (1 << Geometry::SEGMENT);
3510 mesh_geoms |= (1 << Geometry::POINT);
3511 meshgen |= 2;
3512 break;
3513
3514 case Element::WEDGE:
3515 mesh_geoms |= (1 << Geometry::PRISM);
3516 mesh_geoms |= (1 << Geometry::SQUARE);
3517 mesh_geoms |= (1 << Geometry::TRIANGLE);
3518 mesh_geoms |= (1 << Geometry::SEGMENT);
3519 mesh_geoms |= (1 << Geometry::POINT);
3520 meshgen |= 4;
3521 break;
3522
3523 default:
3524 MFEM_ABORT("invalid element type: " << type);
3525 break;
3526 }
3527 }
3528 }
3529
Loader(std::istream & input,int generate_edges,std::string parse_tag)3530 void Mesh::Loader(std::istream &input, int generate_edges,
3531 std::string parse_tag)
3532 {
3533 int curved = 0, read_gf = 1;
3534 bool finalize_topo = true;
3535
3536 if (!input)
3537 {
3538 MFEM_ABORT("Input stream is not open");
3539 }
3540
3541 Clear();
3542
3543 string mesh_type;
3544 input >> ws;
3545 getline(input, mesh_type);
3546 filter_dos(mesh_type);
3547
3548 // MFEM's conforming mesh formats
3549 int mfem_version = 0;
3550 if (mesh_type == "MFEM mesh v1.0") { mfem_version = 10; } // serial
3551 else if (mesh_type == "MFEM mesh v1.2") { mfem_version = 12; } // parallel
3552
3553 // MFEM nonconforming mesh format
3554 // (NOTE: previous v1.1 is now under this branch for backward compatibility)
3555 int mfem_nc_version = 0;
3556 if (mesh_type == "MFEM NC mesh v1.0") { mfem_nc_version = 10; }
3557 else if (mesh_type == "MFEM mesh v1.1") { mfem_nc_version = 1 /*legacy*/; }
3558
3559 if (mfem_version)
3560 {
3561 // Formats mfem_v12 and newer have a tag indicating the end of the mesh
3562 // section in the stream. A user provided parse tag can also be provided
3563 // via the arguments. For example, if this is called from parallel mesh
3564 // object, it can indicate to read until parallel mesh section begins.
3565 if (mfem_version == 12 && parse_tag.empty())
3566 {
3567 parse_tag = "mfem_mesh_end";
3568 }
3569 ReadMFEMMesh(input, mfem_version, curved);
3570 }
3571 else if (mfem_nc_version)
3572 {
3573 MFEM_ASSERT(ncmesh == NULL, "internal error");
3574 int is_nc = 1;
3575
3576 #ifdef MFEM_USE_MPI
3577 ParMesh *pmesh = dynamic_cast<ParMesh*>(this);
3578 if (pmesh)
3579 {
3580 MFEM_VERIFY(mfem_nc_version >= 10,
3581 "Legacy nonconforming format (MFEM mesh v1.1) cannot be "
3582 "used to load a parallel nonconforming mesh, sorry.");
3583
3584 ncmesh = new ParNCMesh(pmesh->GetComm(),
3585 input, mfem_nc_version, curved, is_nc);
3586 }
3587 else
3588 #endif
3589 {
3590 ncmesh = new NCMesh(input, mfem_nc_version, curved, is_nc);
3591 }
3592 InitFromNCMesh(*ncmesh);
3593
3594 if (!is_nc)
3595 {
3596 // special case for backward compatibility with MFEM <=4.2:
3597 // if the "vertex_parents" section is missing in the v1.1 format,
3598 // the mesh is treated as conforming
3599 delete ncmesh;
3600 ncmesh = NULL;
3601 }
3602 }
3603 else if (mesh_type == "linemesh") // 1D mesh
3604 {
3605 ReadLineMesh(input);
3606 }
3607 else if (mesh_type == "areamesh2" || mesh_type == "curved_areamesh2")
3608 {
3609 if (mesh_type == "curved_areamesh2")
3610 {
3611 curved = 1;
3612 }
3613 ReadNetgen2DMesh(input, curved);
3614 }
3615 else if (mesh_type == "NETGEN" || mesh_type == "NETGEN_Neutral_Format")
3616 {
3617 ReadNetgen3DMesh(input);
3618 }
3619 else if (mesh_type == "TrueGrid")
3620 {
3621 ReadTrueGridMesh(input);
3622 }
3623 else if (mesh_type.rfind("# vtk DataFile Version") == 0)
3624 {
3625 int major_vtk_version = mesh_type[mesh_type.length()-3] - '0';
3626 // int minor_vtk_version = mesh_type[mesh_type.length()-1] - '0';
3627 MFEM_VERIFY(major_vtk_version >= 2 && major_vtk_version <= 4,
3628 "Unsupported VTK format");
3629 ReadVTKMesh(input, curved, read_gf, finalize_topo);
3630 }
3631 else if (mesh_type.rfind("<VTKFile ") == 0 || mesh_type.rfind("<?xml") == 0)
3632 {
3633 ReadXML_VTKMesh(input, curved, read_gf, finalize_topo, mesh_type);
3634 }
3635 else if (mesh_type == "MFEM NURBS mesh v1.0")
3636 {
3637 ReadNURBSMesh(input, curved, read_gf);
3638 }
3639 else if (mesh_type == "MFEM INLINE mesh v1.0")
3640 {
3641 ReadInlineMesh(input, generate_edges);
3642 return; // done with inline mesh construction
3643 }
3644 else if (mesh_type == "$MeshFormat") // Gmsh
3645 {
3646 ReadGmshMesh(input, curved, read_gf);
3647 }
3648 else if
3649 ((mesh_type.size() > 2 &&
3650 mesh_type[0] == 'C' && mesh_type[1] == 'D' && mesh_type[2] == 'F') ||
3651 (mesh_type.size() > 3 &&
3652 mesh_type[1] == 'H' && mesh_type[2] == 'D' && mesh_type[3] == 'F'))
3653 {
3654 named_ifgzstream *mesh_input = dynamic_cast<named_ifgzstream *>(&input);
3655 if (mesh_input)
3656 {
3657 #ifdef MFEM_USE_NETCDF
3658 ReadCubit(mesh_input->filename.c_str(), curved, read_gf);
3659 #else
3660 MFEM_ABORT("NetCDF support requires configuration with"
3661 " MFEM_USE_NETCDF=YES");
3662 return;
3663 #endif
3664 }
3665 else
3666 {
3667 MFEM_ABORT("Can not determine Cubit mesh filename!"
3668 " Use mfem::named_ifgzstream for input.");
3669 return;
3670 }
3671 }
3672 else
3673 {
3674 MFEM_ABORT("Unknown input mesh format: " << mesh_type);
3675 return;
3676 }
3677
3678 // at this point the following should be defined:
3679 // 1) Dim
3680 // 2) NumOfElements, elements
3681 // 3) NumOfBdrElements, boundary
3682 // 4) NumOfVertices, with allocated space in vertices
3683 // 5) curved
3684 // 5a) if curved == 0, vertices must be defined
3685 // 5b) if curved != 0 and read_gf != 0,
3686 // 'input' must point to a GridFunction
3687 // 5c) if curved != 0 and read_gf == 0,
3688 // vertices and Nodes must be defined
3689 // optional:
3690 // 1) el_to_edge may be allocated (as in the case of P2 VTK meshes)
3691 // 2) ncmesh may be allocated
3692
3693 // FinalizeTopology() will:
3694 // - assume that generate_edges is true
3695 // - assume that refine is false
3696 // - does not check the orientation of regular and boundary elements
3697 if (finalize_topo)
3698 {
3699 // don't generate any boundary elements, especially in parallel
3700 bool generate_bdr = false;
3701
3702 FinalizeTopology(generate_bdr);
3703 }
3704
3705 if (curved && read_gf)
3706 {
3707 Nodes = new GridFunction(this, input);
3708
3709 own_nodes = 1;
3710 spaceDim = Nodes->VectorDim();
3711 if (ncmesh) { ncmesh->spaceDim = spaceDim; }
3712
3713 // Set vertex coordinates from the 'Nodes'
3714 SetVerticesFromNodes(Nodes);
3715 }
3716
3717 // If a parse tag was supplied, keep reading the stream until the tag is
3718 // encountered.
3719 if (mfem_version == 12)
3720 {
3721 string line;
3722 do
3723 {
3724 skip_comment_lines(input, '#');
3725 MFEM_VERIFY(input.good(), "Required mesh-end tag not found");
3726 getline(input, line);
3727 filter_dos(line);
3728 // mfem v1.2 may not have parse_tag in it, e.g. if trying to read a
3729 // serial mfem v1.2 mesh as parallel with "mfem_serial_mesh_end" as
3730 // parse_tag. That's why, regardless of parse_tag, we stop reading if
3731 // we find "mfem_mesh_end" which is required by mfem v1.2 format.
3732 if (line == "mfem_mesh_end") { break; }
3733 }
3734 while (line != parse_tag);
3735 }
3736 else if (mfem_nc_version >= 10)
3737 {
3738 string ident;
3739 skip_comment_lines(input, '#');
3740 input >> ident;
3741 MFEM_VERIFY(ident == "mfem_mesh_end",
3742 "invalid mesh: end of file tag not found");
3743 }
3744
3745 // Finalize(...) should be called after this, if needed.
3746 }
3747
Mesh(Mesh * mesh_array[],int num_pieces)3748 Mesh::Mesh(Mesh *mesh_array[], int num_pieces)
3749 {
3750 int i, j, ie, ib, iv, *v, nv;
3751 Element *el;
3752 Mesh *m;
3753
3754 SetEmpty();
3755
3756 Dim = mesh_array[0]->Dimension();
3757 spaceDim = mesh_array[0]->SpaceDimension();
3758
3759 if (mesh_array[0]->NURBSext)
3760 {
3761 // assuming the pieces form a partition of a NURBS mesh
3762 NURBSext = new NURBSExtension(mesh_array, num_pieces);
3763
3764 NumOfVertices = NURBSext->GetNV();
3765 NumOfElements = NURBSext->GetNE();
3766
3767 NURBSext->GetElementTopo(elements);
3768
3769 // NumOfBdrElements = NURBSext->GetNBE();
3770 // NURBSext->GetBdrElementTopo(boundary);
3771
3772 Array<int> lvert_vert, lelem_elem;
3773
3774 // Here, for visualization purposes, we copy the boundary elements from
3775 // the individual pieces which include the interior boundaries. This
3776 // creates 'boundary' array that is different from the one generated by
3777 // the NURBSExtension which, in particular, makes the boundary-dof table
3778 // invalid. This, in turn, causes GetBdrElementTransformation to not
3779 // function properly.
3780 NumOfBdrElements = 0;
3781 for (i = 0; i < num_pieces; i++)
3782 {
3783 NumOfBdrElements += mesh_array[i]->GetNBE();
3784 }
3785 boundary.SetSize(NumOfBdrElements);
3786 vertices.SetSize(NumOfVertices);
3787 ib = 0;
3788 for (i = 0; i < num_pieces; i++)
3789 {
3790 m = mesh_array[i];
3791 m->NURBSext->GetVertexLocalToGlobal(lvert_vert);
3792 m->NURBSext->GetElementLocalToGlobal(lelem_elem);
3793 // copy the element attributes
3794 for (j = 0; j < m->GetNE(); j++)
3795 {
3796 elements[lelem_elem[j]]->SetAttribute(m->GetAttribute(j));
3797 }
3798 // copy the boundary
3799 for (j = 0; j < m->GetNBE(); j++)
3800 {
3801 el = m->GetBdrElement(j)->Duplicate(this);
3802 v = el->GetVertices();
3803 nv = el->GetNVertices();
3804 for (int k = 0; k < nv; k++)
3805 {
3806 v[k] = lvert_vert[v[k]];
3807 }
3808 boundary[ib++] = el;
3809 }
3810 // copy the vertices
3811 for (j = 0; j < m->GetNV(); j++)
3812 {
3813 vertices[lvert_vert[j]].SetCoords(m->SpaceDimension(),
3814 m->GetVertex(j));
3815 }
3816 }
3817 }
3818 else // not a NURBS mesh
3819 {
3820 NumOfElements = 0;
3821 NumOfBdrElements = 0;
3822 NumOfVertices = 0;
3823 for (i = 0; i < num_pieces; i++)
3824 {
3825 m = mesh_array[i];
3826 NumOfElements += m->GetNE();
3827 NumOfBdrElements += m->GetNBE();
3828 NumOfVertices += m->GetNV();
3829 }
3830 elements.SetSize(NumOfElements);
3831 boundary.SetSize(NumOfBdrElements);
3832 vertices.SetSize(NumOfVertices);
3833 ie = ib = iv = 0;
3834 for (i = 0; i < num_pieces; i++)
3835 {
3836 m = mesh_array[i];
3837 // copy the elements
3838 for (j = 0; j < m->GetNE(); j++)
3839 {
3840 el = m->GetElement(j)->Duplicate(this);
3841 v = el->GetVertices();
3842 nv = el->GetNVertices();
3843 for (int k = 0; k < nv; k++)
3844 {
3845 v[k] += iv;
3846 }
3847 elements[ie++] = el;
3848 }
3849 // copy the boundary elements
3850 for (j = 0; j < m->GetNBE(); j++)
3851 {
3852 el = m->GetBdrElement(j)->Duplicate(this);
3853 v = el->GetVertices();
3854 nv = el->GetNVertices();
3855 for (int k = 0; k < nv; k++)
3856 {
3857 v[k] += iv;
3858 }
3859 boundary[ib++] = el;
3860 }
3861 // copy the vertices
3862 for (j = 0; j < m->GetNV(); j++)
3863 {
3864 vertices[iv++].SetCoords(m->SpaceDimension(), m->GetVertex(j));
3865 }
3866 }
3867 }
3868
3869 FinalizeTopology();
3870
3871 // copy the nodes (curvilinear meshes)
3872 GridFunction *g = mesh_array[0]->GetNodes();
3873 if (g)
3874 {
3875 Array<GridFunction *> gf_array(num_pieces);
3876 for (i = 0; i < num_pieces; i++)
3877 {
3878 gf_array[i] = mesh_array[i]->GetNodes();
3879 }
3880 Nodes = new GridFunction(this, gf_array, num_pieces);
3881 own_nodes = 1;
3882 }
3883
3884 #ifdef MFEM_DEBUG
3885 CheckElementOrientation(false);
3886 CheckBdrElementOrientation(false);
3887 #endif
3888 }
3889
Mesh(Mesh * orig_mesh,int ref_factor,int ref_type)3890 Mesh::Mesh(Mesh *orig_mesh, int ref_factor, int ref_type)
3891 {
3892 Array<int> ref_factors(orig_mesh->GetNE());
3893 ref_factors = ref_factor;
3894 MakeRefined_(*orig_mesh, ref_factors, ref_type);
3895 }
3896
MakeRefined_(Mesh & orig_mesh,const Array<int> ref_factors,int ref_type)3897 void Mesh::MakeRefined_(Mesh &orig_mesh, const Array<int> ref_factors,
3898 int ref_type)
3899 {
3900 SetEmpty();
3901 Dim = orig_mesh.Dimension();
3902 spaceDim = orig_mesh.SpaceDimension();
3903
3904 int orig_ne = orig_mesh.GetNE();
3905 MFEM_VERIFY(ref_factors.Size() == orig_ne && orig_ne > 0,
3906 "Number of refinement factors must equal number of elements")
3907 MFEM_VERIFY(ref_factors.Min() >= 1, "Refinement factor must be >= 1");
3908 const int q_type = BasisType::GetQuadrature1D(ref_type);
3909 MFEM_VERIFY(Quadrature1D::CheckClosed(q_type) != Quadrature1D::Invalid,
3910 "Invalid refinement type. Must use closed basis type.");
3911
3912 int min_ref = ref_factors.Min();
3913 int max_ref = ref_factors.Max();
3914
3915 bool var_order = (min_ref != max_ref);
3916
3917 // variable order space can only be constructed over an NC mesh
3918 if (var_order) { orig_mesh.EnsureNCMesh(true); }
3919
3920 // Construct a scalar H1 FE space of order ref_factor and use its dofs as
3921 // the indices of the new, refined vertices.
3922 H1_FECollection rfec(min_ref, Dim, ref_type);
3923 FiniteElementSpace rfes(&orig_mesh, &rfec);
3924
3925 if (var_order)
3926 {
3927 rfes.SetRelaxedHpConformity(false);
3928 for (int i = 0; i < orig_ne; i++)
3929 {
3930 rfes.SetElementOrder(i, ref_factors[i]);
3931 }
3932 rfes.Update(false);
3933 }
3934
3935 // Set the number of vertices, set the actual coordinates later
3936 NumOfVertices = rfes.GetNDofs();
3937 vertices.SetSize(NumOfVertices);
3938
3939 Array<int> rdofs;
3940 DenseMatrix phys_pts;
3941
3942 GeometryRefiner refiner;
3943 refiner.SetType(q_type);
3944
3945 // Add refined elements and set vertex coordinates
3946 for (int el = 0; el < orig_ne; el++)
3947 {
3948 Geometry::Type geom = orig_mesh.GetElementGeometry(el);
3949 int attrib = orig_mesh.GetAttribute(el);
3950 int nvert = Geometry::NumVerts[geom];
3951 RefinedGeometry &RG = *refiner.Refine(geom, ref_factors[el]);
3952
3953 rfes.GetElementDofs(el, rdofs);
3954 MFEM_ASSERT(rdofs.Size() == RG.RefPts.Size(), "");
3955 const FiniteElement *rfe = rfes.GetFE(el);
3956 orig_mesh.GetElementTransformation(el)->Transform(rfe->GetNodes(),
3957 phys_pts);
3958 const int *c2h_map = rfec.GetDofMap(geom, ref_factors[el]);
3959 for (int i = 0; i < phys_pts.Width(); i++)
3960 {
3961 vertices[rdofs[i]].SetCoords(spaceDim, phys_pts.GetColumn(i));
3962 }
3963 for (int j = 0; j < RG.RefGeoms.Size()/nvert; j++)
3964 {
3965 Element *elem = NewElement(geom);
3966 elem->SetAttribute(attrib);
3967 int *v = elem->GetVertices();
3968 for (int k = 0; k < nvert; k++)
3969 {
3970 int cid = RG.RefGeoms[k+nvert*j]; // local Cartesian index
3971 v[k] = rdofs[c2h_map[cid]];
3972 }
3973 AddElement(elem);
3974 }
3975 }
3976
3977 // Add refined boundary elements
3978 for (int el = 0; el < orig_mesh.GetNBE(); el++)
3979 {
3980 int i, info;
3981 orig_mesh.GetBdrElementAdjacentElement(el, i, info);
3982 Geometry::Type geom = orig_mesh.GetBdrElementBaseGeometry(el);
3983 int attrib = orig_mesh.GetBdrAttribute(el);
3984 int nvert = Geometry::NumVerts[geom];
3985 RefinedGeometry &RG = *refiner.Refine(geom, ref_factors[i]);
3986
3987 rfes.GetBdrElementDofs(el, rdofs);
3988 MFEM_ASSERT(rdofs.Size() == RG.RefPts.Size(), "");
3989 const int *c2h_map = rfec.GetDofMap(geom, ref_factors[i]);
3990 for (int j = 0; j < RG.RefGeoms.Size()/nvert; j++)
3991 {
3992 Element *elem = NewElement(geom);
3993 elem->SetAttribute(attrib);
3994 int *v = elem->GetVertices();
3995 for (int k = 0; k < nvert; k++)
3996 {
3997 int cid = RG.RefGeoms[k+nvert*j]; // local Cartesian index
3998 v[k] = rdofs[c2h_map[cid]];
3999 }
4000 AddBdrElement(elem);
4001 }
4002 }
4003 FinalizeTopology(false);
4004 sequence = orig_mesh.GetSequence() + 1;
4005 last_operation = Mesh::REFINE;
4006
4007 // Set up the nodes of the new mesh (if the original mesh has nodes). The new
4008 // mesh is always straight-sided (i.e. degree 1 finite element space), but
4009 // the nodes are required for e.g. periodic meshes.
4010 if (orig_mesh.GetNodes())
4011 {
4012 bool discont = orig_mesh.GetNodalFESpace()->IsDGSpace();
4013 Ordering::Type dof_ordering = orig_mesh.GetNodalFESpace()->GetOrdering();
4014 Mesh::SetCurvature(1, discont, spaceDim, dof_ordering);
4015 FiniteElementSpace *nodal_fes = Nodes->FESpace();
4016 const FiniteElementCollection *nodal_fec = nodal_fes->FEColl();
4017 H1_FECollection vertex_fec(1, Dim);
4018 Array<int> dofs;
4019 int el_counter = 0;
4020 for (int iel = 0; iel < orig_ne; iel++)
4021 {
4022 Geometry::Type geom = orig_mesh.GetElementBaseGeometry(iel);
4023 int nvert = Geometry::NumVerts[geom];
4024 RefinedGeometry &RG = *refiner.Refine(geom, ref_factors[iel]);
4025 rfes.GetElementDofs(iel, rdofs);
4026 const FiniteElement *rfe = rfes.GetFE(iel);
4027 orig_mesh.GetElementTransformation(iel)->Transform(rfe->GetNodes(),
4028 phys_pts);
4029 const int *node_map = NULL;
4030 const H1_FECollection *h1_fec =
4031 dynamic_cast<const H1_FECollection *>(nodal_fec);
4032 if (h1_fec != NULL) { node_map = h1_fec->GetDofMap(geom); }
4033 const int *vertex_map = vertex_fec.GetDofMap(geom);
4034 const int *c2h_map = rfec.GetDofMap(geom, ref_factors[iel]);
4035 for (int jel = 0; jel < RG.RefGeoms.Size()/nvert; jel++)
4036 {
4037 nodal_fes->GetElementVDofs(el_counter++, dofs);
4038 for (int iv_lex=0; iv_lex<nvert; ++iv_lex)
4039 {
4040 // convert from lexicographic to vertex index
4041 int iv = vertex_map[iv_lex];
4042 // index of vertex of current element in phys_pts matrix
4043 int pt_idx = c2h_map[RG.RefGeoms[iv+nvert*jel]];
4044 // index of current vertex into DOF array
4045 int node_idx = node_map ? node_map[iv_lex] : iv_lex;
4046 for (int d=0; d<spaceDim; ++d)
4047 {
4048 (*Nodes)[dofs[node_idx + d*nvert]] = phys_pts(d,pt_idx);
4049 }
4050 }
4051 }
4052 }
4053 }
4054
4055 // Setup the data for the coarse-fine refinement transformations
4056 CoarseFineTr.embeddings.SetSize(GetNE());
4057 // First, compute total number of point matrices that we need per geometry
4058 // and the offsets into that array
4059 using GeomRef = std::pair<Geometry::Type, int>;
4060 std::map<GeomRef, int> point_matrices_offsets;
4061 int n_point_matrices[Geometry::NumGeom] = {}; // initialize to zero
4062 for (int el_coarse = 0; el_coarse < orig_ne; ++el_coarse)
4063 {
4064 Geometry::Type geom = orig_mesh.GetElementBaseGeometry(el_coarse);
4065 // Have we seen this pair of (goemetry, refinement level) before?
4066 GeomRef id(geom, ref_factors[el_coarse]);
4067 if (point_matrices_offsets.find(id) == point_matrices_offsets.end())
4068 {
4069 RefinedGeometry &RG = *refiner.Refine(geom, ref_factors[el_coarse]);
4070 int nvert = Geometry::NumVerts[geom];
4071 int nref_el = RG.RefGeoms.Size()/nvert;
4072 // If not, then store the offset and add to the size required
4073 point_matrices_offsets[id] = n_point_matrices[geom];
4074 n_point_matrices[geom] += nref_el;
4075 }
4076 }
4077
4078 // Set up the sizes
4079 for (int geom = 0; geom < Geometry::NumGeom; ++geom)
4080 {
4081 int nmatrices = n_point_matrices[geom];
4082 int nvert = Geometry::NumVerts[geom];
4083 CoarseFineTr.point_matrices[geom].SetSize(Dim, nvert, nmatrices);
4084 }
4085
4086 // Compute the point matrices and embeddings
4087 int el_fine = 0;
4088 for (int el_coarse = 0; el_coarse < orig_ne; ++el_coarse)
4089 {
4090 Geometry::Type geom = orig_mesh.GetElementBaseGeometry(el_coarse);
4091 int ref = ref_factors[el_coarse];
4092 int offset = point_matrices_offsets[GeomRef(geom, ref)];
4093 int nvert = Geometry::NumVerts[geom];
4094 RefinedGeometry &RG = *refiner.Refine(geom, ref);
4095 for (int j = 0; j < RG.RefGeoms.Size()/nvert; j++)
4096 {
4097 DenseMatrix &Pj = CoarseFineTr.point_matrices[geom](offset + j);
4098 for (int k = 0; k < nvert; k++)
4099 {
4100 int cid = RG.RefGeoms[k+nvert*j]; // local Cartesian index
4101 const IntegrationPoint &ip = RG.RefPts[cid];
4102 ip.Get(Pj.GetColumn(k), Dim);
4103 }
4104
4105 Embedding &emb = CoarseFineTr.embeddings[el_fine];
4106 emb.parent = el_coarse;
4107 emb.matrix = offset + j;
4108 ++el_fine;
4109 }
4110 }
4111
4112 MFEM_ASSERT(CheckElementOrientation(false) == 0, "");
4113
4114 // The check below is disabled because is fails for parallel meshes with
4115 // interior "boundary" element that, when such "boundary" element is between
4116 // two elements on different processors.
4117 // MFEM_ASSERT(CheckBdrElementOrientation(false) == 0, "");
4118 }
4119
MakeSimplicial(const Mesh & orig_mesh)4120 Mesh Mesh::MakeSimplicial(const Mesh &orig_mesh)
4121 {
4122 Mesh mesh;
4123 mesh.MakeSimplicial_(orig_mesh, NULL);
4124 return mesh;
4125 }
4126
MakeSimplicial_(const Mesh & orig_mesh,int * vglobal)4127 void Mesh::MakeSimplicial_(const Mesh &orig_mesh, int *vglobal)
4128 {
4129 MFEM_VERIFY(const_cast<Mesh&>(orig_mesh).CheckElementOrientation(false) == 0,
4130 "Mesh::MakeSimplicial requires a properly oriented input mesh");
4131 MFEM_VERIFY(orig_mesh.Conforming(),
4132 "Mesh::MakeSimplicial does not support non-conforming meshes.")
4133
4134 int dim = orig_mesh.Dimension();
4135 int sdim = orig_mesh.SpaceDimension();
4136
4137 if (dim == 1)
4138 {
4139 Mesh copy(orig_mesh);
4140 Swap(copy, true);
4141 return;
4142 }
4143
4144 int nv = orig_mesh.GetNV();
4145 int ne = orig_mesh.GetNE();
4146 int nbe = orig_mesh.GetNBE();
4147
4148 static int num_subdivisions[Geometry::NUM_GEOMETRIES];
4149 num_subdivisions[Geometry::POINT] = 1;
4150 num_subdivisions[Geometry::SEGMENT] = 1;
4151 num_subdivisions[Geometry::TRIANGLE] = 1;
4152 num_subdivisions[Geometry::TETRAHEDRON] = 1;
4153 num_subdivisions[Geometry::SQUARE] = 2;
4154 num_subdivisions[Geometry::PRISM] = 3;
4155 num_subdivisions[Geometry::CUBE] = 6;
4156 // NOTE: some hexes may be subdivided into only 5 tets, so this is an
4157 // estimate only. The actual number of created tets may be less, so the
4158 // elements array will need to be shrunk after mesh creation.
4159 int new_ne = 0, new_nbe = 0;
4160 for (int i=0; i<ne; ++i)
4161 {
4162 new_ne += num_subdivisions[orig_mesh.GetElementBaseGeometry(i)];
4163 }
4164 for (int i=0; i<nbe; ++i)
4165 {
4166 new_nbe += num_subdivisions[orig_mesh.GetBdrElementBaseGeometry(i)];
4167 }
4168
4169 InitMesh(dim, sdim, nv, new_ne, new_nbe);
4170
4171 // Vertices of the new mesh are same as the original mesh
4172 NumOfVertices = nv;
4173 for (int i=0; i<nv; ++i)
4174 {
4175 vertices[i].SetCoords(dim, orig_mesh.vertices[i]());
4176 }
4177
4178 // We need a global vertex numbering to identify which diagonals to split
4179 // (quad faces are split using the diagonal originating from the smallest
4180 // global vertex number). Use the supplied global numbering, if it is
4181 // non-NULL, otherwise use the local numbering.
4182 Array<int> vglobal_id;
4183 if (vglobal == NULL)
4184 {
4185 vglobal_id.SetSize(nv);
4186 for (int i=0; i<nv; ++i) { vglobal_id[i] = i; }
4187 vglobal = vglobal_id.GetData();
4188 }
4189
4190 constexpr int nv_tri = 3, nv_quad = 4, nv_tet = 4, nv_prism = 6, nv_hex = 8;
4191 constexpr int quad_ntris = 2, prism_ntets = 3;
4192 static const int quad_trimap[2][nv_tri*quad_ntris] =
4193 {
4194 {
4195 0, 0,
4196 1, 2,
4197 2, 3
4198 },{
4199 0, 1,
4200 1, 2,
4201 3, 3
4202 }
4203 };
4204 static const int prism_rot[nv_prism*nv_prism] =
4205 {
4206 0, 1, 2, 3, 4, 5,
4207 1, 2, 0, 4, 5, 3,
4208 2, 0, 1, 5, 3, 4,
4209 3, 5, 4, 0, 2, 1,
4210 4, 3, 5, 1, 0, 2,
4211 5, 4, 3, 2, 1, 0
4212 };
4213 static const int prism_f[nv_quad] = {1, 2, 5, 4};
4214 static const int prism_tetmaps[2][nv_prism*prism_ntets] =
4215 {
4216 {
4217 0, 0, 0,
4218 1, 1, 4,
4219 2, 5, 5,
4220 5, 4, 3
4221 },{
4222 0, 0, 0,
4223 1, 4, 4,
4224 2, 2, 5,
4225 4, 5, 3
4226 }
4227 };
4228 static const int hex_rot[nv_hex*nv_hex] =
4229 {
4230 0, 1, 2, 3, 4, 5, 6, 7,
4231 1, 0, 4, 5, 2, 3, 7, 6,
4232 2, 1, 5, 6, 3, 0, 4, 7,
4233 3, 0, 1, 2, 7, 4, 5, 6,
4234 4, 0, 3, 7, 5, 1, 2, 6,
4235 5, 1, 0, 4, 6, 2, 3, 7,
4236 6, 2, 1, 5, 7, 3, 0, 4,
4237 7, 3, 2, 6, 4, 0, 1, 5
4238 };
4239 static const int hex_f0[nv_quad] = {1, 2, 6, 5};
4240 static const int hex_f1[nv_quad] = {2, 3, 7, 6};
4241 static const int hex_f2[nv_quad] = {4, 5, 6, 7};
4242 static const int num_rot[8] = {0, 1, 2, 0, 0, 2, 1, 0};
4243 static const int hex_tetmap0[nv_tet*5] =
4244 {
4245 0, 0, 0, 0, 2,
4246 1, 2, 2, 5, 7,
4247 2, 7, 3, 7, 5,
4248 5, 5, 7, 4, 6
4249 };
4250 static const int hex_tetmap1[nv_tet*6] =
4251 {
4252 0, 0, 1, 0, 0, 1,
4253 5, 1, 6, 7, 7, 7,
4254 7, 7, 7, 2, 1, 6,
4255 4, 5, 5, 3, 2, 2
4256 };
4257 static const int hex_tetmap2[nv_tet*6] =
4258 {
4259 0, 0, 0, 0, 0, 0,
4260 4, 3, 7, 1, 3, 6,
4261 5, 7, 4, 2, 6, 5,
4262 6, 6, 6, 5, 2, 2
4263 };
4264 static const int hex_tetmap3[nv_tet*6] =
4265 {
4266 0, 0, 0, 0, 1, 1,
4267 2, 3, 7, 5, 5, 6,
4268 3, 7, 4, 6, 6, 2,
4269 6, 6, 6, 4, 0, 0
4270 };
4271 static const int *hex_tetmaps[4] =
4272 {
4273 hex_tetmap0, hex_tetmap1, hex_tetmap2, hex_tetmap3
4274 };
4275
4276 auto find_min = [](const int*a, int n) { return std::min_element(a,a+n)-a; };
4277
4278 for (int i=0; i<ne; ++i)
4279 {
4280 const int *v = orig_mesh.elements[i]->GetVertices();
4281 const int attrib = orig_mesh.GetAttribute(i);
4282 const Geometry::Type orig_geom = orig_mesh.GetElementBaseGeometry(i);
4283
4284 if (num_subdivisions[orig_geom] == 1)
4285 {
4286 // (num_subdivisions[orig_geom] == 1) implies that the element does
4287 // not need to be further split (it is either a segment, triangle,
4288 // or tetrahedron), and so it is left unchanged.
4289 Element *e = NewElement(orig_geom);
4290 e->SetAttribute(attrib);
4291 e->SetVertices(v);
4292 AddElement(e);
4293 }
4294 else if (orig_geom == Geometry::SQUARE)
4295 {
4296 for (int itri=0; itri<quad_ntris; ++itri)
4297 {
4298 Element *e = NewElement(Geometry::TRIANGLE);
4299 e->SetAttribute(attrib);
4300 int *v2 = e->GetVertices();
4301 for (int iv=0; iv<nv_tri; ++iv)
4302 {
4303 v2[iv] = v[quad_trimap[0][itri + iv*quad_ntris]];
4304 }
4305 AddElement(e);
4306 }
4307 }
4308 else if (orig_geom == Geometry::PRISM)
4309 {
4310 int vg[nv_prism];
4311 for (int iv=0; iv<nv_prism; ++iv) { vg[iv] = vglobal[v[iv]]; }
4312 // Rotate the vertices of the prism so that the smallest vertex index
4313 // is in the first place
4314 int irot = find_min(vg, nv_prism);
4315 for (int iv=0; iv<nv_prism; ++iv)
4316 {
4317 int jv = prism_rot[iv + irot*nv_prism];
4318 vg[iv] = v[jv];
4319 }
4320 // Two cases according to which diagonal splits third quad face
4321 int q[nv_quad];
4322 for (int iv=0; iv<nv_quad; ++iv) { q[iv] = vglobal[vg[prism_f[iv]]]; }
4323 int j = find_min(q, nv_quad);
4324 const int *tetmap = (j == 0 || j == 2) ? prism_tetmaps[0] : prism_tetmaps[1];
4325 for (int itet=0; itet<prism_ntets; ++itet)
4326 {
4327 Element *e = NewElement(Geometry::TETRAHEDRON);
4328 e->SetAttribute(attrib);
4329 int *v2 = e->GetVertices();
4330 for (int iv=0; iv<nv_tet; ++iv)
4331 {
4332 v2[iv] = vg[tetmap[itet + iv*prism_ntets]];
4333 }
4334 AddElement(e);
4335 }
4336 }
4337 else if (orig_geom == Geometry::CUBE)
4338 {
4339 int vg[nv_hex];
4340 for (int iv=0; iv<nv_hex; ++iv) { vg[iv] = vglobal[v[iv]]; }
4341
4342 // Rotate the vertices of the hex so that the smallest vertex index is
4343 // in the first place
4344 int irot = find_min(vg, nv_hex);
4345 for (int iv=0; iv<nv_hex; ++iv)
4346 {
4347 int jv = hex_rot[iv + irot*nv_hex];
4348 vg[iv] = v[jv];
4349 }
4350
4351 int q[nv_quad];
4352 // Bitmask is three binary digits, each digit is 1 if the diagonal of
4353 // the corresponding face goes through the 7th vertex, and 0 if not.
4354 int bitmask = 0;
4355 int j;
4356 // First quad face
4357 for (int iv=0; iv<nv_quad; ++iv) { q[iv] = vglobal[vg[hex_f0[iv]]]; }
4358 j = find_min(q, nv_quad);
4359 if (j == 0 || j == 2) { bitmask += 4; }
4360 // Second quad face
4361 for (int iv=0; iv<nv_quad; ++iv) { q[iv] = vglobal[vg[hex_f1[iv]]]; }
4362 j = find_min(q, nv_quad);
4363 if (j == 1 || j == 3) { bitmask += 2; }
4364 // Third quad face
4365 for (int iv=0; iv<nv_quad; ++iv) { q[iv] = vglobal[vg[hex_f2[iv]]]; }
4366 j = find_min(q, nv_quad);
4367 if (j == 0 || j == 2) { bitmask += 1; }
4368
4369 // Apply rotations
4370 int nrot = num_rot[bitmask];
4371 for (int irot=0; irot<nrot; ++irot)
4372 {
4373 int vtemp;
4374 vtemp = vg[1];
4375 vg[1] = vg[4];
4376 vg[4] = vg[3];
4377 vg[3] = vtemp;
4378 vtemp = vg[5];
4379 vg[5] = vg[7];
4380 vg[7] = vg[2];
4381 vg[2] = vtemp;
4382 }
4383
4384 // Sum up nonzero bits in bitmask
4385 int ndiags = ((bitmask&4) >> 2) + ((bitmask&2) >> 1) + (bitmask&1);
4386 int ntets = (ndiags == 0) ? 5 : 6;
4387 const int *tetmap = hex_tetmaps[ndiags];
4388 for (int itet=0; itet<ntets; ++itet)
4389 {
4390 Element *e = NewElement(Geometry::TETRAHEDRON);
4391 e->SetAttribute(attrib);
4392 int *v2 = e->GetVertices();
4393 for (int iv=0; iv<nv_tet; ++iv)
4394 {
4395 v2[iv] = vg[tetmap[itet + iv*ntets]];
4396 }
4397 AddElement(e);
4398 }
4399 }
4400 }
4401 // In 3D, shrink the element array because some hexes have only 5 tets
4402 if (dim == 3) { elements.SetSize(NumOfElements); }
4403
4404 for (int i=0; i<nbe; ++i)
4405 {
4406 const int *v = orig_mesh.boundary[i]->GetVertices();
4407 const int attrib = orig_mesh.GetBdrAttribute(i);
4408 const Geometry::Type orig_geom = orig_mesh.GetBdrElementBaseGeometry(i);
4409 if (num_subdivisions[orig_geom] == 1)
4410 {
4411 Element *be = NewElement(orig_geom);
4412 be->SetAttribute(attrib);
4413 be->SetVertices(v);
4414 AddBdrElement(be);
4415 }
4416 else if (orig_geom == Geometry::SQUARE)
4417 {
4418 int vg[nv_quad];
4419 for (int iv=0; iv<nv_quad; ++iv) { vg[iv] = vglobal[v[iv]]; }
4420 // Split quad according the smallest (global) vertex
4421 int iv_min = find_min(vg, nv_quad);
4422 int isplit = (iv_min == 0 || iv_min == 2) ? 0 : 1;
4423 for (int itri=0; itri<quad_ntris; ++itri)
4424 {
4425 Element *be = NewElement(Geometry::TRIANGLE);
4426 be->SetAttribute(attrib);
4427 int *v2 = be->GetVertices();
4428 for (int iv=0; iv<nv_tri; ++iv)
4429 {
4430 v2[iv] = v[quad_trimap[isplit][itri + iv*quad_ntris]];
4431 }
4432 AddBdrElement(be);
4433 }
4434 }
4435 else
4436 {
4437 MFEM_ABORT("Unreachable");
4438 }
4439 }
4440
4441 FinalizeTopology(false);
4442 sequence = orig_mesh.GetSequence();
4443 last_operation = orig_mesh.last_operation;
4444
4445 MFEM_ASSERT(CheckElementOrientation(false) == 0, "");
4446 MFEM_ASSERT(CheckBdrElementOrientation(false) == 0, "");
4447 }
4448
MakePeriodic(const Mesh & orig_mesh,const std::vector<int> & v2v)4449 Mesh Mesh::MakePeriodic(const Mesh &orig_mesh, const std::vector<int> &v2v)
4450 {
4451 Mesh periodic_mesh(orig_mesh, true); // Make a copy of the original mesh
4452 const FiniteElementSpace *nodal_fes = orig_mesh.GetNodalFESpace();
4453 int nodal_order = nodal_fes ? nodal_fes->GetMaxElementOrder() : 1;
4454 periodic_mesh.SetCurvature(nodal_order, true);
4455
4456 // renumber element vertices
4457 for (int i = 0; i < periodic_mesh.GetNE(); i++)
4458 {
4459 Element *el = periodic_mesh.GetElement(i);
4460 int *v = el->GetVertices();
4461 int nv = el->GetNVertices();
4462 for (int j = 0; j < nv; j++)
4463 {
4464 v[j] = v2v[v[j]];
4465 }
4466 }
4467 // renumber boundary element vertices
4468 for (int i = 0; i < periodic_mesh.GetNBE(); i++)
4469 {
4470 Element *el = periodic_mesh.GetBdrElement(i);
4471 int *v = el->GetVertices();
4472 int nv = el->GetNVertices();
4473 for (int j = 0; j < nv; j++)
4474 {
4475 v[j] = v2v[v[j]];
4476 }
4477 }
4478
4479 periodic_mesh.RemoveUnusedVertices();
4480 return periodic_mesh;
4481 }
4482
CreatePeriodicVertexMapping(const std::vector<Vector> & translations,double tol) const4483 std::vector<int> Mesh::CreatePeriodicVertexMapping(
4484 const std::vector<Vector> &translations, double tol) const
4485 {
4486 int sdim = SpaceDimension();
4487
4488 Vector coord(sdim), at(sdim), dx(sdim);
4489 Vector xMax(sdim), xMin(sdim), xDiff(sdim);
4490 xMax = xMin = xDiff = 0.0;
4491
4492 // Get a list of all vertices on the boundary
4493 set<int> bdr_v;
4494 for (int be = 0; be < GetNBE(); be++)
4495 {
4496 Array<int> dofs;
4497 GetBdrElementVertices(be,dofs);
4498
4499 for (int i = 0; i < dofs.Size(); i++)
4500 {
4501 bdr_v.insert(dofs[i]);
4502
4503 coord = GetVertex(dofs[i]);
4504 for (int j = 0; j < sdim; j++)
4505 {
4506 xMax[j] = max(xMax[j], coord[j]);
4507 xMin[j] = min(xMin[j], coord[j]);
4508 }
4509 }
4510 }
4511 add(xMax, -1.0, xMin, xDiff);
4512 double dia = xDiff.Norml2(); // compute mesh diameter
4513
4514 // We now identify coincident vertices. Several originally distinct vertices
4515 // may become coincident under the periodic mapping. One of these vertices
4516 // will be identified as the "primary" vertex, and all other coincident
4517 // vertices will be considered as "replicas".
4518
4519 // replica2primary[v] is the index of the primary vertex of replica `v`
4520 map<int, int> replica2primary;
4521 // primary2replicas[v] is a set of indices of replicas of primary vertex `v`
4522 map<int, set<int>> primary2replicas;
4523
4524 // We begin with the assumption that all vertices are primary, and that there
4525 // are no replicas.
4526 for (int v : bdr_v) { primary2replicas[v]; }
4527
4528 // Make `r` and all of `r`'s replicas be replicas of `p`. Delete `r` from the
4529 // list of primary vertices.
4530 auto make_replica = [&replica2primary, &primary2replicas](int r, int p)
4531 {
4532 if (r == p) { return; }
4533 primary2replicas[p].insert(r);
4534 replica2primary[r] = p;
4535 for (int s : primary2replicas[r])
4536 {
4537 primary2replicas[p].insert(s);
4538 replica2primary[s] = p;
4539 }
4540 primary2replicas.erase(r);
4541 };
4542
4543 for (unsigned int i = 0; i < translations.size(); i++)
4544 {
4545 for (int vi : bdr_v)
4546 {
4547 coord = GetVertex(vi);
4548 add(coord, translations[i], at);
4549
4550 for (int vj : bdr_v)
4551 {
4552 coord = GetVertex(vj);
4553 add(at, -1.0, coord, dx);
4554 if (dx.Norml2() > dia*tol) { continue; }
4555
4556 // The two vertices vi and vj are coincident.
4557
4558 // Are vertices `vi` and `vj` already primary?
4559 bool pi = primary2replicas.find(vi) != primary2replicas.end();
4560 bool pj = primary2replicas.find(vj) != primary2replicas.end();
4561
4562 if (pi && pj)
4563 {
4564 // Both vertices are currently primary
4565 // Demote `vj` to be a replica of `vi`
4566 make_replica(vj, vi);
4567 }
4568 else if (pi && !pj)
4569 {
4570 // `vi` is primary and `vj` is a replica
4571 int owner_of_vj = replica2primary[vj];
4572 // Make `vi` and its replicas be replicas of `vj`'s owner
4573 make_replica(vi, owner_of_vj);
4574 }
4575 else if (!pi && pj)
4576 {
4577 // `vi` is currently a replica and `vj` is currently primary
4578 // Make `vj` and its replicas be replicas of `vi`'s owner
4579 int owner_of_vi = replica2primary[vi];
4580 make_replica(vj, owner_of_vi);
4581 }
4582 else
4583 {
4584 // Both vertices are currently replicas
4585 // Make `vj`'s owner and all of its owner's replicas be replicas
4586 // of `vi`'s owner
4587 int owner_of_vi = replica2primary[vi];
4588 int owner_of_vj = replica2primary[vj];
4589 make_replica(owner_of_vj, owner_of_vi);
4590 }
4591 break;
4592 }
4593 }
4594 }
4595
4596 std::vector<int> v2v(GetNV());
4597 for (size_t i = 0; i < v2v.size(); i++)
4598 {
4599 v2v[i] = i;
4600 }
4601 for (auto &&r2p : replica2primary)
4602 {
4603 v2v[r2p.first] = r2p.second;
4604 }
4605 return v2v;
4606 }
4607
KnotInsert(Array<KnotVector * > & kv)4608 void Mesh::KnotInsert(Array<KnotVector *> &kv)
4609 {
4610 if (NURBSext == NULL)
4611 {
4612 mfem_error("Mesh::KnotInsert : Not a NURBS mesh!");
4613 }
4614
4615 if (kv.Size() != NURBSext->GetNKV())
4616 {
4617 mfem_error("Mesh::KnotInsert : KnotVector array size mismatch!");
4618 }
4619
4620 NURBSext->ConvertToPatches(*Nodes);
4621
4622 NURBSext->KnotInsert(kv);
4623
4624 last_operation = Mesh::NONE; // FiniteElementSpace::Update is not supported
4625 sequence++;
4626
4627 UpdateNURBS();
4628 }
4629
KnotInsert(Array<Vector * > & kv)4630 void Mesh::KnotInsert(Array<Vector *> &kv)
4631 {
4632 if (NURBSext == NULL)
4633 {
4634 mfem_error("Mesh::KnotInsert : Not a NURBS mesh!");
4635 }
4636
4637 if (kv.Size() != NURBSext->GetNKV())
4638 {
4639 mfem_error("Mesh::KnotInsert : KnotVector array size mismatch!");
4640 }
4641
4642 NURBSext->ConvertToPatches(*Nodes);
4643
4644 NURBSext->KnotInsert(kv);
4645
4646 last_operation = Mesh::NONE; // FiniteElementSpace::Update is not supported
4647 sequence++;
4648
4649 UpdateNURBS();
4650 }
4651
NURBSUniformRefinement()4652 void Mesh::NURBSUniformRefinement()
4653 {
4654 // do not check for NURBSext since this method is protected
4655 NURBSext->ConvertToPatches(*Nodes);
4656
4657 NURBSext->UniformRefinement();
4658
4659 last_operation = Mesh::NONE; // FiniteElementSpace::Update is not supported
4660 sequence++;
4661
4662 UpdateNURBS();
4663 }
4664
DegreeElevate(int rel_degree,int degree)4665 void Mesh::DegreeElevate(int rel_degree, int degree)
4666 {
4667 if (NURBSext == NULL)
4668 {
4669 mfem_error("Mesh::DegreeElevate : Not a NURBS mesh!");
4670 }
4671
4672 NURBSext->ConvertToPatches(*Nodes);
4673
4674 NURBSext->DegreeElevate(rel_degree, degree);
4675
4676 last_operation = Mesh::NONE; // FiniteElementSpace::Update is not supported
4677 sequence++;
4678
4679 UpdateNURBS();
4680 }
4681
UpdateNURBS()4682 void Mesh::UpdateNURBS()
4683 {
4684 ResetLazyData();
4685
4686 NURBSext->SetKnotsFromPatches();
4687
4688 Dim = NURBSext->Dimension();
4689 spaceDim = Dim;
4690
4691 if (NumOfElements != NURBSext->GetNE())
4692 {
4693 for (int i = 0; i < elements.Size(); i++)
4694 {
4695 FreeElement(elements[i]);
4696 }
4697 NumOfElements = NURBSext->GetNE();
4698 NURBSext->GetElementTopo(elements);
4699 }
4700
4701 if (NumOfBdrElements != NURBSext->GetNBE())
4702 {
4703 for (int i = 0; i < boundary.Size(); i++)
4704 {
4705 FreeElement(boundary[i]);
4706 }
4707 NumOfBdrElements = NURBSext->GetNBE();
4708 NURBSext->GetBdrElementTopo(boundary);
4709 }
4710
4711 Nodes->FESpace()->Update();
4712 Nodes->Update();
4713 NURBSext->SetCoordsFromPatches(*Nodes);
4714
4715 if (NumOfVertices != NURBSext->GetNV())
4716 {
4717 NumOfVertices = NURBSext->GetNV();
4718 vertices.SetSize(NumOfVertices);
4719 int vd = Nodes->VectorDim();
4720 for (int i = 0; i < vd; i++)
4721 {
4722 Vector vert_val;
4723 Nodes->GetNodalValues(vert_val, i+1);
4724 for (int j = 0; j < NumOfVertices; j++)
4725 {
4726 vertices[j](i) = vert_val(j);
4727 }
4728 }
4729 }
4730
4731 if (el_to_edge)
4732 {
4733 NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
4734 if (Dim == 2)
4735 {
4736 GenerateFaces();
4737 }
4738 }
4739
4740 if (el_to_face)
4741 {
4742 GetElementToFaceTable();
4743 GenerateFaces();
4744 }
4745 }
4746
LoadPatchTopo(std::istream & input,Array<int> & edge_to_knot)4747 void Mesh::LoadPatchTopo(std::istream &input, Array<int> &edge_to_knot)
4748 {
4749 SetEmpty();
4750
4751 // Read MFEM NURBS mesh v1.0 format
4752 string ident;
4753
4754 skip_comment_lines(input, '#');
4755
4756 input >> ident; // 'dimension'
4757 input >> Dim;
4758 spaceDim = Dim;
4759
4760 skip_comment_lines(input, '#');
4761
4762 input >> ident; // 'elements'
4763 input >> NumOfElements;
4764 elements.SetSize(NumOfElements);
4765 for (int j = 0; j < NumOfElements; j++)
4766 {
4767 elements[j] = ReadElement(input);
4768 }
4769
4770 skip_comment_lines(input, '#');
4771
4772 input >> ident; // 'boundary'
4773 input >> NumOfBdrElements;
4774 boundary.SetSize(NumOfBdrElements);
4775 for (int j = 0; j < NumOfBdrElements; j++)
4776 {
4777 boundary[j] = ReadElement(input);
4778 }
4779
4780 skip_comment_lines(input, '#');
4781
4782 input >> ident; // 'edges'
4783 input >> NumOfEdges;
4784 edge_vertex = new Table(NumOfEdges, 2);
4785 edge_to_knot.SetSize(NumOfEdges);
4786 for (int j = 0; j < NumOfEdges; j++)
4787 {
4788 int *v = edge_vertex->GetRow(j);
4789 input >> edge_to_knot[j] >> v[0] >> v[1];
4790 if (v[0] > v[1])
4791 {
4792 edge_to_knot[j] = -1 - edge_to_knot[j];
4793 }
4794 }
4795
4796 skip_comment_lines(input, '#');
4797
4798 input >> ident; // 'vertices'
4799 input >> NumOfVertices;
4800 vertices.SetSize(0);
4801
4802 FinalizeTopology();
4803 CheckBdrElementOrientation(); // check and fix boundary element orientation
4804 }
4805
XYZ_VectorFunction(const Vector & p,Vector & v)4806 void XYZ_VectorFunction(const Vector &p, Vector &v)
4807 {
4808 if (p.Size() >= v.Size())
4809 {
4810 for (int d = 0; d < v.Size(); d++)
4811 {
4812 v(d) = p(d);
4813 }
4814 }
4815 else
4816 {
4817 int d;
4818 for (d = 0; d < p.Size(); d++)
4819 {
4820 v(d) = p(d);
4821 }
4822 for ( ; d < v.Size(); d++)
4823 {
4824 v(d) = 0.0;
4825 }
4826 }
4827 }
4828
GetNodes(GridFunction & nodes) const4829 void Mesh::GetNodes(GridFunction &nodes) const
4830 {
4831 if (Nodes == NULL || Nodes->FESpace() != nodes.FESpace())
4832 {
4833 const int newSpaceDim = nodes.FESpace()->GetVDim();
4834 VectorFunctionCoefficient xyz(newSpaceDim, XYZ_VectorFunction);
4835 nodes.ProjectCoefficient(xyz);
4836 }
4837 else
4838 {
4839 nodes = *Nodes;
4840 }
4841 }
4842
SetNodalFESpace(FiniteElementSpace * nfes)4843 void Mesh::SetNodalFESpace(FiniteElementSpace *nfes)
4844 {
4845 GridFunction *nodes = new GridFunction(nfes);
4846 SetNodalGridFunction(nodes, true);
4847 }
4848
EnsureNodes()4849 void Mesh::EnsureNodes()
4850 {
4851 if (Nodes)
4852 {
4853 const FiniteElementCollection *fec = GetNodalFESpace()->FEColl();
4854 if (dynamic_cast<const H1_FECollection*>(fec)
4855 || dynamic_cast<const L2_FECollection*>(fec))
4856 {
4857 return;
4858 }
4859 else // Mesh using a legacy FE_Collection
4860 {
4861 const int order = GetNodalFESpace()->GetElementOrder(0);
4862 SetCurvature(order, false, -1, Ordering::byVDIM);
4863 }
4864 }
4865 else // First order H1 mesh
4866 {
4867 SetCurvature(1, false, -1, Ordering::byVDIM);
4868 }
4869 }
4870
SetNodalGridFunction(GridFunction * nodes,bool make_owner)4871 void Mesh::SetNodalGridFunction(GridFunction *nodes, bool make_owner)
4872 {
4873 GetNodes(*nodes);
4874 NewNodes(*nodes, make_owner);
4875 }
4876
GetNodalFESpace() const4877 const FiniteElementSpace *Mesh::GetNodalFESpace() const
4878 {
4879 return ((Nodes) ? Nodes->FESpace() : NULL);
4880 }
4881
SetCurvature(int order,bool discont,int space_dim,int ordering)4882 void Mesh::SetCurvature(int order, bool discont, int space_dim, int ordering)
4883 {
4884 space_dim = (space_dim == -1) ? spaceDim : space_dim;
4885 FiniteElementCollection* nfec;
4886 if (discont)
4887 {
4888 const int type = 1; // Gauss-Lobatto points
4889 nfec = new L2_FECollection(order, Dim, type);
4890 }
4891 else
4892 {
4893 nfec = new H1_FECollection(order, Dim);
4894 }
4895 FiniteElementSpace* nfes = new FiniteElementSpace(this, nfec, space_dim,
4896 ordering);
4897 SetNodalFESpace(nfes);
4898 Nodes->MakeOwner(nfec);
4899 }
4900
SetVerticesFromNodes(const GridFunction * nodes)4901 void Mesh::SetVerticesFromNodes(const GridFunction *nodes)
4902 {
4903 MFEM_ASSERT(nodes != NULL, "");
4904 for (int i = 0; i < spaceDim; i++)
4905 {
4906 Vector vert_val;
4907 nodes->GetNodalValues(vert_val, i+1);
4908 for (int j = 0; j < NumOfVertices; j++)
4909 {
4910 vertices[j](i) = vert_val(j);
4911 }
4912 }
4913 }
4914
GetNumFaces() const4915 int Mesh::GetNumFaces() const
4916 {
4917 switch (Dim)
4918 {
4919 case 1: return GetNV();
4920 case 2: return GetNEdges();
4921 case 3: return GetNFaces();
4922 }
4923 return 0;
4924 }
4925
CountFacesByType(const Mesh & mesh,const FaceType type)4926 static int CountFacesByType(const Mesh &mesh, const FaceType type)
4927 {
4928 int e1, e2;
4929 int inf1, inf2;
4930 int nf = 0;
4931 for (int f = 0; f < mesh.GetNumFaces(); ++f)
4932 {
4933 mesh.GetFaceElements(f, &e1, &e2);
4934 mesh.GetFaceInfos(f, &inf1, &inf2);
4935 if ((type==FaceType::Interior && (e2>=0 || (e2<0 && inf2>=0))) ||
4936 (type==FaceType::Boundary && e2<0 && inf2<0) ) { nf++; }
4937 }
4938 return nf;
4939 }
4940
GetNFbyType(FaceType type) const4941 int Mesh::GetNFbyType(FaceType type) const
4942 {
4943 const bool isInt = type==FaceType::Interior;
4944 int &nf = isInt ? nbInteriorFaces : nbBoundaryFaces;
4945 if (nf<0) { nf = CountFacesByType(*this, type); }
4946 return nf;
4947 }
4948
4949 #if (!defined(MFEM_USE_MPI) || defined(MFEM_DEBUG))
4950 static const char *fixed_or_not[] = { "fixed", "NOT FIXED" };
4951 #endif
4952
CheckElementOrientation(bool fix_it)4953 int Mesh::CheckElementOrientation(bool fix_it)
4954 {
4955 int i, j, k, wo = 0, fo = 0;
4956 double *v[4];
4957
4958 if (Dim == 2 && spaceDim == 2)
4959 {
4960 DenseMatrix J(2, 2);
4961
4962 for (i = 0; i < NumOfElements; i++)
4963 {
4964 int *vi = elements[i]->GetVertices();
4965 if (Nodes == NULL)
4966 {
4967 for (j = 0; j < 3; j++)
4968 {
4969 v[j] = vertices[vi[j]]();
4970 }
4971 for (j = 0; j < 2; j++)
4972 for (k = 0; k < 2; k++)
4973 {
4974 J(j, k) = v[j+1][k] - v[0][k];
4975 }
4976 }
4977 else
4978 {
4979 // only check the Jacobian at the center of the element
4980 GetElementJacobian(i, J);
4981 }
4982 if (J.Det() < 0.0)
4983 {
4984 if (fix_it)
4985 {
4986 switch (GetElementType(i))
4987 {
4988 case Element::TRIANGLE:
4989 mfem::Swap(vi[0], vi[1]);
4990 break;
4991 case Element::QUADRILATERAL:
4992 mfem::Swap(vi[1], vi[3]);
4993 break;
4994 default:
4995 MFEM_ABORT("Invalid 2D element type \""
4996 << GetElementType(i) << "\"");
4997 break;
4998 }
4999 fo++;
5000 }
5001 wo++;
5002 }
5003 }
5004 }
5005
5006 if (Dim == 3)
5007 {
5008 DenseMatrix J(3, 3);
5009
5010 for (i = 0; i < NumOfElements; i++)
5011 {
5012 int *vi = elements[i]->GetVertices();
5013 switch (GetElementType(i))
5014 {
5015 case Element::TETRAHEDRON:
5016 if (Nodes == NULL)
5017 {
5018 for (j = 0; j < 4; j++)
5019 {
5020 v[j] = vertices[vi[j]]();
5021 }
5022 for (j = 0; j < 3; j++)
5023 for (k = 0; k < 3; k++)
5024 {
5025 J(j, k) = v[j+1][k] - v[0][k];
5026 }
5027 }
5028 else
5029 {
5030 // only check the Jacobian at the center of the element
5031 GetElementJacobian(i, J);
5032 }
5033 if (J.Det() < 0.0)
5034 {
5035 wo++;
5036 if (fix_it)
5037 {
5038 mfem::Swap(vi[0], vi[1]);
5039 fo++;
5040 }
5041 }
5042 break;
5043
5044 case Element::WEDGE:
5045 // only check the Jacobian at the center of the element
5046 GetElementJacobian(i, J);
5047 if (J.Det() < 0.0)
5048 {
5049 wo++;
5050 if (fix_it)
5051 {
5052 // how?
5053 }
5054 }
5055 break;
5056
5057 case Element::HEXAHEDRON:
5058 // only check the Jacobian at the center of the element
5059 GetElementJacobian(i, J);
5060 if (J.Det() < 0.0)
5061 {
5062 wo++;
5063 if (fix_it)
5064 {
5065 // how?
5066 }
5067 }
5068 break;
5069
5070 default:
5071 MFEM_ABORT("Invalid 3D element type \""
5072 << GetElementType(i) << "\"");
5073 break;
5074 }
5075 }
5076 }
5077 #if (!defined(MFEM_USE_MPI) || defined(MFEM_DEBUG))
5078 if (wo > 0)
5079 {
5080 mfem::out << "Elements with wrong orientation: " << wo << " / "
5081 << NumOfElements << " (" << fixed_or_not[(wo == fo) ? 0 : 1]
5082 << ")" << endl;
5083 }
5084 #endif
5085 return wo;
5086 }
5087
GetTriOrientation(const int * base,const int * test)5088 int Mesh::GetTriOrientation(const int *base, const int *test)
5089 {
5090 // Static method.
5091 // This function computes the index 'j' of the permutation that transforms
5092 // test into base: test[tri_orientation[j][i]]=base[i].
5093 // tri_orientation = Geometry::Constants<Geometry::TRIANGLE>::Orient
5094 int orient;
5095
5096 if (test[0] == base[0])
5097 if (test[1] == base[1])
5098 {
5099 orient = 0; // (0, 1, 2)
5100 }
5101 else
5102 {
5103 orient = 5; // (0, 2, 1)
5104 }
5105 else if (test[0] == base[1])
5106 if (test[1] == base[0])
5107 {
5108 orient = 1; // (1, 0, 2)
5109 }
5110 else
5111 {
5112 orient = 2; // (1, 2, 0)
5113 }
5114 else // test[0] == base[2]
5115 if (test[1] == base[0])
5116 {
5117 orient = 4; // (2, 0, 1)
5118 }
5119 else
5120 {
5121 orient = 3; // (2, 1, 0)
5122 }
5123
5124 #ifdef MFEM_DEBUG
5125 const int *aor = tri_t::Orient[orient];
5126 for (int j = 0; j < 3; j++)
5127 if (test[aor[j]] != base[j])
5128 {
5129 mfem_error("Mesh::GetTriOrientation(...)");
5130 }
5131 #endif
5132
5133 return orient;
5134 }
5135
GetQuadOrientation(const int * base,const int * test)5136 int Mesh::GetQuadOrientation(const int *base, const int *test)
5137 {
5138 int i;
5139
5140 for (i = 0; i < 4; i++)
5141 if (test[i] == base[0])
5142 {
5143 break;
5144 }
5145
5146 #ifdef MFEM_DEBUG
5147 int orient;
5148 if (test[(i+1)%4] == base[1])
5149 {
5150 orient = 2*i;
5151 }
5152 else
5153 {
5154 orient = 2*i+1;
5155 }
5156 const int *aor = quad_t::Orient[orient];
5157 for (int j = 0; j < 4; j++)
5158 if (test[aor[j]] != base[j])
5159 {
5160 mfem::err << "Mesh::GetQuadOrientation(...)" << endl;
5161 mfem::err << " base = [";
5162 for (int k = 0; k < 4; k++)
5163 {
5164 mfem::err << " " << base[k];
5165 }
5166 mfem::err << " ]\n test = [";
5167 for (int k = 0; k < 4; k++)
5168 {
5169 mfem::err << " " << test[k];
5170 }
5171 mfem::err << " ]" << endl;
5172 mfem_error();
5173 }
5174 #endif
5175
5176 if (test[(i+1)%4] == base[1])
5177 {
5178 return 2*i;
5179 }
5180
5181 return 2*i+1;
5182 }
5183
GetTetOrientation(const int * base,const int * test)5184 int Mesh::GetTetOrientation(const int *base, const int *test)
5185 {
5186 // Static method.
5187 // This function computes the index 'j' of the permutation that transforms
5188 // test into base: test[tet_orientation[j][i]]=base[i].
5189 // tet_orientation = Geometry::Constants<Geometry::TETRAHEDRON>::Orient
5190 int orient;
5191
5192 if (test[0] == base[0])
5193 if (test[1] == base[1])
5194 if (test[2] == base[2])
5195 {
5196 orient = 0; // (0, 1, 2, 3)
5197 }
5198 else
5199 {
5200 orient = 1; // (0, 1, 3, 2)
5201 }
5202 else if (test[2] == base[1])
5203 if (test[3] == base[2])
5204 {
5205 orient = 2; // (0, 2, 3, 1)
5206 }
5207 else
5208 {
5209 orient = 3; // (0, 2, 1, 3)
5210 }
5211 else // test[3] == base[1]
5212 if (test[1] == base[2])
5213 {
5214 orient = 4; // (0, 3, 1, 2)
5215 }
5216 else
5217 {
5218 orient = 5; // (0, 3, 2, 1)
5219 }
5220 else if (test[1] == base[0])
5221 if (test[2] == base[1])
5222 if (test[0] == base[2])
5223 {
5224 orient = 6; // (1, 2, 0, 3)
5225 }
5226 else
5227 {
5228 orient = 7; // (1, 2, 3, 0)
5229 }
5230 else if (test[3] == base[1])
5231 if (test[2] == base[2])
5232 {
5233 orient = 8; // (1, 3, 2, 0)
5234 }
5235 else
5236 {
5237 orient = 9; // (1, 3, 0, 2)
5238 }
5239 else // test[0] == base[1]
5240 if (test[3] == base[2])
5241 {
5242 orient = 10; // (1, 0, 3, 2)
5243 }
5244 else
5245 {
5246 orient = 11; // (1, 0, 2, 3)
5247 }
5248 else if (test[2] == base[0])
5249 if (test[3] == base[1])
5250 if (test[0] == base[2])
5251 {
5252 orient = 12; // (2, 3, 0, 1)
5253 }
5254 else
5255 {
5256 orient = 13; // (2, 3, 1, 0)
5257 }
5258 else if (test[0] == base[1])
5259 if (test[1] == base[2])
5260 {
5261 orient = 14; // (2, 0, 1, 3)
5262 }
5263 else
5264 {
5265 orient = 15; // (2, 0, 3, 1)
5266 }
5267 else // test[1] == base[1]
5268 if (test[3] == base[2])
5269 {
5270 orient = 16; // (2, 1, 3, 0)
5271 }
5272 else
5273 {
5274 orient = 17; // (2, 1, 0, 3)
5275 }
5276 else // (test[3] == base[0])
5277 if (test[0] == base[1])
5278 if (test[2] == base[2])
5279 {
5280 orient = 18; // (3, 0, 2, 1)
5281 }
5282 else
5283 {
5284 orient = 19; // (3, 0, 1, 2)
5285 }
5286 else if (test[1] == base[1])
5287 if (test[0] == base[2])
5288 {
5289 orient = 20; // (3, 1, 0, 2)
5290 }
5291 else
5292 {
5293 orient = 21; // (3, 1, 2, 0)
5294 }
5295 else // test[2] == base[1]
5296 if (test[1] == base[2])
5297 {
5298 orient = 22; // (3, 2, 1, 0)
5299 }
5300 else
5301 {
5302 orient = 23; // (3, 2, 0, 1)
5303 }
5304
5305 #ifdef MFEM_DEBUG
5306 const int *aor = tet_t::Orient[orient];
5307 for (int j = 0; j < 4; j++)
5308 if (test[aor[j]] != base[j])
5309 {
5310 mfem_error("Mesh::GetTetOrientation(...)");
5311 }
5312 #endif
5313
5314 return orient;
5315 }
5316
CheckBdrElementOrientation(bool fix_it)5317 int Mesh::CheckBdrElementOrientation(bool fix_it)
5318 {
5319 int wo = 0; // count wrong orientations
5320
5321 if (Dim == 2)
5322 {
5323 if (el_to_edge == NULL) // edges were not generated
5324 {
5325 el_to_edge = new Table;
5326 NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
5327 GenerateFaces(); // 'Faces' in 2D refers to the edges
5328 }
5329 for (int i = 0; i < NumOfBdrElements; i++)
5330 {
5331 if (faces_info[be_to_edge[i]].Elem2No < 0) // boundary face
5332 {
5333 int *bv = boundary[i]->GetVertices();
5334 int *fv = faces[be_to_edge[i]]->GetVertices();
5335 if (bv[0] != fv[0])
5336 {
5337 if (fix_it)
5338 {
5339 mfem::Swap<int>(bv[0], bv[1]);
5340 }
5341 wo++;
5342 }
5343 }
5344 }
5345 }
5346
5347 if (Dim == 3)
5348 {
5349 for (int i = 0; i < NumOfBdrElements; i++)
5350 {
5351 const int fi = be_to_face[i];
5352
5353 if (faces_info[fi].Elem2No >= 0) { continue; }
5354
5355 // boundary face
5356 int *bv = boundary[i]->GetVertices();
5357 // Make sure the 'faces' are generated:
5358 MFEM_ASSERT(fi < faces.Size(), "internal error");
5359 const int *fv = faces[fi]->GetVertices();
5360 int orientation; // orientation of the bdr. elem. w.r.t. the
5361 // corresponding face element (that's the base)
5362 const Element::Type bdr_type = GetBdrElementType(i);
5363 switch (bdr_type)
5364 {
5365 case Element::TRIANGLE:
5366 {
5367 orientation = GetTriOrientation(fv, bv);
5368 break;
5369 }
5370 case Element::QUADRILATERAL:
5371 {
5372 orientation = GetQuadOrientation(fv, bv);
5373 break;
5374 }
5375 default:
5376 MFEM_ABORT("Invalid 2D boundary element type \""
5377 << bdr_type << "\"");
5378 orientation = 0; // suppress a warning
5379 break;
5380 }
5381
5382 if (orientation % 2 == 0) { continue; }
5383 wo++;
5384 if (!fix_it) { continue; }
5385
5386 switch (bdr_type)
5387 {
5388 case Element::TRIANGLE:
5389 {
5390 // swap vertices 0 and 1 so that we don't change the marked edge:
5391 // (0,1,2) -> (1,0,2)
5392 mfem::Swap<int>(bv[0], bv[1]);
5393 if (bel_to_edge)
5394 {
5395 int *be = bel_to_edge->GetRow(i);
5396 mfem::Swap<int>(be[1], be[2]);
5397 }
5398 break;
5399 }
5400 case Element::QUADRILATERAL:
5401 {
5402 mfem::Swap<int>(bv[0], bv[2]);
5403 if (bel_to_edge)
5404 {
5405 int *be = bel_to_edge->GetRow(i);
5406 mfem::Swap<int>(be[0], be[1]);
5407 mfem::Swap<int>(be[2], be[3]);
5408 }
5409 break;
5410 }
5411 default: // unreachable
5412 break;
5413 }
5414 }
5415 }
5416 // #if (!defined(MFEM_USE_MPI) || defined(MFEM_DEBUG))
5417 #ifdef MFEM_DEBUG
5418 if (wo > 0)
5419 {
5420 mfem::out << "Boundary elements with wrong orientation: " << wo << " / "
5421 << NumOfBdrElements << " (" << fixed_or_not[fix_it ? 0 : 1]
5422 << ")" << endl;
5423 }
5424 #endif
5425 return wo;
5426 }
5427
GetNumGeometries(int dim) const5428 int Mesh::GetNumGeometries(int dim) const
5429 {
5430 MFEM_ASSERT(0 <= dim && dim <= Dim, "invalid dim: " << dim);
5431 int num_geoms = 0;
5432 for (int g = Geometry::DimStart[dim]; g < Geometry::DimStart[dim+1]; g++)
5433 {
5434 if (HasGeometry(Geometry::Type(g))) { num_geoms++; }
5435 }
5436 return num_geoms;
5437 }
5438
GetGeometries(int dim,Array<Geometry::Type> & el_geoms) const5439 void Mesh::GetGeometries(int dim, Array<Geometry::Type> &el_geoms) const
5440 {
5441 MFEM_ASSERT(0 <= dim && dim <= Dim, "invalid dim: " << dim);
5442 el_geoms.SetSize(0);
5443 for (int g = Geometry::DimStart[dim]; g < Geometry::DimStart[dim+1]; g++)
5444 {
5445 if (HasGeometry(Geometry::Type(g)))
5446 {
5447 el_geoms.Append(Geometry::Type(g));
5448 }
5449 }
5450 }
5451
GetElementEdges(int i,Array<int> & edges,Array<int> & cor) const5452 void Mesh::GetElementEdges(int i, Array<int> &edges, Array<int> &cor) const
5453 {
5454 if (el_to_edge)
5455 {
5456 el_to_edge->GetRow(i, edges);
5457 }
5458 else
5459 {
5460 mfem_error("Mesh::GetElementEdges(...) element to edge table "
5461 "is not generated.");
5462 }
5463
5464 const int *v = elements[i]->GetVertices();
5465 const int ne = elements[i]->GetNEdges();
5466 cor.SetSize(ne);
5467 for (int j = 0; j < ne; j++)
5468 {
5469 const int *e = elements[i]->GetEdgeVertices(j);
5470 cor[j] = (v[e[0]] < v[e[1]]) ? (1) : (-1);
5471 }
5472 }
5473
GetBdrElementEdges(int i,Array<int> & edges,Array<int> & cor) const5474 void Mesh::GetBdrElementEdges(int i, Array<int> &edges, Array<int> &cor) const
5475 {
5476 if (Dim == 2)
5477 {
5478 edges.SetSize(1);
5479 cor.SetSize(1);
5480 edges[0] = be_to_edge[i];
5481 const int *v = boundary[i]->GetVertices();
5482 cor[0] = (v[0] < v[1]) ? (1) : (-1);
5483 }
5484 else if (Dim == 3)
5485 {
5486 if (bel_to_edge)
5487 {
5488 bel_to_edge->GetRow(i, edges);
5489 }
5490 else
5491 {
5492 mfem_error("Mesh::GetBdrElementEdges(...)");
5493 }
5494
5495 const int *v = boundary[i]->GetVertices();
5496 const int ne = boundary[i]->GetNEdges();
5497 cor.SetSize(ne);
5498 for (int j = 0; j < ne; j++)
5499 {
5500 const int *e = boundary[i]->GetEdgeVertices(j);
5501 cor[j] = (v[e[0]] < v[e[1]]) ? (1) : (-1);
5502 }
5503 }
5504 }
5505
GetFaceEdges(int i,Array<int> & edges,Array<int> & o) const5506 void Mesh::GetFaceEdges(int i, Array<int> &edges, Array<int> &o) const
5507 {
5508 if (Dim == 2)
5509 {
5510 edges.SetSize(1);
5511 edges[0] = i;
5512 o.SetSize(1);
5513 const int *v = faces[i]->GetVertices();
5514 o[0] = (v[0] < v[1]) ? (1) : (-1);
5515 }
5516
5517 if (Dim != 3)
5518 {
5519 return;
5520 }
5521
5522 GetFaceEdgeTable(); // generate face_edge Table (if not generated)
5523
5524 face_edge->GetRow(i, edges);
5525
5526 const int *v = faces[i]->GetVertices();
5527 const int ne = faces[i]->GetNEdges();
5528 o.SetSize(ne);
5529 for (int j = 0; j < ne; j++)
5530 {
5531 const int *e = faces[i]->GetEdgeVertices(j);
5532 o[j] = (v[e[0]] < v[e[1]]) ? (1) : (-1);
5533 }
5534 }
5535
GetEdgeVertices(int i,Array<int> & vert) const5536 void Mesh::GetEdgeVertices(int i, Array<int> &vert) const
5537 {
5538 // the two vertices are sorted: vert[0] < vert[1]
5539 // this is consistent with the global edge orientation
5540 // generate edge_vertex Table (if not generated)
5541 if (!edge_vertex) { GetEdgeVertexTable(); }
5542 edge_vertex->GetRow(i, vert);
5543 }
5544
GetFaceEdgeTable() const5545 Table *Mesh::GetFaceEdgeTable() const
5546 {
5547 if (face_edge)
5548 {
5549 return face_edge;
5550 }
5551
5552 if (Dim != 3)
5553 {
5554 return NULL;
5555 }
5556
5557 #ifdef MFEM_DEBUG
5558 if (faces.Size() != NumOfFaces)
5559 {
5560 mfem_error("Mesh::GetFaceEdgeTable : faces were not generated!");
5561 }
5562 #endif
5563
5564 DSTable v_to_v(NumOfVertices);
5565 GetVertexToVertexTable(v_to_v);
5566
5567 face_edge = new Table;
5568 GetElementArrayEdgeTable(faces, v_to_v, *face_edge);
5569
5570 return (face_edge);
5571 }
5572
GetEdgeVertexTable() const5573 Table *Mesh::GetEdgeVertexTable() const
5574 {
5575 if (edge_vertex)
5576 {
5577 return edge_vertex;
5578 }
5579
5580 DSTable v_to_v(NumOfVertices);
5581 GetVertexToVertexTable(v_to_v);
5582
5583 int nedges = v_to_v.NumberOfEntries();
5584 edge_vertex = new Table(nedges, 2);
5585 for (int i = 0; i < NumOfVertices; i++)
5586 {
5587 for (DSTable::RowIterator it(v_to_v, i); !it; ++it)
5588 {
5589 int j = it.Index();
5590 edge_vertex->Push(j, i);
5591 edge_vertex->Push(j, it.Column());
5592 }
5593 }
5594 edge_vertex->Finalize();
5595
5596 return edge_vertex;
5597 }
5598
GetVertexToElementTable()5599 Table *Mesh::GetVertexToElementTable()
5600 {
5601 int i, j, nv, *v;
5602
5603 Table *vert_elem = new Table;
5604
5605 vert_elem->MakeI(NumOfVertices);
5606
5607 for (i = 0; i < NumOfElements; i++)
5608 {
5609 nv = elements[i]->GetNVertices();
5610 v = elements[i]->GetVertices();
5611 for (j = 0; j < nv; j++)
5612 {
5613 vert_elem->AddAColumnInRow(v[j]);
5614 }
5615 }
5616
5617 vert_elem->MakeJ();
5618
5619 for (i = 0; i < NumOfElements; i++)
5620 {
5621 nv = elements[i]->GetNVertices();
5622 v = elements[i]->GetVertices();
5623 for (j = 0; j < nv; j++)
5624 {
5625 vert_elem->AddConnection(v[j], i);
5626 }
5627 }
5628
5629 vert_elem->ShiftUpI();
5630
5631 return vert_elem;
5632 }
5633
GetFaceToElementTable() const5634 Table *Mesh::GetFaceToElementTable() const
5635 {
5636 Table *face_elem = new Table;
5637
5638 face_elem->MakeI(faces_info.Size());
5639
5640 for (int i = 0; i < faces_info.Size(); i++)
5641 {
5642 if (faces_info[i].Elem2No >= 0)
5643 {
5644 face_elem->AddColumnsInRow(i, 2);
5645 }
5646 else
5647 {
5648 face_elem->AddAColumnInRow(i);
5649 }
5650 }
5651
5652 face_elem->MakeJ();
5653
5654 for (int i = 0; i < faces_info.Size(); i++)
5655 {
5656 face_elem->AddConnection(i, faces_info[i].Elem1No);
5657 if (faces_info[i].Elem2No >= 0)
5658 {
5659 face_elem->AddConnection(i, faces_info[i].Elem2No);
5660 }
5661 }
5662
5663 face_elem->ShiftUpI();
5664
5665 return face_elem;
5666 }
5667
GetElementFaces(int i,Array<int> & faces,Array<int> & ori) const5668 void Mesh::GetElementFaces(int i, Array<int> &faces, Array<int> &ori) const
5669 {
5670 MFEM_VERIFY(el_to_face != NULL, "el_to_face not generated");
5671
5672 el_to_face->GetRow(i, faces);
5673
5674 int n = faces.Size();
5675 ori.SetSize(n);
5676
5677 for (int j = 0; j < n; j++)
5678 {
5679 if (faces_info[faces[j]].Elem1No == i)
5680 {
5681 ori[j] = faces_info[faces[j]].Elem1Inf % 64;
5682 }
5683 else
5684 {
5685 MFEM_ASSERT(faces_info[faces[j]].Elem2No == i, "internal error");
5686 ori[j] = faces_info[faces[j]].Elem2Inf % 64;
5687 }
5688 }
5689 }
5690
GetBdrElementFace(int i,int * f,int * o) const5691 void Mesh::GetBdrElementFace(int i, int *f, int *o) const
5692 {
5693 const int *bv, *fv;
5694
5695 *f = be_to_face[i];
5696 bv = boundary[i]->GetVertices();
5697 fv = faces[be_to_face[i]]->GetVertices();
5698
5699 // find the orientation of the bdr. elem. w.r.t.
5700 // the corresponding face element (that's the base)
5701 switch (GetBdrElementType(i))
5702 {
5703 case Element::TRIANGLE:
5704 *o = GetTriOrientation(fv, bv);
5705 break;
5706 case Element::QUADRILATERAL:
5707 *o = GetQuadOrientation(fv, bv);
5708 break;
5709 default:
5710 MFEM_ABORT("invalid geometry");
5711 }
5712 }
5713
GetBdrElementEdgeIndex(int i) const5714 int Mesh::GetBdrElementEdgeIndex(int i) const
5715 {
5716 switch (Dim)
5717 {
5718 case 1: return boundary[i]->GetVertices()[0];
5719 case 2: return be_to_edge[i];
5720 case 3: return be_to_face[i];
5721 default: MFEM_ABORT("invalid dimension!");
5722 }
5723 return -1;
5724 }
5725
GetBdrElementAdjacentElement(int bdr_el,int & el,int & info) const5726 void Mesh::GetBdrElementAdjacentElement(int bdr_el, int &el, int &info) const
5727 {
5728 int fid = GetBdrElementEdgeIndex(bdr_el);
5729
5730 const FaceInfo &fi = faces_info[fid];
5731 MFEM_ASSERT(fi.Elem1Inf % 64 == 0, "internal error"); // orientation == 0
5732
5733 const int *fv = (Dim > 1) ? faces[fid]->GetVertices() : NULL;
5734 const int *bv = boundary[bdr_el]->GetVertices();
5735 int ori;
5736 switch (GetBdrElementGeometry(bdr_el))
5737 {
5738 case Geometry::POINT: ori = 0; break;
5739 case Geometry::SEGMENT: ori = (fv[0] == bv[0]) ? 0 : 1; break;
5740 case Geometry::TRIANGLE: ori = GetTriOrientation(fv, bv); break;
5741 case Geometry::SQUARE: ori = GetQuadOrientation(fv, bv); break;
5742 default: MFEM_ABORT("boundary element type not implemented"); ori = 0;
5743 }
5744 el = fi.Elem1No;
5745 info = fi.Elem1Inf + ori;
5746 }
5747
GetElementType(int i) const5748 Element::Type Mesh::GetElementType(int i) const
5749 {
5750 return elements[i]->GetType();
5751 }
5752
GetBdrElementType(int i) const5753 Element::Type Mesh::GetBdrElementType(int i) const
5754 {
5755 return boundary[i]->GetType();
5756 }
5757
GetPointMatrix(int i,DenseMatrix & pointmat) const5758 void Mesh::GetPointMatrix(int i, DenseMatrix &pointmat) const
5759 {
5760 int k, j, nv;
5761 const int *v;
5762
5763 v = elements[i]->GetVertices();
5764 nv = elements[i]->GetNVertices();
5765
5766 pointmat.SetSize(spaceDim, nv);
5767 for (k = 0; k < spaceDim; k++)
5768 {
5769 for (j = 0; j < nv; j++)
5770 {
5771 pointmat(k, j) = vertices[v[j]](k);
5772 }
5773 }
5774 }
5775
GetBdrPointMatrix(int i,DenseMatrix & pointmat) const5776 void Mesh::GetBdrPointMatrix(int i,DenseMatrix &pointmat) const
5777 {
5778 int k, j, nv;
5779 const int *v;
5780
5781 v = boundary[i]->GetVertices();
5782 nv = boundary[i]->GetNVertices();
5783
5784 pointmat.SetSize(spaceDim, nv);
5785 for (k = 0; k < spaceDim; k++)
5786 for (j = 0; j < nv; j++)
5787 {
5788 pointmat(k, j) = vertices[v[j]](k);
5789 }
5790 }
5791
GetLength(int i,int j) const5792 double Mesh::GetLength(int i, int j) const
5793 {
5794 const double *vi = vertices[i]();
5795 const double *vj = vertices[j]();
5796 double length = 0.;
5797
5798 for (int k = 0; k < spaceDim; k++)
5799 {
5800 length += (vi[k]-vj[k])*(vi[k]-vj[k]);
5801 }
5802
5803 return sqrt(length);
5804 }
5805
5806 // static method
GetElementArrayEdgeTable(const Array<Element * > & elem_array,const DSTable & v_to_v,Table & el_to_edge)5807 void Mesh::GetElementArrayEdgeTable(const Array<Element*> &elem_array,
5808 const DSTable &v_to_v, Table &el_to_edge)
5809 {
5810 el_to_edge.MakeI(elem_array.Size());
5811 for (int i = 0; i < elem_array.Size(); i++)
5812 {
5813 el_to_edge.AddColumnsInRow(i, elem_array[i]->GetNEdges());
5814 }
5815 el_to_edge.MakeJ();
5816 for (int i = 0; i < elem_array.Size(); i++)
5817 {
5818 const int *v = elem_array[i]->GetVertices();
5819 const int ne = elem_array[i]->GetNEdges();
5820 for (int j = 0; j < ne; j++)
5821 {
5822 const int *e = elem_array[i]->GetEdgeVertices(j);
5823 el_to_edge.AddConnection(i, v_to_v(v[e[0]], v[e[1]]));
5824 }
5825 }
5826 el_to_edge.ShiftUpI();
5827 }
5828
GetVertexToVertexTable(DSTable & v_to_v) const5829 void Mesh::GetVertexToVertexTable(DSTable &v_to_v) const
5830 {
5831 if (edge_vertex)
5832 {
5833 for (int i = 0; i < edge_vertex->Size(); i++)
5834 {
5835 const int *v = edge_vertex->GetRow(i);
5836 v_to_v.Push(v[0], v[1]);
5837 }
5838 }
5839 else
5840 {
5841 for (int i = 0; i < NumOfElements; i++)
5842 {
5843 const int *v = elements[i]->GetVertices();
5844 const int ne = elements[i]->GetNEdges();
5845 for (int j = 0; j < ne; j++)
5846 {
5847 const int *e = elements[i]->GetEdgeVertices(j);
5848 v_to_v.Push(v[e[0]], v[e[1]]);
5849 }
5850 }
5851 }
5852 }
5853
GetElementToEdgeTable(Table & e_to_f,Array<int> & be_to_f)5854 int Mesh::GetElementToEdgeTable(Table & e_to_f, Array<int> &be_to_f)
5855 {
5856 int i, NumberOfEdges;
5857
5858 DSTable v_to_v(NumOfVertices);
5859 GetVertexToVertexTable(v_to_v);
5860
5861 NumberOfEdges = v_to_v.NumberOfEntries();
5862
5863 // Fill the element to edge table
5864 GetElementArrayEdgeTable(elements, v_to_v, e_to_f);
5865
5866 if (Dim == 2)
5867 {
5868 // Initialize the indices for the boundary elements.
5869 be_to_f.SetSize(NumOfBdrElements);
5870 for (i = 0; i < NumOfBdrElements; i++)
5871 {
5872 const int *v = boundary[i]->GetVertices();
5873 be_to_f[i] = v_to_v(v[0], v[1]);
5874 }
5875 }
5876 else if (Dim == 3)
5877 {
5878 if (bel_to_edge == NULL)
5879 {
5880 bel_to_edge = new Table;
5881 }
5882 GetElementArrayEdgeTable(boundary, v_to_v, *bel_to_edge);
5883 }
5884 else
5885 {
5886 mfem_error("1D GetElementToEdgeTable is not yet implemented.");
5887 }
5888
5889 // Return the number of edges
5890 return NumberOfEdges;
5891 }
5892
ElementToElementTable()5893 const Table & Mesh::ElementToElementTable()
5894 {
5895 if (el_to_el)
5896 {
5897 return *el_to_el;
5898 }
5899
5900 // Note that, for ParNCMeshes, faces_info will contain also the ghost faces
5901 MFEM_ASSERT(faces_info.Size() >= GetNumFaces(), "faces were not generated!");
5902
5903 Array<Connection> conn;
5904 conn.Reserve(2*faces_info.Size());
5905
5906 for (int i = 0; i < faces_info.Size(); i++)
5907 {
5908 const FaceInfo &fi = faces_info[i];
5909 if (fi.Elem2No >= 0)
5910 {
5911 conn.Append(Connection(fi.Elem1No, fi.Elem2No));
5912 conn.Append(Connection(fi.Elem2No, fi.Elem1No));
5913 }
5914 else if (fi.Elem2Inf >= 0)
5915 {
5916 int nbr_elem_idx = NumOfElements - 1 - fi.Elem2No;
5917 conn.Append(Connection(fi.Elem1No, nbr_elem_idx));
5918 conn.Append(Connection(nbr_elem_idx, fi.Elem1No));
5919 }
5920 }
5921
5922 conn.Sort();
5923 conn.Unique();
5924 el_to_el = new Table(NumOfElements, conn);
5925
5926 return *el_to_el;
5927 }
5928
ElementToFaceTable() const5929 const Table & Mesh::ElementToFaceTable() const
5930 {
5931 if (el_to_face == NULL)
5932 {
5933 mfem_error("Mesh::ElementToFaceTable()");
5934 }
5935 return *el_to_face;
5936 }
5937
ElementToEdgeTable() const5938 const Table & Mesh::ElementToEdgeTable() const
5939 {
5940 if (el_to_edge == NULL)
5941 {
5942 mfem_error("Mesh::ElementToEdgeTable()");
5943 }
5944 return *el_to_edge;
5945 }
5946
AddPointFaceElement(int lf,int gf,int el)5947 void Mesh::AddPointFaceElement(int lf, int gf, int el)
5948 {
5949 if (faces_info[gf].Elem1No == -1) // this will be elem1
5950 {
5951 // faces[gf] = new Point(&gf);
5952 faces_info[gf].Elem1No = el;
5953 faces_info[gf].Elem1Inf = 64 * lf; // face lf with orientation 0
5954 faces_info[gf].Elem2No = -1; // in case there's no other side
5955 faces_info[gf].Elem2Inf = -1; // face is not shared
5956 }
5957 else // this will be elem2
5958 {
5959 /* WARNING: Without the following check the mesh faces_info data structure
5960 may contain unreliable data. Normally, the order in which elements are
5961 processed could swap which elements appear as Elem1No and Elem2No. In
5962 branched meshes, where more than two elements can meet at a given node,
5963 the indices stored in Elem1No and Elem2No will be the first and last,
5964 respectively, elements found which touch a given node. This can lead to
5965 inconsistencies in any algorithms which rely on this data structure. To
5966 properly support branched meshes this data structure should be extended
5967 to support multiple elements per face. */
5968 /*
5969 MFEM_VERIFY(faces_info[gf].Elem2No < 0, "Invalid mesh topology. "
5970 "Interior point found connecting 1D elements "
5971 << faces_info[gf].Elem1No << ", " << faces_info[gf].Elem2No
5972 << " and " << el << ".");
5973 */
5974 faces_info[gf].Elem2No = el;
5975 faces_info[gf].Elem2Inf = 64 * lf + 1;
5976 }
5977 }
5978
AddSegmentFaceElement(int lf,int gf,int el,int v0,int v1)5979 void Mesh::AddSegmentFaceElement(int lf, int gf, int el, int v0, int v1)
5980 {
5981 if (faces[gf] == NULL) // this will be elem1
5982 {
5983 faces[gf] = new Segment(v0, v1);
5984 faces_info[gf].Elem1No = el;
5985 faces_info[gf].Elem1Inf = 64 * lf; // face lf with orientation 0
5986 faces_info[gf].Elem2No = -1; // in case there's no other side
5987 faces_info[gf].Elem2Inf = -1; // face is not shared
5988 }
5989 else // this will be elem2
5990 {
5991 MFEM_VERIFY(faces_info[gf].Elem2No < 0, "Invalid mesh topology. "
5992 "Interior edge found between 2D elements "
5993 << faces_info[gf].Elem1No << ", " << faces_info[gf].Elem2No
5994 << " and " << el << ".");
5995 int *v = faces[gf]->GetVertices();
5996 faces_info[gf].Elem2No = el;
5997 if ( v[1] == v0 && v[0] == v1 )
5998 {
5999 faces_info[gf].Elem2Inf = 64 * lf + 1;
6000 }
6001 else if ( v[0] == v0 && v[1] == v1 )
6002 {
6003 // Temporarily allow even edge orientations: see the remark in
6004 // AddTriangleFaceElement().
6005 // Also, in a non-orientable surface mesh, the orientation will be even
6006 // for edges that connect elements with opposite orientations.
6007 faces_info[gf].Elem2Inf = 64 * lf;
6008 }
6009 else
6010 {
6011 MFEM_ABORT("internal error");
6012 }
6013 }
6014 }
6015
AddTriangleFaceElement(int lf,int gf,int el,int v0,int v1,int v2)6016 void Mesh::AddTriangleFaceElement(int lf, int gf, int el,
6017 int v0, int v1, int v2)
6018 {
6019 if (faces[gf] == NULL) // this will be elem1
6020 {
6021 faces[gf] = new Triangle(v0, v1, v2);
6022 faces_info[gf].Elem1No = el;
6023 faces_info[gf].Elem1Inf = 64 * lf; // face lf with orientation 0
6024 faces_info[gf].Elem2No = -1; // in case there's no other side
6025 faces_info[gf].Elem2Inf = -1; // face is not shared
6026 }
6027 else // this will be elem2
6028 {
6029 MFEM_VERIFY(faces_info[gf].Elem2No < 0, "Invalid mesh topology. "
6030 "Interior triangular face found connecting elements "
6031 << faces_info[gf].Elem1No << ", " << faces_info[gf].Elem2No
6032 << " and " << el << ".");
6033 int orientation, vv[3] = { v0, v1, v2 };
6034 orientation = GetTriOrientation(faces[gf]->GetVertices(), vv);
6035 // In a valid mesh, we should have (orientation % 2 != 0), however, if
6036 // one of the adjacent elements has wrong orientation, both face
6037 // orientations can be even, until the element orientations are fixed.
6038 // MFEM_ASSERT(orientation % 2 != 0, "");
6039 faces_info[gf].Elem2No = el;
6040 faces_info[gf].Elem2Inf = 64 * lf + orientation;
6041 }
6042 }
6043
AddQuadFaceElement(int lf,int gf,int el,int v0,int v1,int v2,int v3)6044 void Mesh::AddQuadFaceElement(int lf, int gf, int el,
6045 int v0, int v1, int v2, int v3)
6046 {
6047 if (faces_info[gf].Elem1No < 0) // this will be elem1
6048 {
6049 faces[gf] = new Quadrilateral(v0, v1, v2, v3);
6050 faces_info[gf].Elem1No = el;
6051 faces_info[gf].Elem1Inf = 64 * lf; // face lf with orientation 0
6052 faces_info[gf].Elem2No = -1; // in case there's no other side
6053 faces_info[gf].Elem2Inf = -1; // face is not shared
6054 }
6055 else // this will be elem2
6056 {
6057 MFEM_VERIFY(faces_info[gf].Elem2No < 0, "Invalid mesh topology. "
6058 "Interior quadrilateral face found connecting elements "
6059 << faces_info[gf].Elem1No << ", " << faces_info[gf].Elem2No
6060 << " and " << el << ".");
6061 int vv[4] = { v0, v1, v2, v3 };
6062 int oo = GetQuadOrientation(faces[gf]->GetVertices(), vv);
6063 // Temporarily allow even face orientations: see the remark in
6064 // AddTriangleFaceElement().
6065 // MFEM_ASSERT(oo % 2 != 0, "");
6066 faces_info[gf].Elem2No = el;
6067 faces_info[gf].Elem2Inf = 64 * lf + oo;
6068 }
6069 }
6070
GenerateFaces()6071 void Mesh::GenerateFaces()
6072 {
6073 int i, nfaces = GetNumFaces();
6074
6075 for (i = 0; i < faces.Size(); i++)
6076 {
6077 FreeElement(faces[i]);
6078 }
6079
6080 // (re)generate the interior faces and the info for them
6081 faces.SetSize(nfaces);
6082 faces_info.SetSize(nfaces);
6083 for (i = 0; i < nfaces; i++)
6084 {
6085 faces[i] = NULL;
6086 faces_info[i].Elem1No = -1;
6087 faces_info[i].NCFace = -1;
6088 }
6089 for (i = 0; i < NumOfElements; i++)
6090 {
6091 const int *v = elements[i]->GetVertices();
6092 const int *ef;
6093 if (Dim == 1)
6094 {
6095 AddPointFaceElement(0, v[0], i);
6096 AddPointFaceElement(1, v[1], i);
6097 }
6098 else if (Dim == 2)
6099 {
6100 ef = el_to_edge->GetRow(i);
6101 const int ne = elements[i]->GetNEdges();
6102 for (int j = 0; j < ne; j++)
6103 {
6104 const int *e = elements[i]->GetEdgeVertices(j);
6105 AddSegmentFaceElement(j, ef[j], i, v[e[0]], v[e[1]]);
6106 }
6107 }
6108 else
6109 {
6110 ef = el_to_face->GetRow(i);
6111 switch (GetElementType(i))
6112 {
6113 case Element::TETRAHEDRON:
6114 {
6115 for (int j = 0; j < 4; j++)
6116 {
6117 const int *fv = tet_t::FaceVert[j];
6118 AddTriangleFaceElement(j, ef[j], i,
6119 v[fv[0]], v[fv[1]], v[fv[2]]);
6120 }
6121 break;
6122 }
6123 case Element::WEDGE:
6124 {
6125 for (int j = 0; j < 2; j++)
6126 {
6127 const int *fv = pri_t::FaceVert[j];
6128 AddTriangleFaceElement(j, ef[j], i,
6129 v[fv[0]], v[fv[1]], v[fv[2]]);
6130 }
6131 for (int j = 2; j < 5; j++)
6132 {
6133 const int *fv = pri_t::FaceVert[j];
6134 AddQuadFaceElement(j, ef[j], i,
6135 v[fv[0]], v[fv[1]], v[fv[2]], v[fv[3]]);
6136 }
6137 break;
6138 }
6139 case Element::HEXAHEDRON:
6140 {
6141 for (int j = 0; j < 6; j++)
6142 {
6143 const int *fv = hex_t::FaceVert[j];
6144 AddQuadFaceElement(j, ef[j], i,
6145 v[fv[0]], v[fv[1]], v[fv[2]], v[fv[3]]);
6146 }
6147 break;
6148 }
6149 default:
6150 MFEM_ABORT("Unexpected type of Element.");
6151 }
6152 }
6153 }
6154 }
6155
GenerateNCFaceInfo()6156 void Mesh::GenerateNCFaceInfo()
6157 {
6158 MFEM_VERIFY(ncmesh, "missing NCMesh.");
6159
6160 for (int i = 0; i < faces_info.Size(); i++)
6161 {
6162 faces_info[i].NCFace = -1;
6163 }
6164
6165 const NCMesh::NCList &list =
6166 (Dim == 2) ? ncmesh->GetEdgeList() : ncmesh->GetFaceList();
6167
6168 nc_faces_info.SetSize(0);
6169 nc_faces_info.Reserve(list.masters.Size() + list.slaves.Size());
6170
6171 int nfaces = GetNumFaces();
6172
6173 // add records for master faces
6174 for (int i = 0; i < list.masters.Size(); i++)
6175 {
6176 const NCMesh::Master &master = list.masters[i];
6177 if (master.index >= nfaces) { continue; }
6178
6179 faces_info[master.index].NCFace = nc_faces_info.Size();
6180 nc_faces_info.Append(NCFaceInfo(false, master.local, NULL));
6181 // NOTE: one of the unused members stores local face no. to be used below
6182 }
6183
6184 // add records for slave faces
6185 for (int i = 0; i < list.slaves.Size(); i++)
6186 {
6187 const NCMesh::Slave &slave = list.slaves[i];
6188
6189 if (slave.index < 0 || // degenerate slave face
6190 slave.index >= nfaces || // ghost slave
6191 slave.master >= nfaces) // has ghost master
6192 {
6193 continue;
6194 }
6195
6196 FaceInfo &slave_fi = faces_info[slave.index];
6197 FaceInfo &master_fi = faces_info[slave.master];
6198 NCFaceInfo &master_nc = nc_faces_info[master_fi.NCFace];
6199
6200 slave_fi.NCFace = nc_faces_info.Size();
6201 slave_fi.Elem2No = master_fi.Elem1No;
6202 slave_fi.Elem2Inf = 64 * master_nc.MasterFace; // get lf no. stored above
6203 // NOTE: In 3D, the orientation part of Elem2Inf is encoded in the point
6204 // matrix. In 2D, the point matrix has the orientation of the parent
6205 // edge, so its columns need to be flipped when applying it, see
6206 // ApplyLocalSlaveTransformation.
6207
6208 nc_faces_info.Append(
6209 NCFaceInfo(true, slave.master,
6210 list.point_matrices[slave.geom][slave.matrix]));
6211 }
6212 }
6213
GetFacesTable()6214 STable3D *Mesh::GetFacesTable()
6215 {
6216 STable3D *faces_tbl = new STable3D(NumOfVertices);
6217 for (int i = 0; i < NumOfElements; i++)
6218 {
6219 const int *v = elements[i]->GetVertices();
6220 switch (GetElementType(i))
6221 {
6222 case Element::TETRAHEDRON:
6223 {
6224 for (int j = 0; j < 4; j++)
6225 {
6226 const int *fv = tet_t::FaceVert[j];
6227 faces_tbl->Push(v[fv[0]], v[fv[1]], v[fv[2]]);
6228 }
6229 break;
6230 }
6231 case Element::WEDGE:
6232 {
6233 for (int j = 0; j < 2; j++)
6234 {
6235 const int *fv = pri_t::FaceVert[j];
6236 faces_tbl->Push(v[fv[0]], v[fv[1]], v[fv[2]]);
6237 }
6238 for (int j = 2; j < 5; j++)
6239 {
6240 const int *fv = pri_t::FaceVert[j];
6241 faces_tbl->Push4(v[fv[0]], v[fv[1]], v[fv[2]], v[fv[3]]);
6242 }
6243 break;
6244 }
6245 case Element::HEXAHEDRON:
6246 {
6247 // find the face by the vertices with the smallest 3 numbers
6248 // z = 0, y = 0, x = 1, y = 1, x = 0, z = 1
6249 for (int j = 0; j < 6; j++)
6250 {
6251 const int *fv = hex_t::FaceVert[j];
6252 faces_tbl->Push4(v[fv[0]], v[fv[1]], v[fv[2]], v[fv[3]]);
6253 }
6254 break;
6255 }
6256 default:
6257 MFEM_ABORT("Unexpected type of Element.");
6258 }
6259 }
6260 return faces_tbl;
6261 }
6262
GetElementToFaceTable(int ret_ftbl)6263 STable3D *Mesh::GetElementToFaceTable(int ret_ftbl)
6264 {
6265 int i, *v;
6266 STable3D *faces_tbl;
6267
6268 if (el_to_face != NULL)
6269 {
6270 delete el_to_face;
6271 }
6272 el_to_face = new Table(NumOfElements, 6); // must be 6 for hexahedra
6273 faces_tbl = new STable3D(NumOfVertices);
6274 for (i = 0; i < NumOfElements; i++)
6275 {
6276 v = elements[i]->GetVertices();
6277 switch (GetElementType(i))
6278 {
6279 case Element::TETRAHEDRON:
6280 {
6281 for (int j = 0; j < 4; j++)
6282 {
6283 const int *fv = tet_t::FaceVert[j];
6284 el_to_face->Push(
6285 i, faces_tbl->Push(v[fv[0]], v[fv[1]], v[fv[2]]));
6286 }
6287 break;
6288 }
6289 case Element::WEDGE:
6290 {
6291 for (int j = 0; j < 2; j++)
6292 {
6293 const int *fv = pri_t::FaceVert[j];
6294 el_to_face->Push(
6295 i, faces_tbl->Push(v[fv[0]], v[fv[1]], v[fv[2]]));
6296 }
6297 for (int j = 2; j < 5; j++)
6298 {
6299 const int *fv = pri_t::FaceVert[j];
6300 el_to_face->Push(
6301 i, faces_tbl->Push4(v[fv[0]], v[fv[1]], v[fv[2]], v[fv[3]]));
6302 }
6303 break;
6304 }
6305 case Element::HEXAHEDRON:
6306 {
6307 // find the face by the vertices with the smallest 3 numbers
6308 // z = 0, y = 0, x = 1, y = 1, x = 0, z = 1
6309 for (int j = 0; j < 6; j++)
6310 {
6311 const int *fv = hex_t::FaceVert[j];
6312 el_to_face->Push(
6313 i, faces_tbl->Push4(v[fv[0]], v[fv[1]], v[fv[2]], v[fv[3]]));
6314 }
6315 break;
6316 }
6317 default:
6318 MFEM_ABORT("Unexpected type of Element.");
6319 }
6320 }
6321 el_to_face->Finalize();
6322 NumOfFaces = faces_tbl->NumberOfElements();
6323 be_to_face.SetSize(NumOfBdrElements);
6324 for (i = 0; i < NumOfBdrElements; i++)
6325 {
6326 v = boundary[i]->GetVertices();
6327 switch (GetBdrElementType(i))
6328 {
6329 case Element::TRIANGLE:
6330 {
6331 be_to_face[i] = (*faces_tbl)(v[0], v[1], v[2]);
6332 break;
6333 }
6334 case Element::QUADRILATERAL:
6335 {
6336 be_to_face[i] = (*faces_tbl)(v[0], v[1], v[2], v[3]);
6337 break;
6338 }
6339 default:
6340 MFEM_ABORT("Unexpected type of boundary Element.");
6341 }
6342 }
6343
6344 if (ret_ftbl)
6345 {
6346 return faces_tbl;
6347 }
6348 delete faces_tbl;
6349 return NULL;
6350 }
6351
6352 // shift cyclically 3 integers so that the smallest is first
6353 static inline
Rotate3(int & a,int & b,int & c)6354 void Rotate3(int &a, int &b, int &c)
6355 {
6356 if (a < b)
6357 {
6358 if (a > c)
6359 {
6360 ShiftRight(a, b, c);
6361 }
6362 }
6363 else
6364 {
6365 if (b < c)
6366 {
6367 ShiftRight(c, b, a);
6368 }
6369 else
6370 {
6371 ShiftRight(a, b, c);
6372 }
6373 }
6374 }
6375
ReorientTetMesh()6376 void Mesh::ReorientTetMesh()
6377 {
6378 if (Dim != 3 || !(meshgen & 1))
6379 {
6380 return;
6381 }
6382
6383 ResetLazyData();
6384
6385 DSTable *old_v_to_v = NULL;
6386 Table *old_elem_vert = NULL;
6387
6388 if (Nodes)
6389 {
6390 PrepareNodeReorder(&old_v_to_v, &old_elem_vert);
6391 }
6392
6393 for (int i = 0; i < NumOfElements; i++)
6394 {
6395 if (GetElementType(i) == Element::TETRAHEDRON)
6396 {
6397 int *v = elements[i]->GetVertices();
6398
6399 Rotate3(v[0], v[1], v[2]);
6400 if (v[0] < v[3])
6401 {
6402 Rotate3(v[1], v[2], v[3]);
6403 }
6404 else
6405 {
6406 ShiftRight(v[0], v[1], v[3]);
6407 }
6408 }
6409 }
6410
6411 for (int i = 0; i < NumOfBdrElements; i++)
6412 {
6413 if (GetBdrElementType(i) == Element::TRIANGLE)
6414 {
6415 int *v = boundary[i]->GetVertices();
6416
6417 Rotate3(v[0], v[1], v[2]);
6418 }
6419 }
6420
6421 if (!Nodes)
6422 {
6423 GetElementToFaceTable();
6424 GenerateFaces();
6425 if (el_to_edge)
6426 {
6427 NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
6428 }
6429 }
6430 else
6431 {
6432 DoNodeReorder(old_v_to_v, old_elem_vert);
6433 delete old_elem_vert;
6434 delete old_v_to_v;
6435 }
6436 }
6437
CartesianPartitioning(int nxyz[])6438 int *Mesh::CartesianPartitioning(int nxyz[])
6439 {
6440 int *partitioning;
6441 double pmin[3] = { infinity(), infinity(), infinity() };
6442 double pmax[3] = { -infinity(), -infinity(), -infinity() };
6443 // find a bounding box using the vertices
6444 for (int vi = 0; vi < NumOfVertices; vi++)
6445 {
6446 const double *p = vertices[vi]();
6447 for (int i = 0; i < spaceDim; i++)
6448 {
6449 if (p[i] < pmin[i]) { pmin[i] = p[i]; }
6450 if (p[i] > pmax[i]) { pmax[i] = p[i]; }
6451 }
6452 }
6453
6454 partitioning = new int[NumOfElements];
6455
6456 // determine the partitioning using the centers of the elements
6457 double ppt[3];
6458 Vector pt(ppt, spaceDim);
6459 for (int el = 0; el < NumOfElements; el++)
6460 {
6461 GetElementTransformation(el)->Transform(
6462 Geometries.GetCenter(GetElementBaseGeometry(el)), pt);
6463 int part = 0;
6464 for (int i = spaceDim-1; i >= 0; i--)
6465 {
6466 int idx = (int)floor(nxyz[i]*((pt(i) - pmin[i])/(pmax[i] - pmin[i])));
6467 if (idx < 0) { idx = 0; }
6468 if (idx >= nxyz[i]) { idx = nxyz[i]-1; }
6469 part = part * nxyz[i] + idx;
6470 }
6471 partitioning[el] = part;
6472 }
6473
6474 return partitioning;
6475 }
6476
GeneratePartitioning(int nparts,int part_method)6477 int *Mesh::GeneratePartitioning(int nparts, int part_method)
6478 {
6479 #ifdef MFEM_USE_METIS
6480
6481 int print_messages = 1;
6482 // If running in parallel, print messages only from rank 0.
6483 #ifdef MFEM_USE_MPI
6484 int init_flag, fin_flag;
6485 MPI_Initialized(&init_flag);
6486 MPI_Finalized(&fin_flag);
6487 if (init_flag && !fin_flag)
6488 {
6489 int rank;
6490 MPI_Comm_rank(GetGlobalMPI_Comm(), &rank);
6491 if (rank != 0) { print_messages = 0; }
6492 }
6493 #endif
6494
6495 int i, *partitioning;
6496
6497 ElementToElementTable();
6498
6499 partitioning = new int[NumOfElements];
6500
6501 if (nparts == 1)
6502 {
6503 for (i = 0; i < NumOfElements; i++)
6504 {
6505 partitioning[i] = 0;
6506 }
6507 }
6508 else if (NumOfElements <= nparts)
6509 {
6510 for (i = 0; i < NumOfElements; i++)
6511 {
6512 partitioning[i] = i;
6513 }
6514 }
6515 else
6516 {
6517 idx_t *I, *J, n;
6518 #ifndef MFEM_USE_METIS_5
6519 idx_t wgtflag = 0;
6520 idx_t numflag = 0;
6521 idx_t options[5];
6522 #else
6523 idx_t ncon = 1;
6524 idx_t err;
6525 idx_t options[40];
6526 #endif
6527 idx_t edgecut;
6528
6529 // In case METIS have been compiled with 64bit indices
6530 bool freedata = false;
6531 idx_t mparts = (idx_t) nparts;
6532 idx_t *mpartitioning;
6533
6534 n = NumOfElements;
6535 if (sizeof(idx_t) == sizeof(int))
6536 {
6537 I = (idx_t*) el_to_el->GetI();
6538 J = (idx_t*) el_to_el->GetJ();
6539 mpartitioning = (idx_t*) partitioning;
6540 }
6541 else
6542 {
6543 int *iI = el_to_el->GetI();
6544 int *iJ = el_to_el->GetJ();
6545 int m = iI[n];
6546 I = new idx_t[n+1];
6547 J = new idx_t[m];
6548 for (int k = 0; k < n+1; k++) { I[k] = iI[k]; }
6549 for (int k = 0; k < m; k++) { J[k] = iJ[k]; }
6550 mpartitioning = new idx_t[n];
6551 freedata = true;
6552 }
6553 #ifndef MFEM_USE_METIS_5
6554 options[0] = 0;
6555 #else
6556 METIS_SetDefaultOptions(options);
6557 options[METIS_OPTION_CONTIG] = 1; // set METIS_OPTION_CONTIG
6558 #endif
6559
6560 // Sort the neighbor lists
6561 if (part_method >= 0 && part_method <= 2)
6562 {
6563 for (i = 0; i < n; i++)
6564 {
6565 // Sort in increasing order.
6566 // std::sort(J+I[i], J+I[i+1]);
6567
6568 // Sort in decreasing order, as in previous versions of MFEM.
6569 std::sort(J+I[i], J+I[i+1], std::greater<idx_t>());
6570 }
6571 }
6572
6573 // This function should be used to partition a graph into a small
6574 // number of partitions (less than 8).
6575 if (part_method == 0 || part_method == 3)
6576 {
6577 #ifndef MFEM_USE_METIS_5
6578 METIS_PartGraphRecursive(&n,
6579 I,
6580 J,
6581 NULL,
6582 NULL,
6583 &wgtflag,
6584 &numflag,
6585 &mparts,
6586 options,
6587 &edgecut,
6588 mpartitioning);
6589 #else
6590 err = METIS_PartGraphRecursive(&n,
6591 &ncon,
6592 I,
6593 J,
6594 NULL,
6595 NULL,
6596 NULL,
6597 &mparts,
6598 NULL,
6599 NULL,
6600 options,
6601 &edgecut,
6602 mpartitioning);
6603 if (err != 1)
6604 {
6605 mfem_error("Mesh::GeneratePartitioning: "
6606 " error in METIS_PartGraphRecursive!");
6607 }
6608 #endif
6609 }
6610
6611 // This function should be used to partition a graph into a large
6612 // number of partitions (greater than 8).
6613 if (part_method == 1 || part_method == 4)
6614 {
6615 #ifndef MFEM_USE_METIS_5
6616 METIS_PartGraphKway(&n,
6617 I,
6618 J,
6619 NULL,
6620 NULL,
6621 &wgtflag,
6622 &numflag,
6623 &mparts,
6624 options,
6625 &edgecut,
6626 mpartitioning);
6627 #else
6628 err = METIS_PartGraphKway(&n,
6629 &ncon,
6630 I,
6631 J,
6632 NULL,
6633 NULL,
6634 NULL,
6635 &mparts,
6636 NULL,
6637 NULL,
6638 options,
6639 &edgecut,
6640 mpartitioning);
6641 if (err != 1)
6642 {
6643 mfem_error("Mesh::GeneratePartitioning: "
6644 " error in METIS_PartGraphKway!");
6645 }
6646 #endif
6647 }
6648
6649 // The objective of this partitioning is to minimize the total
6650 // communication volume
6651 if (part_method == 2 || part_method == 5)
6652 {
6653 #ifndef MFEM_USE_METIS_5
6654 METIS_PartGraphVKway(&n,
6655 I,
6656 J,
6657 NULL,
6658 NULL,
6659 &wgtflag,
6660 &numflag,
6661 &mparts,
6662 options,
6663 &edgecut,
6664 mpartitioning);
6665 #else
6666 options[METIS_OPTION_OBJTYPE] = METIS_OBJTYPE_VOL;
6667 err = METIS_PartGraphKway(&n,
6668 &ncon,
6669 I,
6670 J,
6671 NULL,
6672 NULL,
6673 NULL,
6674 &mparts,
6675 NULL,
6676 NULL,
6677 options,
6678 &edgecut,
6679 mpartitioning);
6680 if (err != 1)
6681 {
6682 mfem_error("Mesh::GeneratePartitioning: "
6683 " error in METIS_PartGraphKway!");
6684 }
6685 #endif
6686 }
6687
6688 #ifdef MFEM_DEBUG
6689 if (print_messages)
6690 {
6691 mfem::out << "Mesh::GeneratePartitioning(...): edgecut = "
6692 << edgecut << endl;
6693 }
6694 #endif
6695 nparts = (int) mparts;
6696 if (mpartitioning != (idx_t*)partitioning)
6697 {
6698 for (int k = 0; k<NumOfElements; k++)
6699 {
6700 partitioning[k] = mpartitioning[k];
6701 }
6702 }
6703 if (freedata)
6704 {
6705 delete[] I;
6706 delete[] J;
6707 delete[] mpartitioning;
6708 }
6709 }
6710
6711 delete el_to_el;
6712 el_to_el = NULL;
6713
6714 // Check for empty partitionings (a "feature" in METIS)
6715 if (nparts > 1 && NumOfElements > nparts)
6716 {
6717 Array< Pair<int,int> > psize(nparts);
6718 int empty_parts;
6719
6720 // Count how many elements are in each partition, and store the result in
6721 // psize, where psize[i].one is the number of elements, and psize[i].two
6722 // is partition index. Keep track of the number of empty parts.
6723 auto count_partition_elements = [&]()
6724 {
6725 for (i = 0; i < nparts; i++)
6726 {
6727 psize[i].one = 0;
6728 psize[i].two = i;
6729 }
6730
6731 for (i = 0; i < NumOfElements; i++)
6732 {
6733 psize[partitioning[i]].one++;
6734 }
6735
6736 empty_parts = 0;
6737 for (i = 0; i < nparts; i++)
6738 {
6739 if (psize[i].one == 0) { empty_parts++; }
6740 }
6741 };
6742
6743 count_partition_elements();
6744
6745 // This code just split the largest partitionings in two.
6746 // Do we need to replace it with something better?
6747 while (empty_parts)
6748 {
6749 if (print_messages)
6750 {
6751 mfem::err << "Mesh::GeneratePartitioning(...): METIS returned "
6752 << empty_parts << " empty parts!"
6753 << " Applying a simple fix ..." << endl;
6754 }
6755
6756 SortPairs<int,int>(psize, nparts);
6757
6758 for (i = nparts-1; i > nparts-1-empty_parts; i--)
6759 {
6760 psize[i].one /= 2;
6761 }
6762
6763 for (int j = 0; j < NumOfElements; j++)
6764 {
6765 for (i = nparts-1; i > nparts-1-empty_parts; i--)
6766 {
6767 if (psize[i].one == 0 || partitioning[j] != psize[i].two)
6768 {
6769 continue;
6770 }
6771 else
6772 {
6773 partitioning[j] = psize[nparts-1-i].two;
6774 psize[i].one--;
6775 }
6776 }
6777 }
6778
6779 // Check for empty partitionings again
6780 count_partition_elements();
6781 }
6782 }
6783
6784 return partitioning;
6785
6786 #else
6787
6788 mfem_error("Mesh::GeneratePartitioning(...): "
6789 "MFEM was compiled without Metis.");
6790
6791 return NULL;
6792
6793 #endif
6794 }
6795
6796 /* required: 0 <= partitioning[i] < num_part */
FindPartitioningComponents(Table & elem_elem,const Array<int> & partitioning,Array<int> & component,Array<int> & num_comp)6797 void FindPartitioningComponents(Table &elem_elem,
6798 const Array<int> &partitioning,
6799 Array<int> &component,
6800 Array<int> &num_comp)
6801 {
6802 int i, j, k;
6803 int num_elem, *i_elem_elem, *j_elem_elem;
6804
6805 num_elem = elem_elem.Size();
6806 i_elem_elem = elem_elem.GetI();
6807 j_elem_elem = elem_elem.GetJ();
6808
6809 component.SetSize(num_elem);
6810
6811 Array<int> elem_stack(num_elem);
6812 int stack_p, stack_top_p, elem;
6813 int num_part;
6814
6815 num_part = -1;
6816 for (i = 0; i < num_elem; i++)
6817 {
6818 if (partitioning[i] > num_part)
6819 {
6820 num_part = partitioning[i];
6821 }
6822 component[i] = -1;
6823 }
6824 num_part++;
6825
6826 num_comp.SetSize(num_part);
6827 for (i = 0; i < num_part; i++)
6828 {
6829 num_comp[i] = 0;
6830 }
6831
6832 stack_p = 0;
6833 stack_top_p = 0; // points to the first unused element in the stack
6834 for (elem = 0; elem < num_elem; elem++)
6835 {
6836 if (component[elem] >= 0)
6837 {
6838 continue;
6839 }
6840
6841 component[elem] = num_comp[partitioning[elem]]++;
6842
6843 elem_stack[stack_top_p++] = elem;
6844
6845 for ( ; stack_p < stack_top_p; stack_p++)
6846 {
6847 i = elem_stack[stack_p];
6848 for (j = i_elem_elem[i]; j < i_elem_elem[i+1]; j++)
6849 {
6850 k = j_elem_elem[j];
6851 if (partitioning[k] == partitioning[i])
6852 {
6853 if (component[k] < 0)
6854 {
6855 component[k] = component[i];
6856 elem_stack[stack_top_p++] = k;
6857 }
6858 else if (component[k] != component[i])
6859 {
6860 mfem_error("FindPartitioningComponents");
6861 }
6862 }
6863 }
6864 }
6865 }
6866 }
6867
CheckPartitioning(int * partitioning_)6868 void Mesh::CheckPartitioning(int *partitioning_)
6869 {
6870 int i, n_empty, n_mcomp;
6871 Array<int> component, num_comp;
6872 const Array<int> partitioning(partitioning_, GetNE());
6873
6874 ElementToElementTable();
6875
6876 FindPartitioningComponents(*el_to_el, partitioning, component, num_comp);
6877
6878 n_empty = n_mcomp = 0;
6879 for (i = 0; i < num_comp.Size(); i++)
6880 if (num_comp[i] == 0)
6881 {
6882 n_empty++;
6883 }
6884 else if (num_comp[i] > 1)
6885 {
6886 n_mcomp++;
6887 }
6888
6889 if (n_empty > 0)
6890 {
6891 mfem::out << "Mesh::CheckPartitioning(...) :\n"
6892 << "The following subdomains are empty :\n";
6893 for (i = 0; i < num_comp.Size(); i++)
6894 if (num_comp[i] == 0)
6895 {
6896 mfem::out << ' ' << i;
6897 }
6898 mfem::out << endl;
6899 }
6900 if (n_mcomp > 0)
6901 {
6902 mfem::out << "Mesh::CheckPartitioning(...) :\n"
6903 << "The following subdomains are NOT connected :\n";
6904 for (i = 0; i < num_comp.Size(); i++)
6905 if (num_comp[i] > 1)
6906 {
6907 mfem::out << ' ' << i;
6908 }
6909 mfem::out << endl;
6910 }
6911 if (n_empty == 0 && n_mcomp == 0)
6912 mfem::out << "Mesh::CheckPartitioning(...) : "
6913 "All subdomains are connected." << endl;
6914
6915 if (el_to_el)
6916 {
6917 delete el_to_el;
6918 }
6919 el_to_el = NULL;
6920 }
6921
6922 // compute the coefficients of the polynomial in t:
6923 // c(0)+c(1)*t+...+c(d)*t^d = det(A+t*B)
6924 // where A, B are (d x d), d=2,3
DetOfLinComb(const DenseMatrix & A,const DenseMatrix & B,Vector & c)6925 void DetOfLinComb(const DenseMatrix &A, const DenseMatrix &B, Vector &c)
6926 {
6927 const double *a = A.Data();
6928 const double *b = B.Data();
6929
6930 c.SetSize(A.Width()+1);
6931 switch (A.Width())
6932 {
6933 case 2:
6934 {
6935 // det(A+t*B) = |a0 a2| / |a0 b2| + |b0 a2| \ |b0 b2|
6936 // |a1 a3| + \ |a1 b3| |b1 a3| / * t + |b1 b3| * t^2
6937 c(0) = a[0]*a[3]-a[1]*a[2];
6938 c(1) = a[0]*b[3]-a[1]*b[2]+b[0]*a[3]-b[1]*a[2];
6939 c(2) = b[0]*b[3]-b[1]*b[2];
6940 }
6941 break;
6942
6943 case 3:
6944 {
6945 /* |a0 a3 a6|
6946 * det(A+t*B) = |a1 a4 a7| +
6947 * |a2 a5 a8|
6948
6949 * / |b0 a3 a6| |a0 b3 a6| |a0 a3 b6| \
6950 * + | |b1 a4 a7| + |a1 b4 a7| + |a1 a4 b7| | * t +
6951 * \ |b2 a5 a8| |a2 b5 a8| |a2 a5 b8| /
6952
6953 * / |a0 b3 b6| |b0 a3 b6| |b0 b3 a6| \
6954 * + | |a1 b4 b7| + |b1 a4 b7| + |b1 b4 a7| | * t^2 +
6955 * \ |a2 b5 b8| |b2 a5 b8| |b2 b5 a8| /
6956
6957 * |b0 b3 b6|
6958 * + |b1 b4 b7| * t^3
6959 * |b2 b5 b8| */
6960 c(0) = (a[0] * (a[4] * a[8] - a[5] * a[7]) +
6961 a[1] * (a[5] * a[6] - a[3] * a[8]) +
6962 a[2] * (a[3] * a[7] - a[4] * a[6]));
6963
6964 c(1) = (b[0] * (a[4] * a[8] - a[5] * a[7]) +
6965 b[1] * (a[5] * a[6] - a[3] * a[8]) +
6966 b[2] * (a[3] * a[7] - a[4] * a[6]) +
6967
6968 a[0] * (b[4] * a[8] - b[5] * a[7]) +
6969 a[1] * (b[5] * a[6] - b[3] * a[8]) +
6970 a[2] * (b[3] * a[7] - b[4] * a[6]) +
6971
6972 a[0] * (a[4] * b[8] - a[5] * b[7]) +
6973 a[1] * (a[5] * b[6] - a[3] * b[8]) +
6974 a[2] * (a[3] * b[7] - a[4] * b[6]));
6975
6976 c(2) = (a[0] * (b[4] * b[8] - b[5] * b[7]) +
6977 a[1] * (b[5] * b[6] - b[3] * b[8]) +
6978 a[2] * (b[3] * b[7] - b[4] * b[6]) +
6979
6980 b[0] * (a[4] * b[8] - a[5] * b[7]) +
6981 b[1] * (a[5] * b[6] - a[3] * b[8]) +
6982 b[2] * (a[3] * b[7] - a[4] * b[6]) +
6983
6984 b[0] * (b[4] * a[8] - b[5] * a[7]) +
6985 b[1] * (b[5] * a[6] - b[3] * a[8]) +
6986 b[2] * (b[3] * a[7] - b[4] * a[6]));
6987
6988 c(3) = (b[0] * (b[4] * b[8] - b[5] * b[7]) +
6989 b[1] * (b[5] * b[6] - b[3] * b[8]) +
6990 b[2] * (b[3] * b[7] - b[4] * b[6]));
6991 }
6992 break;
6993
6994 default:
6995 mfem_error("DetOfLinComb(...)");
6996 }
6997 }
6998
6999 // compute the real roots of
7000 // z(0)+z(1)*x+...+z(d)*x^d = 0, d=2,3;
7001 // the roots are returned in x, sorted in increasing order;
7002 // it is assumed that x is at least of size d;
7003 // return the number of roots counting multiplicity;
7004 // return -1 if all z(i) are 0.
FindRoots(const Vector & z,Vector & x)7005 int FindRoots(const Vector &z, Vector &x)
7006 {
7007 int d = z.Size()-1;
7008 if (d > 3 || d < 0)
7009 {
7010 mfem_error("FindRoots(...)");
7011 }
7012
7013 while (z(d) == 0.0)
7014 {
7015 if (d == 0)
7016 {
7017 return (-1);
7018 }
7019 d--;
7020 }
7021 switch (d)
7022 {
7023 case 0:
7024 {
7025 return 0;
7026 }
7027
7028 case 1:
7029 {
7030 x(0) = -z(0)/z(1);
7031 return 1;
7032 }
7033
7034 case 2:
7035 {
7036 double a = z(2), b = z(1), c = z(0);
7037 double D = b*b-4*a*c;
7038 if (D < 0.0)
7039 {
7040 return 0;
7041 }
7042 if (D == 0.0)
7043 {
7044 x(0) = x(1) = -0.5 * b / a;
7045 return 2; // root with multiplicity 2
7046 }
7047 if (b == 0.0)
7048 {
7049 x(0) = -(x(1) = fabs(0.5 * sqrt(D) / a));
7050 return 2;
7051 }
7052 else
7053 {
7054 double t;
7055 if (b > 0.0)
7056 {
7057 t = -0.5 * (b + sqrt(D));
7058 }
7059 else
7060 {
7061 t = -0.5 * (b - sqrt(D));
7062 }
7063 x(0) = t / a;
7064 x(1) = c / t;
7065 if (x(0) > x(1))
7066 {
7067 Swap<double>(x(0), x(1));
7068 }
7069 return 2;
7070 }
7071 }
7072
7073 case 3:
7074 {
7075 double a = z(2)/z(3), b = z(1)/z(3), c = z(0)/z(3);
7076
7077 // find the real roots of x^3 + a x^2 + b x + c = 0
7078 double Q = (a * a - 3 * b) / 9;
7079 double R = (2 * a * a * a - 9 * a * b + 27 * c) / 54;
7080 double Q3 = Q * Q * Q;
7081 double R2 = R * R;
7082
7083 if (R2 == Q3)
7084 {
7085 if (Q == 0)
7086 {
7087 x(0) = x(1) = x(2) = - a / 3;
7088 }
7089 else
7090 {
7091 double sqrtQ = sqrt(Q);
7092
7093 if (R > 0)
7094 {
7095 x(0) = -2 * sqrtQ - a / 3;
7096 x(1) = x(2) = sqrtQ - a / 3;
7097 }
7098 else
7099 {
7100 x(0) = x(1) = - sqrtQ - a / 3;
7101 x(2) = 2 * sqrtQ - a / 3;
7102 }
7103 }
7104 return 3;
7105 }
7106 else if (R2 < Q3)
7107 {
7108 double theta = acos(R / sqrt(Q3));
7109 double A = -2 * sqrt(Q);
7110 double x0, x1, x2;
7111 x0 = A * cos(theta / 3) - a / 3;
7112 x1 = A * cos((theta + 2.0 * M_PI) / 3) - a / 3;
7113 x2 = A * cos((theta - 2.0 * M_PI) / 3) - a / 3;
7114
7115 /* Sort x0, x1, x2 */
7116 if (x0 > x1)
7117 {
7118 Swap<double>(x0, x1);
7119 }
7120 if (x1 > x2)
7121 {
7122 Swap<double>(x1, x2);
7123 if (x0 > x1)
7124 {
7125 Swap<double>(x0, x1);
7126 }
7127 }
7128 x(0) = x0;
7129 x(1) = x1;
7130 x(2) = x2;
7131 return 3;
7132 }
7133 else
7134 {
7135 double A;
7136 if (R >= 0.0)
7137 {
7138 A = -pow(sqrt(R2 - Q3) + R, 1.0/3.0);
7139 }
7140 else
7141 {
7142 A = pow(sqrt(R2 - Q3) - R, 1.0/3.0);
7143 }
7144 x(0) = A + Q / A - a / 3;
7145 return 1;
7146 }
7147 }
7148 }
7149 return 0;
7150 }
7151
FindTMax(Vector & c,Vector & x,double & tmax,const double factor,const int Dim)7152 void FindTMax(Vector &c, Vector &x, double &tmax,
7153 const double factor, const int Dim)
7154 {
7155 const double c0 = c(0);
7156 c(0) = c0 * (1.0 - pow(factor, -Dim));
7157 int nr = FindRoots(c, x);
7158 for (int j = 0; j < nr; j++)
7159 {
7160 if (x(j) > tmax)
7161 {
7162 break;
7163 }
7164 if (x(j) >= 0.0)
7165 {
7166 tmax = x(j);
7167 break;
7168 }
7169 }
7170 c(0) = c0 * (1.0 - pow(factor, Dim));
7171 nr = FindRoots(c, x);
7172 for (int j = 0; j < nr; j++)
7173 {
7174 if (x(j) > tmax)
7175 {
7176 break;
7177 }
7178 if (x(j) >= 0.0)
7179 {
7180 tmax = x(j);
7181 break;
7182 }
7183 }
7184 }
7185
CheckDisplacements(const Vector & displacements,double & tmax)7186 void Mesh::CheckDisplacements(const Vector &displacements, double &tmax)
7187 {
7188 int nvs = vertices.Size();
7189 DenseMatrix P, V, DS, PDS(spaceDim), VDS(spaceDim);
7190 Vector c(spaceDim+1), x(spaceDim);
7191 const double factor = 2.0;
7192
7193 // check for tangling assuming constant speed
7194 if (tmax < 1.0)
7195 {
7196 tmax = 1.0;
7197 }
7198 for (int i = 0; i < NumOfElements; i++)
7199 {
7200 Element *el = elements[i];
7201 int nv = el->GetNVertices();
7202 int *v = el->GetVertices();
7203 P.SetSize(spaceDim, nv);
7204 V.SetSize(spaceDim, nv);
7205 for (int j = 0; j < spaceDim; j++)
7206 for (int k = 0; k < nv; k++)
7207 {
7208 P(j, k) = vertices[v[k]](j);
7209 V(j, k) = displacements(v[k]+j*nvs);
7210 }
7211 DS.SetSize(nv, spaceDim);
7212 const FiniteElement *fe =
7213 GetTransformationFEforElementType(el->GetType());
7214 // check if det(P.DShape+t*V.DShape) > 0 for all x and 0<=t<=1
7215 switch (el->GetType())
7216 {
7217 case Element::TRIANGLE:
7218 case Element::TETRAHEDRON:
7219 {
7220 // DS is constant
7221 fe->CalcDShape(Geometries.GetCenter(fe->GetGeomType()), DS);
7222 Mult(P, DS, PDS);
7223 Mult(V, DS, VDS);
7224 DetOfLinComb(PDS, VDS, c);
7225 if (c(0) <= 0.0)
7226 {
7227 tmax = 0.0;
7228 }
7229 else
7230 {
7231 FindTMax(c, x, tmax, factor, Dim);
7232 }
7233 }
7234 break;
7235
7236 case Element::QUADRILATERAL:
7237 {
7238 const IntegrationRule &ir = fe->GetNodes();
7239 for (int j = 0; j < nv; j++)
7240 {
7241 fe->CalcDShape(ir.IntPoint(j), DS);
7242 Mult(P, DS, PDS);
7243 Mult(V, DS, VDS);
7244 DetOfLinComb(PDS, VDS, c);
7245 if (c(0) <= 0.0)
7246 {
7247 tmax = 0.0;
7248 }
7249 else
7250 {
7251 FindTMax(c, x, tmax, factor, Dim);
7252 }
7253 }
7254 }
7255 break;
7256
7257 default:
7258 mfem_error("Mesh::CheckDisplacements(...)");
7259 }
7260 }
7261 }
7262
MoveVertices(const Vector & displacements)7263 void Mesh::MoveVertices(const Vector &displacements)
7264 {
7265 for (int i = 0, nv = vertices.Size(); i < nv; i++)
7266 for (int j = 0; j < spaceDim; j++)
7267 {
7268 vertices[i](j) += displacements(j*nv+i);
7269 }
7270 }
7271
GetVertices(Vector & vert_coord) const7272 void Mesh::GetVertices(Vector &vert_coord) const
7273 {
7274 int nv = vertices.Size();
7275 vert_coord.SetSize(nv*spaceDim);
7276 for (int i = 0; i < nv; i++)
7277 for (int j = 0; j < spaceDim; j++)
7278 {
7279 vert_coord(j*nv+i) = vertices[i](j);
7280 }
7281 }
7282
SetVertices(const Vector & vert_coord)7283 void Mesh::SetVertices(const Vector &vert_coord)
7284 {
7285 for (int i = 0, nv = vertices.Size(); i < nv; i++)
7286 for (int j = 0; j < spaceDim; j++)
7287 {
7288 vertices[i](j) = vert_coord(j*nv+i);
7289 }
7290 }
7291
GetNode(int i,double * coord) const7292 void Mesh::GetNode(int i, double *coord) const
7293 {
7294 if (Nodes)
7295 {
7296 FiniteElementSpace *fes = Nodes->FESpace();
7297 for (int j = 0; j < spaceDim; j++)
7298 {
7299 coord[j] = (*Nodes)(fes->DofToVDof(i, j));
7300 }
7301 }
7302 else
7303 {
7304 for (int j = 0; j < spaceDim; j++)
7305 {
7306 coord[j] = vertices[i](j);
7307 }
7308 }
7309 }
7310
SetNode(int i,const double * coord)7311 void Mesh::SetNode(int i, const double *coord)
7312 {
7313 if (Nodes)
7314 {
7315 FiniteElementSpace *fes = Nodes->FESpace();
7316 for (int j = 0; j < spaceDim; j++)
7317 {
7318 (*Nodes)(fes->DofToVDof(i, j)) = coord[j];
7319 }
7320 }
7321 else
7322 {
7323 for (int j = 0; j < spaceDim; j++)
7324 {
7325 vertices[i](j) = coord[j];
7326 }
7327
7328 }
7329 }
7330
MoveNodes(const Vector & displacements)7331 void Mesh::MoveNodes(const Vector &displacements)
7332 {
7333 if (Nodes)
7334 {
7335 (*Nodes) += displacements;
7336 }
7337 else
7338 {
7339 MoveVertices(displacements);
7340 }
7341 }
7342
GetNodes(Vector & node_coord) const7343 void Mesh::GetNodes(Vector &node_coord) const
7344 {
7345 if (Nodes)
7346 {
7347 node_coord = (*Nodes);
7348 }
7349 else
7350 {
7351 GetVertices(node_coord);
7352 }
7353 }
7354
SetNodes(const Vector & node_coord)7355 void Mesh::SetNodes(const Vector &node_coord)
7356 {
7357 if (Nodes)
7358 {
7359 (*Nodes) = node_coord;
7360 }
7361 else
7362 {
7363 SetVertices(node_coord);
7364 }
7365 }
7366
NewNodes(GridFunction & nodes,bool make_owner)7367 void Mesh::NewNodes(GridFunction &nodes, bool make_owner)
7368 {
7369 if (own_nodes) { delete Nodes; }
7370 Nodes = &nodes;
7371 spaceDim = Nodes->FESpace()->GetVDim();
7372 own_nodes = (int)make_owner;
7373
7374 if (NURBSext != nodes.FESpace()->GetNURBSext())
7375 {
7376 delete NURBSext;
7377 NURBSext = nodes.FESpace()->StealNURBSext();
7378 }
7379
7380 if (ncmesh)
7381 {
7382 ncmesh->MakeTopologyOnly();
7383 }
7384 }
7385
SwapNodes(GridFunction * & nodes,int & own_nodes_)7386 void Mesh::SwapNodes(GridFunction *&nodes, int &own_nodes_)
7387 {
7388 mfem::Swap<GridFunction*>(Nodes, nodes);
7389 mfem::Swap<int>(own_nodes, own_nodes_);
7390 // TODO:
7391 // if (nodes)
7392 // nodes->FESpace()->MakeNURBSextOwner();
7393 // NURBSext = (Nodes) ? Nodes->FESpace()->StealNURBSext() : NULL;
7394 }
7395
AverageVertices(const int * indexes,int n,int result)7396 void Mesh::AverageVertices(const int *indexes, int n, int result)
7397 {
7398 int j, k;
7399
7400 for (k = 0; k < spaceDim; k++)
7401 {
7402 vertices[result](k) = vertices[indexes[0]](k);
7403 }
7404
7405 for (j = 1; j < n; j++)
7406 for (k = 0; k < spaceDim; k++)
7407 {
7408 vertices[result](k) += vertices[indexes[j]](k);
7409 }
7410
7411 for (k = 0; k < spaceDim; k++)
7412 {
7413 vertices[result](k) *= (1.0 / n);
7414 }
7415 }
7416
UpdateNodes()7417 void Mesh::UpdateNodes()
7418 {
7419 if (Nodes)
7420 {
7421 Nodes->FESpace()->Update();
7422 Nodes->Update();
7423
7424 // update vertex coordinates for compatibility (e.g., GetVertex())
7425 SetVerticesFromNodes(Nodes);
7426 }
7427 }
7428
UniformRefinement2D_base(bool update_nodes)7429 void Mesh::UniformRefinement2D_base(bool update_nodes)
7430 {
7431 ResetLazyData();
7432
7433 if (el_to_edge == NULL)
7434 {
7435 el_to_edge = new Table;
7436 NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
7437 }
7438
7439 int quad_counter = 0;
7440 for (int i = 0; i < NumOfElements; i++)
7441 {
7442 if (elements[i]->GetType() == Element::QUADRILATERAL)
7443 {
7444 quad_counter++;
7445 }
7446 }
7447
7448 const int oedge = NumOfVertices;
7449 const int oelem = oedge + NumOfEdges;
7450
7451 Array<Element*> new_elements;
7452 Array<Element*> new_boundary;
7453
7454 vertices.SetSize(oelem + quad_counter);
7455 new_elements.SetSize(4 * NumOfElements);
7456 quad_counter = 0;
7457
7458 for (int i = 0, j = 0; i < NumOfElements; i++)
7459 {
7460 const Element::Type el_type = elements[i]->GetType();
7461 const int attr = elements[i]->GetAttribute();
7462 int *v = elements[i]->GetVertices();
7463 const int *e = el_to_edge->GetRow(i);
7464 int vv[2];
7465
7466 if (el_type == Element::TRIANGLE)
7467 {
7468 for (int ei = 0; ei < 3; ei++)
7469 {
7470 for (int k = 0; k < 2; k++)
7471 {
7472 vv[k] = v[tri_t::Edges[ei][k]];
7473 }
7474 AverageVertices(vv, 2, oedge+e[ei]);
7475 }
7476
7477 new_elements[j++] =
7478 new Triangle(v[0], oedge+e[0], oedge+e[2], attr);
7479 new_elements[j++] =
7480 new Triangle(oedge+e[1], oedge+e[2], oedge+e[0], attr);
7481 new_elements[j++] =
7482 new Triangle(oedge+e[0], v[1], oedge+e[1], attr);
7483 new_elements[j++] =
7484 new Triangle(oedge+e[2], oedge+e[1], v[2], attr);
7485 }
7486 else if (el_type == Element::QUADRILATERAL)
7487 {
7488 const int qe = quad_counter;
7489 quad_counter++;
7490 AverageVertices(v, 4, oelem+qe);
7491
7492 for (int ei = 0; ei < 4; ei++)
7493 {
7494 for (int k = 0; k < 2; k++)
7495 {
7496 vv[k] = v[quad_t::Edges[ei][k]];
7497 }
7498 AverageVertices(vv, 2, oedge+e[ei]);
7499 }
7500
7501 new_elements[j++] =
7502 new Quadrilateral(v[0], oedge+e[0], oelem+qe, oedge+e[3], attr);
7503 new_elements[j++] =
7504 new Quadrilateral(oedge+e[0], v[1], oedge+e[1], oelem+qe, attr);
7505 new_elements[j++] =
7506 new Quadrilateral(oelem+qe, oedge+e[1], v[2], oedge+e[2], attr);
7507 new_elements[j++] =
7508 new Quadrilateral(oedge+e[3], oelem+qe, oedge+e[2], v[3], attr);
7509 }
7510 else
7511 {
7512 MFEM_ABORT("unknown element type: " << el_type);
7513 }
7514 FreeElement(elements[i]);
7515 }
7516 mfem::Swap(elements, new_elements);
7517
7518 // refine boundary elements
7519 new_boundary.SetSize(2 * NumOfBdrElements);
7520 for (int i = 0, j = 0; i < NumOfBdrElements; i++)
7521 {
7522 const int attr = boundary[i]->GetAttribute();
7523 int *v = boundary[i]->GetVertices();
7524
7525 new_boundary[j++] = new Segment(v[0], oedge+be_to_edge[i], attr);
7526 new_boundary[j++] = new Segment(oedge+be_to_edge[i], v[1], attr);
7527
7528 FreeElement(boundary[i]);
7529 }
7530 mfem::Swap(boundary, new_boundary);
7531
7532 static const double A = 0.0, B = 0.5, C = 1.0;
7533 static double tri_children[2*3*4] =
7534 {
7535 A,A, B,A, A,B,
7536 B,B, A,B, B,A,
7537 B,A, C,A, B,B,
7538 A,B, B,B, A,C
7539 };
7540 static double quad_children[2*4*4] =
7541 {
7542 A,A, B,A, B,B, A,B, // lower-left
7543 B,A, C,A, C,B, B,B, // lower-right
7544 B,B, C,B, C,C, B,C, // upper-right
7545 A,B, B,B, B,C, A,C // upper-left
7546 };
7547
7548 CoarseFineTr.point_matrices[Geometry::TRIANGLE]
7549 .UseExternalData(tri_children, 2, 3, 4);
7550 CoarseFineTr.point_matrices[Geometry::SQUARE]
7551 .UseExternalData(quad_children, 2, 4, 4);
7552 CoarseFineTr.embeddings.SetSize(elements.Size());
7553
7554 for (int i = 0; i < elements.Size(); i++)
7555 {
7556 Embedding &emb = CoarseFineTr.embeddings[i];
7557 emb.parent = i / 4;
7558 emb.matrix = i % 4;
7559 }
7560
7561 NumOfVertices = vertices.Size();
7562 NumOfElements = 4 * NumOfElements;
7563 NumOfBdrElements = 2 * NumOfBdrElements;
7564 NumOfFaces = 0;
7565
7566 NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
7567 GenerateFaces();
7568
7569 last_operation = Mesh::REFINE;
7570 sequence++;
7571
7572 if (update_nodes) { UpdateNodes(); }
7573
7574 #ifdef MFEM_DEBUG
7575 if (!Nodes || update_nodes)
7576 {
7577 CheckElementOrientation(false);
7578 }
7579 CheckBdrElementOrientation(false);
7580 #endif
7581 }
7582
sqr(const double & x)7583 static inline double sqr(const double &x)
7584 {
7585 return x*x;
7586 }
7587
UniformRefinement3D_base(Array<int> * f2qf_ptr,DSTable * v_to_v_p,bool update_nodes)7588 void Mesh::UniformRefinement3D_base(Array<int> *f2qf_ptr, DSTable *v_to_v_p,
7589 bool update_nodes)
7590 {
7591 ResetLazyData();
7592
7593 if (el_to_edge == NULL)
7594 {
7595 el_to_edge = new Table;
7596 NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
7597 }
7598
7599 if (el_to_face == NULL)
7600 {
7601 GetElementToFaceTable();
7602 }
7603
7604 Array<int> f2qf_loc;
7605 Array<int> &f2qf = f2qf_ptr ? *f2qf_ptr : f2qf_loc;
7606 f2qf.SetSize(0);
7607
7608 int NumOfQuadFaces = 0;
7609 if (HasGeometry(Geometry::SQUARE))
7610 {
7611 if (HasGeometry(Geometry::TRIANGLE))
7612 {
7613 f2qf.SetSize(faces.Size());
7614 for (int i = 0; i < faces.Size(); i++)
7615 {
7616 if (faces[i]->GetType() == Element::QUADRILATERAL)
7617 {
7618 f2qf[i] = NumOfQuadFaces;
7619 NumOfQuadFaces++;
7620 }
7621 }
7622 }
7623 else
7624 {
7625 NumOfQuadFaces = faces.Size();
7626 }
7627 }
7628
7629 int hex_counter = 0;
7630 if (HasGeometry(Geometry::CUBE))
7631 {
7632 for (int i = 0; i < elements.Size(); i++)
7633 {
7634 if (elements[i]->GetType() == Element::HEXAHEDRON)
7635 {
7636 hex_counter++;
7637 }
7638 }
7639 }
7640
7641 // Map from edge-index to vertex-index, needed for ReorientTetMesh() for
7642 // parallel meshes.
7643 Array<int> e2v;
7644 if (HasGeometry(Geometry::TETRAHEDRON))
7645 {
7646 e2v.SetSize(NumOfEdges);
7647
7648 DSTable *v_to_v_ptr = v_to_v_p;
7649 if (!v_to_v_p)
7650 {
7651 v_to_v_ptr = new DSTable(NumOfVertices);
7652 GetVertexToVertexTable(*v_to_v_ptr);
7653 }
7654
7655 Array<Pair<int,int> > J_v2v(NumOfEdges); // (second vertex id, edge id)
7656 J_v2v.SetSize(0);
7657 for (int i = 0; i < NumOfVertices; i++)
7658 {
7659 Pair<int,int> *row_start = J_v2v.end();
7660 for (DSTable::RowIterator it(*v_to_v_ptr, i); !it; ++it)
7661 {
7662 J_v2v.Append(Pair<int,int>(it.Column(), it.Index()));
7663 }
7664 std::sort(row_start, J_v2v.end());
7665 }
7666
7667 for (int i = 0; i < J_v2v.Size(); i++)
7668 {
7669 e2v[J_v2v[i].two] = i;
7670 }
7671
7672 if (!v_to_v_p)
7673 {
7674 delete v_to_v_ptr;
7675 }
7676 else
7677 {
7678 for (int i = 0; i < NumOfVertices; i++)
7679 {
7680 for (DSTable::RowIterator it(*v_to_v_ptr, i); !it; ++it)
7681 {
7682 it.SetIndex(e2v[it.Index()]);
7683 }
7684 }
7685 }
7686 }
7687
7688 // Offsets for new vertices from edges, faces (quads only), and elements
7689 // (hexes only); each of these entities generates one new vertex.
7690 const int oedge = NumOfVertices;
7691 const int oface = oedge + NumOfEdges;
7692 const int oelem = oface + NumOfQuadFaces;
7693
7694 Array<Element*> new_elements;
7695 Array<Element*> new_boundary;
7696
7697 vertices.SetSize(oelem + hex_counter);
7698 new_elements.SetSize(8 * NumOfElements);
7699 CoarseFineTr.embeddings.SetSize(new_elements.Size());
7700
7701 hex_counter = 0;
7702 for (int i = 0, j = 0; i < NumOfElements; i++)
7703 {
7704 const Element::Type el_type = elements[i]->GetType();
7705 const int attr = elements[i]->GetAttribute();
7706 int *v = elements[i]->GetVertices();
7707 const int *e = el_to_edge->GetRow(i);
7708 int vv[4], ev[12];
7709
7710 if (e2v.Size())
7711 {
7712 const int ne = el_to_edge->RowSize(i);
7713 for (int k = 0; k < ne; k++) { ev[k] = e2v[e[k]]; }
7714 e = ev;
7715 }
7716
7717 switch (el_type)
7718 {
7719 case Element::TETRAHEDRON:
7720 {
7721 for (int ei = 0; ei < 6; ei++)
7722 {
7723 for (int k = 0; k < 2; k++)
7724 {
7725 vv[k] = v[tet_t::Edges[ei][k]];
7726 }
7727 AverageVertices(vv, 2, oedge+e[ei]);
7728 }
7729
7730 // Algorithm for choosing refinement type:
7731 // 0: smallest octahedron diagonal
7732 // 1: best aspect ratio
7733 const int rt_algo = 1;
7734 // Refinement type:
7735 // 0: (v0,v1)-(v2,v3), 1: (v0,v2)-(v1,v3), 2: (v0,v3)-(v1,v2)
7736 // 0: e0-e5, 1: e1-e4, 2: e2-e3
7737 int rt;
7738 ElementTransformation *T = GetElementTransformation(i);
7739 T->SetIntPoint(&Geometries.GetCenter(Geometry::TETRAHEDRON));
7740 const DenseMatrix &J = T->Jacobian();
7741 if (rt_algo == 0)
7742 {
7743 // smallest octahedron diagonal
7744 double len_sqr, min_len;
7745
7746 min_len = sqr(J(0,0)-J(0,1)-J(0,2)) +
7747 sqr(J(1,0)-J(1,1)-J(1,2)) +
7748 sqr(J(2,0)-J(2,1)-J(2,2));
7749 rt = 0;
7750
7751 len_sqr = sqr(J(0,1)-J(0,0)-J(0,2)) +
7752 sqr(J(1,1)-J(1,0)-J(1,2)) +
7753 sqr(J(2,1)-J(2,0)-J(2,2));
7754 if (len_sqr < min_len) { min_len = len_sqr; rt = 1; }
7755
7756 len_sqr = sqr(J(0,2)-J(0,0)-J(0,1)) +
7757 sqr(J(1,2)-J(1,0)-J(1,1)) +
7758 sqr(J(2,2)-J(2,0)-J(2,1));
7759 if (len_sqr < min_len) { rt = 2; }
7760 }
7761 else
7762 {
7763 // best aspect ratio
7764 double Em_data[18], Js_data[9], Jp_data[9];
7765 DenseMatrix Em(Em_data, 3, 6);
7766 DenseMatrix Js(Js_data, 3, 3), Jp(Jp_data, 3, 3);
7767 double ar1, ar2, kappa, kappa_min;
7768
7769 for (int s = 0; s < 3; s++)
7770 {
7771 for (int t = 0; t < 3; t++)
7772 {
7773 Em(t,s) = 0.5*J(t,s);
7774 }
7775 }
7776 for (int t = 0; t < 3; t++)
7777 {
7778 Em(t,3) = 0.5*(J(t,0)+J(t,1));
7779 Em(t,4) = 0.5*(J(t,0)+J(t,2));
7780 Em(t,5) = 0.5*(J(t,1)+J(t,2));
7781 }
7782
7783 // rt = 0; Em: {0,5,1,2}, {0,5,2,4}
7784 for (int t = 0; t < 3; t++)
7785 {
7786 Js(t,0) = Em(t,5)-Em(t,0);
7787 Js(t,1) = Em(t,1)-Em(t,0);
7788 Js(t,2) = Em(t,2)-Em(t,0);
7789 }
7790 Geometries.JacToPerfJac(Geometry::TETRAHEDRON, Js, Jp);
7791 ar1 = Jp.CalcSingularvalue(0)/Jp.CalcSingularvalue(2);
7792 for (int t = 0; t < 3; t++)
7793 {
7794 Js(t,0) = Em(t,5)-Em(t,0);
7795 Js(t,1) = Em(t,2)-Em(t,0);
7796 Js(t,2) = Em(t,4)-Em(t,0);
7797 }
7798 Geometries.JacToPerfJac(Geometry::TETRAHEDRON, Js, Jp);
7799 ar2 = Jp.CalcSingularvalue(0)/Jp.CalcSingularvalue(2);
7800 kappa_min = std::max(ar1, ar2);
7801 rt = 0;
7802
7803 // rt = 1; Em: {1,0,4,2}, {1,2,4,5}
7804 for (int t = 0; t < 3; t++)
7805 {
7806 Js(t,0) = Em(t,0)-Em(t,1);
7807 Js(t,1) = Em(t,4)-Em(t,1);
7808 Js(t,2) = Em(t,2)-Em(t,1);
7809 }
7810 Geometries.JacToPerfJac(Geometry::TETRAHEDRON, Js, Jp);
7811 ar1 = Jp.CalcSingularvalue(0)/Jp.CalcSingularvalue(2);
7812 for (int t = 0; t < 3; t++)
7813 {
7814 Js(t,0) = Em(t,2)-Em(t,1);
7815 Js(t,1) = Em(t,4)-Em(t,1);
7816 Js(t,2) = Em(t,5)-Em(t,1);
7817 }
7818 Geometries.JacToPerfJac(Geometry::TETRAHEDRON, Js, Jp);
7819 ar2 = Jp.CalcSingularvalue(0)/Jp.CalcSingularvalue(2);
7820 kappa = std::max(ar1, ar2);
7821 if (kappa < kappa_min) { kappa_min = kappa; rt = 1; }
7822
7823 // rt = 2; Em: {2,0,1,3}, {2,1,5,3}
7824 for (int t = 0; t < 3; t++)
7825 {
7826 Js(t,0) = Em(t,0)-Em(t,2);
7827 Js(t,1) = Em(t,1)-Em(t,2);
7828 Js(t,2) = Em(t,3)-Em(t,2);
7829 }
7830 Geometries.JacToPerfJac(Geometry::TETRAHEDRON, Js, Jp);
7831 ar1 = Jp.CalcSingularvalue(0)/Jp.CalcSingularvalue(2);
7832 for (int t = 0; t < 3; t++)
7833 {
7834 Js(t,0) = Em(t,1)-Em(t,2);
7835 Js(t,1) = Em(t,5)-Em(t,2);
7836 Js(t,2) = Em(t,3)-Em(t,2);
7837 }
7838 Geometries.JacToPerfJac(Geometry::TETRAHEDRON, Js, Jp);
7839 ar2 = Jp.CalcSingularvalue(0)/Jp.CalcSingularvalue(2);
7840 kappa = std::max(ar1, ar2);
7841 if (kappa < kappa_min) { rt = 2; }
7842 }
7843
7844 static const int mv_all[3][4][4] =
7845 {
7846 { {0,5,1,2}, {0,5,2,4}, {0,5,4,3}, {0,5,3,1} }, // rt = 0
7847 { {1,0,4,2}, {1,2,4,5}, {1,5,4,3}, {1,3,4,0} }, // rt = 1
7848 { {2,0,1,3}, {2,1,5,3}, {2,5,4,3}, {2,4,0,3} } // rt = 2
7849 };
7850 const int (&mv)[4][4] = mv_all[rt];
7851
7852 #ifndef MFEM_USE_MEMALLOC
7853 new_elements[j+0] =
7854 new Tetrahedron(v[0], oedge+e[0], oedge+e[1], oedge+e[2], attr);
7855 new_elements[j+1] =
7856 new Tetrahedron(oedge+e[0], v[1], oedge+e[3], oedge+e[4], attr);
7857 new_elements[j+2] =
7858 new Tetrahedron(oedge+e[1], oedge+e[3], v[2], oedge+e[5], attr);
7859 new_elements[j+3] =
7860 new Tetrahedron(oedge+e[2], oedge+e[4], oedge+e[5], v[3], attr);
7861
7862 for (int k = 0; k < 4; k++)
7863 {
7864 new_elements[j+4+k] =
7865 new Tetrahedron(oedge+e[mv[k][0]], oedge+e[mv[k][1]],
7866 oedge+e[mv[k][2]], oedge+e[mv[k][3]], attr);
7867 }
7868 #else
7869 Tetrahedron *tet;
7870 new_elements[j+0] = tet = TetMemory.Alloc();
7871 tet->Init(v[0], oedge+e[0], oedge+e[1], oedge+e[2], attr);
7872
7873 new_elements[j+1] = tet = TetMemory.Alloc();
7874 tet->Init(oedge+e[0], v[1], oedge+e[3], oedge+e[4], attr);
7875
7876 new_elements[j+2] = tet = TetMemory.Alloc();
7877 tet->Init(oedge+e[1], oedge+e[3], v[2], oedge+e[5], attr);
7878
7879 new_elements[j+3] = tet = TetMemory.Alloc();
7880 tet->Init(oedge+e[2], oedge+e[4], oedge+e[5], v[3], attr);
7881
7882 for (int k = 0; k < 4; k++)
7883 {
7884 new_elements[j+4+k] = tet = TetMemory.Alloc();
7885 tet->Init(oedge+e[mv[k][0]], oedge+e[mv[k][1]],
7886 oedge+e[mv[k][2]], oedge+e[mv[k][3]], attr);
7887 }
7888 #endif
7889 for (int k = 0; k < 4; k++)
7890 {
7891 CoarseFineTr.embeddings[j+k].parent = i;
7892 CoarseFineTr.embeddings[j+k].matrix = k;
7893 }
7894 for (int k = 0; k < 4; k++)
7895 {
7896 CoarseFineTr.embeddings[j+4+k].parent = i;
7897 CoarseFineTr.embeddings[j+4+k].matrix = 4*(rt+1)+k;
7898 }
7899
7900 j += 8;
7901 }
7902 break;
7903
7904 case Element::WEDGE:
7905 {
7906 const int *f = el_to_face->GetRow(i);
7907
7908 for (int fi = 2; fi < 5; fi++)
7909 {
7910 for (int k = 0; k < 4; k++)
7911 {
7912 vv[k] = v[pri_t::FaceVert[fi][k]];
7913 }
7914 AverageVertices(vv, 4, oface + f2qf[f[fi]]);
7915 }
7916
7917 for (int ei = 0; ei < 9; ei++)
7918 {
7919 for (int k = 0; k < 2; k++)
7920 {
7921 vv[k] = v[pri_t::Edges[ei][k]];
7922 }
7923 AverageVertices(vv, 2, oedge+e[ei]);
7924 }
7925
7926 const int qf2 = f2qf[f[2]];
7927 const int qf3 = f2qf[f[3]];
7928 const int qf4 = f2qf[f[4]];
7929
7930 new_elements[j++] =
7931 new Wedge(v[0], oedge+e[0], oedge+e[2],
7932 oedge+e[6], oface+qf2, oface+qf4, attr);
7933
7934 new_elements[j++] =
7935 new Wedge(oedge+e[1], oedge+e[2], oedge+e[0],
7936 oface+qf3, oface+qf4, oface+qf2, attr);
7937
7938 new_elements[j++] =
7939 new Wedge(oedge+e[0], v[1], oedge+e[1],
7940 oface+qf2, oedge+e[7], oface+qf3, attr);
7941
7942 new_elements[j++] =
7943 new Wedge(oedge+e[2], oedge+e[1], v[2],
7944 oface+qf4, oface+qf3, oedge+e[8], attr);
7945
7946 new_elements[j++] =
7947 new Wedge(oedge+e[6], oface+qf2, oface+qf4,
7948 v[3], oedge+e[3], oedge+e[5], attr);
7949
7950 new_elements[j++] =
7951 new Wedge(oface+qf3, oface+qf4, oface+qf2,
7952 oedge+e[4], oedge+e[5], oedge+e[3], attr);
7953
7954 new_elements[j++] =
7955 new Wedge(oface+qf2, oedge+e[7], oface+qf3,
7956 oedge+e[3], v[4], oedge+e[4], attr);
7957
7958 new_elements[j++] =
7959 new Wedge(oface+qf4, oface+qf3, oedge+e[8],
7960 oedge+e[5], oedge+e[4], v[5], attr);
7961 }
7962 break;
7963
7964 case Element::HEXAHEDRON:
7965 {
7966 const int *f = el_to_face->GetRow(i);
7967 const int he = hex_counter;
7968 hex_counter++;
7969
7970 const int *qf;
7971 int qf_data[6];
7972 if (f2qf.Size() == 0)
7973 {
7974 qf = f;
7975 }
7976 else
7977 {
7978 for (int k = 0; k < 6; k++) { qf_data[k] = f2qf[f[k]]; }
7979 qf = qf_data;
7980 }
7981
7982 AverageVertices(v, 8, oelem+he);
7983
7984 for (int fi = 0; fi < 6; fi++)
7985 {
7986 for (int k = 0; k < 4; k++)
7987 {
7988 vv[k] = v[hex_t::FaceVert[fi][k]];
7989 }
7990 AverageVertices(vv, 4, oface + qf[fi]);
7991 }
7992
7993 for (int ei = 0; ei < 12; ei++)
7994 {
7995 for (int k = 0; k < 2; k++)
7996 {
7997 vv[k] = v[hex_t::Edges[ei][k]];
7998 }
7999 AverageVertices(vv, 2, oedge+e[ei]);
8000 }
8001
8002 new_elements[j++] =
8003 new Hexahedron(v[0], oedge+e[0], oface+qf[0],
8004 oedge+e[3], oedge+e[8], oface+qf[1],
8005 oelem+he, oface+qf[4], attr);
8006 new_elements[j++] =
8007 new Hexahedron(oedge+e[0], v[1], oedge+e[1],
8008 oface+qf[0], oface+qf[1], oedge+e[9],
8009 oface+qf[2], oelem+he, attr);
8010 new_elements[j++] =
8011 new Hexahedron(oface+qf[0], oedge+e[1], v[2],
8012 oedge+e[2], oelem+he, oface+qf[2],
8013 oedge+e[10], oface+qf[3], attr);
8014 new_elements[j++] =
8015 new Hexahedron(oedge+e[3], oface+qf[0], oedge+e[2],
8016 v[3], oface+qf[4], oelem+he,
8017 oface+qf[3], oedge+e[11], attr);
8018 new_elements[j++] =
8019 new Hexahedron(oedge+e[8], oface+qf[1], oelem+he,
8020 oface+qf[4], v[4], oedge+e[4],
8021 oface+qf[5], oedge+e[7], attr);
8022 new_elements[j++] =
8023 new Hexahedron(oface+qf[1], oedge+e[9], oface+qf[2],
8024 oelem+he, oedge+e[4], v[5],
8025 oedge+e[5], oface+qf[5], attr);
8026 new_elements[j++] =
8027 new Hexahedron(oelem+he, oface+qf[2], oedge+e[10],
8028 oface+qf[3], oface+qf[5], oedge+e[5],
8029 v[6], oedge+e[6], attr);
8030 new_elements[j++] =
8031 new Hexahedron(oface+qf[4], oelem+he, oface+qf[3],
8032 oedge+e[11], oedge+e[7], oface+qf[5],
8033 oedge+e[6], v[7], attr);
8034 }
8035 break;
8036
8037 default:
8038 MFEM_ABORT("Unknown 3D element type \"" << el_type << "\"");
8039 break;
8040 }
8041 FreeElement(elements[i]);
8042 }
8043 mfem::Swap(elements, new_elements);
8044
8045 // refine boundary elements
8046 new_boundary.SetSize(4 * NumOfBdrElements);
8047 for (int i = 0, j = 0; i < NumOfBdrElements; i++)
8048 {
8049 const Element::Type bdr_el_type = boundary[i]->GetType();
8050 const int attr = boundary[i]->GetAttribute();
8051 int *v = boundary[i]->GetVertices();
8052 const int *e = bel_to_edge->GetRow(i);
8053 int ev[4];
8054
8055 if (e2v.Size())
8056 {
8057 const int ne = bel_to_edge->RowSize(i);
8058 for (int k = 0; k < ne; k++) { ev[k] = e2v[e[k]]; }
8059 e = ev;
8060 }
8061
8062 if (bdr_el_type == Element::TRIANGLE)
8063 {
8064 new_boundary[j++] =
8065 new Triangle(v[0], oedge+e[0], oedge+e[2], attr);
8066 new_boundary[j++] =
8067 new Triangle(oedge+e[1], oedge+e[2], oedge+e[0], attr);
8068 new_boundary[j++] =
8069 new Triangle(oedge+e[0], v[1], oedge+e[1], attr);
8070 new_boundary[j++] =
8071 new Triangle(oedge+e[2], oedge+e[1], v[2], attr);
8072 }
8073 else if (bdr_el_type == Element::QUADRILATERAL)
8074 {
8075 const int qf =
8076 (f2qf.Size() == 0) ? be_to_face[i] : f2qf[be_to_face[i]];
8077
8078 new_boundary[j++] =
8079 new Quadrilateral(v[0], oedge+e[0], oface+qf, oedge+e[3], attr);
8080 new_boundary[j++] =
8081 new Quadrilateral(oedge+e[0], v[1], oedge+e[1], oface+qf, attr);
8082 new_boundary[j++] =
8083 new Quadrilateral(oface+qf, oedge+e[1], v[2], oedge+e[2], attr);
8084 new_boundary[j++] =
8085 new Quadrilateral(oedge+e[3], oface+qf, oedge+e[2], v[3], attr);
8086 }
8087 else
8088 {
8089 MFEM_ABORT("boundary Element is not a triangle or a quad!");
8090 }
8091 FreeElement(boundary[i]);
8092 }
8093 mfem::Swap(boundary, new_boundary);
8094
8095 static const double A = 0.0, B = 0.5, C = 1.0;
8096 static double tet_children[3*4*16] =
8097 {
8098 A,A,A, B,A,A, A,B,A, A,A,B,
8099 B,A,A, C,A,A, B,B,A, B,A,B,
8100 A,B,A, B,B,A, A,C,A, A,B,B,
8101 A,A,B, B,A,B, A,B,B, A,A,C,
8102 // edge coordinates:
8103 // 0 -> B,A,A 1 -> A,B,A 2 -> A,A,B
8104 // 3 -> B,B,A 4 -> B,A,B 5 -> A,B,B
8105 // rt = 0: {0,5,1,2}, {0,5,2,4}, {0,5,4,3}, {0,5,3,1}
8106 B,A,A, A,B,B, A,B,A, A,A,B,
8107 B,A,A, A,B,B, A,A,B, B,A,B,
8108 B,A,A, A,B,B, B,A,B, B,B,A,
8109 B,A,A, A,B,B, B,B,A, A,B,A,
8110 // rt = 1: {1,0,4,2}, {1,2,4,5}, {1,5,4,3}, {1,3,4,0}
8111 A,B,A, B,A,A, B,A,B, A,A,B,
8112 A,B,A, A,A,B, B,A,B, A,B,B,
8113 A,B,A, A,B,B, B,A,B, B,B,A,
8114 A,B,A, B,B,A, B,A,B, B,A,A,
8115 // rt = 2: {2,0,1,3}, {2,1,5,3}, {2,5,4,3}, {2,4,0,3}
8116 A,A,B, B,A,A, A,B,A, B,B,A,
8117 A,A,B, A,B,A, A,B,B, B,B,A,
8118 A,A,B, A,B,B, B,A,B, B,B,A,
8119 A,A,B, B,A,B, B,A,A, B,B,A
8120 };
8121 static double pri_children[3*6*8] =
8122 {
8123 A,A,A, B,A,A, A,B,A, A,A,B, B,A,B, A,B,B,
8124 B,B,A, A,B,A, B,A,A, B,B,B, A,B,B, B,A,B,
8125 B,A,A, C,A,A, B,B,A, B,A,B, C,A,B, B,B,B,
8126 A,B,A, B,B,A, A,C,A, A,B,B, B,B,B, A,C,B,
8127 A,A,B, B,A,B, A,B,B, A,A,C, B,A,C, A,B,C,
8128 B,B,B, A,B,B, B,A,B, B,B,C, A,B,C, B,A,C,
8129 B,A,B, C,A,B, B,B,B, B,A,C, C,A,C, B,B,C,
8130 A,B,B, B,B,B, A,C,B, A,B,C, B,B,C, A,C,C
8131 };
8132 static double hex_children[3*8*8] =
8133 {
8134 A,A,A, B,A,A, B,B,A, A,B,A, A,A,B, B,A,B, B,B,B, A,B,B,
8135 B,A,A, C,A,A, C,B,A, B,B,A, B,A,B, C,A,B, C,B,B, B,B,B,
8136 B,B,A, C,B,A, C,C,A, B,C,A, B,B,B, C,B,B, C,C,B, B,C,B,
8137 A,B,A, B,B,A, B,C,A, A,C,A, A,B,B, B,B,B, B,C,B, A,C,B,
8138 A,A,B, B,A,B, B,B,B, A,B,B, A,A,C, B,A,C, B,B,C, A,B,C,
8139 B,A,B, C,A,B, C,B,B, B,B,B, B,A,C, C,A,C, C,B,C, B,B,C,
8140 B,B,B, C,B,B, C,C,B, B,C,B, B,B,C, C,B,C, C,C,C, B,C,C,
8141 A,B,B, B,B,B, B,C,B, A,C,B, A,B,C, B,B,C, B,C,C, A,C,C
8142 };
8143
8144 CoarseFineTr.point_matrices[Geometry::TETRAHEDRON]
8145 .UseExternalData(tet_children, 3, 4, 16);
8146 CoarseFineTr.point_matrices[Geometry::PRISM]
8147 .UseExternalData(pri_children, 3, 6, 8);
8148 CoarseFineTr.point_matrices[Geometry::CUBE]
8149 .UseExternalData(hex_children, 3, 8, 8);
8150
8151 for (int i = 0; i < elements.Size(); i++)
8152 {
8153 // tetrahedron elements are handled above:
8154 if (elements[i]->GetType() == Element::TETRAHEDRON) { continue; }
8155
8156 Embedding &emb = CoarseFineTr.embeddings[i];
8157 emb.parent = i / 8;
8158 emb.matrix = i % 8;
8159 }
8160
8161 NumOfVertices = vertices.Size();
8162 NumOfElements = 8 * NumOfElements;
8163 NumOfBdrElements = 4 * NumOfBdrElements;
8164
8165 GetElementToFaceTable();
8166 GenerateFaces();
8167
8168 #ifdef MFEM_DEBUG
8169 CheckBdrElementOrientation(false);
8170 #endif
8171
8172 NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
8173
8174 last_operation = Mesh::REFINE;
8175 sequence++;
8176
8177 if (update_nodes) { UpdateNodes(); }
8178 }
8179
LocalRefinement(const Array<int> & marked_el,int type)8180 void Mesh::LocalRefinement(const Array<int> &marked_el, int type)
8181 {
8182 int i, j, ind, nedges;
8183 Array<int> v;
8184
8185 ResetLazyData();
8186
8187 if (ncmesh)
8188 {
8189 MFEM_ABORT("Local and nonconforming refinements cannot be mixed.");
8190 }
8191
8192 InitRefinementTransforms();
8193
8194 if (Dim == 1) // --------------------------------------------------------
8195 {
8196 int cne = NumOfElements, cnv = NumOfVertices;
8197 NumOfVertices += marked_el.Size();
8198 NumOfElements += marked_el.Size();
8199 vertices.SetSize(NumOfVertices);
8200 elements.SetSize(NumOfElements);
8201 CoarseFineTr.embeddings.SetSize(NumOfElements);
8202
8203 for (j = 0; j < marked_el.Size(); j++)
8204 {
8205 i = marked_el[j];
8206 Segment *c_seg = (Segment *)elements[i];
8207 int *vert = c_seg->GetVertices(), attr = c_seg->GetAttribute();
8208 int new_v = cnv + j, new_e = cne + j;
8209 AverageVertices(vert, 2, new_v);
8210 elements[new_e] = new Segment(new_v, vert[1], attr);
8211 vert[1] = new_v;
8212
8213 CoarseFineTr.embeddings[i] = Embedding(i, 1);
8214 CoarseFineTr.embeddings[new_e] = Embedding(i, 2);
8215 }
8216
8217 static double seg_children[3*2] = { 0.0,1.0, 0.0,0.5, 0.5,1.0 };
8218 CoarseFineTr.point_matrices[Geometry::SEGMENT].
8219 UseExternalData(seg_children, 1, 2, 3);
8220
8221 GenerateFaces();
8222
8223 } // end of 'if (Dim == 1)'
8224 else if (Dim == 2) // ---------------------------------------------------
8225 {
8226 // 1. Get table of vertex to vertex connections.
8227 DSTable v_to_v(NumOfVertices);
8228 GetVertexToVertexTable(v_to_v);
8229
8230 // 2. Get edge to element connections in arrays edge1 and edge2
8231 nedges = v_to_v.NumberOfEntries();
8232 int *edge1 = new int[nedges];
8233 int *edge2 = new int[nedges];
8234 int *middle = new int[nedges];
8235
8236 for (i = 0; i < nedges; i++)
8237 {
8238 edge1[i] = edge2[i] = middle[i] = -1;
8239 }
8240
8241 for (i = 0; i < NumOfElements; i++)
8242 {
8243 elements[i]->GetVertices(v);
8244 for (j = 1; j < v.Size(); j++)
8245 {
8246 ind = v_to_v(v[j-1], v[j]);
8247 (edge1[ind] == -1) ? (edge1[ind] = i) : (edge2[ind] = i);
8248 }
8249 ind = v_to_v(v[0], v[v.Size()-1]);
8250 (edge1[ind] == -1) ? (edge1[ind] = i) : (edge2[ind] = i);
8251 }
8252
8253 // 3. Do the red refinement.
8254 for (i = 0; i < marked_el.Size(); i++)
8255 {
8256 RedRefinement(marked_el[i], v_to_v, edge1, edge2, middle);
8257 }
8258
8259 // 4. Do the green refinement (to get conforming mesh).
8260 int need_refinement;
8261 do
8262 {
8263 need_refinement = 0;
8264 for (i = 0; i < nedges; i++)
8265 {
8266 if (middle[i] != -1 && edge1[i] != -1)
8267 {
8268 need_refinement = 1;
8269 GreenRefinement(edge1[i], v_to_v, edge1, edge2, middle);
8270 }
8271 }
8272 }
8273 while (need_refinement == 1);
8274
8275 // 5. Update the boundary elements.
8276 int v1[2], v2[2], bisect, temp;
8277 temp = NumOfBdrElements;
8278 for (i = 0; i < temp; i++)
8279 {
8280 boundary[i]->GetVertices(v);
8281 bisect = v_to_v(v[0], v[1]);
8282 if (middle[bisect] != -1) // the element was refined (needs updating)
8283 {
8284 if (boundary[i]->GetType() == Element::SEGMENT)
8285 {
8286 v1[0] = v[0]; v1[1] = middle[bisect];
8287 v2[0] = middle[bisect]; v2[1] = v[1];
8288
8289 boundary[i]->SetVertices(v1);
8290 boundary.Append(new Segment(v2, boundary[i]->GetAttribute()));
8291 }
8292 else
8293 mfem_error("Only bisection of segment is implemented"
8294 " for bdr elem.");
8295 }
8296 }
8297 NumOfBdrElements = boundary.Size();
8298
8299 // 6. Free the allocated memory.
8300 delete [] edge1;
8301 delete [] edge2;
8302 delete [] middle;
8303
8304 if (el_to_edge != NULL)
8305 {
8306 NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
8307 GenerateFaces();
8308 }
8309
8310 }
8311 else if (Dim == 3) // ---------------------------------------------------
8312 {
8313 // 1. Hash table of vertex to vertex connections corresponding to refined
8314 // edges.
8315 HashTable<Hashed2> v_to_v;
8316
8317 MFEM_VERIFY(GetNE() == 0 ||
8318 ((Tetrahedron*)elements[0])->GetRefinementFlag() != 0,
8319 "tetrahedral mesh is not marked for refinement:"
8320 " call Finalize(true)");
8321
8322 // 2. Do the red refinement.
8323 int ii;
8324 switch (type)
8325 {
8326 case 1:
8327 for (i = 0; i < marked_el.Size(); i++)
8328 {
8329 Bisection(marked_el[i], v_to_v);
8330 }
8331 break;
8332 case 2:
8333 for (i = 0; i < marked_el.Size(); i++)
8334 {
8335 Bisection(marked_el[i], v_to_v);
8336
8337 Bisection(NumOfElements - 1, v_to_v);
8338 Bisection(marked_el[i], v_to_v);
8339 }
8340 break;
8341 case 3:
8342 for (i = 0; i < marked_el.Size(); i++)
8343 {
8344 Bisection(marked_el[i], v_to_v);
8345
8346 ii = NumOfElements - 1;
8347 Bisection(ii, v_to_v);
8348 Bisection(NumOfElements - 1, v_to_v);
8349 Bisection(ii, v_to_v);
8350
8351 Bisection(marked_el[i], v_to_v);
8352 Bisection(NumOfElements-1, v_to_v);
8353 Bisection(marked_el[i], v_to_v);
8354 }
8355 break;
8356 }
8357
8358 // 3. Do the green refinement (to get conforming mesh).
8359 int need_refinement;
8360 // int need_refinement, onoe, max_gen = 0;
8361 do
8362 {
8363 // int redges[2], type, flag;
8364 need_refinement = 0;
8365 // onoe = NumOfElements;
8366 // for (i = 0; i < onoe; i++)
8367 for (i = 0; i < NumOfElements; i++)
8368 {
8369 // ((Tetrahedron *)elements[i])->
8370 // ParseRefinementFlag(redges, type, flag);
8371 // if (flag > max_gen) max_gen = flag;
8372 if (elements[i]->NeedRefinement(v_to_v))
8373 {
8374 need_refinement = 1;
8375 Bisection(i, v_to_v);
8376 }
8377 }
8378 }
8379 while (need_refinement == 1);
8380
8381 // mfem::out << "Maximum generation: " << max_gen << endl;
8382
8383 // 4. Update the boundary elements.
8384 do
8385 {
8386 need_refinement = 0;
8387 for (i = 0; i < NumOfBdrElements; i++)
8388 if (boundary[i]->NeedRefinement(v_to_v))
8389 {
8390 need_refinement = 1;
8391 BdrBisection(i, v_to_v);
8392 }
8393 }
8394 while (need_refinement == 1);
8395
8396 NumOfVertices = vertices.Size();
8397 NumOfBdrElements = boundary.Size();
8398
8399 // 5. Update element-to-edge and element-to-face relations.
8400 if (el_to_edge != NULL)
8401 {
8402 NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
8403 }
8404 if (el_to_face != NULL)
8405 {
8406 GetElementToFaceTable();
8407 GenerateFaces();
8408 }
8409
8410 } // end 'if (Dim == 3)'
8411
8412 last_operation = Mesh::REFINE;
8413 sequence++;
8414
8415 UpdateNodes();
8416
8417 #ifdef MFEM_DEBUG
8418 CheckElementOrientation(false);
8419 #endif
8420 }
8421
NonconformingRefinement(const Array<Refinement> & refinements,int nc_limit)8422 void Mesh::NonconformingRefinement(const Array<Refinement> &refinements,
8423 int nc_limit)
8424 {
8425 MFEM_VERIFY(!NURBSext, "Nonconforming refinement of NURBS meshes is "
8426 "not supported. Project the NURBS to Nodes first.");
8427
8428 ResetLazyData();
8429
8430 if (!ncmesh)
8431 {
8432 // start tracking refinement hierarchy
8433 ncmesh = new NCMesh(this);
8434 }
8435
8436 if (!refinements.Size())
8437 {
8438 last_operation = Mesh::NONE;
8439 return;
8440 }
8441
8442 // do the refinements
8443 ncmesh->MarkCoarseLevel();
8444 ncmesh->Refine(refinements);
8445
8446 if (nc_limit > 0)
8447 {
8448 ncmesh->LimitNCLevel(nc_limit);
8449 }
8450
8451 // create a second mesh containing the finest elements from 'ncmesh'
8452 Mesh* mesh2 = new Mesh(*ncmesh);
8453 ncmesh->OnMeshUpdated(mesh2);
8454
8455 // now swap the meshes, the second mesh will become the old coarse mesh
8456 // and this mesh will be the new fine mesh
8457 Swap(*mesh2, false);
8458 delete mesh2;
8459
8460 GenerateNCFaceInfo();
8461
8462 last_operation = Mesh::REFINE;
8463 sequence++;
8464
8465 if (Nodes) // update/interpolate curved mesh
8466 {
8467 Nodes->FESpace()->Update();
8468 Nodes->Update();
8469 }
8470 }
8471
AggregateError(const Array<double> & elem_error,const int * fine,int nfine,int op)8472 double Mesh::AggregateError(const Array<double> &elem_error,
8473 const int *fine, int nfine, int op)
8474 {
8475 double error = 0.0;
8476 for (int i = 0; i < nfine; i++)
8477 {
8478 MFEM_VERIFY(fine[i] < elem_error.Size(), "");
8479
8480 double err_fine = elem_error[fine[i]];
8481 switch (op)
8482 {
8483 case 0: error = std::min(error, err_fine); break;
8484 case 1: error += err_fine; break;
8485 case 2: error = std::max(error, err_fine); break;
8486 }
8487 }
8488 return error;
8489 }
8490
NonconformingDerefinement(Array<double> & elem_error,double threshold,int nc_limit,int op)8491 bool Mesh::NonconformingDerefinement(Array<double> &elem_error,
8492 double threshold, int nc_limit, int op)
8493 {
8494 MFEM_VERIFY(ncmesh, "Only supported for non-conforming meshes.");
8495 MFEM_VERIFY(!NURBSext, "Derefinement of NURBS meshes is not supported. "
8496 "Project the NURBS to Nodes first.");
8497
8498 ResetLazyData();
8499
8500 const Table &dt = ncmesh->GetDerefinementTable();
8501
8502 Array<int> level_ok;
8503 if (nc_limit > 0)
8504 {
8505 ncmesh->CheckDerefinementNCLevel(dt, level_ok, nc_limit);
8506 }
8507
8508 Array<int> derefs;
8509 for (int i = 0; i < dt.Size(); i++)
8510 {
8511 if (nc_limit > 0 && !level_ok[i]) { continue; }
8512
8513 double error =
8514 AggregateError(elem_error, dt.GetRow(i), dt.RowSize(i), op);
8515
8516 if (error < threshold) { derefs.Append(i); }
8517 }
8518
8519 if (!derefs.Size()) { return false; }
8520
8521 ncmesh->Derefine(derefs);
8522
8523 Mesh* mesh2 = new Mesh(*ncmesh);
8524 ncmesh->OnMeshUpdated(mesh2);
8525
8526 Swap(*mesh2, false);
8527 delete mesh2;
8528
8529 GenerateNCFaceInfo();
8530
8531 last_operation = Mesh::DEREFINE;
8532 sequence++;
8533
8534 UpdateNodes();
8535
8536 return true;
8537 }
8538
DerefineByError(Array<double> & elem_error,double threshold,int nc_limit,int op)8539 bool Mesh::DerefineByError(Array<double> &elem_error, double threshold,
8540 int nc_limit, int op)
8541 {
8542 // NOTE: the error array is not const because it will be expanded in parallel
8543 // by ghost element errors
8544 if (Nonconforming())
8545 {
8546 return NonconformingDerefinement(elem_error, threshold, nc_limit, op);
8547 }
8548 else
8549 {
8550 MFEM_ABORT("Derefinement is currently supported for non-conforming "
8551 "meshes only.");
8552 return false;
8553 }
8554 }
8555
DerefineByError(const Vector & elem_error,double threshold,int nc_limit,int op)8556 bool Mesh::DerefineByError(const Vector &elem_error, double threshold,
8557 int nc_limit, int op)
8558 {
8559 Array<double> tmp(elem_error.Size());
8560 for (int i = 0; i < tmp.Size(); i++)
8561 {
8562 tmp[i] = elem_error(i);
8563 }
8564 return DerefineByError(tmp, threshold, nc_limit, op);
8565 }
8566
8567
InitFromNCMesh(const NCMesh & ncmesh)8568 void Mesh::InitFromNCMesh(const NCMesh &ncmesh)
8569 {
8570 Dim = ncmesh.Dimension();
8571 spaceDim = ncmesh.SpaceDimension();
8572
8573 DeleteTables();
8574
8575 ncmesh.GetMeshComponents(*this);
8576
8577 NumOfVertices = vertices.Size();
8578 NumOfElements = elements.Size();
8579 NumOfBdrElements = boundary.Size();
8580
8581 SetMeshGen(); // set the mesh type: 'meshgen', ...
8582
8583 NumOfEdges = NumOfFaces = 0;
8584 nbInteriorFaces = nbBoundaryFaces = -1;
8585
8586 if (Dim > 1)
8587 {
8588 el_to_edge = new Table;
8589 NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
8590 }
8591 if (Dim > 2)
8592 {
8593 GetElementToFaceTable();
8594 }
8595 GenerateFaces();
8596 #ifdef MFEM_DEBUG
8597 CheckBdrElementOrientation(false);
8598 #endif
8599
8600 // NOTE: ncmesh->OnMeshUpdated() and GenerateNCFaceInfo() should be called
8601 // outside after this method.
8602 }
8603
Mesh(const NCMesh & ncmesh)8604 Mesh::Mesh(const NCMesh &ncmesh)
8605 {
8606 Init();
8607 InitTables();
8608 InitFromNCMesh(ncmesh);
8609 SetAttributes();
8610 }
8611
Swap(Mesh & other,bool non_geometry)8612 void Mesh::Swap(Mesh& other, bool non_geometry)
8613 {
8614 mfem::Swap(Dim, other.Dim);
8615 mfem::Swap(spaceDim, other.spaceDim);
8616
8617 mfem::Swap(NumOfVertices, other.NumOfVertices);
8618 mfem::Swap(NumOfElements, other.NumOfElements);
8619 mfem::Swap(NumOfBdrElements, other.NumOfBdrElements);
8620 mfem::Swap(NumOfEdges, other.NumOfEdges);
8621 mfem::Swap(NumOfFaces, other.NumOfFaces);
8622
8623 mfem::Swap(meshgen, other.meshgen);
8624 mfem::Swap(mesh_geoms, other.mesh_geoms);
8625
8626 mfem::Swap(elements, other.elements);
8627 mfem::Swap(vertices, other.vertices);
8628 mfem::Swap(boundary, other.boundary);
8629 mfem::Swap(faces, other.faces);
8630 mfem::Swap(faces_info, other.faces_info);
8631 mfem::Swap(nc_faces_info, other.nc_faces_info);
8632
8633 mfem::Swap(el_to_edge, other.el_to_edge);
8634 mfem::Swap(el_to_face, other.el_to_face);
8635 mfem::Swap(el_to_el, other.el_to_el);
8636 mfem::Swap(be_to_edge, other.be_to_edge);
8637 mfem::Swap(bel_to_edge, other.bel_to_edge);
8638 mfem::Swap(be_to_face, other.be_to_face);
8639 mfem::Swap(face_edge, other.face_edge);
8640 mfem::Swap(edge_vertex, other.edge_vertex);
8641
8642 mfem::Swap(attributes, other.attributes);
8643 mfem::Swap(bdr_attributes, other.bdr_attributes);
8644
8645 mfem::Swap(geom_factors, other.geom_factors);
8646
8647 #ifdef MFEM_USE_MEMALLOC
8648 TetMemory.Swap(other.TetMemory);
8649 #endif
8650
8651 if (non_geometry)
8652 {
8653 mfem::Swap(NURBSext, other.NURBSext);
8654 mfem::Swap(ncmesh, other.ncmesh);
8655
8656 mfem::Swap(Nodes, other.Nodes);
8657 if (Nodes) { Nodes->FESpace()->UpdateMeshPointer(this); }
8658 if (other.Nodes) { other.Nodes->FESpace()->UpdateMeshPointer(&other); }
8659 mfem::Swap(own_nodes, other.own_nodes);
8660
8661 mfem::Swap(CoarseFineTr, other.CoarseFineTr);
8662
8663 mfem::Swap(sequence, other.sequence);
8664 mfem::Swap(last_operation, other.last_operation);
8665 }
8666 }
8667
GetElementData(const Array<Element * > & elem_array,int geom,Array<int> & elem_vtx,Array<int> & attr) const8668 void Mesh::GetElementData(const Array<Element*> &elem_array, int geom,
8669 Array<int> &elem_vtx, Array<int> &attr) const
8670 {
8671 // protected method
8672 const int nv = Geometry::NumVerts[geom];
8673 int num_elems = 0;
8674 for (int i = 0; i < elem_array.Size(); i++)
8675 {
8676 if (elem_array[i]->GetGeometryType() == geom)
8677 {
8678 num_elems++;
8679 }
8680 }
8681 elem_vtx.SetSize(nv*num_elems);
8682 attr.SetSize(num_elems);
8683 elem_vtx.SetSize(0);
8684 attr.SetSize(0);
8685 for (int i = 0; i < elem_array.Size(); i++)
8686 {
8687 Element *el = elem_array[i];
8688 if (el->GetGeometryType() != geom) { continue; }
8689
8690 Array<int> loc_vtx(el->GetVertices(), nv);
8691 elem_vtx.Append(loc_vtx);
8692 attr.Append(el->GetAttribute());
8693 }
8694 }
8695
AllElements(Array<int> & list,int nelem)8696 static Array<int>& AllElements(Array<int> &list, int nelem)
8697 {
8698 list.SetSize(nelem);
8699 for (int i = 0; i < nelem; i++) { list[i] = i; }
8700 return list;
8701 }
8702
UniformRefinement(int ref_algo)8703 void Mesh::UniformRefinement(int ref_algo)
8704 {
8705 Array<int> list;
8706
8707 if (NURBSext)
8708 {
8709 NURBSUniformRefinement();
8710 }
8711 else if (ncmesh)
8712 {
8713 GeneralRefinement(AllElements(list, GetNE()));
8714 }
8715 else if (ref_algo == 1 && meshgen == 1 && Dim == 3)
8716 {
8717 // algorithm "B" for an all-tet mesh
8718 LocalRefinement(AllElements(list, GetNE()));
8719 }
8720 else
8721 {
8722 switch (Dim)
8723 {
8724 case 1: LocalRefinement(AllElements(list, GetNE())); break;
8725 case 2: UniformRefinement2D(); break;
8726 case 3: UniformRefinement3D(); break;
8727 default: MFEM_ABORT("internal error");
8728 }
8729 }
8730 }
8731
GeneralRefinement(const Array<Refinement> & refinements,int nonconforming,int nc_limit)8732 void Mesh::GeneralRefinement(const Array<Refinement> &refinements,
8733 int nonconforming, int nc_limit)
8734 {
8735 if (ncmesh)
8736 {
8737 nonconforming = 1;
8738 }
8739 else if (Dim == 1 || (Dim == 3 && (meshgen & 1)))
8740 {
8741 nonconforming = 0;
8742 }
8743 else if (nonconforming < 0)
8744 {
8745 // determine if nonconforming refinement is suitable
8746 if ((meshgen & 2) || (meshgen & 4))
8747 {
8748 nonconforming = 1; // tensor product elements and wedges
8749 }
8750 else
8751 {
8752 nonconforming = 0; // simplices
8753 }
8754 }
8755
8756 if (nonconforming)
8757 {
8758 // non-conforming refinement (hanging nodes)
8759 NonconformingRefinement(refinements, nc_limit);
8760 }
8761 else
8762 {
8763 Array<int> el_to_refine(refinements.Size());
8764 for (int i = 0; i < refinements.Size(); i++)
8765 {
8766 el_to_refine[i] = refinements[i].index;
8767 }
8768
8769 // infer 'type' of local refinement from first element's 'ref_type'
8770 int type, rt = (refinements.Size() ? refinements[0].ref_type : 7);
8771 if (rt == 1 || rt == 2 || rt == 4)
8772 {
8773 type = 1; // bisection
8774 }
8775 else if (rt == 3 || rt == 5 || rt == 6)
8776 {
8777 type = 2; // quadrisection
8778 }
8779 else
8780 {
8781 type = 3; // octasection
8782 }
8783
8784 // red-green refinement and bisection, no hanging nodes
8785 LocalRefinement(el_to_refine, type);
8786 }
8787 }
8788
GeneralRefinement(const Array<int> & el_to_refine,int nonconforming,int nc_limit)8789 void Mesh::GeneralRefinement(const Array<int> &el_to_refine, int nonconforming,
8790 int nc_limit)
8791 {
8792 Array<Refinement> refinements(el_to_refine.Size());
8793 for (int i = 0; i < el_to_refine.Size(); i++)
8794 {
8795 refinements[i] = Refinement(el_to_refine[i]);
8796 }
8797 GeneralRefinement(refinements, nonconforming, nc_limit);
8798 }
8799
EnsureNCMesh(bool simplices_nonconforming)8800 void Mesh::EnsureNCMesh(bool simplices_nonconforming)
8801 {
8802 MFEM_VERIFY(!NURBSext, "Cannot convert a NURBS mesh to an NC mesh. "
8803 "Please project the NURBS to Nodes first, with SetCurvature().");
8804
8805 #ifdef MFEM_USE_MPI
8806 MFEM_VERIFY(ncmesh != NULL || dynamic_cast<const ParMesh*>(this) == NULL,
8807 "Sorry, converting a conforming ParMesh to an NC mesh is "
8808 "not possible.");
8809 #endif
8810
8811 if (!ncmesh)
8812 {
8813 if ((meshgen & 0x2) /* quads/hexes */ ||
8814 (meshgen & 0x4) /* wedges */ ||
8815 (simplices_nonconforming && (meshgen & 0x1)) /* simplices */)
8816 {
8817 ncmesh = new NCMesh(this);
8818 ncmesh->OnMeshUpdated(this);
8819 GenerateNCFaceInfo();
8820 }
8821 }
8822 }
8823
RandomRefinement(double prob,bool aniso,int nonconforming,int nc_limit)8824 void Mesh::RandomRefinement(double prob, bool aniso, int nonconforming,
8825 int nc_limit)
8826 {
8827 Array<Refinement> refs;
8828 for (int i = 0; i < GetNE(); i++)
8829 {
8830 if ((double) rand() / RAND_MAX < prob)
8831 {
8832 int type = 7;
8833 if (aniso)
8834 {
8835 type = (Dim == 3) ? (rand() % 7 + 1) : (rand() % 3 + 1);
8836 }
8837 refs.Append(Refinement(i, type));
8838 }
8839 }
8840 GeneralRefinement(refs, nonconforming, nc_limit);
8841 }
8842
RefineAtVertex(const Vertex & vert,double eps,int nonconforming)8843 void Mesh::RefineAtVertex(const Vertex& vert, double eps, int nonconforming)
8844 {
8845 Array<int> v;
8846 Array<Refinement> refs;
8847 for (int i = 0; i < GetNE(); i++)
8848 {
8849 GetElementVertices(i, v);
8850 bool refine = false;
8851 for (int j = 0; j < v.Size(); j++)
8852 {
8853 double dist = 0.0;
8854 for (int l = 0; l < spaceDim; l++)
8855 {
8856 double d = vert(l) - vertices[v[j]](l);
8857 dist += d*d;
8858 }
8859 if (dist <= eps*eps) { refine = true; break; }
8860 }
8861 if (refine)
8862 {
8863 refs.Append(Refinement(i));
8864 }
8865 }
8866 GeneralRefinement(refs, nonconforming);
8867 }
8868
RefineByError(const Array<double> & elem_error,double threshold,int nonconforming,int nc_limit)8869 bool Mesh::RefineByError(const Array<double> &elem_error, double threshold,
8870 int nonconforming, int nc_limit)
8871 {
8872 MFEM_VERIFY(elem_error.Size() == GetNE(), "");
8873 Array<Refinement> refs;
8874 for (int i = 0; i < GetNE(); i++)
8875 {
8876 if (elem_error[i] > threshold)
8877 {
8878 refs.Append(Refinement(i));
8879 }
8880 }
8881 if (ReduceInt(refs.Size()))
8882 {
8883 GeneralRefinement(refs, nonconforming, nc_limit);
8884 return true;
8885 }
8886 return false;
8887 }
8888
RefineByError(const Vector & elem_error,double threshold,int nonconforming,int nc_limit)8889 bool Mesh::RefineByError(const Vector &elem_error, double threshold,
8890 int nonconforming, int nc_limit)
8891 {
8892 Array<double> tmp(const_cast<double*>(elem_error.GetData()),
8893 elem_error.Size());
8894 return RefineByError(tmp, threshold, nonconforming, nc_limit);
8895 }
8896
8897
Bisection(int i,const DSTable & v_to_v,int * edge1,int * edge2,int * middle)8898 void Mesh::Bisection(int i, const DSTable &v_to_v,
8899 int *edge1, int *edge2, int *middle)
8900 {
8901 int *vert;
8902 int v[2][4], v_new, bisect, t;
8903 Element *el = elements[i];
8904 Vertex V;
8905
8906 t = el->GetType();
8907 if (t == Element::TRIANGLE)
8908 {
8909 Triangle *tri = (Triangle *) el;
8910
8911 vert = tri->GetVertices();
8912
8913 // 1. Get the index for the new vertex in v_new.
8914 bisect = v_to_v(vert[0], vert[1]);
8915 MFEM_ASSERT(bisect >= 0, "");
8916
8917 if (middle[bisect] == -1)
8918 {
8919 v_new = NumOfVertices++;
8920 for (int d = 0; d < spaceDim; d++)
8921 {
8922 V(d) = 0.5 * (vertices[vert[0]](d) + vertices[vert[1]](d));
8923 }
8924 vertices.Append(V);
8925
8926 // Put the element that may need refinement (because of this
8927 // bisection) in edge1, or -1 if no more refinement is needed.
8928 if (edge1[bisect] == i)
8929 {
8930 edge1[bisect] = edge2[bisect];
8931 }
8932
8933 middle[bisect] = v_new;
8934 }
8935 else
8936 {
8937 v_new = middle[bisect];
8938
8939 // This edge will require no more refinement.
8940 edge1[bisect] = -1;
8941 }
8942
8943 // 2. Set the node indices for the new elements in v[0] and v[1] so that
8944 // the edge marked for refinement is between the first two nodes.
8945 v[0][0] = vert[2]; v[0][1] = vert[0]; v[0][2] = v_new;
8946 v[1][0] = vert[1]; v[1][1] = vert[2]; v[1][2] = v_new;
8947
8948 tri->SetVertices(v[0]); // changes vert[0..2] !!!
8949
8950 Triangle* tri_new = new Triangle(v[1], tri->GetAttribute());
8951 elements.Append(tri_new);
8952
8953 int tr = tri->GetTransform();
8954 tri_new->ResetTransform(tr);
8955
8956 // record the sequence of refinements
8957 tri->PushTransform(4);
8958 tri_new->PushTransform(5);
8959
8960 int coarse = FindCoarseElement(i);
8961 CoarseFineTr.embeddings[i].parent = coarse;
8962 CoarseFineTr.embeddings.Append(Embedding(coarse));
8963
8964 // 3. edge1 and edge2 may have to be changed for the second triangle.
8965 if (v[1][0] < v_to_v.NumberOfRows() && v[1][1] < v_to_v.NumberOfRows())
8966 {
8967 bisect = v_to_v(v[1][0], v[1][1]);
8968 MFEM_ASSERT(bisect >= 0, "");
8969
8970 if (edge1[bisect] == i)
8971 {
8972 edge1[bisect] = NumOfElements;
8973 }
8974 else if (edge2[bisect] == i)
8975 {
8976 edge2[bisect] = NumOfElements;
8977 }
8978 }
8979 NumOfElements++;
8980 }
8981 else
8982 {
8983 MFEM_ABORT("Bisection for now works only for triangles.");
8984 }
8985 }
8986
Bisection(int i,HashTable<Hashed2> & v_to_v)8987 void Mesh::Bisection(int i, HashTable<Hashed2> &v_to_v)
8988 {
8989 int *vert;
8990 int v[2][4], v_new, bisect, t;
8991 Element *el = elements[i];
8992 Vertex V;
8993
8994 t = el->GetType();
8995 if (t == Element::TETRAHEDRON)
8996 {
8997 int j, type, new_type, old_redges[2], new_redges[2][2], flag;
8998 Tetrahedron *tet = (Tetrahedron *) el;
8999
9000 MFEM_VERIFY(tet->GetRefinementFlag() != 0,
9001 "TETRAHEDRON element is not marked for refinement.");
9002
9003 vert = tet->GetVertices();
9004
9005 // 1. Get the index for the new vertex in v_new.
9006 bisect = v_to_v.FindId(vert[0], vert[1]);
9007 if (bisect == -1)
9008 {
9009 v_new = NumOfVertices + v_to_v.GetId(vert[0],vert[1]);
9010 for (j = 0; j < 3; j++)
9011 {
9012 V(j) = 0.5 * (vertices[vert[0]](j) + vertices[vert[1]](j));
9013 }
9014 vertices.Append(V);
9015 }
9016 else
9017 {
9018 v_new = NumOfVertices + bisect;
9019 }
9020
9021 // 2. Set the node indices for the new elements in v[2][4] so that
9022 // the edge marked for refinement is between the first two nodes.
9023 tet->ParseRefinementFlag(old_redges, type, flag);
9024
9025 v[0][3] = v_new;
9026 v[1][3] = v_new;
9027 new_redges[0][0] = 2;
9028 new_redges[0][1] = 1;
9029 new_redges[1][0] = 2;
9030 new_redges[1][1] = 1;
9031 int tr1 = -1, tr2 = -1;
9032 switch (old_redges[0])
9033 {
9034 case 2:
9035 v[0][0] = vert[0]; v[0][1] = vert[2]; v[0][2] = vert[3];
9036 if (type == Tetrahedron::TYPE_PF) { new_redges[0][1] = 4; }
9037 tr1 = 0;
9038 break;
9039 case 3:
9040 v[0][0] = vert[3]; v[0][1] = vert[0]; v[0][2] = vert[2];
9041 tr1 = 2;
9042 break;
9043 case 5:
9044 v[0][0] = vert[2]; v[0][1] = vert[3]; v[0][2] = vert[0];
9045 tr1 = 4;
9046 }
9047 switch (old_redges[1])
9048 {
9049 case 1:
9050 v[1][0] = vert[2]; v[1][1] = vert[1]; v[1][2] = vert[3];
9051 if (type == Tetrahedron::TYPE_PF) { new_redges[1][0] = 3; }
9052 tr2 = 1;
9053 break;
9054 case 4:
9055 v[1][0] = vert[1]; v[1][1] = vert[3]; v[1][2] = vert[2];
9056 tr2 = 3;
9057 break;
9058 case 5:
9059 v[1][0] = vert[3]; v[1][1] = vert[2]; v[1][2] = vert[1];
9060 tr2 = 5;
9061 }
9062
9063 int attr = tet->GetAttribute();
9064 tet->SetVertices(v[0]);
9065
9066 #ifdef MFEM_USE_MEMALLOC
9067 Tetrahedron *tet2 = TetMemory.Alloc();
9068 tet2->SetVertices(v[1]);
9069 tet2->SetAttribute(attr);
9070 #else
9071 Tetrahedron *tet2 = new Tetrahedron(v[1], attr);
9072 #endif
9073 tet2->ResetTransform(tet->GetTransform());
9074 elements.Append(tet2);
9075
9076 // record the sequence of refinements
9077 tet->PushTransform(tr1);
9078 tet2->PushTransform(tr2);
9079
9080 int coarse = FindCoarseElement(i);
9081 CoarseFineTr.embeddings[i].parent = coarse;
9082 CoarseFineTr.embeddings.Append(Embedding(coarse));
9083
9084 // 3. Set the bisection flag
9085 switch (type)
9086 {
9087 case Tetrahedron::TYPE_PU:
9088 new_type = Tetrahedron::TYPE_PF; break;
9089 case Tetrahedron::TYPE_PF:
9090 new_type = Tetrahedron::TYPE_A; break;
9091 default:
9092 new_type = Tetrahedron::TYPE_PU;
9093 }
9094
9095 tet->CreateRefinementFlag(new_redges[0], new_type, flag+1);
9096 tet2->CreateRefinementFlag(new_redges[1], new_type, flag+1);
9097
9098 NumOfElements++;
9099 }
9100 else
9101 {
9102 MFEM_ABORT("Bisection with HashTable for now works only for tetrahedra.");
9103 }
9104 }
9105
BdrBisection(int i,const HashTable<Hashed2> & v_to_v)9106 void Mesh::BdrBisection(int i, const HashTable<Hashed2> &v_to_v)
9107 {
9108 int *vert;
9109 int v[2][3], v_new, bisect, t;
9110 Element *bdr_el = boundary[i];
9111
9112 t = bdr_el->GetType();
9113 if (t == Element::TRIANGLE)
9114 {
9115 Triangle *tri = (Triangle *) bdr_el;
9116
9117 vert = tri->GetVertices();
9118
9119 // 1. Get the index for the new vertex in v_new.
9120 bisect = v_to_v.FindId(vert[0], vert[1]);
9121 MFEM_ASSERT(bisect >= 0, "");
9122 v_new = NumOfVertices + bisect;
9123 MFEM_ASSERT(v_new != -1, "");
9124
9125 // 2. Set the node indices for the new elements in v[0] and v[1] so that
9126 // the edge marked for refinement is between the first two nodes.
9127 v[0][0] = vert[2]; v[0][1] = vert[0]; v[0][2] = v_new;
9128 v[1][0] = vert[1]; v[1][1] = vert[2]; v[1][2] = v_new;
9129
9130 tri->SetVertices(v[0]);
9131
9132 boundary.Append(new Triangle(v[1], tri->GetAttribute()));
9133
9134 NumOfBdrElements++;
9135 }
9136 else
9137 {
9138 MFEM_ABORT("Bisection of boundary elements with HashTable works only for"
9139 " triangles!");
9140 }
9141 }
9142
UniformRefinement(int i,const DSTable & v_to_v,int * edge1,int * edge2,int * middle)9143 void Mesh::UniformRefinement(int i, const DSTable &v_to_v,
9144 int *edge1, int *edge2, int *middle)
9145 {
9146 Array<int> v;
9147 int j, v1[3], v2[3], v3[3], v4[3], v_new[3], bisect[3];
9148 Vertex V;
9149
9150 if (elements[i]->GetType() == Element::TRIANGLE)
9151 {
9152 Triangle *tri0 = (Triangle*) elements[i];
9153 tri0->GetVertices(v);
9154
9155 // 1. Get the indeces for the new vertices in array v_new
9156 bisect[0] = v_to_v(v[0],v[1]);
9157 bisect[1] = v_to_v(v[1],v[2]);
9158 bisect[2] = v_to_v(v[0],v[2]);
9159 MFEM_ASSERT(bisect[0] >= 0 && bisect[1] >= 0 && bisect[2] >= 0, "");
9160
9161 for (j = 0; j < 3; j++) // for the 3 edges fix v_new
9162 {
9163 if (middle[bisect[j]] == -1)
9164 {
9165 v_new[j] = NumOfVertices++;
9166 for (int d = 0; d < spaceDim; d++)
9167 {
9168 V(d) = (vertices[v[j]](d) + vertices[v[(j+1)%3]](d))/2.;
9169 }
9170 vertices.Append(V);
9171
9172 // Put the element that may need refinement (because of this
9173 // bisection) in edge1, or -1 if no more refinement is needed.
9174 if (edge1[bisect[j]] == i)
9175 {
9176 edge1[bisect[j]] = edge2[bisect[j]];
9177 }
9178
9179 middle[bisect[j]] = v_new[j];
9180 }
9181 else
9182 {
9183 v_new[j] = middle[bisect[j]];
9184
9185 // This edge will require no more refinement.
9186 edge1[bisect[j]] = -1;
9187 }
9188 }
9189
9190 // 2. Set the node indeces for the new elements in v1, v2, v3 & v4 so that
9191 // the edges marked for refinement be between the first two nodes.
9192 v1[0] = v[0]; v1[1] = v_new[0]; v1[2] = v_new[2];
9193 v2[0] = v_new[0]; v2[1] = v[1]; v2[2] = v_new[1];
9194 v3[0] = v_new[2]; v3[1] = v_new[1]; v3[2] = v[2];
9195 v4[0] = v_new[1]; v4[1] = v_new[2]; v4[2] = v_new[0];
9196
9197 Triangle* tri1 = new Triangle(v1, tri0->GetAttribute());
9198 Triangle* tri2 = new Triangle(v2, tri0->GetAttribute());
9199 Triangle* tri3 = new Triangle(v3, tri0->GetAttribute());
9200
9201 elements.Append(tri1);
9202 elements.Append(tri2);
9203 elements.Append(tri3);
9204
9205 tri0->SetVertices(v4);
9206
9207 // record the sequence of refinements
9208 unsigned code = tri0->GetTransform();
9209 tri1->ResetTransform(code);
9210 tri2->ResetTransform(code);
9211 tri3->ResetTransform(code);
9212
9213 tri0->PushTransform(3);
9214 tri1->PushTransform(0);
9215 tri2->PushTransform(1);
9216 tri3->PushTransform(2);
9217
9218 // set parent indices
9219 int coarse = FindCoarseElement(i);
9220 CoarseFineTr.embeddings[i] = Embedding(coarse);
9221 CoarseFineTr.embeddings.Append(Embedding(coarse));
9222 CoarseFineTr.embeddings.Append(Embedding(coarse));
9223 CoarseFineTr.embeddings.Append(Embedding(coarse));
9224
9225 NumOfElements += 3;
9226 }
9227 else
9228 {
9229 MFEM_ABORT("Uniform refinement for now works only for triangles.");
9230 }
9231 }
9232
InitRefinementTransforms()9233 void Mesh::InitRefinementTransforms()
9234 {
9235 // initialize CoarseFineTr
9236 CoarseFineTr.Clear();
9237 CoarseFineTr.embeddings.SetSize(NumOfElements);
9238 for (int i = 0; i < NumOfElements; i++)
9239 {
9240 elements[i]->ResetTransform(0);
9241 CoarseFineTr.embeddings[i] = Embedding(i);
9242 }
9243 }
9244
FindCoarseElement(int i)9245 int Mesh::FindCoarseElement(int i)
9246 {
9247 int coarse;
9248 while ((coarse = CoarseFineTr.embeddings[i].parent) != i)
9249 {
9250 i = coarse;
9251 }
9252 return coarse;
9253 }
9254
GetRefinementTransforms()9255 const CoarseFineTransformations& Mesh::GetRefinementTransforms()
9256 {
9257 MFEM_VERIFY(GetLastOperation() == Mesh::REFINE, "");
9258
9259 if (ncmesh)
9260 {
9261 return ncmesh->GetRefinementTransforms();
9262 }
9263
9264 Mesh::GeometryList elem_geoms(*this);
9265 for (int i = 0; i < elem_geoms.Size(); i++)
9266 {
9267 const Geometry::Type geom = elem_geoms[i];
9268 if (CoarseFineTr.point_matrices[geom].SizeK()) { continue; }
9269
9270 if (geom == Geometry::TRIANGLE ||
9271 geom == Geometry::TETRAHEDRON)
9272 {
9273 std::map<unsigned, int> mat_no;
9274 mat_no[0] = 1; // identity
9275
9276 // assign matrix indices to element transformations
9277 for (int i = 0; i < elements.Size(); i++)
9278 {
9279 int index = 0;
9280 unsigned code = elements[i]->GetTransform();
9281 if (code)
9282 {
9283 int &matrix = mat_no[code];
9284 if (!matrix) { matrix = mat_no.size(); }
9285 index = matrix-1;
9286 }
9287 CoarseFineTr.embeddings[i].matrix = index;
9288 }
9289
9290 DenseTensor &pmats = CoarseFineTr.point_matrices[geom];
9291 pmats.SetSize(Dim, Dim+1, mat_no.size());
9292
9293 // calculate the point matrices used
9294 std::map<unsigned, int>::iterator it;
9295 for (it = mat_no.begin(); it != mat_no.end(); ++it)
9296 {
9297 if (geom == Geometry::TRIANGLE)
9298 {
9299 Triangle::GetPointMatrix(it->first, pmats(it->second-1));
9300 }
9301 else
9302 {
9303 Tetrahedron::GetPointMatrix(it->first, pmats(it->second-1));
9304 }
9305 }
9306 }
9307 else
9308 {
9309 MFEM_ABORT("Don't know how to construct CoarseFineTransformations for"
9310 " geom = " << geom);
9311 }
9312 }
9313
9314 // NOTE: quads and hexes already have trivial transformations ready
9315 return CoarseFineTr;
9316 }
9317
PrintXG(std::ostream & out) const9318 void Mesh::PrintXG(std::ostream &out) const
9319 {
9320 MFEM_ASSERT(Dim==spaceDim, "2D Manifold meshes not supported");
9321 int i, j;
9322 Array<int> v;
9323
9324 if (Dim == 2)
9325 {
9326 // Print the type of the mesh.
9327 if (Nodes == NULL)
9328 {
9329 out << "areamesh2\n\n";
9330 }
9331 else
9332 {
9333 out << "curved_areamesh2\n\n";
9334 }
9335
9336 // Print the boundary elements.
9337 out << NumOfBdrElements << '\n';
9338 for (i = 0; i < NumOfBdrElements; i++)
9339 {
9340 boundary[i]->GetVertices(v);
9341
9342 out << boundary[i]->GetAttribute();
9343 for (j = 0; j < v.Size(); j++)
9344 {
9345 out << ' ' << v[j] + 1;
9346 }
9347 out << '\n';
9348 }
9349
9350 // Print the elements.
9351 out << NumOfElements << '\n';
9352 for (i = 0; i < NumOfElements; i++)
9353 {
9354 elements[i]->GetVertices(v);
9355
9356 out << elements[i]->GetAttribute() << ' ' << v.Size();
9357 for (j = 0; j < v.Size(); j++)
9358 {
9359 out << ' ' << v[j] + 1;
9360 }
9361 out << '\n';
9362 }
9363
9364 if (Nodes == NULL)
9365 {
9366 // Print the vertices.
9367 out << NumOfVertices << '\n';
9368 for (i = 0; i < NumOfVertices; i++)
9369 {
9370 out << vertices[i](0);
9371 for (j = 1; j < Dim; j++)
9372 {
9373 out << ' ' << vertices[i](j);
9374 }
9375 out << '\n';
9376 }
9377 }
9378 else
9379 {
9380 out << NumOfVertices << '\n';
9381 Nodes->Save(out);
9382 }
9383 }
9384 else // ===== Dim != 2 =====
9385 {
9386 if (Nodes)
9387 {
9388 mfem_error("Mesh::PrintXG(...) : Curved mesh in 3D");
9389 }
9390
9391 if (meshgen == 1)
9392 {
9393 int nv;
9394 const int *ind;
9395
9396 out << "NETGEN_Neutral_Format\n";
9397 // print the vertices
9398 out << NumOfVertices << '\n';
9399 for (i = 0; i < NumOfVertices; i++)
9400 {
9401 for (j = 0; j < Dim; j++)
9402 {
9403 out << ' ' << vertices[i](j);
9404 }
9405 out << '\n';
9406 }
9407
9408 // print the elements
9409 out << NumOfElements << '\n';
9410 for (i = 0; i < NumOfElements; i++)
9411 {
9412 nv = elements[i]->GetNVertices();
9413 ind = elements[i]->GetVertices();
9414 out << elements[i]->GetAttribute();
9415 for (j = 0; j < nv; j++)
9416 {
9417 out << ' ' << ind[j]+1;
9418 }
9419 out << '\n';
9420 }
9421
9422 // print the boundary information.
9423 out << NumOfBdrElements << '\n';
9424 for (i = 0; i < NumOfBdrElements; i++)
9425 {
9426 nv = boundary[i]->GetNVertices();
9427 ind = boundary[i]->GetVertices();
9428 out << boundary[i]->GetAttribute();
9429 for (j = 0; j < nv; j++)
9430 {
9431 out << ' ' << ind[j]+1;
9432 }
9433 out << '\n';
9434 }
9435 }
9436 else if (meshgen == 2) // TrueGrid
9437 {
9438 int nv;
9439 const int *ind;
9440
9441 out << "TrueGrid\n"
9442 << "1 " << NumOfVertices << " " << NumOfElements
9443 << " 0 0 0 0 0 0 0\n"
9444 << "0 0 0 1 0 0 0 0 0 0 0\n"
9445 << "0 0 " << NumOfBdrElements << " 0 0 0 0 0 0 0 0 0 0 0 0 0\n"
9446 << "0.0 0.0 0.0 0 0 0.0 0.0 0 0.0\n"
9447 << "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n";
9448
9449 for (i = 0; i < NumOfVertices; i++)
9450 out << i+1 << " 0.0 " << vertices[i](0) << ' ' << vertices[i](1)
9451 << ' ' << vertices[i](2) << " 0.0\n";
9452
9453 for (i = 0; i < NumOfElements; i++)
9454 {
9455 nv = elements[i]->GetNVertices();
9456 ind = elements[i]->GetVertices();
9457 out << i+1 << ' ' << elements[i]->GetAttribute();
9458 for (j = 0; j < nv; j++)
9459 {
9460 out << ' ' << ind[j]+1;
9461 }
9462 out << '\n';
9463 }
9464
9465 for (i = 0; i < NumOfBdrElements; i++)
9466 {
9467 nv = boundary[i]->GetNVertices();
9468 ind = boundary[i]->GetVertices();
9469 out << boundary[i]->GetAttribute();
9470 for (j = 0; j < nv; j++)
9471 {
9472 out << ' ' << ind[j]+1;
9473 }
9474 out << " 1.0 1.0 1.0 1.0\n";
9475 }
9476 }
9477 }
9478
9479 out << flush;
9480 }
9481
Printer(std::ostream & out,std::string section_delimiter) const9482 void Mesh::Printer(std::ostream &out, std::string section_delimiter) const
9483 {
9484 int i, j;
9485
9486 if (NURBSext)
9487 {
9488 // general format
9489 NURBSext->Print(out);
9490 out << '\n';
9491 Nodes->Save(out);
9492
9493 // patch-wise format
9494 // NURBSext->ConvertToPatches(*Nodes);
9495 // NURBSext->Print(out);
9496
9497 return;
9498 }
9499
9500 if (Nonconforming())
9501 {
9502 // nonconforming mesh format
9503 ncmesh->Print(out);
9504
9505 if (Nodes)
9506 {
9507 out << "\n# mesh curvature GridFunction";
9508 out << "\nnodes\n";
9509 Nodes->Save(out);
9510 }
9511
9512 out << "\nmfem_mesh_end" << endl;
9513 return;
9514 }
9515
9516 // serial/parallel conforming mesh format
9517 out << (section_delimiter.empty()
9518 ? "MFEM mesh v1.0\n" : "MFEM mesh v1.2\n");
9519
9520 // optional
9521 out <<
9522 "\n#\n# MFEM Geometry Types (see mesh/geom.hpp):\n#\n"
9523 "# POINT = 0\n"
9524 "# SEGMENT = 1\n"
9525 "# TRIANGLE = 2\n"
9526 "# SQUARE = 3\n"
9527 "# TETRAHEDRON = 4\n"
9528 "# CUBE = 5\n"
9529 "# PRISM = 6\n"
9530 "#\n";
9531
9532 out << "\ndimension\n" << Dim;
9533
9534 out << "\n\nelements\n" << NumOfElements << '\n';
9535 for (i = 0; i < NumOfElements; i++)
9536 {
9537 PrintElement(elements[i], out);
9538 }
9539
9540 out << "\nboundary\n" << NumOfBdrElements << '\n';
9541 for (i = 0; i < NumOfBdrElements; i++)
9542 {
9543 PrintElement(boundary[i], out);
9544 }
9545
9546 out << "\nvertices\n" << NumOfVertices << '\n';
9547 if (Nodes == NULL)
9548 {
9549 out << spaceDim << '\n';
9550 for (i = 0; i < NumOfVertices; i++)
9551 {
9552 out << vertices[i](0);
9553 for (j = 1; j < spaceDim; j++)
9554 {
9555 out << ' ' << vertices[i](j);
9556 }
9557 out << '\n';
9558 }
9559 out.flush();
9560 }
9561 else
9562 {
9563 out << "\nnodes\n";
9564 Nodes->Save(out);
9565 }
9566
9567 if (!section_delimiter.empty())
9568 {
9569 out << section_delimiter << endl; // only with format v1.2
9570 }
9571 }
9572
PrintTopo(std::ostream & out,const Array<int> & e_to_k) const9573 void Mesh::PrintTopo(std::ostream &out,const Array<int> &e_to_k) const
9574 {
9575 int i;
9576 Array<int> vert;
9577
9578 out << "MFEM NURBS mesh v1.0\n";
9579
9580 // optional
9581 out <<
9582 "\n#\n# MFEM Geometry Types (see mesh/geom.hpp):\n#\n"
9583 "# SEGMENT = 1\n"
9584 "# SQUARE = 3\n"
9585 "# CUBE = 5\n"
9586 "#\n";
9587
9588 out << "\ndimension\n" << Dim
9589 << "\n\nelements\n" << NumOfElements << '\n';
9590 for (i = 0; i < NumOfElements; i++)
9591 {
9592 PrintElement(elements[i], out);
9593 }
9594
9595 out << "\nboundary\n" << NumOfBdrElements << '\n';
9596 for (i = 0; i < NumOfBdrElements; i++)
9597 {
9598 PrintElement(boundary[i], out);
9599 }
9600
9601 out << "\nedges\n" << NumOfEdges << '\n';
9602 for (i = 0; i < NumOfEdges; i++)
9603 {
9604 edge_vertex->GetRow(i, vert);
9605 int ki = e_to_k[i];
9606 if (ki < 0)
9607 {
9608 ki = -1 - ki;
9609 }
9610 out << ki << ' ' << vert[0] << ' ' << vert[1] << '\n';
9611 }
9612 out << "\nvertices\n" << NumOfVertices << '\n';
9613 }
9614
Save(const char * fname,int precision) const9615 void Mesh::Save(const char *fname, int precision) const
9616 {
9617 ofstream ofs(fname);
9618 ofs.precision(precision);
9619 Print(ofs);
9620 }
9621
9622 #ifdef MFEM_USE_ADIOS2
Print(adios2stream & out) const9623 void Mesh::Print(adios2stream &out) const
9624 {
9625 out.Print(*this);
9626 }
9627 #endif
9628
PrintVTK(std::ostream & out)9629 void Mesh::PrintVTK(std::ostream &out)
9630 {
9631 out <<
9632 "# vtk DataFile Version 3.0\n"
9633 "Generated by MFEM\n"
9634 "ASCII\n"
9635 "DATASET UNSTRUCTURED_GRID\n";
9636
9637 if (Nodes == NULL)
9638 {
9639 out << "POINTS " << NumOfVertices << " double\n";
9640 for (int i = 0; i < NumOfVertices; i++)
9641 {
9642 out << vertices[i](0);
9643 int j;
9644 for (j = 1; j < spaceDim; j++)
9645 {
9646 out << ' ' << vertices[i](j);
9647 }
9648 for ( ; j < 3; j++)
9649 {
9650 out << ' ' << 0.0;
9651 }
9652 out << '\n';
9653 }
9654 }
9655 else
9656 {
9657 Array<int> vdofs(3);
9658 out << "POINTS " << Nodes->FESpace()->GetNDofs() << " double\n";
9659 for (int i = 0; i < Nodes->FESpace()->GetNDofs(); i++)
9660 {
9661 vdofs.SetSize(1);
9662 vdofs[0] = i;
9663 Nodes->FESpace()->DofsToVDofs(vdofs);
9664 out << (*Nodes)(vdofs[0]);
9665 int j;
9666 for (j = 1; j < spaceDim; j++)
9667 {
9668 out << ' ' << (*Nodes)(vdofs[j]);
9669 }
9670 for ( ; j < 3; j++)
9671 {
9672 out << ' ' << 0.0;
9673 }
9674 out << '\n';
9675 }
9676 }
9677
9678 int order = -1;
9679 if (Nodes == NULL)
9680 {
9681 int size = 0;
9682 for (int i = 0; i < NumOfElements; i++)
9683 {
9684 size += elements[i]->GetNVertices() + 1;
9685 }
9686 out << "CELLS " << NumOfElements << ' ' << size << '\n';
9687 for (int i = 0; i < NumOfElements; i++)
9688 {
9689 const int *v = elements[i]->GetVertices();
9690 const int nv = elements[i]->GetNVertices();
9691 out << nv;
9692 Geometry::Type geom = elements[i]->GetGeometryType();
9693 const int *perm = VTKGeometry::VertexPermutation[geom];
9694 for (int j = 0; j < nv; j++)
9695 {
9696 out << ' ' << v[perm ? perm[j] : j];
9697 }
9698 out << '\n';
9699 }
9700 order = 1;
9701 }
9702 else
9703 {
9704 Array<int> dofs;
9705 int size = 0;
9706 for (int i = 0; i < NumOfElements; i++)
9707 {
9708 Nodes->FESpace()->GetElementDofs(i, dofs);
9709 MFEM_ASSERT(Dim != 0 || dofs.Size() == 1,
9710 "Point meshes should have a single dof per element");
9711 size += dofs.Size() + 1;
9712 }
9713 out << "CELLS " << NumOfElements << ' ' << size << '\n';
9714 const char *fec_name = Nodes->FESpace()->FEColl()->Name();
9715
9716 if (!strcmp(fec_name, "Linear") ||
9717 !strcmp(fec_name, "H1_0D_P1") ||
9718 !strcmp(fec_name, "H1_1D_P1") ||
9719 !strcmp(fec_name, "H1_2D_P1") ||
9720 !strcmp(fec_name, "H1_3D_P1"))
9721 {
9722 order = 1;
9723 }
9724 else if (!strcmp(fec_name, "Quadratic") ||
9725 !strcmp(fec_name, "H1_1D_P2") ||
9726 !strcmp(fec_name, "H1_2D_P2") ||
9727 !strcmp(fec_name, "H1_3D_P2"))
9728 {
9729 order = 2;
9730 }
9731 if (order == -1)
9732 {
9733 mfem::err << "Mesh::PrintVTK : can not save '"
9734 << fec_name << "' elements!" << endl;
9735 mfem_error();
9736 }
9737 for (int i = 0; i < NumOfElements; i++)
9738 {
9739 Nodes->FESpace()->GetElementDofs(i, dofs);
9740 out << dofs.Size();
9741 if (order == 1)
9742 {
9743 for (int j = 0; j < dofs.Size(); j++)
9744 {
9745 out << ' ' << dofs[j];
9746 }
9747 }
9748 else if (order == 2)
9749 {
9750 const int *vtk_mfem;
9751 switch (elements[i]->GetGeometryType())
9752 {
9753 case Geometry::SEGMENT:
9754 case Geometry::TRIANGLE:
9755 case Geometry::SQUARE:
9756 vtk_mfem = vtk_quadratic_hex; break; // identity map
9757 case Geometry::TETRAHEDRON:
9758 vtk_mfem = vtk_quadratic_tet; break;
9759 case Geometry::PRISM:
9760 vtk_mfem = vtk_quadratic_wedge; break;
9761 case Geometry::CUBE:
9762 default:
9763 vtk_mfem = vtk_quadratic_hex; break;
9764 }
9765 for (int j = 0; j < dofs.Size(); j++)
9766 {
9767 out << ' ' << dofs[vtk_mfem[j]];
9768 }
9769 }
9770 out << '\n';
9771 }
9772 }
9773
9774 out << "CELL_TYPES " << NumOfElements << '\n';
9775 for (int i = 0; i < NumOfElements; i++)
9776 {
9777 int vtk_cell_type = 5;
9778 Geometry::Type geom = GetElement(i)->GetGeometryType();
9779 if (order == 1) { vtk_cell_type = VTKGeometry::Map[geom]; }
9780 else if (order == 2) { vtk_cell_type = VTKGeometry::QuadraticMap[geom]; }
9781 out << vtk_cell_type << '\n';
9782 }
9783
9784 // write attributes
9785 out << "CELL_DATA " << NumOfElements << '\n'
9786 << "SCALARS material int\n"
9787 << "LOOKUP_TABLE default\n";
9788 for (int i = 0; i < NumOfElements; i++)
9789 {
9790 out << elements[i]->GetAttribute() << '\n';
9791 }
9792 out.flush();
9793 }
9794
PrintVTU(std::string fname,VTKFormat format,bool high_order_output,int compression_level,bool bdr)9795 void Mesh::PrintVTU(std::string fname,
9796 VTKFormat format,
9797 bool high_order_output,
9798 int compression_level,
9799 bool bdr)
9800 {
9801 int ref = (high_order_output && Nodes)
9802 ? Nodes->FESpace()->GetElementOrder(0) : 1;
9803
9804 fname = fname + ".vtu";
9805 std::fstream out(fname.c_str(),std::ios::out);
9806 out << "<VTKFile type=\"UnstructuredGrid\" version=\"0.1\"";
9807 if (compression_level != 0)
9808 {
9809 out << " compressor=\"vtkZLibDataCompressor\"";
9810 }
9811 out << " byte_order=\"" << VTKByteOrder() << "\">\n";
9812 out << "<UnstructuredGrid>\n";
9813 PrintVTU(out, ref, format, high_order_output, compression_level, bdr);
9814 out << "</Piece>\n"; // need to close the piece open in the PrintVTU method
9815 out << "</UnstructuredGrid>\n";
9816 out << "</VTKFile>" << std::endl;
9817
9818 out.close();
9819 }
9820
PrintBdrVTU(std::string fname,VTKFormat format,bool high_order_output,int compression_level)9821 void Mesh::PrintBdrVTU(std::string fname,
9822 VTKFormat format,
9823 bool high_order_output,
9824 int compression_level)
9825 {
9826 PrintVTU(fname, format, high_order_output, compression_level, true);
9827 }
9828
9829 template <typename T>
WriteBinaryOrASCII(std::ostream & out,std::vector<char> & buf,const T & val,const char * suffix,VTKFormat format)9830 void WriteBinaryOrASCII(std::ostream &out, std::vector<char> &buf, const T &val,
9831 const char *suffix, VTKFormat format)
9832 {
9833 if (format == VTKFormat::ASCII) { out << val << suffix; }
9834 else { bin_io::AppendBytes(buf, val); }
9835 }
9836
9837 // Ensure ASCII output of uint8_t to stream is integer rather than character
9838 template <>
WriteBinaryOrASCII(std::ostream & out,std::vector<char> & buf,const uint8_t & val,const char * suffix,VTKFormat format)9839 void WriteBinaryOrASCII<uint8_t>(std::ostream &out, std::vector<char> &buf,
9840 const uint8_t &val, const char *suffix,
9841 VTKFormat format)
9842 {
9843 if (format == VTKFormat::ASCII) { out << static_cast<int>(val) << suffix; }
9844 else { bin_io::AppendBytes(buf, val); }
9845 }
9846
9847 template <>
WriteBinaryOrASCII(std::ostream & out,std::vector<char> & buf,const double & val,const char * suffix,VTKFormat format)9848 void WriteBinaryOrASCII<double>(std::ostream &out, std::vector<char> &buf,
9849 const double &val, const char *suffix,
9850 VTKFormat format)
9851 {
9852 if (format == VTKFormat::BINARY32)
9853 {
9854 bin_io::AppendBytes<float>(buf, float(val));
9855 }
9856 else if (format == VTKFormat::BINARY)
9857 {
9858 bin_io::AppendBytes(buf, val);
9859 }
9860 else
9861 {
9862 out << val << suffix;
9863 }
9864 }
9865
9866 template <>
WriteBinaryOrASCII(std::ostream & out,std::vector<char> & buf,const float & val,const char * suffix,VTKFormat format)9867 void WriteBinaryOrASCII<float>(std::ostream &out, std::vector<char> &buf,
9868 const float &val, const char *suffix,
9869 VTKFormat format)
9870 {
9871 if (format == VTKFormat::BINARY) { bin_io::AppendBytes<double>(buf, val); }
9872 else if (format == VTKFormat::BINARY32) { bin_io::AppendBytes(buf, val); }
9873 else { out << val << suffix; }
9874 }
9875
WriteBase64WithSizeAndClear(std::ostream & out,std::vector<char> & buf,int compression_level)9876 void WriteBase64WithSizeAndClear(std::ostream &out, std::vector<char> &buf,
9877 int compression_level)
9878 {
9879 WriteVTKEncodedCompressed(out, buf.data(), buf.size(), compression_level);
9880 out << '\n';
9881 buf.clear();
9882 }
9883
PrintVTU(std::ostream & out,int ref,VTKFormat format,bool high_order_output,int compression_level,bool bdr_elements)9884 void Mesh::PrintVTU(std::ostream &out, int ref, VTKFormat format,
9885 bool high_order_output, int compression_level,
9886 bool bdr_elements)
9887 {
9888 RefinedGeometry *RefG;
9889 DenseMatrix pmat;
9890
9891 const char *fmt_str = (format == VTKFormat::ASCII) ? "ascii" : "binary";
9892 const char *type_str = (format != VTKFormat::BINARY32) ? "Float64" : "Float32";
9893 std::vector<char> buf;
9894
9895 auto get_geom = [&](int i)
9896 {
9897 if (bdr_elements) { return GetBdrElementBaseGeometry(i); }
9898 else { return GetElementBaseGeometry(i); }
9899 };
9900
9901 int ne = bdr_elements ? GetNBE() : GetNE();
9902 // count the points, cells, size
9903 int np = 0, nc_ref = 0, size = 0;
9904 for (int i = 0; i < ne; i++)
9905 {
9906 Geometry::Type geom = get_geom(i);
9907 int nv = Geometries.GetVertices(geom)->GetNPoints();
9908 RefG = GlobGeometryRefiner.Refine(geom, ref, 1);
9909 np += RefG->RefPts.GetNPoints();
9910 nc_ref += RefG->RefGeoms.Size() / nv;
9911 size += (RefG->RefGeoms.Size() / nv) * (nv + 1);
9912 }
9913
9914 out << "<Piece NumberOfPoints=\"" << np << "\" NumberOfCells=\""
9915 << (high_order_output ? ne : nc_ref) << "\">\n";
9916
9917 // print out the points
9918 out << "<Points>\n";
9919 out << "<DataArray type=\"" << type_str
9920 << "\" NumberOfComponents=\"3\" format=\"" << fmt_str << "\">\n";
9921 for (int i = 0; i < ne; i++)
9922 {
9923 RefG = GlobGeometryRefiner.Refine(get_geom(i), ref, 1);
9924
9925 if (bdr_elements)
9926 {
9927 GetBdrElementTransformation(i)->Transform(RefG->RefPts, pmat);
9928 }
9929 else
9930 {
9931 GetElementTransformation(i)->Transform(RefG->RefPts, pmat);
9932 }
9933
9934 for (int j = 0; j < pmat.Width(); j++)
9935 {
9936 WriteBinaryOrASCII(out, buf, pmat(0,j), " ", format);
9937 if (pmat.Height() > 1)
9938 {
9939 WriteBinaryOrASCII(out, buf, pmat(1,j), " ", format);
9940 }
9941 else
9942 {
9943 WriteBinaryOrASCII(out, buf, 0.0, " ", format);
9944 }
9945 if (pmat.Height() > 2)
9946 {
9947 WriteBinaryOrASCII(out, buf, pmat(2,j), "", format);
9948 }
9949 else
9950 {
9951 WriteBinaryOrASCII(out, buf, 0.0, "", format);
9952 }
9953 if (format == VTKFormat::ASCII) { out << '\n'; }
9954 }
9955 }
9956 if (format != VTKFormat::ASCII)
9957 {
9958 WriteBase64WithSizeAndClear(out, buf, compression_level);
9959 }
9960 out << "</DataArray>" << std::endl;
9961 out << "</Points>" << std::endl;
9962
9963 out << "<Cells>" << std::endl;
9964 out << "<DataArray type=\"Int32\" Name=\"connectivity\" format=\""
9965 << fmt_str << "\">" << std::endl;
9966 // connectivity
9967 std::vector<int> offset;
9968
9969 np = 0;
9970 if (high_order_output)
9971 {
9972 Array<int> local_connectivity;
9973 for (int iel = 0; iel < ne; iel++)
9974 {
9975 Geometry::Type geom = get_geom(iel);
9976 CreateVTKElementConnectivity(local_connectivity, geom, ref);
9977 int nnodes = local_connectivity.Size();
9978 for (int i=0; i<nnodes; ++i)
9979 {
9980 WriteBinaryOrASCII(out, buf, np+local_connectivity[i], " ", format);
9981 }
9982 if (format == VTKFormat::ASCII) { out << '\n'; }
9983 np += nnodes;
9984 offset.push_back(np);
9985 }
9986 }
9987 else
9988 {
9989 int coff = 0;
9990 for (int i = 0; i < ne; i++)
9991 {
9992 Geometry::Type geom = get_geom(i);
9993 int nv = Geometries.GetVertices(geom)->GetNPoints();
9994 RefG = GlobGeometryRefiner.Refine(geom, ref, 1);
9995 Array<int> &RG = RefG->RefGeoms;
9996 for (int j = 0; j < RG.Size(); )
9997 {
9998 coff = coff+nv;
9999 offset.push_back(coff);
10000 const int *p = VTKGeometry::VertexPermutation[geom];
10001 for (int k = 0; k < nv; k++, j++)
10002 {
10003 WriteBinaryOrASCII(out, buf, np + RG[p ? p[j] : j], " ", format);
10004 }
10005 if (format == VTKFormat::ASCII) { out << '\n'; }
10006 }
10007 np += RefG->RefPts.GetNPoints();
10008 }
10009 }
10010 if (format != VTKFormat::ASCII)
10011 {
10012 WriteBase64WithSizeAndClear(out, buf, compression_level);
10013 }
10014 out << "</DataArray>" << std::endl;
10015
10016 out << "<DataArray type=\"Int32\" Name=\"offsets\" format=\""
10017 << fmt_str << "\">" << std::endl;
10018 // offsets
10019 for (size_t ii=0; ii<offset.size(); ii++)
10020 {
10021 WriteBinaryOrASCII(out, buf, offset[ii], "\n", format);
10022 }
10023 if (format != VTKFormat::ASCII)
10024 {
10025 WriteBase64WithSizeAndClear(out, buf, compression_level);
10026 }
10027 out << "</DataArray>" << std::endl;
10028 out << "<DataArray type=\"UInt8\" Name=\"types\" format=\""
10029 << fmt_str << "\">" << std::endl;
10030 // cell types
10031 const int *vtk_geom_map =
10032 high_order_output ? VTKGeometry::HighOrderMap : VTKGeometry::Map;
10033 for (int i = 0; i < ne; i++)
10034 {
10035 Geometry::Type geom = get_geom(i);
10036 uint8_t vtk_cell_type = 5;
10037
10038 vtk_cell_type = vtk_geom_map[geom];
10039
10040 if (high_order_output)
10041 {
10042 WriteBinaryOrASCII(out, buf, vtk_cell_type, "\n", format);
10043 }
10044 else
10045 {
10046 int nv = Geometries.GetVertices(geom)->GetNPoints();
10047 RefG = GlobGeometryRefiner.Refine(geom, ref, 1);
10048 Array<int> &RG = RefG->RefGeoms;
10049 for (int j = 0; j < RG.Size(); j += nv)
10050 {
10051 WriteBinaryOrASCII(out, buf, vtk_cell_type, "\n", format);
10052 }
10053 }
10054 }
10055 if (format != VTKFormat::ASCII)
10056 {
10057 WriteBase64WithSizeAndClear(out, buf, compression_level);
10058 }
10059 out << "</DataArray>" << std::endl;
10060 out << "</Cells>" << std::endl;
10061
10062 out << "<CellData Scalars=\"attribute\">" << std::endl;
10063 out << "<DataArray type=\"Int32\" Name=\"attribute\" format=\""
10064 << fmt_str << "\">" << std::endl;
10065 for (int i = 0; i < ne; i++)
10066 {
10067 int attr = bdr_elements ? GetBdrAttribute(i) : GetAttribute(i);
10068 if (high_order_output)
10069 {
10070 WriteBinaryOrASCII(out, buf, attr, "\n", format);
10071 }
10072 else
10073 {
10074 Geometry::Type geom = get_geom(i);
10075 int nv = Geometries.GetVertices(geom)->GetNPoints();
10076 RefG = GlobGeometryRefiner.Refine(geom, ref, 1);
10077 for (int j = 0; j < RefG->RefGeoms.Size(); j += nv)
10078 {
10079 WriteBinaryOrASCII(out, buf, attr, "\n", format);
10080 }
10081 }
10082 }
10083 if (format != VTKFormat::ASCII)
10084 {
10085 WriteBase64WithSizeAndClear(out, buf, compression_level);
10086 }
10087 out << "</DataArray>" << std::endl;
10088 out << "</CellData>" << std::endl;
10089 }
10090
10091
PrintVTK(std::ostream & out,int ref,int field_data)10092 void Mesh::PrintVTK(std::ostream &out, int ref, int field_data)
10093 {
10094 int np, nc, size;
10095 RefinedGeometry *RefG;
10096 DenseMatrix pmat;
10097
10098 out <<
10099 "# vtk DataFile Version 3.0\n"
10100 "Generated by MFEM\n"
10101 "ASCII\n"
10102 "DATASET UNSTRUCTURED_GRID\n";
10103
10104 // additional dataset information
10105 if (field_data)
10106 {
10107 out << "FIELD FieldData 1\n"
10108 << "MaterialIds " << 1 << " " << attributes.Size() << " int\n";
10109 for (int i = 0; i < attributes.Size(); i++)
10110 {
10111 out << ' ' << attributes[i];
10112 }
10113 out << '\n';
10114 }
10115
10116 // count the points, cells, size
10117 np = nc = size = 0;
10118 for (int i = 0; i < GetNE(); i++)
10119 {
10120 Geometry::Type geom = GetElementBaseGeometry(i);
10121 int nv = Geometries.GetVertices(geom)->GetNPoints();
10122 RefG = GlobGeometryRefiner.Refine(geom, ref, 1);
10123 np += RefG->RefPts.GetNPoints();
10124 nc += RefG->RefGeoms.Size() / nv;
10125 size += (RefG->RefGeoms.Size() / nv) * (nv + 1);
10126 }
10127 out << "POINTS " << np << " double\n";
10128 // write the points
10129 for (int i = 0; i < GetNE(); i++)
10130 {
10131 RefG = GlobGeometryRefiner.Refine(
10132 GetElementBaseGeometry(i), ref, 1);
10133
10134 GetElementTransformation(i)->Transform(RefG->RefPts, pmat);
10135
10136 for (int j = 0; j < pmat.Width(); j++)
10137 {
10138 out << pmat(0, j) << ' ';
10139 if (pmat.Height() > 1)
10140 {
10141 out << pmat(1, j) << ' ';
10142 if (pmat.Height() > 2)
10143 {
10144 out << pmat(2, j);
10145 }
10146 else
10147 {
10148 out << 0.0;
10149 }
10150 }
10151 else
10152 {
10153 out << 0.0 << ' ' << 0.0;
10154 }
10155 out << '\n';
10156 }
10157 }
10158
10159 // write the cells
10160 out << "CELLS " << nc << ' ' << size << '\n';
10161 np = 0;
10162 for (int i = 0; i < GetNE(); i++)
10163 {
10164 Geometry::Type geom = GetElementBaseGeometry(i);
10165 int nv = Geometries.GetVertices(geom)->GetNPoints();
10166 RefG = GlobGeometryRefiner.Refine(geom, ref, 1);
10167 Array<int> &RG = RefG->RefGeoms;
10168
10169 for (int j = 0; j < RG.Size(); )
10170 {
10171 out << nv;
10172 for (int k = 0; k < nv; k++, j++)
10173 {
10174 out << ' ' << np + RG[j];
10175 }
10176 out << '\n';
10177 }
10178 np += RefG->RefPts.GetNPoints();
10179 }
10180 out << "CELL_TYPES " << nc << '\n';
10181 for (int i = 0; i < GetNE(); i++)
10182 {
10183 Geometry::Type geom = GetElementBaseGeometry(i);
10184 int nv = Geometries.GetVertices(geom)->GetNPoints();
10185 RefG = GlobGeometryRefiner.Refine(geom, ref, 1);
10186 Array<int> &RG = RefG->RefGeoms;
10187 int vtk_cell_type = VTKGeometry::Map[geom];
10188
10189 for (int j = 0; j < RG.Size(); j += nv)
10190 {
10191 out << vtk_cell_type << '\n';
10192 }
10193 }
10194 // write attributes (materials)
10195 out << "CELL_DATA " << nc << '\n'
10196 << "SCALARS material int\n"
10197 << "LOOKUP_TABLE default\n";
10198 for (int i = 0; i < GetNE(); i++)
10199 {
10200 Geometry::Type geom = GetElementBaseGeometry(i);
10201 int nv = Geometries.GetVertices(geom)->GetNPoints();
10202 RefG = GlobGeometryRefiner.Refine(geom, ref, 1);
10203 int attr = GetAttribute(i);
10204 for (int j = 0; j < RefG->RefGeoms.Size(); j += nv)
10205 {
10206 out << attr << '\n';
10207 }
10208 }
10209
10210 if (Dim > 1)
10211 {
10212 Array<int> coloring;
10213 srand((unsigned)time(0));
10214 double a = double(rand()) / (double(RAND_MAX) + 1.);
10215 int el0 = (int)floor(a * GetNE());
10216 GetElementColoring(coloring, el0);
10217 out << "SCALARS element_coloring int\n"
10218 << "LOOKUP_TABLE default\n";
10219 for (int i = 0; i < GetNE(); i++)
10220 {
10221 Geometry::Type geom = GetElementBaseGeometry(i);
10222 int nv = Geometries.GetVertices(geom)->GetNPoints();
10223 RefG = GlobGeometryRefiner.Refine(geom, ref, 1);
10224 for (int j = 0; j < RefG->RefGeoms.Size(); j += nv)
10225 {
10226 out << coloring[i] + 1 << '\n';
10227 }
10228 }
10229 }
10230
10231 // prepare to write data
10232 out << "POINT_DATA " << np << '\n' << flush;
10233 }
10234
GetElementColoring(Array<int> & colors,int el0)10235 void Mesh::GetElementColoring(Array<int> &colors, int el0)
10236 {
10237 int delete_el_to_el = (el_to_el) ? (0) : (1);
10238 const Table &el_el = ElementToElementTable();
10239 int num_el = GetNE(), stack_p, stack_top_p, max_num_col;
10240 Array<int> el_stack(num_el);
10241
10242 const int *i_el_el = el_el.GetI();
10243 const int *j_el_el = el_el.GetJ();
10244
10245 colors.SetSize(num_el);
10246 colors = -2;
10247 max_num_col = 1;
10248 stack_p = stack_top_p = 0;
10249 for (int el = el0; stack_top_p < num_el; el=(el+1)%num_el)
10250 {
10251 if (colors[el] != -2)
10252 {
10253 continue;
10254 }
10255
10256 colors[el] = -1;
10257 el_stack[stack_top_p++] = el;
10258
10259 for ( ; stack_p < stack_top_p; stack_p++)
10260 {
10261 int i = el_stack[stack_p];
10262 int num_nb = i_el_el[i+1] - i_el_el[i];
10263 if (max_num_col < num_nb + 1)
10264 {
10265 max_num_col = num_nb + 1;
10266 }
10267 for (int j = i_el_el[i]; j < i_el_el[i+1]; j++)
10268 {
10269 int k = j_el_el[j];
10270 if (colors[k] == -2)
10271 {
10272 colors[k] = -1;
10273 el_stack[stack_top_p++] = k;
10274 }
10275 }
10276 }
10277 }
10278
10279 Array<int> col_marker(max_num_col);
10280
10281 for (stack_p = 0; stack_p < stack_top_p; stack_p++)
10282 {
10283 int i = el_stack[stack_p], col;
10284 col_marker = 0;
10285 for (int j = i_el_el[i]; j < i_el_el[i+1]; j++)
10286 {
10287 col = colors[j_el_el[j]];
10288 if (col != -1)
10289 {
10290 col_marker[col] = 1;
10291 }
10292 }
10293
10294 for (col = 0; col < max_num_col; col++)
10295 if (col_marker[col] == 0)
10296 {
10297 break;
10298 }
10299
10300 colors[i] = col;
10301 }
10302
10303 if (delete_el_to_el)
10304 {
10305 delete el_to_el;
10306 el_to_el = NULL;
10307 }
10308 }
10309
PrintWithPartitioning(int * partitioning,std::ostream & out,int elem_attr) const10310 void Mesh::PrintWithPartitioning(int *partitioning, std::ostream &out,
10311 int elem_attr) const
10312 {
10313 if (Dim != 3 && Dim != 2) { return; }
10314
10315 int i, j, k, l, nv, nbe, *v;
10316
10317 out << "MFEM mesh v1.0\n";
10318
10319 // optional
10320 out <<
10321 "\n#\n# MFEM Geometry Types (see mesh/geom.hpp):\n#\n"
10322 "# POINT = 0\n"
10323 "# SEGMENT = 1\n"
10324 "# TRIANGLE = 2\n"
10325 "# SQUARE = 3\n"
10326 "# TETRAHEDRON = 4\n"
10327 "# CUBE = 5\n"
10328 "# PRISM = 6\n"
10329 "#\n";
10330
10331 out << "\ndimension\n" << Dim
10332 << "\n\nelements\n" << NumOfElements << '\n';
10333 for (i = 0; i < NumOfElements; i++)
10334 {
10335 out << int((elem_attr) ? partitioning[i]+1 : elements[i]->GetAttribute())
10336 << ' ' << elements[i]->GetGeometryType();
10337 nv = elements[i]->GetNVertices();
10338 v = elements[i]->GetVertices();
10339 for (j = 0; j < nv; j++)
10340 {
10341 out << ' ' << v[j];
10342 }
10343 out << '\n';
10344 }
10345 nbe = 0;
10346 for (i = 0; i < faces_info.Size(); i++)
10347 {
10348 if ((l = faces_info[i].Elem2No) >= 0)
10349 {
10350 k = partitioning[faces_info[i].Elem1No];
10351 l = partitioning[l];
10352 if (k != l)
10353 {
10354 nbe++;
10355 if (!Nonconforming() || !IsSlaveFace(faces_info[i]))
10356 {
10357 nbe++;
10358 }
10359 }
10360 }
10361 else
10362 {
10363 nbe++;
10364 }
10365 }
10366 out << "\nboundary\n" << nbe << '\n';
10367 for (i = 0; i < faces_info.Size(); i++)
10368 {
10369 if ((l = faces_info[i].Elem2No) >= 0)
10370 {
10371 k = partitioning[faces_info[i].Elem1No];
10372 l = partitioning[l];
10373 if (k != l)
10374 {
10375 nv = faces[i]->GetNVertices();
10376 v = faces[i]->GetVertices();
10377 out << k+1 << ' ' << faces[i]->GetGeometryType();
10378 for (j = 0; j < nv; j++)
10379 {
10380 out << ' ' << v[j];
10381 }
10382 out << '\n';
10383 if (!Nonconforming() || !IsSlaveFace(faces_info[i]))
10384 {
10385 out << l+1 << ' ' << faces[i]->GetGeometryType();
10386 for (j = nv-1; j >= 0; j--)
10387 {
10388 out << ' ' << v[j];
10389 }
10390 out << '\n';
10391 }
10392 }
10393 }
10394 else
10395 {
10396 k = partitioning[faces_info[i].Elem1No];
10397 nv = faces[i]->GetNVertices();
10398 v = faces[i]->GetVertices();
10399 out << k+1 << ' ' << faces[i]->GetGeometryType();
10400 for (j = 0; j < nv; j++)
10401 {
10402 out << ' ' << v[j];
10403 }
10404 out << '\n';
10405 }
10406 }
10407 out << "\nvertices\n" << NumOfVertices << '\n';
10408 if (Nodes == NULL)
10409 {
10410 out << spaceDim << '\n';
10411 for (i = 0; i < NumOfVertices; i++)
10412 {
10413 out << vertices[i](0);
10414 for (j = 1; j < spaceDim; j++)
10415 {
10416 out << ' ' << vertices[i](j);
10417 }
10418 out << '\n';
10419 }
10420 out.flush();
10421 }
10422 else
10423 {
10424 out << "\nnodes\n";
10425 Nodes->Save(out);
10426 }
10427 }
10428
PrintElementsWithPartitioning(int * partitioning,std::ostream & out,int interior_faces)10429 void Mesh::PrintElementsWithPartitioning(int *partitioning,
10430 std::ostream &out,
10431 int interior_faces)
10432 {
10433 MFEM_ASSERT(Dim == spaceDim, "2D Manifolds not supported\n");
10434 if (Dim != 3 && Dim != 2) { return; }
10435
10436 int i, j, k, l, s;
10437
10438 int nv;
10439 const int *ind;
10440
10441 int *vcount = new int[NumOfVertices];
10442 for (i = 0; i < NumOfVertices; i++)
10443 {
10444 vcount[i] = 0;
10445 }
10446 for (i = 0; i < NumOfElements; i++)
10447 {
10448 nv = elements[i]->GetNVertices();
10449 ind = elements[i]->GetVertices();
10450 for (j = 0; j < nv; j++)
10451 {
10452 vcount[ind[j]]++;
10453 }
10454 }
10455
10456 int *voff = new int[NumOfVertices+1];
10457 voff[0] = 0;
10458 for (i = 1; i <= NumOfVertices; i++)
10459 {
10460 voff[i] = vcount[i-1] + voff[i-1];
10461 }
10462
10463 int **vown = new int*[NumOfVertices];
10464 for (i = 0; i < NumOfVertices; i++)
10465 {
10466 vown[i] = new int[vcount[i]];
10467 }
10468
10469 // 2D
10470 if (Dim == 2)
10471 {
10472 int nv, nbe;
10473 int *ind;
10474
10475 Table edge_el;
10476 Transpose(ElementToEdgeTable(), edge_el);
10477
10478 // Fake printing of the elements.
10479 for (i = 0; i < NumOfElements; i++)
10480 {
10481 nv = elements[i]->GetNVertices();
10482 ind = elements[i]->GetVertices();
10483 for (j = 0; j < nv; j++)
10484 {
10485 vcount[ind[j]]--;
10486 vown[ind[j]][vcount[ind[j]]] = i;
10487 }
10488 }
10489
10490 for (i = 0; i < NumOfVertices; i++)
10491 {
10492 vcount[i] = voff[i+1] - voff[i];
10493 }
10494
10495 nbe = 0;
10496 for (i = 0; i < edge_el.Size(); i++)
10497 {
10498 const int *el = edge_el.GetRow(i);
10499 if (edge_el.RowSize(i) > 1)
10500 {
10501 k = partitioning[el[0]];
10502 l = partitioning[el[1]];
10503 if (interior_faces || k != l)
10504 {
10505 nbe += 2;
10506 }
10507 }
10508 else
10509 {
10510 nbe++;
10511 }
10512 }
10513
10514 // Print the type of the mesh and the boundary elements.
10515 out << "areamesh2\n\n" << nbe << '\n';
10516
10517 for (i = 0; i < edge_el.Size(); i++)
10518 {
10519 const int *el = edge_el.GetRow(i);
10520 if (edge_el.RowSize(i) > 1)
10521 {
10522 k = partitioning[el[0]];
10523 l = partitioning[el[1]];
10524 if (interior_faces || k != l)
10525 {
10526 Array<int> ev;
10527 GetEdgeVertices(i,ev);
10528 out << k+1; // attribute
10529 for (j = 0; j < 2; j++)
10530 for (s = 0; s < vcount[ev[j]]; s++)
10531 if (vown[ev[j]][s] == el[0])
10532 {
10533 out << ' ' << voff[ev[j]]+s+1;
10534 }
10535 out << '\n';
10536 out << l+1; // attribute
10537 for (j = 1; j >= 0; j--)
10538 for (s = 0; s < vcount[ev[j]]; s++)
10539 if (vown[ev[j]][s] == el[1])
10540 {
10541 out << ' ' << voff[ev[j]]+s+1;
10542 }
10543 out << '\n';
10544 }
10545 }
10546 else
10547 {
10548 k = partitioning[el[0]];
10549 Array<int> ev;
10550 GetEdgeVertices(i,ev);
10551 out << k+1; // attribute
10552 for (j = 0; j < 2; j++)
10553 for (s = 0; s < vcount[ev[j]]; s++)
10554 if (vown[ev[j]][s] == el[0])
10555 {
10556 out << ' ' << voff[ev[j]]+s+1;
10557 }
10558 out << '\n';
10559 }
10560 }
10561
10562 // Print the elements.
10563 out << NumOfElements << '\n';
10564 for (i = 0; i < NumOfElements; i++)
10565 {
10566 nv = elements[i]->GetNVertices();
10567 ind = elements[i]->GetVertices();
10568 out << partitioning[i]+1 << ' '; // use subdomain number as attribute
10569 out << nv << ' ';
10570 for (j = 0; j < nv; j++)
10571 {
10572 out << ' ' << voff[ind[j]]+vcount[ind[j]]--;
10573 vown[ind[j]][vcount[ind[j]]] = i;
10574 }
10575 out << '\n';
10576 }
10577
10578 for (i = 0; i < NumOfVertices; i++)
10579 {
10580 vcount[i] = voff[i+1] - voff[i];
10581 }
10582
10583 // Print the vertices.
10584 out << voff[NumOfVertices] << '\n';
10585 for (i = 0; i < NumOfVertices; i++)
10586 for (k = 0; k < vcount[i]; k++)
10587 {
10588 for (j = 0; j < Dim; j++)
10589 {
10590 out << vertices[i](j) << ' ';
10591 }
10592 out << '\n';
10593 }
10594 }
10595 // Dim is 3
10596 else if (meshgen == 1)
10597 {
10598 out << "NETGEN_Neutral_Format\n";
10599 // print the vertices
10600 out << voff[NumOfVertices] << '\n';
10601 for (i = 0; i < NumOfVertices; i++)
10602 for (k = 0; k < vcount[i]; k++)
10603 {
10604 for (j = 0; j < Dim; j++)
10605 {
10606 out << ' ' << vertices[i](j);
10607 }
10608 out << '\n';
10609 }
10610
10611 // print the elements
10612 out << NumOfElements << '\n';
10613 for (i = 0; i < NumOfElements; i++)
10614 {
10615 nv = elements[i]->GetNVertices();
10616 ind = elements[i]->GetVertices();
10617 out << partitioning[i]+1; // use subdomain number as attribute
10618 for (j = 0; j < nv; j++)
10619 {
10620 out << ' ' << voff[ind[j]]+vcount[ind[j]]--;
10621 vown[ind[j]][vcount[ind[j]]] = i;
10622 }
10623 out << '\n';
10624 }
10625
10626 for (i = 0; i < NumOfVertices; i++)
10627 {
10628 vcount[i] = voff[i+1] - voff[i];
10629 }
10630
10631 // print the boundary information.
10632 int k, l, nbe;
10633 nbe = 0;
10634 for (i = 0; i < NumOfFaces; i++)
10635 if ((l = faces_info[i].Elem2No) >= 0)
10636 {
10637 k = partitioning[faces_info[i].Elem1No];
10638 l = partitioning[l];
10639 if (interior_faces || k != l)
10640 {
10641 nbe += 2;
10642 }
10643 }
10644 else
10645 {
10646 nbe++;
10647 }
10648
10649 out << nbe << '\n';
10650 for (i = 0; i < NumOfFaces; i++)
10651 if ((l = faces_info[i].Elem2No) >= 0)
10652 {
10653 k = partitioning[faces_info[i].Elem1No];
10654 l = partitioning[l];
10655 if (interior_faces || k != l)
10656 {
10657 nv = faces[i]->GetNVertices();
10658 ind = faces[i]->GetVertices();
10659 out << k+1; // attribute
10660 for (j = 0; j < nv; j++)
10661 for (s = 0; s < vcount[ind[j]]; s++)
10662 if (vown[ind[j]][s] == faces_info[i].Elem1No)
10663 {
10664 out << ' ' << voff[ind[j]]+s+1;
10665 }
10666 out << '\n';
10667 out << l+1; // attribute
10668 for (j = nv-1; j >= 0; j--)
10669 for (s = 0; s < vcount[ind[j]]; s++)
10670 if (vown[ind[j]][s] == faces_info[i].Elem2No)
10671 {
10672 out << ' ' << voff[ind[j]]+s+1;
10673 }
10674 out << '\n';
10675 }
10676 }
10677 else
10678 {
10679 k = partitioning[faces_info[i].Elem1No];
10680 nv = faces[i]->GetNVertices();
10681 ind = faces[i]->GetVertices();
10682 out << k+1; // attribute
10683 for (j = 0; j < nv; j++)
10684 for (s = 0; s < vcount[ind[j]]; s++)
10685 if (vown[ind[j]][s] == faces_info[i].Elem1No)
10686 {
10687 out << ' ' << voff[ind[j]]+s+1;
10688 }
10689 out << '\n';
10690 }
10691 }
10692 // Dim is 3
10693 else if (meshgen == 2) // TrueGrid
10694 {
10695 // count the number of the boundary elements.
10696 int k, l, nbe;
10697 nbe = 0;
10698 for (i = 0; i < NumOfFaces; i++)
10699 if ((l = faces_info[i].Elem2No) >= 0)
10700 {
10701 k = partitioning[faces_info[i].Elem1No];
10702 l = partitioning[l];
10703 if (interior_faces || k != l)
10704 {
10705 nbe += 2;
10706 }
10707 }
10708 else
10709 {
10710 nbe++;
10711 }
10712
10713
10714 out << "TrueGrid\n"
10715 << "1 " << voff[NumOfVertices] << " " << NumOfElements
10716 << " 0 0 0 0 0 0 0\n"
10717 << "0 0 0 1 0 0 0 0 0 0 0\n"
10718 << "0 0 " << nbe << " 0 0 0 0 0 0 0 0 0 0 0 0 0\n"
10719 << "0.0 0.0 0.0 0 0 0.0 0.0 0 0.0\n"
10720 << "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n";
10721
10722 for (i = 0; i < NumOfVertices; i++)
10723 for (k = 0; k < vcount[i]; k++)
10724 out << voff[i]+k << " 0.0 " << vertices[i](0) << ' '
10725 << vertices[i](1) << ' ' << vertices[i](2) << " 0.0\n";
10726
10727 for (i = 0; i < NumOfElements; i++)
10728 {
10729 nv = elements[i]->GetNVertices();
10730 ind = elements[i]->GetVertices();
10731 out << i+1 << ' ' << partitioning[i]+1; // partitioning as attribute
10732 for (j = 0; j < nv; j++)
10733 {
10734 out << ' ' << voff[ind[j]]+vcount[ind[j]]--;
10735 vown[ind[j]][vcount[ind[j]]] = i;
10736 }
10737 out << '\n';
10738 }
10739
10740 for (i = 0; i < NumOfVertices; i++)
10741 {
10742 vcount[i] = voff[i+1] - voff[i];
10743 }
10744
10745 // boundary elements
10746 for (i = 0; i < NumOfFaces; i++)
10747 if ((l = faces_info[i].Elem2No) >= 0)
10748 {
10749 k = partitioning[faces_info[i].Elem1No];
10750 l = partitioning[l];
10751 if (interior_faces || k != l)
10752 {
10753 nv = faces[i]->GetNVertices();
10754 ind = faces[i]->GetVertices();
10755 out << k+1; // attribute
10756 for (j = 0; j < nv; j++)
10757 for (s = 0; s < vcount[ind[j]]; s++)
10758 if (vown[ind[j]][s] == faces_info[i].Elem1No)
10759 {
10760 out << ' ' << voff[ind[j]]+s+1;
10761 }
10762 out << " 1.0 1.0 1.0 1.0\n";
10763 out << l+1; // attribute
10764 for (j = nv-1; j >= 0; j--)
10765 for (s = 0; s < vcount[ind[j]]; s++)
10766 if (vown[ind[j]][s] == faces_info[i].Elem2No)
10767 {
10768 out << ' ' << voff[ind[j]]+s+1;
10769 }
10770 out << " 1.0 1.0 1.0 1.0\n";
10771 }
10772 }
10773 else
10774 {
10775 k = partitioning[faces_info[i].Elem1No];
10776 nv = faces[i]->GetNVertices();
10777 ind = faces[i]->GetVertices();
10778 out << k+1; // attribute
10779 for (j = 0; j < nv; j++)
10780 for (s = 0; s < vcount[ind[j]]; s++)
10781 if (vown[ind[j]][s] == faces_info[i].Elem1No)
10782 {
10783 out << ' ' << voff[ind[j]]+s+1;
10784 }
10785 out << " 1.0 1.0 1.0 1.0\n";
10786 }
10787 }
10788
10789 out << flush;
10790
10791 for (i = 0; i < NumOfVertices; i++)
10792 {
10793 delete [] vown[i];
10794 }
10795
10796 delete [] vcount;
10797 delete [] voff;
10798 delete [] vown;
10799 }
10800
PrintSurfaces(const Table & Aface_face,std::ostream & out) const10801 void Mesh::PrintSurfaces(const Table & Aface_face, std::ostream &out) const
10802 {
10803 int i, j;
10804
10805 if (NURBSext)
10806 {
10807 mfem_error("Mesh::PrintSurfaces"
10808 " NURBS mesh is not supported!");
10809 return;
10810 }
10811
10812 out << "MFEM mesh v1.0\n";
10813
10814 // optional
10815 out <<
10816 "\n#\n# MFEM Geometry Types (see mesh/geom.hpp):\n#\n"
10817 "# POINT = 0\n"
10818 "# SEGMENT = 1\n"
10819 "# TRIANGLE = 2\n"
10820 "# SQUARE = 3\n"
10821 "# TETRAHEDRON = 4\n"
10822 "# CUBE = 5\n"
10823 "# PRISM = 6\n"
10824 "#\n";
10825
10826 out << "\ndimension\n" << Dim
10827 << "\n\nelements\n" << NumOfElements << '\n';
10828 for (i = 0; i < NumOfElements; i++)
10829 {
10830 PrintElement(elements[i], out);
10831 }
10832
10833 out << "\nboundary\n" << Aface_face.Size_of_connections() << '\n';
10834 const int * const i_AF_f = Aface_face.GetI();
10835 const int * const j_AF_f = Aface_face.GetJ();
10836
10837 for (int iAF=0; iAF < Aface_face.Size(); ++iAF)
10838 for (const int * iface = j_AF_f + i_AF_f[iAF];
10839 iface < j_AF_f + i_AF_f[iAF+1];
10840 ++iface)
10841 {
10842 out << iAF+1 << ' ';
10843 PrintElementWithoutAttr(faces[*iface],out);
10844 }
10845
10846 out << "\nvertices\n" << NumOfVertices << '\n';
10847 if (Nodes == NULL)
10848 {
10849 out << spaceDim << '\n';
10850 for (i = 0; i < NumOfVertices; i++)
10851 {
10852 out << vertices[i](0);
10853 for (j = 1; j < spaceDim; j++)
10854 {
10855 out << ' ' << vertices[i](j);
10856 }
10857 out << '\n';
10858 }
10859 out.flush();
10860 }
10861 else
10862 {
10863 out << "\nnodes\n";
10864 Nodes->Save(out);
10865 }
10866 }
10867
ScaleSubdomains(double sf)10868 void Mesh::ScaleSubdomains(double sf)
10869 {
10870 int i,j,k;
10871 Array<int> vert;
10872 DenseMatrix pointmat;
10873 int na = attributes.Size();
10874 double *cg = new double[na*spaceDim];
10875 int *nbea = new int[na];
10876
10877 int *vn = new int[NumOfVertices];
10878 for (i = 0; i < NumOfVertices; i++)
10879 {
10880 vn[i] = 0;
10881 }
10882 for (i = 0; i < na; i++)
10883 {
10884 for (j = 0; j < spaceDim; j++)
10885 {
10886 cg[i*spaceDim+j] = 0.0;
10887 }
10888 nbea[i] = 0;
10889 }
10890
10891 for (i = 0; i < NumOfElements; i++)
10892 {
10893 GetElementVertices(i, vert);
10894 for (k = 0; k < vert.Size(); k++)
10895 {
10896 vn[vert[k]] = 1;
10897 }
10898 }
10899
10900 for (i = 0; i < NumOfElements; i++)
10901 {
10902 int bea = GetAttribute(i)-1;
10903 GetPointMatrix(i, pointmat);
10904 GetElementVertices(i, vert);
10905
10906 for (k = 0; k < vert.Size(); k++)
10907 if (vn[vert[k]] == 1)
10908 {
10909 nbea[bea]++;
10910 for (j = 0; j < spaceDim; j++)
10911 {
10912 cg[bea*spaceDim+j] += pointmat(j,k);
10913 }
10914 vn[vert[k]] = 2;
10915 }
10916 }
10917
10918 for (i = 0; i < NumOfElements; i++)
10919 {
10920 int bea = GetAttribute(i)-1;
10921 GetElementVertices (i, vert);
10922
10923 for (k = 0; k < vert.Size(); k++)
10924 if (vn[vert[k]])
10925 {
10926 for (j = 0; j < spaceDim; j++)
10927 vertices[vert[k]](j) = sf*vertices[vert[k]](j) +
10928 (1-sf)*cg[bea*spaceDim+j]/nbea[bea];
10929 vn[vert[k]] = 0;
10930 }
10931 }
10932
10933 delete [] cg;
10934 delete [] nbea;
10935 delete [] vn;
10936 }
10937
ScaleElements(double sf)10938 void Mesh::ScaleElements(double sf)
10939 {
10940 int i,j,k;
10941 Array<int> vert;
10942 DenseMatrix pointmat;
10943 int na = NumOfElements;
10944 double *cg = new double[na*spaceDim];
10945 int *nbea = new int[na];
10946
10947 int *vn = new int[NumOfVertices];
10948 for (i = 0; i < NumOfVertices; i++)
10949 {
10950 vn[i] = 0;
10951 }
10952 for (i = 0; i < na; i++)
10953 {
10954 for (j = 0; j < spaceDim; j++)
10955 {
10956 cg[i*spaceDim+j] = 0.0;
10957 }
10958 nbea[i] = 0;
10959 }
10960
10961 for (i = 0; i < NumOfElements; i++)
10962 {
10963 GetElementVertices(i, vert);
10964 for (k = 0; k < vert.Size(); k++)
10965 {
10966 vn[vert[k]] = 1;
10967 }
10968 }
10969
10970 for (i = 0; i < NumOfElements; i++)
10971 {
10972 int bea = i;
10973 GetPointMatrix(i, pointmat);
10974 GetElementVertices(i, vert);
10975
10976 for (k = 0; k < vert.Size(); k++)
10977 if (vn[vert[k]] == 1)
10978 {
10979 nbea[bea]++;
10980 for (j = 0; j < spaceDim; j++)
10981 {
10982 cg[bea*spaceDim+j] += pointmat(j,k);
10983 }
10984 vn[vert[k]] = 2;
10985 }
10986 }
10987
10988 for (i = 0; i < NumOfElements; i++)
10989 {
10990 int bea = i;
10991 GetElementVertices(i, vert);
10992
10993 for (k = 0; k < vert.Size(); k++)
10994 if (vn[vert[k]])
10995 {
10996 for (j = 0; j < spaceDim; j++)
10997 vertices[vert[k]](j) = sf*vertices[vert[k]](j) +
10998 (1-sf)*cg[bea*spaceDim+j]/nbea[bea];
10999 vn[vert[k]] = 0;
11000 }
11001 }
11002
11003 delete [] cg;
11004 delete [] nbea;
11005 delete [] vn;
11006 }
11007
Transform(void (* f)(const Vector &,Vector &))11008 void Mesh::Transform(void (*f)(const Vector&, Vector&))
11009 {
11010 // TODO: support for different new spaceDim.
11011 if (Nodes == NULL)
11012 {
11013 Vector vold(spaceDim), vnew(NULL, spaceDim);
11014 for (int i = 0; i < vertices.Size(); i++)
11015 {
11016 for (int j = 0; j < spaceDim; j++)
11017 {
11018 vold(j) = vertices[i](j);
11019 }
11020 vnew.SetData(vertices[i]());
11021 (*f)(vold, vnew);
11022 }
11023 }
11024 else
11025 {
11026 GridFunction xnew(Nodes->FESpace());
11027 VectorFunctionCoefficient f_pert(spaceDim, f);
11028 xnew.ProjectCoefficient(f_pert);
11029 *Nodes = xnew;
11030 }
11031 }
11032
Transform(VectorCoefficient & deformation)11033 void Mesh::Transform(VectorCoefficient &deformation)
11034 {
11035 MFEM_VERIFY(spaceDim == deformation.GetVDim(),
11036 "incompatible vector dimensions");
11037 if (Nodes == NULL)
11038 {
11039 LinearFECollection fec;
11040 FiniteElementSpace fes(this, &fec, spaceDim, Ordering::byVDIM);
11041 GridFunction xnew(&fes);
11042 xnew.ProjectCoefficient(deformation);
11043 for (int i = 0; i < NumOfVertices; i++)
11044 for (int d = 0; d < spaceDim; d++)
11045 {
11046 vertices[i](d) = xnew(d + spaceDim*i);
11047 }
11048 }
11049 else
11050 {
11051 GridFunction xnew(Nodes->FESpace());
11052 xnew.ProjectCoefficient(deformation);
11053 *Nodes = xnew;
11054 }
11055 }
11056
RemoveUnusedVertices()11057 void Mesh::RemoveUnusedVertices()
11058 {
11059 if (NURBSext || ncmesh) { return; }
11060
11061 Array<int> v2v(GetNV());
11062 v2v = -1;
11063 for (int i = 0; i < GetNE(); i++)
11064 {
11065 Element *el = GetElement(i);
11066 int nv = el->GetNVertices();
11067 int *v = el->GetVertices();
11068 for (int j = 0; j < nv; j++)
11069 {
11070 v2v[v[j]] = 0;
11071 }
11072 }
11073 for (int i = 0; i < GetNBE(); i++)
11074 {
11075 Element *el = GetBdrElement(i);
11076 int *v = el->GetVertices();
11077 int nv = el->GetNVertices();
11078 for (int j = 0; j < nv; j++)
11079 {
11080 v2v[v[j]] = 0;
11081 }
11082 }
11083 int num_vert = 0;
11084 for (int i = 0; i < v2v.Size(); i++)
11085 {
11086 if (v2v[i] == 0)
11087 {
11088 vertices[num_vert] = vertices[i];
11089 v2v[i] = num_vert++;
11090 }
11091 }
11092
11093 if (num_vert == v2v.Size()) { return; }
11094
11095 Vector nodes_by_element;
11096 Array<int> vdofs;
11097 if (Nodes)
11098 {
11099 int s = 0;
11100 for (int i = 0; i < GetNE(); i++)
11101 {
11102 Nodes->FESpace()->GetElementVDofs(i, vdofs);
11103 s += vdofs.Size();
11104 }
11105 nodes_by_element.SetSize(s);
11106 s = 0;
11107 for (int i = 0; i < GetNE(); i++)
11108 {
11109 Nodes->FESpace()->GetElementVDofs(i, vdofs);
11110 Nodes->GetSubVector(vdofs, &nodes_by_element(s));
11111 s += vdofs.Size();
11112 }
11113 }
11114 vertices.SetSize(num_vert);
11115 NumOfVertices = num_vert;
11116 for (int i = 0; i < GetNE(); i++)
11117 {
11118 Element *el = GetElement(i);
11119 int *v = el->GetVertices();
11120 int nv = el->GetNVertices();
11121 for (int j = 0; j < nv; j++)
11122 {
11123 v[j] = v2v[v[j]];
11124 }
11125 }
11126 for (int i = 0; i < GetNBE(); i++)
11127 {
11128 Element *el = GetBdrElement(i);
11129 int *v = el->GetVertices();
11130 int nv = el->GetNVertices();
11131 for (int j = 0; j < nv; j++)
11132 {
11133 v[j] = v2v[v[j]];
11134 }
11135 }
11136 DeleteTables();
11137 if (Dim > 1)
11138 {
11139 // generate el_to_edge, be_to_edge (2D), bel_to_edge (3D)
11140 el_to_edge = new Table;
11141 NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
11142 }
11143 if (Dim > 2)
11144 {
11145 // generate el_to_face, be_to_face
11146 GetElementToFaceTable();
11147 }
11148 // Update faces and faces_info
11149 GenerateFaces();
11150 if (Nodes)
11151 {
11152 Nodes->FESpace()->Update();
11153 Nodes->Update();
11154 int s = 0;
11155 for (int i = 0; i < GetNE(); i++)
11156 {
11157 Nodes->FESpace()->GetElementVDofs(i, vdofs);
11158 Nodes->SetSubVector(vdofs, &nodes_by_element(s));
11159 s += vdofs.Size();
11160 }
11161 }
11162 }
11163
RemoveInternalBoundaries()11164 void Mesh::RemoveInternalBoundaries()
11165 {
11166 if (NURBSext || ncmesh) { return; }
11167
11168 int num_bdr_elem = 0;
11169 int new_bel_to_edge_nnz = 0;
11170 for (int i = 0; i < GetNBE(); i++)
11171 {
11172 if (FaceIsInterior(GetBdrElementEdgeIndex(i)))
11173 {
11174 FreeElement(boundary[i]);
11175 }
11176 else
11177 {
11178 num_bdr_elem++;
11179 if (Dim == 3)
11180 {
11181 new_bel_to_edge_nnz += bel_to_edge->RowSize(i);
11182 }
11183 }
11184 }
11185
11186 if (num_bdr_elem == GetNBE()) { return; }
11187
11188 Array<Element *> new_boundary(num_bdr_elem);
11189 Array<int> new_be_to_edge, new_be_to_face;
11190 Table *new_bel_to_edge = NULL;
11191 new_boundary.SetSize(0);
11192 if (Dim == 2)
11193 {
11194 new_be_to_edge.Reserve(num_bdr_elem);
11195 }
11196 else if (Dim == 3)
11197 {
11198 new_be_to_face.Reserve(num_bdr_elem);
11199 new_bel_to_edge = new Table;
11200 new_bel_to_edge->SetDims(num_bdr_elem, new_bel_to_edge_nnz);
11201 }
11202 for (int i = 0; i < GetNBE(); i++)
11203 {
11204 if (!FaceIsInterior(GetBdrElementEdgeIndex(i)))
11205 {
11206 new_boundary.Append(boundary[i]);
11207 if (Dim == 2)
11208 {
11209 new_be_to_edge.Append(be_to_edge[i]);
11210 }
11211 else if (Dim == 3)
11212 {
11213 int row = new_be_to_face.Size();
11214 new_be_to_face.Append(be_to_face[i]);
11215 int *e = bel_to_edge->GetRow(i);
11216 int ne = bel_to_edge->RowSize(i);
11217 int *new_e = new_bel_to_edge->GetRow(row);
11218 for (int j = 0; j < ne; j++)
11219 {
11220 new_e[j] = e[j];
11221 }
11222 new_bel_to_edge->GetI()[row+1] = new_bel_to_edge->GetI()[row] + ne;
11223 }
11224 }
11225 }
11226
11227 NumOfBdrElements = new_boundary.Size();
11228 mfem::Swap(boundary, new_boundary);
11229
11230 if (Dim == 2)
11231 {
11232 mfem::Swap(be_to_edge, new_be_to_edge);
11233 }
11234 else if (Dim == 3)
11235 {
11236 mfem::Swap(be_to_face, new_be_to_face);
11237 delete bel_to_edge;
11238 bel_to_edge = new_bel_to_edge;
11239 }
11240
11241 Array<int> attribs(num_bdr_elem);
11242 for (int i = 0; i < attribs.Size(); i++)
11243 {
11244 attribs[i] = GetBdrAttribute(i);
11245 }
11246 attribs.Sort();
11247 attribs.Unique();
11248 bdr_attributes.DeleteAll();
11249 attribs.Copy(bdr_attributes);
11250 }
11251
FreeElement(Element * E)11252 void Mesh::FreeElement(Element *E)
11253 {
11254 #ifdef MFEM_USE_MEMALLOC
11255 if (E)
11256 {
11257 if (E->GetType() == Element::TETRAHEDRON)
11258 {
11259 TetMemory.Free((Tetrahedron*) E);
11260 }
11261 else
11262 {
11263 delete E;
11264 }
11265 }
11266 #else
11267 delete E;
11268 #endif
11269 }
11270
operator <<(std::ostream & out,const Mesh & mesh)11271 std::ostream &operator<<(std::ostream &out, const Mesh &mesh)
11272 {
11273 mesh.Print(out);
11274 return out;
11275 }
11276
FindPoints(DenseMatrix & point_mat,Array<int> & elem_ids,Array<IntegrationPoint> & ips,bool warn,InverseElementTransformation * inv_trans)11277 int Mesh::FindPoints(DenseMatrix &point_mat, Array<int>& elem_ids,
11278 Array<IntegrationPoint>& ips, bool warn,
11279 InverseElementTransformation *inv_trans)
11280 {
11281 const int npts = point_mat.Width();
11282 if (!npts) { return 0; }
11283 MFEM_VERIFY(point_mat.Height() == spaceDim,"Invalid points matrix");
11284 elem_ids.SetSize(npts);
11285 ips.SetSize(npts);
11286 elem_ids = -1;
11287 if (!GetNE()) { return 0; }
11288
11289 double *data = point_mat.GetData();
11290 InverseElementTransformation *inv_tr = inv_trans;
11291 inv_tr = inv_tr ? inv_tr : new InverseElementTransformation;
11292
11293 // For each point in 'point_mat', find the element whose center is closest.
11294 Vector min_dist(npts);
11295 Array<int> e_idx(npts);
11296 min_dist = std::numeric_limits<double>::max();
11297 e_idx = -1;
11298
11299 Vector pt(spaceDim);
11300 for (int i = 0; i < GetNE(); i++)
11301 {
11302 GetElementTransformation(i)->Transform(
11303 Geometries.GetCenter(GetElementBaseGeometry(i)), pt);
11304 for (int k = 0; k < npts; k++)
11305 {
11306 double dist = pt.DistanceTo(data+k*spaceDim);
11307 if (dist < min_dist(k))
11308 {
11309 min_dist(k) = dist;
11310 e_idx[k] = i;
11311 }
11312 }
11313 }
11314
11315 // Checks if the points lie in the closest element
11316 int pts_found = 0;
11317 pt.NewDataAndSize(NULL, spaceDim);
11318 for (int k = 0; k < npts; k++)
11319 {
11320 pt.SetData(data+k*spaceDim);
11321 inv_tr->SetTransformation(*GetElementTransformation(e_idx[k]));
11322 int res = inv_tr->Transform(pt, ips[k]);
11323 if (res == InverseElementTransformation::Inside)
11324 {
11325 elem_ids[k] = e_idx[k];
11326 pts_found++;
11327 }
11328 }
11329 if (pts_found != npts)
11330 {
11331 Array<int> vertices;
11332 Table *vtoel = GetVertexToElementTable();
11333 for (int k = 0; k < npts; k++)
11334 {
11335 if (elem_ids[k] != -1) { continue; }
11336 // Try all vertex-neighbors of element e_idx[k]
11337 pt.SetData(data+k*spaceDim);
11338 GetElementVertices(e_idx[k], vertices);
11339 for (int v = 0; v < vertices.Size(); v++)
11340 {
11341 int vv = vertices[v];
11342 int ne = vtoel->RowSize(vv);
11343 const int* els = vtoel->GetRow(vv);
11344 for (int e = 0; e < ne; e++)
11345 {
11346 if (els[e] == e_idx[k]) { continue; }
11347 inv_tr->SetTransformation(*GetElementTransformation(els[e]));
11348 int res = inv_tr->Transform(pt, ips[k]);
11349 if (res == InverseElementTransformation::Inside)
11350 {
11351 elem_ids[k] = els[e];
11352 pts_found++;
11353 goto next_point;
11354 }
11355 }
11356 }
11357 // Try neighbors for non-conforming meshes
11358 if (ncmesh)
11359 {
11360 Array<int> neigh;
11361 int le = ncmesh->leaf_elements[e_idx[k]];
11362 ncmesh->FindNeighbors(le,neigh);
11363 for (int e = 0; e < neigh.Size(); e++)
11364 {
11365 int nn = neigh[e];
11366 if (ncmesh->IsGhost(ncmesh->elements[nn])) { continue; }
11367 int el = ncmesh->elements[nn].index;
11368 inv_tr->SetTransformation(*GetElementTransformation(el));
11369 int res = inv_tr->Transform(pt, ips[k]);
11370 if (res == InverseElementTransformation::Inside)
11371 {
11372 elem_ids[k] = el;
11373 pts_found++;
11374 goto next_point;
11375 }
11376 }
11377 }
11378 next_point: ;
11379 }
11380 delete vtoel;
11381 }
11382 if (inv_trans == NULL) { delete inv_tr; }
11383
11384 if (warn && pts_found != npts)
11385 {
11386 MFEM_WARNING((npts-pts_found) << " points were not found");
11387 }
11388 return pts_found;
11389 }
11390
11391
GeometricFactors(const Mesh * mesh,const IntegrationRule & ir,int flags,MemoryType d_mt)11392 GeometricFactors::GeometricFactors(const Mesh *mesh, const IntegrationRule &ir,
11393 int flags, MemoryType d_mt)
11394 {
11395 this->mesh = mesh;
11396 IntRule = &ir;
11397 computed_factors = flags;
11398
11399 MFEM_ASSERT(mesh->GetNumGeometries(mesh->Dimension()) <= 1,
11400 "mixed meshes are not supported!");
11401 MFEM_ASSERT(mesh->GetNodes(), "meshes without nodes are not supported!");
11402
11403 Compute(*mesh->GetNodes(), d_mt);
11404 }
11405
GeometricFactors(const GridFunction & nodes,const IntegrationRule & ir,int flags,MemoryType d_mt)11406 GeometricFactors::GeometricFactors(const GridFunction &nodes,
11407 const IntegrationRule &ir,
11408 int flags, MemoryType d_mt)
11409 {
11410 this->mesh = nodes.FESpace()->GetMesh();
11411 IntRule = &ir;
11412 computed_factors = flags;
11413
11414 Compute(nodes, d_mt);
11415 }
11416
Compute(const GridFunction & nodes,MemoryType d_mt)11417 void GeometricFactors::Compute(const GridFunction &nodes,
11418 MemoryType d_mt)
11419 {
11420
11421 const FiniteElementSpace *fespace = nodes.FESpace();
11422 const FiniteElement *fe = fespace->GetFE(0);
11423 const int dim = fe->GetDim();
11424 const int vdim = fespace->GetVDim();
11425 const int NE = fespace->GetNE();
11426 const int ND = fe->GetDof();
11427 const int NQ = IntRule->GetNPoints();
11428
11429 unsigned eval_flags = 0;
11430 MemoryType my_d_mt = (d_mt != MemoryType::DEFAULT) ? d_mt :
11431 Device::GetDeviceMemoryType();
11432 if (computed_factors & GeometricFactors::COORDINATES)
11433 {
11434 X.SetSize(vdim*NQ*NE, my_d_mt); // NQ x SDIM x NE
11435 eval_flags |= QuadratureInterpolator::VALUES;
11436 }
11437 if (computed_factors & GeometricFactors::JACOBIANS)
11438 {
11439 J.SetSize(dim*vdim*NQ*NE, my_d_mt); // NQ x SDIM x DIM x NE
11440 eval_flags |= QuadratureInterpolator::DERIVATIVES;
11441 }
11442 if (computed_factors & GeometricFactors::DETERMINANTS)
11443 {
11444 detJ.SetSize(NQ*NE, my_d_mt); // NQ x NE
11445 eval_flags |= QuadratureInterpolator::DETERMINANTS;
11446 }
11447
11448 const QuadratureInterpolator *qi = fespace->GetQuadratureInterpolator(*IntRule);
11449 // All X, J, and detJ use this layout:
11450 qi->SetOutputLayout(QVectorLayout::byNODES);
11451
11452 const bool use_tensor_products = UsesTensorBasis(*fespace);
11453
11454 qi->DisableTensorProducts(!use_tensor_products);
11455 const ElementDofOrdering e_ordering = use_tensor_products ?
11456 ElementDofOrdering::LEXICOGRAPHIC :
11457 ElementDofOrdering::NATIVE;
11458 const Operator *elem_restr = fespace->GetElementRestriction(e_ordering);
11459
11460 if (elem_restr) // Always true as of 2021-04-27
11461 {
11462 Vector Enodes(vdim*ND*NE, my_d_mt);
11463 elem_restr->Mult(nodes, Enodes);
11464 qi->Mult(Enodes, eval_flags, X, J, detJ);
11465 }
11466 else
11467 {
11468 qi->Mult(nodes, eval_flags, X, J, detJ);
11469 }
11470 }
11471
FaceGeometricFactors(const Mesh * mesh,const IntegrationRule & ir,int flags,FaceType type)11472 FaceGeometricFactors::FaceGeometricFactors(const Mesh *mesh,
11473 const IntegrationRule &ir,
11474 int flags, FaceType type)
11475 : type(type)
11476 {
11477 this->mesh = mesh;
11478 IntRule = &ir;
11479 computed_factors = flags;
11480
11481 const GridFunction *nodes = mesh->GetNodes();
11482 const FiniteElementSpace *fespace = nodes->FESpace();
11483 const int vdim = fespace->GetVDim();
11484 const int NF = fespace->GetNFbyType(type);
11485 const int NQ = ir.GetNPoints();
11486
11487 const FaceRestriction *face_restr = fespace->GetFaceRestriction(
11488 ElementDofOrdering::LEXICOGRAPHIC,
11489 type,
11490 L2FaceValues::SingleValued );
11491 Vector Fnodes(face_restr->Height());
11492 face_restr->Mult(*nodes, Fnodes);
11493
11494 unsigned eval_flags = 0;
11495 if (flags & FaceGeometricFactors::COORDINATES)
11496 {
11497 X.SetSize(vdim*NQ*NF);
11498 eval_flags |= FaceQuadratureInterpolator::VALUES;
11499 }
11500 if (flags & FaceGeometricFactors::JACOBIANS)
11501 {
11502 J.SetSize(vdim*vdim*NQ*NF);
11503 eval_flags |= FaceQuadratureInterpolator::DERIVATIVES;
11504 }
11505 if (flags & FaceGeometricFactors::DETERMINANTS)
11506 {
11507 detJ.SetSize(NQ*NF);
11508 eval_flags |= FaceQuadratureInterpolator::DETERMINANTS;
11509 }
11510 if (flags & FaceGeometricFactors::NORMALS)
11511 {
11512 normal.SetSize(vdim*NQ*NF);
11513 eval_flags |= FaceQuadratureInterpolator::NORMALS;
11514 }
11515
11516 const FaceQuadratureInterpolator *qi = fespace->GetFaceQuadratureInterpolator(
11517 ir, type);
11518 qi->Mult(Fnodes, eval_flags, X, J, detJ, normal);
11519 }
11520
NodeExtrudeCoefficient(const int dim,const int n_,const double s_)11521 NodeExtrudeCoefficient::NodeExtrudeCoefficient(const int dim, const int n_,
11522 const double s_)
11523 : VectorCoefficient(dim), n(n_), s(s_), tip(p, dim-1)
11524 {
11525 }
11526
Eval(Vector & V,ElementTransformation & T,const IntegrationPoint & ip)11527 void NodeExtrudeCoefficient::Eval(Vector &V, ElementTransformation &T,
11528 const IntegrationPoint &ip)
11529 {
11530 V.SetSize(vdim);
11531 T.Transform(ip, tip);
11532 V(0) = p[0];
11533 if (vdim == 2)
11534 {
11535 V(1) = s * ((ip.y + layer) / n);
11536 }
11537 else
11538 {
11539 V(1) = p[1];
11540 V(2) = s * ((ip.z + layer) / n);
11541 }
11542 }
11543
11544
Extrude1D(Mesh * mesh,const int ny,const double sy,const bool closed)11545 Mesh *Extrude1D(Mesh *mesh, const int ny, const double sy, const bool closed)
11546 {
11547 if (mesh->Dimension() != 1)
11548 {
11549 mfem::err << "Extrude1D : Not a 1D mesh!" << endl;
11550 mfem_error();
11551 }
11552
11553 int nvy = (closed) ? (ny) : (ny + 1);
11554 int nvt = mesh->GetNV() * nvy;
11555
11556 Mesh *mesh2d;
11557
11558 if (closed)
11559 {
11560 mesh2d = new Mesh(2, nvt, mesh->GetNE()*ny, mesh->GetNBE()*ny);
11561 }
11562 else
11563 mesh2d = new Mesh(2, nvt, mesh->GetNE()*ny,
11564 mesh->GetNBE()*ny+2*mesh->GetNE());
11565
11566 // vertices
11567 double vc[2];
11568 for (int i = 0; i < mesh->GetNV(); i++)
11569 {
11570 vc[0] = mesh->GetVertex(i)[0];
11571 for (int j = 0; j < nvy; j++)
11572 {
11573 vc[1] = sy * (double(j) / ny);
11574 mesh2d->AddVertex(vc);
11575 }
11576 }
11577 // elements
11578 Array<int> vert;
11579 for (int i = 0; i < mesh->GetNE(); i++)
11580 {
11581 const Element *elem = mesh->GetElement(i);
11582 elem->GetVertices(vert);
11583 const int attr = elem->GetAttribute();
11584 for (int j = 0; j < ny; j++)
11585 {
11586 int qv[4];
11587 qv[0] = vert[0] * nvy + j;
11588 qv[1] = vert[1] * nvy + j;
11589 qv[2] = vert[1] * nvy + (j + 1) % nvy;
11590 qv[3] = vert[0] * nvy + (j + 1) % nvy;
11591
11592 mesh2d->AddQuad(qv, attr);
11593 }
11594 }
11595 // 2D boundary from the 1D boundary
11596 for (int i = 0; i < mesh->GetNBE(); i++)
11597 {
11598 const Element *elem = mesh->GetBdrElement(i);
11599 elem->GetVertices(vert);
11600 const int attr = elem->GetAttribute();
11601 for (int j = 0; j < ny; j++)
11602 {
11603 int sv[2];
11604 sv[0] = vert[0] * nvy + j;
11605 sv[1] = vert[0] * nvy + (j + 1) % nvy;
11606
11607 if (attr%2)
11608 {
11609 Swap<int>(sv[0], sv[1]);
11610 }
11611
11612 mesh2d->AddBdrSegment(sv, attr);
11613 }
11614 }
11615
11616 if (!closed)
11617 {
11618 // 2D boundary from the 1D elements (bottom + top)
11619 int nba = (mesh->bdr_attributes.Size() > 0 ?
11620 mesh->bdr_attributes.Max() : 0);
11621 for (int i = 0; i < mesh->GetNE(); i++)
11622 {
11623 const Element *elem = mesh->GetElement(i);
11624 elem->GetVertices(vert);
11625 const int attr = nba + elem->GetAttribute();
11626 int sv[2];
11627 sv[0] = vert[0] * nvy;
11628 sv[1] = vert[1] * nvy;
11629
11630 mesh2d->AddBdrSegment(sv, attr);
11631
11632 sv[0] = vert[1] * nvy + ny;
11633 sv[1] = vert[0] * nvy + ny;
11634
11635 mesh2d->AddBdrSegment(sv, attr);
11636 }
11637 }
11638
11639 mesh2d->FinalizeQuadMesh(1, 0, false);
11640
11641 GridFunction *nodes = mesh->GetNodes();
11642 if (nodes)
11643 {
11644 // duplicate the fec of the 1D mesh so that it can be deleted safely
11645 // along with its nodes, fes and fec
11646 FiniteElementCollection *fec2d = NULL;
11647 FiniteElementSpace *fes2d;
11648 const char *name = nodes->FESpace()->FEColl()->Name();
11649 string cname = name;
11650 if (cname == "Linear")
11651 {
11652 fec2d = new LinearFECollection;
11653 }
11654 else if (cname == "Quadratic")
11655 {
11656 fec2d = new QuadraticFECollection;
11657 }
11658 else if (cname == "Cubic")
11659 {
11660 fec2d = new CubicFECollection;
11661 }
11662 else if (!strncmp(name, "H1_", 3))
11663 {
11664 fec2d = new H1_FECollection(atoi(name + 7), 2);
11665 }
11666 else if (!strncmp(name, "L2_T", 4))
11667 {
11668 fec2d = new L2_FECollection(atoi(name + 10), 2, atoi(name + 4));
11669 }
11670 else if (!strncmp(name, "L2_", 3))
11671 {
11672 fec2d = new L2_FECollection(atoi(name + 7), 2);
11673 }
11674 else
11675 {
11676 delete mesh2d;
11677 mfem::err << "Extrude1D : The mesh uses unknown FE collection : "
11678 << cname << endl;
11679 mfem_error();
11680 }
11681 fes2d = new FiniteElementSpace(mesh2d, fec2d, 2);
11682 mesh2d->SetNodalFESpace(fes2d);
11683 GridFunction *nodes2d = mesh2d->GetNodes();
11684 nodes2d->MakeOwner(fec2d);
11685
11686 NodeExtrudeCoefficient ecoeff(2, ny, sy);
11687 Vector lnodes;
11688 Array<int> vdofs2d;
11689 for (int i = 0; i < mesh->GetNE(); i++)
11690 {
11691 ElementTransformation &T = *mesh->GetElementTransformation(i);
11692 for (int j = ny-1; j >= 0; j--)
11693 {
11694 fes2d->GetElementVDofs(i*ny+j, vdofs2d);
11695 lnodes.SetSize(vdofs2d.Size());
11696 ecoeff.SetLayer(j);
11697 fes2d->GetFE(i*ny+j)->Project(ecoeff, T, lnodes);
11698 nodes2d->SetSubVector(vdofs2d, lnodes);
11699 }
11700 }
11701 }
11702 return mesh2d;
11703 }
11704
Extrude2D(Mesh * mesh,const int nz,const double sz)11705 Mesh *Extrude2D(Mesh *mesh, const int nz, const double sz)
11706 {
11707 if (mesh->Dimension() != 2)
11708 {
11709 mfem::err << "Extrude2D : Not a 2D mesh!" << endl;
11710 mfem_error();
11711 }
11712
11713 int nvz = nz + 1;
11714 int nvt = mesh->GetNV() * nvz;
11715
11716 Mesh *mesh3d = new Mesh(3, nvt, mesh->GetNE()*nz,
11717 mesh->GetNBE()*nz+2*mesh->GetNE());
11718
11719 bool wdgMesh = false;
11720 bool hexMesh = false;
11721
11722 // vertices
11723 double vc[3];
11724 for (int i = 0; i < mesh->GetNV(); i++)
11725 {
11726 vc[0] = mesh->GetVertex(i)[0];
11727 vc[1] = mesh->GetVertex(i)[1];
11728 for (int j = 0; j < nvz; j++)
11729 {
11730 vc[2] = sz * (double(j) / nz);
11731 mesh3d->AddVertex(vc);
11732 }
11733 }
11734 // elements
11735 Array<int> vert;
11736 for (int i = 0; i < mesh->GetNE(); i++)
11737 {
11738 const Element *elem = mesh->GetElement(i);
11739 elem->GetVertices(vert);
11740 const int attr = elem->GetAttribute();
11741 Geometry::Type geom = elem->GetGeometryType();
11742 switch (geom)
11743 {
11744 case Geometry::TRIANGLE:
11745 wdgMesh = true;
11746 for (int j = 0; j < nz; j++)
11747 {
11748 int pv[6];
11749 pv[0] = vert[0] * nvz + j;
11750 pv[1] = vert[1] * nvz + j;
11751 pv[2] = vert[2] * nvz + j;
11752 pv[3] = vert[0] * nvz + (j + 1) % nvz;
11753 pv[4] = vert[1] * nvz + (j + 1) % nvz;
11754 pv[5] = vert[2] * nvz + (j + 1) % nvz;
11755
11756 mesh3d->AddWedge(pv, attr);
11757 }
11758 break;
11759 case Geometry::SQUARE:
11760 hexMesh = true;
11761 for (int j = 0; j < nz; j++)
11762 {
11763 int hv[8];
11764 hv[0] = vert[0] * nvz + j;
11765 hv[1] = vert[1] * nvz + j;
11766 hv[2] = vert[2] * nvz + j;
11767 hv[3] = vert[3] * nvz + j;
11768 hv[4] = vert[0] * nvz + (j + 1) % nvz;
11769 hv[5] = vert[1] * nvz + (j + 1) % nvz;
11770 hv[6] = vert[2] * nvz + (j + 1) % nvz;
11771 hv[7] = vert[3] * nvz + (j + 1) % nvz;
11772
11773 mesh3d->AddHex(hv, attr);
11774 }
11775 break;
11776 default:
11777 mfem::err << "Extrude2D : Invalid 2D element type \'"
11778 << geom << "\'" << endl;
11779 mfem_error();
11780 break;
11781 }
11782 }
11783 // 3D boundary from the 2D boundary
11784 for (int i = 0; i < mesh->GetNBE(); i++)
11785 {
11786 const Element *elem = mesh->GetBdrElement(i);
11787 elem->GetVertices(vert);
11788 const int attr = elem->GetAttribute();
11789 for (int j = 0; j < nz; j++)
11790 {
11791 int qv[4];
11792 qv[0] = vert[0] * nvz + j;
11793 qv[1] = vert[1] * nvz + j;
11794 qv[2] = vert[1] * nvz + (j + 1) % nvz;
11795 qv[3] = vert[0] * nvz + (j + 1) % nvz;
11796
11797 mesh3d->AddBdrQuad(qv, attr);
11798 }
11799 }
11800
11801 // 3D boundary from the 2D elements (bottom + top)
11802 int nba = (mesh->bdr_attributes.Size() > 0 ?
11803 mesh->bdr_attributes.Max() : 0);
11804 for (int i = 0; i < mesh->GetNE(); i++)
11805 {
11806 const Element *elem = mesh->GetElement(i);
11807 elem->GetVertices(vert);
11808 const int attr = nba + elem->GetAttribute();
11809 Geometry::Type geom = elem->GetGeometryType();
11810 switch (geom)
11811 {
11812 case Geometry::TRIANGLE:
11813 {
11814 int tv[3];
11815 tv[0] = vert[0] * nvz;
11816 tv[1] = vert[2] * nvz;
11817 tv[2] = vert[1] * nvz;
11818
11819 mesh3d->AddBdrTriangle(tv, attr);
11820
11821 tv[0] = vert[0] * nvz + nz;
11822 tv[1] = vert[1] * nvz + nz;
11823 tv[2] = vert[2] * nvz + nz;
11824
11825 mesh3d->AddBdrTriangle(tv, attr);
11826 }
11827 break;
11828 case Geometry::SQUARE:
11829 {
11830 int qv[4];
11831 qv[0] = vert[0] * nvz;
11832 qv[1] = vert[3] * nvz;
11833 qv[2] = vert[2] * nvz;
11834 qv[3] = vert[1] * nvz;
11835
11836 mesh3d->AddBdrQuad(qv, attr);
11837
11838 qv[0] = vert[0] * nvz + nz;
11839 qv[1] = vert[1] * nvz + nz;
11840 qv[2] = vert[2] * nvz + nz;
11841 qv[3] = vert[3] * nvz + nz;
11842
11843 mesh3d->AddBdrQuad(qv, attr);
11844 }
11845 break;
11846 default:
11847 mfem::err << "Extrude2D : Invalid 2D element type \'"
11848 << geom << "\'" << endl;
11849 mfem_error();
11850 break;
11851 }
11852 }
11853
11854 if ( hexMesh && wdgMesh )
11855 {
11856 mesh3d->FinalizeMesh(0, false);
11857 }
11858 else if ( hexMesh )
11859 {
11860 mesh3d->FinalizeHexMesh(1, 0, false);
11861 }
11862 else if ( wdgMesh )
11863 {
11864 mesh3d->FinalizeWedgeMesh(1, 0, false);
11865 }
11866
11867 GridFunction *nodes = mesh->GetNodes();
11868 if (nodes)
11869 {
11870 // duplicate the fec of the 2D mesh so that it can be deleted safely
11871 // along with its nodes, fes and fec
11872 FiniteElementCollection *fec3d = NULL;
11873 FiniteElementSpace *fes3d;
11874 const char *name = nodes->FESpace()->FEColl()->Name();
11875 string cname = name;
11876 if (cname == "Linear")
11877 {
11878 fec3d = new LinearFECollection;
11879 }
11880 else if (cname == "Quadratic")
11881 {
11882 fec3d = new QuadraticFECollection;
11883 }
11884 else if (cname == "Cubic")
11885 {
11886 fec3d = new CubicFECollection;
11887 }
11888 else if (!strncmp(name, "H1_", 3))
11889 {
11890 fec3d = new H1_FECollection(atoi(name + 7), 3);
11891 }
11892 else if (!strncmp(name, "L2_T", 4))
11893 {
11894 fec3d = new L2_FECollection(atoi(name + 10), 3, atoi(name + 4));
11895 }
11896 else if (!strncmp(name, "L2_", 3))
11897 {
11898 fec3d = new L2_FECollection(atoi(name + 7), 3);
11899 }
11900 else
11901 {
11902 delete mesh3d;
11903 mfem::err << "Extrude3D : The mesh uses unknown FE collection : "
11904 << cname << endl;
11905 mfem_error();
11906 }
11907 fes3d = new FiniteElementSpace(mesh3d, fec3d, 3);
11908 mesh3d->SetNodalFESpace(fes3d);
11909 GridFunction *nodes3d = mesh3d->GetNodes();
11910 nodes3d->MakeOwner(fec3d);
11911
11912 NodeExtrudeCoefficient ecoeff(3, nz, sz);
11913 Vector lnodes;
11914 Array<int> vdofs3d;
11915 for (int i = 0; i < mesh->GetNE(); i++)
11916 {
11917 ElementTransformation &T = *mesh->GetElementTransformation(i);
11918 for (int j = nz-1; j >= 0; j--)
11919 {
11920 fes3d->GetElementVDofs(i*nz+j, vdofs3d);
11921 lnodes.SetSize(vdofs3d.Size());
11922 ecoeff.SetLayer(j);
11923 fes3d->GetFE(i*nz+j)->Project(ecoeff, T, lnodes);
11924 nodes3d->SetSubVector(vdofs3d, lnodes);
11925 }
11926 }
11927 }
11928 return mesh3d;
11929 }
11930
11931 #ifdef MFEM_DEBUG
DebugDump(std::ostream & out) const11932 void Mesh::DebugDump(std::ostream &out) const
11933 {
11934 // dump vertices and edges (NCMesh "nodes")
11935 out << NumOfVertices + NumOfEdges << "\n";
11936 for (int i = 0; i < NumOfVertices; i++)
11937 {
11938 const double *v = GetVertex(i);
11939 out << i << " " << v[0] << " " << v[1] << " " << v[2]
11940 << " 0 0 " << i << " -1 0\n";
11941 }
11942
11943 Array<int> ev;
11944 for (int i = 0; i < NumOfEdges; i++)
11945 {
11946 GetEdgeVertices(i, ev);
11947 double mid[3] = {0, 0, 0};
11948 for (int j = 0; j < 2; j++)
11949 {
11950 for (int k = 0; k < spaceDim; k++)
11951 {
11952 mid[k] += GetVertex(ev[j])[k];
11953 }
11954 }
11955 out << NumOfVertices+i << " "
11956 << mid[0]/2 << " " << mid[1]/2 << " " << mid[2]/2 << " "
11957 << ev[0] << " " << ev[1] << " -1 " << i << " 0\n";
11958 }
11959
11960 // dump elements
11961 out << NumOfElements << "\n";
11962 for (int i = 0; i < NumOfElements; i++)
11963 {
11964 const Element* e = elements[i];
11965 out << e->GetNVertices() << " ";
11966 for (int j = 0; j < e->GetNVertices(); j++)
11967 {
11968 out << e->GetVertices()[j] << " ";
11969 }
11970 out << e->GetAttribute() << " 0 " << i << "\n";
11971 }
11972
11973 // dump faces
11974 out << "0\n";
11975 }
11976 #endif
11977
11978 }
11979