Files
mfem/mesh/mesh.cpp
T

10995 lines
293 KiB
C++

// Copyright (c) 2010-2020, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
// Implementation of data type mesh
#include "mesh_headers.hpp"
#include "../fem/fem.hpp"
#include "../general/sort_pairs.hpp"
#include "../general/binaryio.hpp"
#include "../general/text.hpp"
#include "../general/device.hpp"
#include "../general/tic_toc.hpp"
#include "../general/gecko.hpp"
#include "../fem/quadinterpolator.hpp"
#include <iostream>
#include <sstream>
#include <fstream>
#include <limits>
#include <cmath>
#include <cstring>
#include <ctime>
#include <functional>
// Include the METIS header, if using version 5. If using METIS 4, the needed
// declarations are inlined below, i.e. no header is needed.
#if defined(MFEM_USE_METIS) && defined(MFEM_USE_METIS_5)
#include "metis.h"
#endif
// METIS 4 prototypes
#if defined(MFEM_USE_METIS) && !defined(MFEM_USE_METIS_5)
typedef int idx_t;
typedef int idxtype;
extern "C" {
void METIS_PartGraphRecursive(int*, idxtype*, idxtype*, idxtype*, idxtype*,
int*, int*, int*, int*, int*, idxtype*);
void METIS_PartGraphKway(int*, idxtype*, idxtype*, idxtype*, idxtype*,
int*, int*, int*, int*, int*, idxtype*);
void METIS_PartGraphVKway(int*, idxtype*, idxtype*, idxtype*, idxtype*,
int*, int*, int*, int*, int*, idxtype*);
}
#endif
using namespace std;
namespace mfem
{
void Mesh::GetElementJacobian(int i, DenseMatrix &J)
{
Geometry::Type geom = GetElementBaseGeometry(i);
ElementTransformation *eltransf = GetElementTransformation(i);
eltransf->SetIntPoint(&Geometries.GetCenter(geom));
Geometries.JacToPerfJac(geom, eltransf->Jacobian(), J);
}
void Mesh::GetElementCenter(int i, Vector &center)
{
center.SetSize(spaceDim);
int geom = GetElementBaseGeometry(i);
ElementTransformation *eltransf = GetElementTransformation(i);
eltransf->Transform(Geometries.GetCenter(geom), center);
}
double Mesh::GetElementSize(int i, int type)
{
DenseMatrix J(Dim);
GetElementJacobian(i, J);
if (type == 0)
{
return pow(fabs(J.Det()), 1./Dim);
}
else if (type == 1)
{
return J.CalcSingularvalue(Dim-1); // h_min
}
else
{
return J.CalcSingularvalue(0); // h_max
}
}
double Mesh::GetElementSize(int i, const Vector &dir)
{
DenseMatrix J(Dim);
Vector d_hat(Dim);
GetElementJacobian(i, J);
J.MultTranspose(dir, d_hat);
return sqrt((d_hat * d_hat) / (dir * dir));
}
double Mesh::GetElementVolume(int i)
{
ElementTransformation *et = GetElementTransformation(i);
const IntegrationRule &ir = IntRules.Get(GetElementBaseGeometry(i),
et->OrderJ());
double volume = 0.0;
for (int j = 0; j < ir.GetNPoints(); j++)
{
const IntegrationPoint &ip = ir.IntPoint(j);
et->SetIntPoint(&ip);
volume += ip.weight * et->Weight();
}
return volume;
}
// Similar to VisualizationSceneSolution3d::FindNewBox in GLVis
void Mesh::GetBoundingBox(Vector &min, Vector &max, int ref)
{
min.SetSize(spaceDim);
max.SetSize(spaceDim);
for (int d = 0; d < spaceDim; d++)
{
min(d) = infinity();
max(d) = -infinity();
}
if (Nodes == NULL)
{
double *coord;
for (int i = 0; i < NumOfVertices; i++)
{
coord = GetVertex(i);
for (int d = 0; d < spaceDim; d++)
{
if (coord[d] < min(d)) { min(d) = coord[d]; }
if (coord[d] > max(d)) { max(d) = coord[d]; }
}
}
}
else
{
const bool use_boundary = false; // make this a parameter?
int ne = use_boundary ? GetNBE() : GetNE();
int fn, fo;
DenseMatrix pointmat;
RefinedGeometry *RefG;
IntegrationRule eir;
FaceElementTransformations *Tr;
ElementTransformation *T;
for (int i = 0; i < ne; i++)
{
if (use_boundary)
{
GetBdrElementFace(i, &fn, &fo);
RefG = GlobGeometryRefiner.Refine(GetFaceBaseGeometry(fn), ref);
Tr = GetFaceElementTransformations(fn, 5);
eir.SetSize(RefG->RefPts.GetNPoints());
Tr->Loc1.Transform(RefG->RefPts, eir);
Tr->Elem1->Transform(eir, pointmat);
}
else
{
T = GetElementTransformation(i);
RefG = GlobGeometryRefiner.Refine(GetElementBaseGeometry(i), ref);
T->Transform(RefG->RefPts, pointmat);
}
for (int j = 0; j < pointmat.Width(); j++)
{
for (int d = 0; d < pointmat.Height(); d++)
{
if (pointmat(d,j) < min(d)) { min(d) = pointmat(d,j); }
if (pointmat(d,j) > max(d)) { max(d) = pointmat(d,j); }
}
}
}
}
}
void Mesh::GetCharacteristics(double &h_min, double &h_max,
double &kappa_min, double &kappa_max,
Vector *Vh, Vector *Vk)
{
int i, dim, sdim;
DenseMatrix J;
double h, kappa;
dim = Dimension();
sdim = SpaceDimension();
if (Vh) { Vh->SetSize(NumOfElements); }
if (Vk) { Vk->SetSize(NumOfElements); }
h_min = kappa_min = infinity();
h_max = kappa_max = -h_min;
if (dim == 0) { if (Vh) { *Vh = 1.0; } if (Vk) {*Vk = 1.0; } return; }
J.SetSize(sdim, dim);
for (i = 0; i < NumOfElements; i++)
{
GetElementJacobian(i, J);
h = pow(fabs(J.Weight()), 1.0/double(dim));
kappa = (dim == sdim) ?
J.CalcSingularvalue(0) / J.CalcSingularvalue(dim-1) : -1.0;
if (Vh) { (*Vh)(i) = h; }
if (Vk) { (*Vk)(i) = kappa; }
if (h < h_min) { h_min = h; }
if (h > h_max) { h_max = h; }
if (kappa < kappa_min) { kappa_min = kappa; }
if (kappa > kappa_max) { kappa_max = kappa; }
}
}
// static method
void Mesh::PrintElementsByGeometry(int dim,
const Array<int> &num_elems_by_geom,
std::ostream &out)
{
for (int g = Geometry::DimStart[dim], first = 1;
g < Geometry::DimStart[dim+1]; g++)
{
if (!num_elems_by_geom[g]) { continue; }
if (!first) { out << " + "; }
else { first = 0; }
out << num_elems_by_geom[g] << ' ' << Geometry::Name[g] << "(s)";
}
}
void Mesh::PrintCharacteristics(Vector *Vh, Vector *Vk, std::ostream &out)
{
double h_min, h_max, kappa_min, kappa_max;
out << "Mesh Characteristics:";
this->GetCharacteristics(h_min, h_max, kappa_min, kappa_max, Vh, Vk);
Array<int> num_elems_by_geom(Geometry::NumGeom);
num_elems_by_geom = 0;
for (int i = 0; i < GetNE(); i++)
{
num_elems_by_geom[GetElementBaseGeometry(i)]++;
}
out << '\n'
<< "Dimension : " << Dimension() << '\n'
<< "Space dimension : " << SpaceDimension();
if (Dim == 0)
{
out << '\n'
<< "Number of vertices : " << GetNV() << '\n'
<< "Number of elements : " << GetNE() << '\n'
<< "Number of bdr elem : " << GetNBE() << '\n';
}
else if (Dim == 1)
{
out << '\n'
<< "Number of vertices : " << GetNV() << '\n'
<< "Number of elements : " << GetNE() << '\n'
<< "Number of bdr elem : " << GetNBE() << '\n'
<< "h_min : " << h_min << '\n'
<< "h_max : " << h_max << '\n';
}
else if (Dim == 2)
{
out << '\n'
<< "Number of vertices : " << GetNV() << '\n'
<< "Number of edges : " << GetNEdges() << '\n'
<< "Number of elements : " << GetNE() << " -- ";
PrintElementsByGeometry(2, num_elems_by_geom, out);
out << '\n'
<< "Number of bdr elem : " << GetNBE() << '\n'
<< "Euler Number : " << EulerNumber2D() << '\n'
<< "h_min : " << h_min << '\n'
<< "h_max : " << h_max << '\n'
<< "kappa_min : " << kappa_min << '\n'
<< "kappa_max : " << kappa_max << '\n';
}
else
{
Array<int> num_bdr_elems_by_geom(Geometry::NumGeom);
num_bdr_elems_by_geom = 0;
for (int i = 0; i < GetNBE(); i++)
{
num_bdr_elems_by_geom[GetBdrElementBaseGeometry(i)]++;
}
Array<int> num_faces_by_geom(Geometry::NumGeom);
num_faces_by_geom = 0;
for (int i = 0; i < GetNFaces(); i++)
{
num_faces_by_geom[GetFaceBaseGeometry(i)]++;
}
out << '\n'
<< "Number of vertices : " << GetNV() << '\n'
<< "Number of edges : " << GetNEdges() << '\n'
<< "Number of faces : " << GetNFaces() << " -- ";
PrintElementsByGeometry(Dim-1, num_faces_by_geom, out);
out << '\n'
<< "Number of elements : " << GetNE() << " -- ";
PrintElementsByGeometry(Dim, num_elems_by_geom, out);
out << '\n'
<< "Number of bdr elem : " << GetNBE() << " -- ";
PrintElementsByGeometry(Dim-1, num_bdr_elems_by_geom, out);
out << '\n'
<< "Euler Number : " << EulerNumber() << '\n'
<< "h_min : " << h_min << '\n'
<< "h_max : " << h_max << '\n'
<< "kappa_min : " << kappa_min << '\n'
<< "kappa_max : " << kappa_max << '\n';
}
out << '\n' << std::flush;
}
FiniteElement *Mesh::GetTransformationFEforElementType(Element::Type ElemType)
{
switch (ElemType)
{
case Element::POINT : return &PointFE;
case Element::SEGMENT : return &SegmentFE;
case Element::TRIANGLE : return &TriangleFE;
case Element::QUADRILATERAL : return &QuadrilateralFE;
case Element::TETRAHEDRON : return &TetrahedronFE;
case Element::HEXAHEDRON : return &HexahedronFE;
case Element::WEDGE : return &WedgeFE;
default:
MFEM_ABORT("Unknown element type \"" << ElemType << "\"");
break;
}
MFEM_ABORT("Unknown element type");
return NULL;
}
void Mesh::GetElementTransformation(int i, IsoparametricTransformation *ElTr)
{
ElTr->Attribute = GetAttribute(i);
ElTr->ElementNo = i;
ElTr->ElementType = ElementTransformation::ELEMENT;
if (Nodes == NULL)
{
GetPointMatrix(i, ElTr->GetPointMat());
ElTr->SetFE(GetTransformationFEforElementType(GetElementType(i)));
}
else
{
DenseMatrix &pm = ElTr->GetPointMat();
Array<int> vdofs;
Nodes->FESpace()->GetElementVDofs(i, vdofs);
Nodes->HostRead();
const GridFunction &nodes = *Nodes;
int n = vdofs.Size()/spaceDim;
pm.SetSize(spaceDim, n);
for (int k = 0; k < spaceDim; k++)
{
for (int j = 0; j < n; j++)
{
pm(k,j) = nodes(vdofs[n*k+j]);
}
}
ElTr->SetFE(Nodes->FESpace()->GetFE(i));
}
}
void Mesh::GetElementTransformation(int i, const Vector &nodes,
IsoparametricTransformation *ElTr)
{
ElTr->Attribute = GetAttribute(i);
ElTr->ElementNo = i;
ElTr->ElementType = ElementTransformation::ELEMENT;
DenseMatrix &pm = ElTr->GetPointMat();
nodes.HostRead();
if (Nodes == NULL)
{
MFEM_ASSERT(nodes.Size() == spaceDim*GetNV(), "");
int nv = elements[i]->GetNVertices();
const int *v = elements[i]->GetVertices();
int n = vertices.Size();
pm.SetSize(spaceDim, nv);
for (int k = 0; k < spaceDim; k++)
{
for (int j = 0; j < nv; j++)
{
pm(k, j) = nodes(k*n+v[j]);
}
}
ElTr->SetFE(GetTransformationFEforElementType(GetElementType(i)));
}
else
{
MFEM_ASSERT(nodes.Size() == Nodes->Size(), "");
Array<int> vdofs;
Nodes->FESpace()->GetElementVDofs(i, vdofs);
int n = vdofs.Size()/spaceDim;
pm.SetSize(spaceDim, n);
for (int k = 0; k < spaceDim; k++)
{
for (int j = 0; j < n; j++)
{
pm(k,j) = nodes(vdofs[n*k+j]);
}
}
ElTr->SetFE(Nodes->FESpace()->GetFE(i));
}
}
ElementTransformation *Mesh::GetElementTransformation(int i)
{
GetElementTransformation(i, &Transformation);
return &Transformation;
}
ElementTransformation *Mesh::GetBdrElementTransformation(int i)
{
GetBdrElementTransformation(i, &BdrTransformation);
return &BdrTransformation;
}
void Mesh::GetBdrElementTransformation(int i, IsoparametricTransformation* ElTr)
{
ElTr->Attribute = GetBdrAttribute(i);
ElTr->ElementNo = i; // boundary element number
ElTr->ElementType = ElementTransformation::BDR_ELEMENT;
DenseMatrix &pm = ElTr->GetPointMat();
if (Nodes == NULL)
{
GetBdrPointMatrix(i, pm);
ElTr->SetFE(GetTransformationFEforElementType(GetBdrElementType(i)));
}
else
{
const FiniteElement *bdr_el = Nodes->FESpace()->GetBE(i);
Nodes->HostRead();
const GridFunction &nodes = *Nodes;
if (bdr_el)
{
Array<int> vdofs;
Nodes->FESpace()->GetBdrElementVDofs(i, vdofs);
int n = vdofs.Size()/spaceDim;
pm.SetSize(spaceDim, n);
for (int k = 0; k < spaceDim; k++)
{
for (int j = 0; j < n; j++)
{
pm(k,j) = nodes(vdofs[n*k+j]);
}
}
ElTr->SetFE(bdr_el);
}
else // L2 Nodes (e.g., periodic mesh)
{
int elem_id, face_info;
GetBdrElementAdjacentElement(i, elem_id, face_info);
GetLocalFaceTransformation(GetBdrElementType(i),
GetElementType(elem_id),
FaceElemTr.Loc1.Transf, face_info);
// NOTE: FaceElemTr.Loc1 is overwritten here -- used as a temporary
const FiniteElement *face_el =
Nodes->FESpace()->GetTraceElement(elem_id,
GetBdrElementBaseGeometry(i));
IntegrationRule eir(face_el->GetDof());
FaceElemTr.Loc1.Transf.ElementNo = elem_id;
FaceElemTr.Loc1.Transf.ElementType = ElementTransformation::ELEMENT;
FaceElemTr.Loc1.Transform(face_el->GetNodes(), eir);
Nodes->GetVectorValues(FaceElemTr.Loc1.Transf, eir, pm);
ElTr->SetFE(face_el);
}
}
}
void Mesh::GetFaceTransformation(int FaceNo, IsoparametricTransformation *FTr)
{
FTr->Attribute = (Dim == 1) ? 1 : faces[FaceNo]->GetAttribute();
FTr->ElementNo = FaceNo;
FTr->ElementType = ElementTransformation::FACE;
DenseMatrix &pm = FTr->GetPointMat();
if (Nodes == NULL)
{
const int *v = (Dim == 1) ? &FaceNo : faces[FaceNo]->GetVertices();
const int nv = (Dim == 1) ? 1 : faces[FaceNo]->GetNVertices();
pm.SetSize(spaceDim, nv);
for (int i = 0; i < spaceDim; i++)
{
for (int j = 0; j < nv; j++)
{
pm(i, j) = vertices[v[j]](i);
}
}
FTr->SetFE(GetTransformationFEforElementType(GetFaceElementType(FaceNo)));
}
else // curved mesh
{
const FiniteElement *face_el = Nodes->FESpace()->GetFaceElement(FaceNo);
Nodes->HostRead();
const GridFunction &nodes = *Nodes;
if (face_el)
{
Array<int> vdofs;
Nodes->FESpace()->GetFaceVDofs(FaceNo, vdofs);
int n = vdofs.Size()/spaceDim;
pm.SetSize(spaceDim, n);
for (int i = 0; i < spaceDim; i++)
{
for (int j = 0; j < n; j++)
{
pm(i, j) = nodes(vdofs[n*i+j]);
}
}
FTr->SetFE(face_el);
}
else // L2 Nodes (e.g., periodic mesh), go through the volume of Elem1
{
FaceInfo &face_info = faces_info[FaceNo];
Geometry::Type face_geom = GetFaceGeometryType(FaceNo);
Element::Type face_type = GetFaceElementType(FaceNo);
GetLocalFaceTransformation(face_type,
GetElementType(face_info.Elem1No),
FaceElemTr.Loc1.Transf, face_info.Elem1Inf);
// NOTE: FaceElemTr.Loc1 is overwritten here -- used as a temporary
face_el = Nodes->FESpace()->GetTraceElement(face_info.Elem1No,
face_geom);
IntegrationRule eir(face_el->GetDof());
FaceElemTr.Loc1.Transf.ElementNo = face_info.Elem1No;
FaceElemTr.Loc1.Transf.ElementType = ElementTransformation::ELEMENT;
FaceElemTr.Loc1.Transform(face_el->GetNodes(), eir);
Nodes->GetVectorValues(FaceElemTr.Loc1.Transf, eir, pm);
FTr->SetFE(face_el);
}
}
}
ElementTransformation *Mesh::GetFaceTransformation(int FaceNo)
{
GetFaceTransformation(FaceNo, &FaceTransformation);
return &FaceTransformation;
}
void Mesh::GetEdgeTransformation(int EdgeNo, IsoparametricTransformation *EdTr)
{
if (Dim == 2)
{
GetFaceTransformation(EdgeNo, EdTr);
return;
}
if (Dim == 1)
{
mfem_error("Mesh::GetEdgeTransformation not defined in 1D \n");
}
EdTr->Attribute = 1;
EdTr->ElementNo = EdgeNo;
EdTr->ElementType = ElementTransformation::EDGE;
DenseMatrix &pm = EdTr->GetPointMat();
if (Nodes == NULL)
{
Array<int> v;
GetEdgeVertices(EdgeNo, v);
const int nv = 2;
pm.SetSize(spaceDim, nv);
for (int i = 0; i < spaceDim; i++)
{
for (int j = 0; j < nv; j++)
{
pm(i, j) = vertices[v[j]](i);
}
}
EdTr->SetFE(GetTransformationFEforElementType(Element::SEGMENT));
}
else
{
const FiniteElement *edge_el = Nodes->FESpace()->GetEdgeElement(EdgeNo);
if (edge_el)
{
Array<int> vdofs;
Nodes->FESpace()->GetEdgeVDofs(EdgeNo, vdofs);
int n = vdofs.Size()/spaceDim;
pm.SetSize(spaceDim, n);
for (int i = 0; i < spaceDim; i++)
{
for (int j = 0; j < n; j++)
{
pm(i, j) = (*Nodes)(vdofs[n*i+j]);
}
}
EdTr->SetFE(edge_el);
}
else
{
MFEM_ABORT("Not implemented.");
}
}
}
ElementTransformation *Mesh::GetEdgeTransformation(int EdgeNo)
{
GetEdgeTransformation(EdgeNo, &EdgeTransformation);
return &EdgeTransformation;
}
void Mesh::GetLocalPtToSegTransformation(
IsoparametricTransformation &Transf, int i)
{
const IntegrationRule *SegVert;
DenseMatrix &locpm = Transf.GetPointMat();
Transf.SetFE(&PointFE);
SegVert = Geometries.GetVertices(Geometry::SEGMENT);
locpm.SetSize(1, 1);
locpm(0, 0) = SegVert->IntPoint(i/64).x;
// (i/64) is the local face no. in the segment
// (i%64) is the orientation of the point (not used)
}
void Mesh::GetLocalSegToTriTransformation(
IsoparametricTransformation &Transf, int i)
{
const int *tv, *so;
const IntegrationRule *TriVert;
DenseMatrix &locpm = Transf.GetPointMat();
Transf.SetFE(&SegmentFE);
tv = tri_t::Edges[i/64]; // (i/64) is the local face no. in the triangle
so = seg_t::Orient[i%64]; // (i%64) is the orientation of the segment
TriVert = Geometries.GetVertices(Geometry::TRIANGLE);
locpm.SetSize(2, 2);
for (int j = 0; j < 2; j++)
{
locpm(0, so[j]) = TriVert->IntPoint(tv[j]).x;
locpm(1, so[j]) = TriVert->IntPoint(tv[j]).y;
}
}
void Mesh::GetLocalSegToQuadTransformation(
IsoparametricTransformation &Transf, int i)
{
const int *qv, *so;
const IntegrationRule *QuadVert;
DenseMatrix &locpm = Transf.GetPointMat();
Transf.SetFE(&SegmentFE);
qv = quad_t::Edges[i/64]; // (i/64) is the local face no. in the quad
so = seg_t::Orient[i%64]; // (i%64) is the orientation of the segment
QuadVert = Geometries.GetVertices(Geometry::SQUARE);
locpm.SetSize(2, 2);
for (int j = 0; j < 2; j++)
{
locpm(0, so[j]) = QuadVert->IntPoint(qv[j]).x;
locpm(1, so[j]) = QuadVert->IntPoint(qv[j]).y;
}
}
void Mesh::GetLocalTriToTetTransformation(
IsoparametricTransformation &Transf, int i)
{
DenseMatrix &locpm = Transf.GetPointMat();
Transf.SetFE(&TriangleFE);
// (i/64) is the local face no. in the tet
const int *tv = tet_t::FaceVert[i/64];
// (i%64) is the orientation of the tetrahedron face
// w.r.t. the face element
const int *to = tri_t::Orient[i%64];
const IntegrationRule *TetVert =
Geometries.GetVertices(Geometry::TETRAHEDRON);
locpm.SetSize(3, 3);
for (int j = 0; j < 3; j++)
{
const IntegrationPoint &vert = TetVert->IntPoint(tv[to[j]]);
locpm(0, j) = vert.x;
locpm(1, j) = vert.y;
locpm(2, j) = vert.z;
}
}
void Mesh::GetLocalTriToWdgTransformation(
IsoparametricTransformation &Transf, int i)
{
DenseMatrix &locpm = Transf.GetPointMat();
Transf.SetFE(&TriangleFE);
// (i/64) is the local face no. in the pri
MFEM_VERIFY(i < 128, "Local face index " << i/64
<< " is not a triangular face of a wedge.");
const int *pv = pri_t::FaceVert[i/64];
// (i%64) is the orientation of the wedge face
// w.r.t. the face element
const int *to = tri_t::Orient[i%64];
const IntegrationRule *PriVert =
Geometries.GetVertices(Geometry::PRISM);
locpm.SetSize(3, 3);
for (int j = 0; j < 3; j++)
{
const IntegrationPoint &vert = PriVert->IntPoint(pv[to[j]]);
locpm(0, j) = vert.x;
locpm(1, j) = vert.y;
locpm(2, j) = vert.z;
}
}
void Mesh::GetLocalQuadToHexTransformation(
IsoparametricTransformation &Transf, int i)
{
DenseMatrix &locpm = Transf.GetPointMat();
Transf.SetFE(&QuadrilateralFE);
// (i/64) is the local face no. in the hex
const int *hv = hex_t::FaceVert[i/64];
// (i%64) is the orientation of the quad
const int *qo = quad_t::Orient[i%64];
const IntegrationRule *HexVert = Geometries.GetVertices(Geometry::CUBE);
locpm.SetSize(3, 4);
for (int j = 0; j < 4; j++)
{
const IntegrationPoint &vert = HexVert->IntPoint(hv[qo[j]]);
locpm(0, j) = vert.x;
locpm(1, j) = vert.y;
locpm(2, j) = vert.z;
}
}
void Mesh::GetLocalQuadToWdgTransformation(
IsoparametricTransformation &Transf, int i)
{
DenseMatrix &locpm = Transf.GetPointMat();
Transf.SetFE(&QuadrilateralFE);
// (i/64) is the local face no. in the pri
MFEM_VERIFY(i >= 128, "Local face index " << i/64
<< " is not a quadrilateral face of a wedge.");
const int *pv = pri_t::FaceVert[i/64];
// (i%64) is the orientation of the quad
const int *qo = quad_t::Orient[i%64];
const IntegrationRule *PriVert = Geometries.GetVertices(Geometry::PRISM);
locpm.SetSize(3, 4);
for (int j = 0; j < 4; j++)
{
const IntegrationPoint &vert = PriVert->IntPoint(pv[qo[j]]);
locpm(0, j) = vert.x;
locpm(1, j) = vert.y;
locpm(2, j) = vert.z;
}
}
const GeometricFactors* Mesh::GetGeometricFactors(const IntegrationRule& ir,
const int flags)
{
for (int i = 0; i < geom_factors.Size(); i++)
{
GeometricFactors *gf = geom_factors[i];
if (gf->IntRule == &ir && (gf->computed_factors & flags) == flags)
{
return gf;
}
}
this->EnsureNodes();
GeometricFactors *gf = new GeometricFactors(this, ir, flags);
geom_factors.Append(gf);
return gf;
}
const FaceGeometricFactors* Mesh::GetFaceGeometricFactors(
const IntegrationRule& ir,
const int flags, FaceType type)
{
for (int i = 0; i < face_geom_factors.Size(); i++)
{
FaceGeometricFactors *gf = face_geom_factors[i];
if (gf->IntRule == &ir && (gf->computed_factors & flags) == flags &&
gf->type==type)
{
return gf;
}
}
this->EnsureNodes();
FaceGeometricFactors *gf = new FaceGeometricFactors(this, ir, flags, type);
face_geom_factors.Append(gf);
return gf;
}
void Mesh::DeleteGeometricFactors()
{
for (int i = 0; i < geom_factors.Size(); i++)
{
delete geom_factors[i];
}
geom_factors.SetSize(0);
for (int i = 0; i < face_geom_factors.Size(); i++)
{
delete face_geom_factors[i];
}
face_geom_factors.SetSize(0);
}
void Mesh::GetLocalFaceTransformation(
int face_type, int elem_type, IsoparametricTransformation &Transf, int info)
{
switch (face_type)
{
case Element::POINT:
GetLocalPtToSegTransformation(Transf, info);
break;
case Element::SEGMENT:
if (elem_type == Element::TRIANGLE)
{
GetLocalSegToTriTransformation(Transf, info);
}
else
{
MFEM_ASSERT(elem_type == Element::QUADRILATERAL, "");
GetLocalSegToQuadTransformation(Transf, info);
}
break;
case Element::TRIANGLE:
if (elem_type == Element::TETRAHEDRON)
{
GetLocalTriToTetTransformation(Transf, info);
}
else
{
MFEM_ASSERT(elem_type == Element::WEDGE, "");
GetLocalTriToWdgTransformation(Transf, info);
}
break;
case Element::QUADRILATERAL:
if (elem_type == Element::HEXAHEDRON)
{
GetLocalQuadToHexTransformation(Transf, info);
}
else
{
MFEM_ASSERT(elem_type == Element::WEDGE, "");
GetLocalQuadToWdgTransformation(Transf, info);
}
break;
}
}
FaceElementTransformations *Mesh::GetFaceElementTransformations(int FaceNo,
int mask)
{
FaceInfo &face_info = faces_info[FaceNo];
FaceElemTr.SetConfigurationMask(0);
FaceElemTr.Elem1 = NULL;
FaceElemTr.Elem2 = NULL;
// setup the transformation for the first element
FaceElemTr.Elem1No = face_info.Elem1No;
if (mask & 1)
{
GetElementTransformation(FaceElemTr.Elem1No, &Transformation);
FaceElemTr.Elem1 = &Transformation;
}
// setup the transformation for the second element
// return NULL in the Elem2 field if there's no second element, i.e.
// the face is on the "boundary"
FaceElemTr.Elem2No = face_info.Elem2No;
if ((mask & 2) && FaceElemTr.Elem2No >= 0)
{
#ifdef MFEM_DEBUG
if (NURBSext && (mask & 1)) { MFEM_ABORT("NURBS mesh not supported!"); }
#endif
GetElementTransformation(FaceElemTr.Elem2No, &Transformation2);
FaceElemTr.Elem2 = &Transformation2;
}
// setup the face transformation
if (mask & 16)
{
GetFaceTransformation(FaceNo, &FaceElemTr);
}
else
{
FaceElemTr.SetGeometryType(GetFaceGeometryType(FaceNo));
}
// setup Loc1 & Loc2
int face_type = GetFaceElementType(FaceNo);
if (mask & 4)
{
int elem_type = GetElementType(face_info.Elem1No);
GetLocalFaceTransformation(face_type, elem_type,
FaceElemTr.Loc1.Transf, face_info.Elem1Inf);
}
if ((mask & 8) && FaceElemTr.Elem2No >= 0)
{
int elem_type = GetElementType(face_info.Elem2No);
GetLocalFaceTransformation(face_type, elem_type,
FaceElemTr.Loc2.Transf, face_info.Elem2Inf);
// NC meshes: prepend slave edge/face transformation to Loc2
if (Nonconforming() && IsSlaveFace(face_info))
{
ApplyLocalSlaveTransformation(FaceElemTr.Loc2.Transf, face_info);
if (face_type == Element::SEGMENT)
{
// flip Loc2 to match Loc1 and Face
DenseMatrix &pm = FaceElemTr.Loc2.Transf.GetPointMat();
std::swap(pm(0,0), pm(0,1));
std::swap(pm(1,0), pm(1,1));
}
}
}
FaceElemTr.SetConfigurationMask(mask);
return &FaceElemTr;
}
bool Mesh::IsSlaveFace(const FaceInfo &fi) const
{
return fi.NCFace >= 0 && nc_faces_info[fi.NCFace].Slave;
}
void Mesh::ApplyLocalSlaveTransformation(IsoparametricTransformation &transf,
const FaceInfo &fi)
{
#ifdef MFEM_THREAD_SAFE
DenseMatrix composition;
#else
static DenseMatrix composition;
#endif
MFEM_ASSERT(fi.NCFace >= 0, "");
transf.Transform(*nc_faces_info[fi.NCFace].PointMatrix, composition);
transf.SetPointMat(composition);
}
FaceElementTransformations *Mesh::GetBdrFaceTransformations(int BdrElemNo)
{
FaceElementTransformations *tr;
int fn;
if (Dim == 3)
{
fn = be_to_face[BdrElemNo];
}
else if (Dim == 2)
{
fn = be_to_edge[BdrElemNo];
}
else
{
fn = boundary[BdrElemNo]->GetVertices()[0];
}
// Check if the face is interior, shared, or non-conforming.
if (FaceIsTrueInterior(fn) || faces_info[fn].NCFace >= 0)
{
return NULL;
}
tr = GetFaceElementTransformations(fn);
tr->Attribute = boundary[BdrElemNo]->GetAttribute();
tr->ElementNo = BdrElemNo;
tr->ElementType = ElementTransformation::BDR_FACE;
return tr;
}
void Mesh::GetFaceElements(int Face, int *Elem1, int *Elem2) const
{
*Elem1 = faces_info[Face].Elem1No;
*Elem2 = faces_info[Face].Elem2No;
}
void Mesh::GetFaceInfos(int Face, int *Inf1, int *Inf2) const
{
*Inf1 = faces_info[Face].Elem1Inf;
*Inf2 = faces_info[Face].Elem2Inf;
}
Geometry::Type Mesh::GetFaceGeometryType(int Face) const
{
return (Dim == 1) ? Geometry::POINT : faces[Face]->GetGeometryType();
}
Element::Type Mesh::GetFaceElementType(int Face) const
{
return (Dim == 1) ? Element::POINT : faces[Face]->GetType();
}
void Mesh::Init()
{
// in order of declaration:
Dim = spaceDim = 0;
NumOfVertices = -1;
NumOfElements = NumOfBdrElements = 0;
NumOfEdges = NumOfFaces = 0;
nbInteriorFaces = -1;
nbBoundaryFaces = -1;
meshgen = mesh_geoms = 0;
sequence = 0;
Nodes = NULL;
own_nodes = 1;
NURBSext = NULL;
ncmesh = NULL;
last_operation = Mesh::NONE;
}
void Mesh::InitTables()
{
el_to_edge =
el_to_face = el_to_el = bel_to_edge = face_edge = edge_vertex = NULL;
}
void Mesh::SetEmpty()
{
Init();
InitTables();
}
void Mesh::DestroyTables()
{
delete el_to_edge;
delete el_to_face;
delete el_to_el;
DeleteGeometricFactors();
if (Dim == 3)
{
delete bel_to_edge;
}
delete face_edge;
delete edge_vertex;
}
void Mesh::DestroyPointers()
{
if (own_nodes) { delete Nodes; }
delete ncmesh;
delete NURBSext;
for (int i = 0; i < NumOfElements; i++)
{
FreeElement(elements[i]);
}
for (int i = 0; i < NumOfBdrElements; i++)
{
FreeElement(boundary[i]);
}
for (int i = 0; i < faces.Size(); i++)
{
FreeElement(faces[i]);
}
DestroyTables();
}
void Mesh::Destroy()
{
DestroyPointers();
elements.DeleteAll();
vertices.DeleteAll();
boundary.DeleteAll();
faces.DeleteAll();
faces_info.DeleteAll();
nc_faces_info.DeleteAll();
be_to_edge.DeleteAll();
be_to_face.DeleteAll();
// TODO:
// IsoparametricTransformations
// Transformation, Transformation2, BdrTransformation, FaceTransformation,
// EdgeTransformation;
// FaceElementTransformations FaceElemTr;
CoarseFineTr.Clear();
#ifdef MFEM_USE_MEMALLOC
TetMemory.Clear();
#endif
attributes.DeleteAll();
bdr_attributes.DeleteAll();
}
void Mesh::ResetLazyData()
{
delete el_to_el; el_to_el = NULL;
delete face_edge; face_edge = NULL;
delete edge_vertex; edge_vertex = NULL;
DeleteGeometricFactors();
nbInteriorFaces = -1;
nbBoundaryFaces = -1;
}
void Mesh::SetAttributes()
{
Array<int> attribs;
attribs.SetSize(GetNBE());
for (int i = 0; i < attribs.Size(); i++)
{
attribs[i] = GetBdrAttribute(i);
}
attribs.Sort();
attribs.Unique();
attribs.Copy(bdr_attributes);
if (bdr_attributes.Size() > 0 && bdr_attributes[0] <= 0)
{
MFEM_WARNING("Non-positive attributes on the boundary!");
}
attribs.SetSize(GetNE());
for (int i = 0; i < attribs.Size(); i++)
{
attribs[i] = GetAttribute(i);
}
attribs.Sort();
attribs.Unique();
attribs.Copy(attributes);
if (attributes.Size() > 0 && attributes[0] <= 0)
{
MFEM_WARNING("Non-positive attributes in the domain!");
}
}
void Mesh::InitMesh(int _Dim, int _spaceDim, int NVert, int NElem, int NBdrElem)
{
SetEmpty();
Dim = _Dim;
spaceDim = _spaceDim;
NumOfVertices = 0;
vertices.SetSize(NVert); // just allocate space for vertices
NumOfElements = 0;
elements.SetSize(NElem); // just allocate space for Element *
NumOfBdrElements = 0;
boundary.SetSize(NBdrElem); // just allocate space for Element *
}
void Mesh::AddVertex(const double *x)
{
double *y = vertices[NumOfVertices]();
for (int i = 0; i < spaceDim; i++)
{
y[i] = x[i];
}
NumOfVertices++;
}
void Mesh::AddSegment(const int *vi, int attr)
{
elements[NumOfElements++] = new Segment(vi, attr);
}
void Mesh::AddTri(const int *vi, int attr)
{
elements[NumOfElements++] = new Triangle(vi, attr);
}
void Mesh::AddTriangle(const int *vi, int attr)
{
elements[NumOfElements++] = new Triangle(vi, attr);
}
void Mesh::AddQuad(const int *vi, int attr)
{
elements[NumOfElements++] = new Quadrilateral(vi, attr);
}
void Mesh::AddTet(const int *vi, int attr)
{
#ifdef MFEM_USE_MEMALLOC
Tetrahedron *tet;
tet = TetMemory.Alloc();
tet->SetVertices(vi);
tet->SetAttribute(attr);
elements[NumOfElements++] = tet;
#else
elements[NumOfElements++] = new Tetrahedron(vi, attr);
#endif
}
void Mesh::AddWedge(const int *vi, int attr)
{
elements[NumOfElements++] = new Wedge(vi, attr);
}
void Mesh::AddHex(const int *vi, int attr)
{
elements[NumOfElements++] = new Hexahedron(vi, attr);
}
void Mesh::AddHexAsTets(const int *vi, int attr)
{
static const int hex_to_tet[6][4] =
{
{ 0, 1, 2, 6 }, { 0, 5, 1, 6 }, { 0, 4, 5, 6 },
{ 0, 2, 3, 6 }, { 0, 3, 7, 6 }, { 0, 7, 4, 6 }
};
int ti[4];
for (int i = 0; i < 6; i++)
{
for (int j = 0; j < 4; j++)
{
ti[j] = vi[hex_to_tet[i][j]];
}
AddTet(ti, attr);
}
}
void Mesh::AddHexAsWedges(const int *vi, int attr)
{
static const int hex_to_wdg[2][6] =
{
{ 0, 1, 2, 4, 5, 6 }, { 0, 2, 3, 4, 6, 7 }
};
int ti[6];
for (int i = 0; i < 2; i++)
{
for (int j = 0; j < 6; j++)
{
ti[j] = vi[hex_to_wdg[i][j]];
}
AddWedge(ti, attr);
}
}
void Mesh::AddBdrSegment(const int *vi, int attr)
{
boundary[NumOfBdrElements++] = new Segment(vi, attr);
}
void Mesh::AddBdrTriangle(const int *vi, int attr)
{
boundary[NumOfBdrElements++] = new Triangle(vi, attr);
}
void Mesh::AddBdrQuad(const int *vi, int attr)
{
boundary[NumOfBdrElements++] = new Quadrilateral(vi, attr);
}
void Mesh::AddBdrQuadAsTriangles(const int *vi, int attr)
{
static const int quad_to_tri[2][3] = { { 0, 1, 2 }, { 0, 2, 3 } };
int ti[3];
for (int i = 0; i < 2; i++)
{
for (int j = 0; j < 3; j++)
{
ti[j] = vi[quad_to_tri[i][j]];
}
AddBdrTriangle(ti, attr);
}
}
void Mesh::GenerateBoundaryElements()
{
int i, j;
Array<int> &be2face = (Dim == 2) ? be_to_edge : be_to_face;
// GenerateFaces();
for (i = 0; i < boundary.Size(); i++)
{
FreeElement(boundary[i]);
}
if (Dim == 3)
{
delete bel_to_edge;
bel_to_edge = NULL;
}
// count the 'NumOfBdrElements'
NumOfBdrElements = 0;
for (i = 0; i < faces_info.Size(); i++)
{
if (faces_info[i].Elem2No < 0) { NumOfBdrElements++; }
}
boundary.SetSize(NumOfBdrElements);
be2face.SetSize(NumOfBdrElements);
for (j = i = 0; i < faces_info.Size(); i++)
{
if (faces_info[i].Elem2No < 0)
{
boundary[j] = faces[i]->Duplicate(this);
be2face[j++] = i;
}
}
// In 3D, 'bel_to_edge' is destroyed but it's not updated.
}
void Mesh::FinalizeCheck()
{
MFEM_VERIFY(vertices.Size() == NumOfVertices ||
vertices.Size() == 0,
"incorrect number of vertices: preallocated: " << vertices.Size()
<< ", actually added: " << NumOfVertices);
MFEM_VERIFY(elements.Size() == NumOfElements,
"incorrect number of elements: preallocated: " << elements.Size()
<< ", actually added: " << NumOfElements);
MFEM_VERIFY(boundary.Size() == NumOfBdrElements,
"incorrect number of boundary elements: preallocated: "
<< boundary.Size() << ", actually added: " << NumOfBdrElements);
}
void Mesh::FinalizeTriMesh(int generate_edges, int refine, bool fix_orientation)
{
FinalizeCheck();
CheckElementOrientation(fix_orientation);
if (refine)
{
MarkTriMeshForRefinement();
}
if (generate_edges)
{
el_to_edge = new Table;
NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
GenerateFaces();
CheckBdrElementOrientation();
}
else
{
NumOfEdges = 0;
}
NumOfFaces = 0;
SetAttributes();
SetMeshGen();
}
void Mesh::FinalizeQuadMesh(int generate_edges, int refine,
bool fix_orientation)
{
FinalizeCheck();
if (fix_orientation)
{
CheckElementOrientation(fix_orientation);
}
if (generate_edges)
{
el_to_edge = new Table;
NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
GenerateFaces();
CheckBdrElementOrientation();
}
else
{
NumOfEdges = 0;
}
NumOfFaces = 0;
SetAttributes();
SetMeshGen();
}
class GeckoProgress : public Gecko::Progress
{
double limit;
mutable StopWatch sw;
public:
GeckoProgress(double limit) : limit(limit) { sw.Start(); }
virtual bool quit() const { return limit > 0 && sw.UserTime() > limit; }
};
class GeckoVerboseProgress : public GeckoProgress
{
using Float = Gecko::Float;
using Graph = Gecko::Graph;
using uint = Gecko::uint;
public:
GeckoVerboseProgress(double limit) : GeckoProgress(limit) {}
virtual void beginorder(const Graph* graph, Float cost) const
{ mfem::out << "Begin Gecko ordering, cost = " << cost << std::endl; }
virtual void endorder(const Graph* graph, Float cost) const
{ mfem::out << "End ordering, cost = " << cost << std::endl; }
virtual void beginiter(const Graph* graph,
uint iter, uint maxiter, uint window) const
{
mfem::out << "Iteration " << iter << "/" << maxiter << ", window "
<< window << std::flush;
}
virtual void enditer(const Graph* graph, Float mincost, Float cost) const
{ mfem::out << ", cost = " << cost << endl; }
};
double Mesh::GetGeckoElementOrdering(Array<int> &ordering,
int iterations, int window,
int period, int seed, bool verbose,
double time_limit)
{
Gecko::Graph graph;
Gecko::FunctionalGeometric functional; // edge product cost
GeckoProgress progress(time_limit);
GeckoVerboseProgress vprogress(time_limit);
// insert elements as nodes in the graph
for (int elemid = 0; elemid < GetNE(); ++elemid)
{
graph.insert_node();
}
// insert graph edges for element neighbors
// NOTE: indices in Gecko are 1 based hence the +1 on insertion
const Table &my_el_to_el = ElementToElementTable();
for (int elemid = 0; elemid < GetNE(); ++elemid)
{
const int *neighid = my_el_to_el.GetRow(elemid);
for (int i = 0; i < my_el_to_el.RowSize(elemid); ++i)
{
graph.insert_arc(elemid + 1, neighid[i] + 1);
}
}
// get the ordering from Gecko and copy it into the Array<int>
graph.order(&functional, iterations, window, period, seed,
verbose ? &vprogress : &progress);
ordering.SetSize(GetNE());
Gecko::Node::Index NE = GetNE();
for (Gecko::Node::Index gnodeid = 1; gnodeid <= NE; ++gnodeid)
{
ordering[gnodeid - 1] = graph.rank(gnodeid);
}
return graph.cost();
}
struct HilbertCmp
{
int coord;
bool dir;
const Array<double> &points;
double mid;
HilbertCmp(int coord, bool dir, const Array<double> &points, double mid)
: coord(coord), dir(dir), points(points), mid(mid) {}
bool operator()(int i) const
{
return (points[3*i + coord] < mid) != dir;
}
};
static void HilbertSort2D(int coord1, // major coordinate to sort points by
bool dir1, // sort coord1 ascending/descending?
bool dir2, // sort coord2 ascending/descending?
const Array<double> &points, int *beg, int *end,
double xmin, double ymin, double xmax, double ymax)
{
if (end - beg <= 1) { return; }
double xmid = (xmin + xmax)*0.5;
double ymid = (ymin + ymax)*0.5;
int coord2 = (coord1 + 1) % 2; // the 'other' coordinate
// sort (partition) points into four quadrants
int *p0 = beg, *p4 = end;
int *p2 = std::partition(p0, p4, HilbertCmp(coord1, dir1, points, xmid));
int *p1 = std::partition(p0, p2, HilbertCmp(coord2, dir2, points, ymid));
int *p3 = std::partition(p2, p4, HilbertCmp(coord2, !dir2, points, ymid));
if (p1 != p4)
{
HilbertSort2D(coord2, dir2, dir1, points, p0, p1,
ymin, xmin, ymid, xmid);
}
if (p1 != p0 || p2 != p4)
{
HilbertSort2D(coord1, dir1, dir2, points, p1, p2,
xmin, ymid, xmid, ymax);
}
if (p2 != p0 || p3 != p4)
{
HilbertSort2D(coord1, dir1, dir2, points, p2, p3,
xmid, ymid, xmax, ymax);
}
if (p3 != p0)
{
HilbertSort2D(coord2, !dir2, !dir1, points, p3, p4,
ymid, xmax, ymin, xmid);
}
}
static void 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)
{
if (end - beg <= 1) { return; }
double xmid = (xmin + xmax)*0.5;
double ymid = (ymin + ymax)*0.5;
double zmid = (zmin + zmax)*0.5;
int coord2 = (coord1 + 1) % 3;
int coord3 = (coord1 + 2) % 3;
// sort (partition) points into eight octants
int *p0 = beg, *p8 = end;
int *p4 = std::partition(p0, p8, HilbertCmp(coord1, dir1, points, xmid));
int *p2 = std::partition(p0, p4, HilbertCmp(coord2, dir2, points, ymid));
int *p6 = std::partition(p4, p8, HilbertCmp(coord2, !dir2, points, ymid));
int *p1 = std::partition(p0, p2, HilbertCmp(coord3, dir3, points, zmid));
int *p3 = std::partition(p2, p4, HilbertCmp(coord3, !dir3, points, zmid));
int *p5 = std::partition(p4, p6, HilbertCmp(coord3, dir3, points, zmid));
int *p7 = std::partition(p6, p8, HilbertCmp(coord3, !dir3, points, zmid));
if (p1 != p8)
{
HilbertSort3D(coord3, dir3, dir1, dir2, points, p0, p1,
zmin, xmin, ymin, zmid, xmid, ymid);
}
if (p1 != p0 || p2 != p8)
{
HilbertSort3D(coord2, dir2, dir3, dir1, points, p1, p2,
ymin, zmid, xmin, ymid, zmax, xmid);
}
if (p2 != p0 || p3 != p8)
{
HilbertSort3D(coord2, dir2, dir3, dir1, points, p2, p3,
ymid, zmid, xmin, ymax, zmax, xmid);
}
if (p3 != p0 || p4 != p8)
{
HilbertSort3D(coord1, dir1, !dir2, !dir3, points, p3, p4,
xmin, ymax, zmid, xmid, ymid, zmin);
}
if (p4 != p0 || p5 != p8)
{
HilbertSort3D(coord1, dir1, !dir2, !dir3, points, p4, p5,
xmid, ymax, zmid, xmax, ymid, zmin);
}
if (p5 != p0 || p6 != p8)
{
HilbertSort3D(coord2, !dir2, dir3, !dir1, points, p5, p6,
ymax, zmid, xmax, ymid, zmax, xmid);
}
if (p6 != p0 || p7 != p8)
{
HilbertSort3D(coord2, !dir2, dir3, !dir1, points, p6, p7,
ymid, zmid, xmax, ymin, zmax, xmid);
}
if (p7 != p0)
{
HilbertSort3D(coord3, !dir3, !dir1, dir2, points, p7, p8,
zmid, xmax, ymin, zmin, xmid, ymid);
}
}
void Mesh::GetHilbertElementOrdering(Array<int> &ordering)
{
MFEM_VERIFY(spaceDim <= 3, "");
Vector min, max, center;
GetBoundingBox(min, max);
Array<int> indices(GetNE());
Array<double> points(3*GetNE());
if (spaceDim < 3) { points = 0.0; }
// calculate element centers
for (int i = 0; i < GetNE(); i++)
{
GetElementCenter(i, center);
for (int j = 0; j < spaceDim; j++)
{
points[3*i + j] = center(j);
}
indices[i] = i;
}
if (spaceDim == 1)
{
indices.Sort([&](int a, int b)
{ return points[3*a] < points[3*b]; });
}
else if (spaceDim == 2)
{
// recursively partition the points in 2D
HilbertSort2D(0, false, false,
points, indices.begin(), indices.end(),
min(0), min(1), max(0), max(1));
}
else
{
// recursively partition the points in 3D
HilbertSort3D(0, false, false, false,
points, indices.begin(), indices.end(),
min(0), min(1), min(2), max(0), max(1), max(2));
}
// return ordering in the format required by ReorderElements
ordering.SetSize(GetNE());
for (int i = 0; i < GetNE(); i++)
{
ordering[indices[i]] = i;
}
}
void Mesh::ReorderElements(const Array<int> &ordering, bool reorder_vertices)
{
if (NURBSext)
{
MFEM_WARNING("element reordering of NURBS meshes is not supported.");
return;
}
if (ncmesh)
{
MFEM_WARNING("element reordering of non-conforming meshes is not"
" supported.");
return;
}
MFEM_VERIFY(ordering.Size() == GetNE(), "invalid reordering array.")
// Data members that need to be updated:
// - elements - reorder of the pointers and the vertex ids if reordering
// the vertices
// - vertices - if reordering the vertices
// - boundary - update the vertex ids, if reordering the vertices
// - faces - regenerate
// - faces_info - regenerate
// Deleted by DeleteTables():
// - el_to_edge - rebuild in 2D and 3D only
// - el_to_face - rebuild in 3D only
// - bel_to_edge - rebuild in 3D only
// - el_to_el - no need to rebuild
// - face_edge - no need to rebuild
// - edge_vertex - no need to rebuild
// - geom_factors - no need to rebuild
// - be_to_edge - 2D only
// - be_to_face - 3D only
// - Nodes
// Save the locations of the Nodes so we can rebuild them later
Array<Vector*> old_elem_node_vals;
FiniteElementSpace *nodes_fes = NULL;
if (Nodes)
{
old_elem_node_vals.SetSize(GetNE());
nodes_fes = Nodes->FESpace();
Array<int> old_dofs;
Vector vals;
for (int old_elid = 0; old_elid < GetNE(); ++old_elid)
{
nodes_fes->GetElementVDofs(old_elid, old_dofs);
Nodes->GetSubVector(old_dofs, vals);
old_elem_node_vals[old_elid] = new Vector(vals);
}
}
// Get the newly ordered elements
Array<Element *> new_elements(GetNE());
for (int old_elid = 0; old_elid < ordering.Size(); ++old_elid)
{
int new_elid = ordering[old_elid];
new_elements[new_elid] = elements[old_elid];
}
mfem::Swap(elements, new_elements);
new_elements.DeleteAll();
if (reorder_vertices)
{
// Get the new vertex ordering permutation vectors and fill the new
// vertices
Array<int> vertex_ordering(GetNV());
vertex_ordering = -1;
Array<Vertex> new_vertices(GetNV());
int new_vertex_ind = 0;
for (int new_elid = 0; new_elid < GetNE(); ++new_elid)
{
int *elem_vert = elements[new_elid]->GetVertices();
int nv = elements[new_elid]->GetNVertices();
for (int vi = 0; vi < nv; ++vi)
{
int old_vertex_ind = elem_vert[vi];
if (vertex_ordering[old_vertex_ind] == -1)
{
vertex_ordering[old_vertex_ind] = new_vertex_ind;
new_vertices[new_vertex_ind] = vertices[old_vertex_ind];
new_vertex_ind++;
}
}
}
mfem::Swap(vertices, new_vertices);
new_vertices.DeleteAll();
// Replace the vertex ids in the elements with the reordered vertex
// numbers
for (int new_elid = 0; new_elid < GetNE(); ++new_elid)
{
int *elem_vert = elements[new_elid]->GetVertices();
int nv = elements[new_elid]->GetNVertices();
for (int vi = 0; vi < nv; ++vi)
{
elem_vert[vi] = vertex_ordering[elem_vert[vi]];
}
}
// Replace the vertex ids in the boundary with reordered vertex numbers
for (int belid = 0; belid < GetNBE(); ++belid)
{
int *be_vert = boundary[belid]->GetVertices();
int nv = boundary[belid]->GetNVertices();
for (int vi = 0; vi < nv; ++vi)
{
be_vert[vi] = vertex_ordering[be_vert[vi]];
}
}
}
// Destroy tables that need to be rebuild
DeleteTables();
if (Dim > 1)
{
// generate el_to_edge, be_to_edge (2D), bel_to_edge (3D)
el_to_edge = new Table;
NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
}
if (Dim > 2)
{
// generate el_to_face, be_to_face
GetElementToFaceTable();
}
// Update faces and faces_info
GenerateFaces();
// Build the nodes from the saved locations if they were around before
if (Nodes)
{
// To force FE space update, we need to increase 'sequence':
sequence++;
last_operation = Mesh::NONE;
nodes_fes->Update(false); // want_transform = false
Nodes->Update(); // just needed to update Nodes->sequence
Array<int> new_dofs;
for (int old_elid = 0; old_elid < GetNE(); ++old_elid)
{
int new_elid = ordering[old_elid];
nodes_fes->GetElementVDofs(new_elid, new_dofs);
Nodes->SetSubVector(new_dofs, *(old_elem_node_vals[old_elid]));
delete old_elem_node_vals[old_elid];
}
}
}
void Mesh::MarkForRefinement()
{
if (meshgen & 1)
{
if (Dim == 2)
{
MarkTriMeshForRefinement();
}
else if (Dim == 3)
{
DSTable v_to_v(NumOfVertices);
GetVertexToVertexTable(v_to_v);
MarkTetMeshForRefinement(v_to_v);
}
}
}
void Mesh::MarkTriMeshForRefinement()
{
// Mark the longest triangle edge by rotating the indeces so that
// vertex 0 - vertex 1 is the longest edge in the triangle.
DenseMatrix pmat;
for (int i = 0; i < NumOfElements; i++)
{
if (elements[i]->GetType() == Element::TRIANGLE)
{
GetPointMatrix(i, pmat);
static_cast<Triangle*>(elements[i])->MarkEdge(pmat);
}
}
}
void Mesh::GetEdgeOrdering(DSTable &v_to_v, Array<int> &order)
{
NumOfEdges = v_to_v.NumberOfEntries();
order.SetSize(NumOfEdges);
Array<Pair<double, int> > length_idx(NumOfEdges);
for (int i = 0; i < NumOfVertices; i++)
{
for (DSTable::RowIterator it(v_to_v, i); !it; ++it)
{
int j = it.Index();
length_idx[j].one = GetLength(i, it.Column());
length_idx[j].two = j;
}
}
// Sort by increasing edge-length.
length_idx.Sort();
for (int i = 0; i < NumOfEdges; i++)
{
order[length_idx[i].two] = i;
}
}
void Mesh::MarkTetMeshForRefinement(DSTable &v_to_v)
{
// Mark the longest tetrahedral edge by rotating the indices so that
// vertex 0 - vertex 1 is the longest edge in the element.
Array<int> order;
GetEdgeOrdering(v_to_v, order);
for (int i = 0; i < NumOfElements; i++)
{
if (elements[i]->GetType() == Element::TETRAHEDRON)
{
elements[i]->MarkEdge(v_to_v, order);
}
}
for (int i = 0; i < NumOfBdrElements; i++)
{
if (boundary[i]->GetType() == Element::TRIANGLE)
{
boundary[i]->MarkEdge(v_to_v, order);
}
}
}
void Mesh::PrepareNodeReorder(DSTable **old_v_to_v, Table **old_elem_vert)
{
if (*old_v_to_v && *old_elem_vert)
{
return;
}
FiniteElementSpace *fes = Nodes->FESpace();
if (*old_v_to_v == NULL)
{
bool need_v_to_v = false;
Array<int> dofs;
for (int i = 0; i < GetNEdges(); i++)
{
// Since edge indices may change, we need to permute edge interior dofs
// any time an edge index changes and there is at least one dof on that
// edge.
fes->GetEdgeInteriorDofs(i, dofs);
if (dofs.Size() > 0)
{
need_v_to_v = true;
break;
}
}
if (need_v_to_v)
{
*old_v_to_v = new DSTable(NumOfVertices);
GetVertexToVertexTable(*(*old_v_to_v));
}
}
if (*old_elem_vert == NULL)
{
bool need_elem_vert = false;
Array<int> dofs;
for (int i = 0; i < GetNE(); i++)
{
// Since element indices do not change, we need to permute element
// interior dofs only when there are at least 2 interior dofs in an
// element (assuming the nodal dofs are non-directional).
fes->GetElementInteriorDofs(i, dofs);
if (dofs.Size() > 1)
{
need_elem_vert = true;
break;
}
}
if (need_elem_vert)
{
*old_elem_vert = new Table;
(*old_elem_vert)->MakeI(GetNE());
for (int i = 0; i < GetNE(); i++)
{
(*old_elem_vert)->AddColumnsInRow(i, elements[i]->GetNVertices());
}
(*old_elem_vert)->MakeJ();
for (int i = 0; i < GetNE(); i++)
{
(*old_elem_vert)->AddConnections(i, elements[i]->GetVertices(),
elements[i]->GetNVertices());
}
(*old_elem_vert)->ShiftUpI();
}
}
}
void Mesh::DoNodeReorder(DSTable *old_v_to_v, Table *old_elem_vert)
{
FiniteElementSpace *fes = Nodes->FESpace();
const FiniteElementCollection *fec = fes->FEColl();
Array<int> old_dofs, new_dofs;
// assuming that all edges have the same number of dofs
if (NumOfEdges) { fes->GetEdgeInteriorDofs(0, old_dofs); }
const int num_edge_dofs = old_dofs.Size();
// Save the original nodes
const Vector onodes = *Nodes;
// vertex dofs do not need to be moved
fes->GetVertexDofs(0, old_dofs);
int offset = NumOfVertices * old_dofs.Size();
// edge dofs:
// edge enumeration may be different but edge orientation is the same
if (num_edge_dofs > 0)
{
DSTable new_v_to_v(NumOfVertices);
GetVertexToVertexTable(new_v_to_v);
for (int i = 0; i < NumOfVertices; i++)
{
for (DSTable::RowIterator it(new_v_to_v, i); !it; ++it)
{
const int old_i = (*old_v_to_v)(i, it.Column());
const int new_i = it.Index();
if (new_i == old_i) { continue; }
old_dofs.SetSize(num_edge_dofs);
new_dofs.SetSize(num_edge_dofs);
for (int j = 0; j < num_edge_dofs; j++)
{
old_dofs[j] = offset + old_i * num_edge_dofs + j;
new_dofs[j] = offset + new_i * num_edge_dofs + j;
}
fes->DofsToVDofs(old_dofs);
fes->DofsToVDofs(new_dofs);
for (int j = 0; j < old_dofs.Size(); j++)
{
(*Nodes)(new_dofs[j]) = onodes(old_dofs[j]);
}
}
}
offset += NumOfEdges * num_edge_dofs;
}
// face dofs:
// both enumeration and orientation of the faces may be different
if (fes->GetNFDofs() > 0)
{
// generate the old face-vertex table using the unmodified 'faces'
Table old_face_vertex;
old_face_vertex.MakeI(NumOfFaces);
for (int i = 0; i < NumOfFaces; i++)
{
old_face_vertex.AddColumnsInRow(i, faces[i]->GetNVertices());
}
old_face_vertex.MakeJ();
for (int i = 0; i < NumOfFaces; i++)
old_face_vertex.AddConnections(i, faces[i]->GetVertices(),
faces[i]->GetNVertices());
old_face_vertex.ShiftUpI();
// update 'el_to_face', 'be_to_face', 'faces', and 'faces_info'
STable3D *faces_tbl = GetElementToFaceTable(1);
GenerateFaces();
// compute the new face dof offsets
Array<int> new_fdofs(NumOfFaces+1);
new_fdofs[0] = 0;
for (int i = 0; i < NumOfFaces; i++) // i = old face index
{
const int *old_v = old_face_vertex.GetRow(i);
int new_i; // new face index
switch (old_face_vertex.RowSize(i))
{
case 3:
new_i = (*faces_tbl)(old_v[0], old_v[1], old_v[2]);
break;
case 4:
default:
new_i = (*faces_tbl)(old_v[0], old_v[1], old_v[2], old_v[3]);
break;
}
fes->GetFaceInteriorDofs(i, old_dofs);
new_fdofs[new_i+1] = old_dofs.Size();
}
new_fdofs.PartialSum();
// loop over the old face numbers
for (int i = 0; i < NumOfFaces; i++)
{
const int *old_v = old_face_vertex.GetRow(i), *new_v;
const int *dof_ord;
int new_i, new_or;
switch (old_face_vertex.RowSize(i))
{
case 3:
new_i = (*faces_tbl)(old_v[0], old_v[1], old_v[2]);
new_v = faces[new_i]->GetVertices();
new_or = GetTriOrientation(old_v, new_v);
dof_ord = fec->DofOrderForOrientation(Geometry::TRIANGLE, new_or);
break;
case 4:
default:
new_i = (*faces_tbl)(old_v[0], old_v[1], old_v[2], old_v[3]);
new_v = faces[new_i]->GetVertices();
new_or = GetQuadOrientation(old_v, new_v);
dof_ord = fec->DofOrderForOrientation(Geometry::SQUARE, new_or);
break;
}
fes->GetFaceInteriorDofs(i, old_dofs);
new_dofs.SetSize(old_dofs.Size());
for (int j = 0; j < old_dofs.Size(); j++)
{
// we assume the dofs are non-directional, i.e. dof_ord[j] is >= 0
const int old_j = dof_ord[j];
new_dofs[old_j] = offset + new_fdofs[new_i] + j;
}
fes->DofsToVDofs(old_dofs);
fes->DofsToVDofs(new_dofs);
for (int j = 0; j < old_dofs.Size(); j++)
{
(*Nodes)(new_dofs[j]) = onodes(old_dofs[j]);
}
}
offset += fes->GetNFDofs();
delete faces_tbl;
}
// element dofs:
// element orientation may be different
if (old_elem_vert) // have elements with 2 or more dofs
{
// matters when the 'fec' is
// (this code is executed only for triangles/tets)
// - Pk on triangles, k >= 4
// - Qk on quads, k >= 3
// - Pk on tets, k >= 5
// - Qk on hexes, k >= 3
// - DG spaces
// - ...
// loop over all elements
for (int i = 0; i < GetNE(); i++)
{
const int *old_v = old_elem_vert->GetRow(i);
const int *new_v = elements[i]->GetVertices();
const int *dof_ord;
int new_or;
const Geometry::Type geom = elements[i]->GetGeometryType();
switch (geom)
{
case Geometry::SEGMENT:
new_or = (old_v[0] == new_v[0]) ? +1 : -1;
break;
case Geometry::TRIANGLE:
new_or = GetTriOrientation(old_v, new_v);
break;
case Geometry::SQUARE:
new_or = GetQuadOrientation(old_v, new_v);
break;
case Geometry::TETRAHEDRON:
new_or = GetTetOrientation(old_v, new_v);
break;
default:
new_or = 0;
MFEM_ABORT(Geometry::Name[geom] << " elements (" << fec->Name()
<< " FE collection) are not supported yet!");
break;
}
dof_ord = fec->DofOrderForOrientation(geom, new_or);
MFEM_VERIFY(dof_ord != NULL,
"FE collection '" << fec->Name()
<< "' does not define reordering for "
<< Geometry::Name[geom] << " elements!");
fes->GetElementInteriorDofs(i, old_dofs);
new_dofs.SetSize(old_dofs.Size());
for (int j = 0; j < new_dofs.Size(); j++)
{
// we assume the dofs are non-directional, i.e. dof_ord[j] is >= 0
const int old_j = dof_ord[j];
new_dofs[old_j] = offset + j;
}
offset += new_dofs.Size();
fes->DofsToVDofs(old_dofs);
fes->DofsToVDofs(new_dofs);
for (int j = 0; j < old_dofs.Size(); j++)
{
(*Nodes)(new_dofs[j]) = onodes(old_dofs[j]);
}
}
}
// Update Tables, faces, etc
if (Dim > 2)
{
if (fes->GetNFDofs() == 0)
{
// needed for FE spaces that have face dofs, even if
// the 'Nodes' do not have face dofs.
GetElementToFaceTable();
GenerateFaces();
}
CheckBdrElementOrientation();
}
if (el_to_edge)
{
// update 'el_to_edge', 'be_to_edge' (2D), 'bel_to_edge' (3D)
NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
if (Dim == 2)
{
// update 'faces' and 'faces_info'
GenerateFaces();
CheckBdrElementOrientation();
}
}
// To force FE space update, we need to increase 'sequence':
sequence++;
last_operation = Mesh::NONE;
fes->Update(false); // want_transform = false
Nodes->Update(); // just needed to update Nodes->sequence
}
void Mesh::FinalizeTetMesh(int generate_edges, int refine, bool fix_orientation)
{
FinalizeCheck();
CheckElementOrientation(fix_orientation);
if (NumOfBdrElements == 0)
{
GetElementToFaceTable();
GenerateFaces();
GenerateBoundaryElements();
}
if (refine)
{
DSTable v_to_v(NumOfVertices);
GetVertexToVertexTable(v_to_v);
MarkTetMeshForRefinement(v_to_v);
}
GetElementToFaceTable();
GenerateFaces();
CheckBdrElementOrientation();
if (generate_edges == 1)
{
el_to_edge = new Table;
NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
}
else
{
el_to_edge = NULL; // Not really necessary -- InitTables was called
bel_to_edge = NULL;
NumOfEdges = 0;
}
SetAttributes();
SetMeshGen();
}
void Mesh::FinalizeWedgeMesh(int generate_edges, int refine,
bool fix_orientation)
{
FinalizeCheck();
CheckElementOrientation(fix_orientation);
if (NumOfBdrElements == 0)
{
GetElementToFaceTable();
GenerateFaces();
GenerateBoundaryElements();
}
GetElementToFaceTable();
GenerateFaces();
CheckBdrElementOrientation();
if (generate_edges == 1)
{
el_to_edge = new Table;
NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
}
else
{
el_to_edge = NULL; // Not really necessary -- InitTables was called
bel_to_edge = NULL;
NumOfEdges = 0;
}
SetAttributes();
SetMeshGen();
}
void Mesh::FinalizeHexMesh(int generate_edges, int refine, bool fix_orientation)
{
FinalizeCheck();
CheckElementOrientation(fix_orientation);
GetElementToFaceTable();
GenerateFaces();
if (NumOfBdrElements == 0)
{
GenerateBoundaryElements();
}
CheckBdrElementOrientation();
if (generate_edges)
{
el_to_edge = new Table;
NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
}
else
{
NumOfEdges = 0;
}
SetAttributes();
SetMeshGen();
}
void Mesh::FinalizeMesh(int refine, bool fix_orientation)
{
FinalizeTopology();
Finalize(refine, fix_orientation);
}
void Mesh::FinalizeTopology(bool generate_bdr)
{
// Requirements: the following should be defined:
// 1) Dim
// 2) NumOfElements, elements
// 3) NumOfBdrElements, boundary
// 4) NumOfVertices
// Optional:
// 2) ncmesh may be defined
// 3) el_to_edge may be allocated (it will be re-computed)
FinalizeCheck();
bool generate_edges = true;
if (spaceDim == 0) { spaceDim = Dim; }
if (ncmesh) { ncmesh->spaceDim = spaceDim; }
// set the mesh type: 'meshgen', ...
SetMeshGen();
// generate the faces
if (Dim > 2)
{
GetElementToFaceTable();
GenerateFaces();
if (NumOfBdrElements == 0 && generate_bdr)
{
GenerateBoundaryElements();
GetElementToFaceTable(); // update be_to_face
}
}
else
{
NumOfFaces = 0;
}
// generate edges if requested
if (Dim > 1 && generate_edges)
{
// el_to_edge may already be allocated (P2 VTK meshes)
if (!el_to_edge) { el_to_edge = new Table; }
NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
if (Dim == 2)
{
GenerateFaces(); // 'Faces' in 2D refers to the edges
if (NumOfBdrElements == 0 && generate_bdr)
{
GenerateBoundaryElements();
}
}
}
else
{
NumOfEdges = 0;
}
if (Dim == 1)
{
GenerateFaces();
}
if (ncmesh)
{
// tell NCMesh the numbering of edges/faces
ncmesh->OnMeshUpdated(this);
// update faces_info with NC relations
GenerateNCFaceInfo();
}
// generate the arrays 'attributes' and 'bdr_attributes'
SetAttributes();
}
void Mesh::Finalize(bool refine, bool fix_orientation)
{
if (NURBSext || ncmesh)
{
MFEM_ASSERT(CheckElementOrientation(false) == 0, "");
MFEM_ASSERT(CheckBdrElementOrientation() == 0, "");
return;
}
// Requirements:
// 1) FinalizeTopology() or equivalent was called
// 2) if (Nodes == NULL), vertices must be defined
// 3) if (Nodes != NULL), Nodes must be defined
const bool check_orientation = true; // for regular elements, not boundary
const bool curved = (Nodes != NULL);
const bool may_change_topology =
( refine && (Dim > 1 && (meshgen & 1)) ) ||
( check_orientation && fix_orientation &&
(Dim == 2 || (Dim == 3 && (meshgen & 1))) );
DSTable *old_v_to_v = NULL;
Table *old_elem_vert = NULL;
if (curved && may_change_topology)
{
PrepareNodeReorder(&old_v_to_v, &old_elem_vert);
}
if (check_orientation)
{
// check and optionally fix element orientation
CheckElementOrientation(fix_orientation);
}
if (refine)
{
MarkForRefinement(); // may change topology!
}
if (may_change_topology)
{
if (curved)
{
DoNodeReorder(old_v_to_v, old_elem_vert); // updates the mesh topology
delete old_elem_vert;
delete old_v_to_v;
}
else
{
FinalizeTopology(); // Re-computes some data unnecessarily.
}
// TODO: maybe introduce Mesh::NODE_REORDER operation and FESpace::
// NodeReorderMatrix and do Nodes->Update() instead of DoNodeReorder?
}
// check and fix boundary element orientation
CheckBdrElementOrientation();
#ifdef MFEM_DEBUG
// For non-orientable surfaces/manifolds, the check below will fail, so we
// only perform it when Dim == spaceDim.
if (Dim >= 2 && Dim == spaceDim)
{
const int num_faces = GetNumFaces();
for (int i = 0; i < num_faces; i++)
{
MFEM_VERIFY(faces_info[i].Elem2No < 0 ||
faces_info[i].Elem2Inf%2 != 0, "invalid mesh topology");
}
}
#endif
}
void Mesh::Make3D(int nx, int ny, int nz, Element::Type type,
double sx, double sy, double sz, bool sfc_ordering)
{
int x, y, z;
int NVert, NElem, NBdrElem;
NVert = (nx+1) * (ny+1) * (nz+1);
NElem = nx * ny * nz;
NBdrElem = 2*(nx*ny+nx*nz+ny*nz);
if (type == Element::TETRAHEDRON)
{
NElem *= 6;
NBdrElem *= 2;
}
else if (type == Element::WEDGE)
{
NElem *= 2;
NBdrElem += 2*nx*ny;
}
InitMesh(3, 3, NVert, NElem, NBdrElem);
double coord[3];
int ind[8];
// Sets vertices and the corresponding coordinates
for (z = 0; z <= nz; z++)
{
coord[2] = ((double) z / nz) * sz;
for (y = 0; y <= ny; y++)
{
coord[1] = ((double) y / ny) * sy;
for (x = 0; x <= nx; x++)
{
coord[0] = ((double) x / nx) * sx;
AddVertex(coord);
}
}
}
#define VTX(XC, YC, ZC) ((XC)+((YC)+(ZC)*(ny+1))*(nx+1))
// Sets elements and the corresponding indices of vertices
if (sfc_ordering && type == Element::HEXAHEDRON)
{
Array<int> sfc;
NCMesh::GridSfcOrdering3D(nx, ny, nz, sfc);
MFEM_VERIFY(sfc.Size() == 3*nx*ny*nz, "");
for (int k = 0; k < nx*ny*nz; k++)
{
x = sfc[3*k + 0];
y = sfc[3*k + 1];
z = sfc[3*k + 2];
ind[0] = VTX(x , y , z );
ind[1] = VTX(x+1, y , z );
ind[2] = VTX(x+1, y+1, z );
ind[3] = VTX(x , y+1, z );
ind[4] = VTX(x , y , z+1);
ind[5] = VTX(x+1, y , z+1);
ind[6] = VTX(x+1, y+1, z+1);
ind[7] = VTX(x , y+1, z+1);
AddHex(ind, 1);
}
}
else
{
for (z = 0; z < nz; z++)
{
for (y = 0; y < ny; y++)
{
for (x = 0; x < nx; x++)
{
ind[0] = VTX(x , y , z );
ind[1] = VTX(x+1, y , z );
ind[2] = VTX(x+1, y+1, z );
ind[3] = VTX(x , y+1, z );
ind[4] = VTX(x , y , z+1);
ind[5] = VTX(x+1, y , z+1);
ind[6] = VTX(x+1, y+1, z+1);
ind[7] = VTX( x, y+1, z+1);
if (type == Element::TETRAHEDRON)
{
AddHexAsTets(ind, 1);
}
else if (type == Element::WEDGE)
{
AddHexAsWedges(ind, 1);
}
else
{
AddHex(ind, 1);
}
}
}
}
}
// Sets boundary elements and the corresponding indices of vertices
// bottom, bdr. attribute 1
for (y = 0; y < ny; y++)
{
for (x = 0; x < nx; x++)
{
ind[0] = VTX(x , y , 0);
ind[1] = VTX(x , y+1, 0);
ind[2] = VTX(x+1, y+1, 0);
ind[3] = VTX(x+1, y , 0);
if (type == Element::TETRAHEDRON)
{
AddBdrQuadAsTriangles(ind, 1);
}
else if (type == Element::WEDGE)
{
AddBdrQuadAsTriangles(ind, 1);
}
else
{
AddBdrQuad(ind, 1);
}
}
}
// top, bdr. attribute 6
for (y = 0; y < ny; y++)
{
for (x = 0; x < nx; x++)
{
ind[0] = VTX(x , y , nz);
ind[1] = VTX(x+1, y , nz);
ind[2] = VTX(x+1, y+1, nz);
ind[3] = VTX(x , y+1, nz);
if (type == Element::TETRAHEDRON)
{
AddBdrQuadAsTriangles(ind, 6);
}
else if (type == Element::WEDGE)
{
AddBdrQuadAsTriangles(ind, 1);
}
else
{
AddBdrQuad(ind, 6);
}
}
}
// left, bdr. attribute 5
for (z = 0; z < nz; z++)
{
for (y = 0; y < ny; y++)
{
ind[0] = VTX(0 , y , z );
ind[1] = VTX(0 , y , z+1);
ind[2] = VTX(0 , y+1, z+1);
ind[3] = VTX(0 , y+1, z );
if (type == Element::TETRAHEDRON)
{
AddBdrQuadAsTriangles(ind, 5);
}
else
{
AddBdrQuad(ind, 5);
}
}
}
// right, bdr. attribute 3
for (z = 0; z < nz; z++)
{
for (y = 0; y < ny; y++)
{
ind[0] = VTX(nx, y , z );
ind[1] = VTX(nx, y+1, z );
ind[2] = VTX(nx, y+1, z+1);
ind[3] = VTX(nx, y , z+1);
if (type == Element::TETRAHEDRON)
{
AddBdrQuadAsTriangles(ind, 3);
}
else
{
AddBdrQuad(ind, 3);
}
}
}
// front, bdr. attribute 2
for (x = 0; x < nx; x++)
{
for (z = 0; z < nz; z++)
{
ind[0] = VTX(x , 0, z );
ind[1] = VTX(x+1, 0, z );
ind[2] = VTX(x+1, 0, z+1);
ind[3] = VTX(x , 0, z+1);
if (type == Element::TETRAHEDRON)
{
AddBdrQuadAsTriangles(ind, 2);
}
else
{
AddBdrQuad(ind, 2);
}
}
}
// back, bdr. attribute 4
for (x = 0; x < nx; x++)
{
for (z = 0; z < nz; z++)
{
ind[0] = VTX(x , ny, z );
ind[1] = VTX(x , ny, z+1);
ind[2] = VTX(x+1, ny, z+1);
ind[3] = VTX(x+1, ny, z );
if (type == Element::TETRAHEDRON)
{
AddBdrQuadAsTriangles(ind, 4);
}
else
{
AddBdrQuad(ind, 4);
}
}
}
#undef VTX
#if 0
ofstream test_stream("debug.mesh");
Print(test_stream);
test_stream.close();
#endif
FinalizeTopology();
// Finalize(...) can be called after this method, if needed
}
void Mesh::Make2D(int nx, int ny, Element::Type type,
double sx, double sy,
bool generate_edges, bool sfc_ordering)
{
int i, j, k;
SetEmpty();
Dim = spaceDim = 2;
// Creates quadrilateral mesh
if (type == Element::QUADRILATERAL)
{
NumOfVertices = (nx+1) * (ny+1);
NumOfElements = nx * ny;
NumOfBdrElements = 2 * nx + 2 * ny;
vertices.SetSize(NumOfVertices);
elements.SetSize(NumOfElements);
boundary.SetSize(NumOfBdrElements);
double cx, cy;
int ind[4];
// Sets vertices and the corresponding coordinates
k = 0;
for (j = 0; j < ny+1; j++)
{
cy = ((double) j / ny) * sy;
for (i = 0; i < nx+1; i++)
{
cx = ((double) i / nx) * sx;
vertices[k](0) = cx;
vertices[k](1) = cy;
k++;
}
}
// Sets elements and the corresponding indices of vertices
if (sfc_ordering)
{
Array<int> sfc;
NCMesh::GridSfcOrdering2D(nx, ny, sfc);
MFEM_VERIFY(sfc.Size() == 2*nx*ny, "");
for (k = 0; k < nx*ny; k++)
{
i = sfc[2*k + 0];
j = sfc[2*k + 1];
ind[0] = i + j*(nx+1);
ind[1] = i + 1 +j*(nx+1);
ind[2] = i + 1 + (j+1)*(nx+1);
ind[3] = i + (j+1)*(nx+1);
elements[k] = new Quadrilateral(ind);
}
}
else
{
k = 0;
for (j = 0; j < ny; j++)
{
for (i = 0; i < nx; i++)
{
ind[0] = i + j*(nx+1);
ind[1] = i + 1 +j*(nx+1);
ind[2] = i + 1 + (j+1)*(nx+1);
ind[3] = i + (j+1)*(nx+1);
elements[k] = new Quadrilateral(ind);
k++;
}
}
}
// Sets boundary elements and the corresponding indices of vertices
int m = (nx+1)*ny;
for (i = 0; i < nx; i++)
{
boundary[i] = new Segment(i, i+1, 1);
boundary[nx+i] = new Segment(m+i+1, m+i, 3);
}
m = nx+1;
for (j = 0; j < ny; j++)
{
boundary[2*nx+j] = new Segment((j+1)*m, j*m, 4);
boundary[2*nx+ny+j] = new Segment(j*m+nx, (j+1)*m+nx, 2);
}
}
// Creates triangular mesh
else if (type == Element::TRIANGLE)
{
NumOfVertices = (nx+1) * (ny+1);
NumOfElements = 2 * nx * ny;
NumOfBdrElements = 2 * nx + 2 * ny;
vertices.SetSize(NumOfVertices);
elements.SetSize(NumOfElements);
boundary.SetSize(NumOfBdrElements);
double cx, cy;
int ind[3];
// Sets vertices and the corresponding coordinates
k = 0;
for (j = 0; j < ny+1; j++)
{
cy = ((double) j / ny) * sy;
for (i = 0; i < nx+1; i++)
{
cx = ((double) i / nx) * sx;
vertices[k](0) = cx;
vertices[k](1) = cy;
k++;
}
}
// Sets the elements and the corresponding indices of vertices
k = 0;
for (j = 0; j < ny; j++)
{
for (i = 0; i < nx; i++)
{
ind[0] = i + j*(nx+1);
ind[1] = i + 1 + (j+1)*(nx+1);
ind[2] = i + (j+1)*(nx+1);
elements[k] = new Triangle(ind);
k++;
ind[1] = i + 1 + j*(nx+1);
ind[2] = i + 1 + (j+1)*(nx+1);
elements[k] = new Triangle(ind);
k++;
}
}
// Sets boundary elements and the corresponding indices of vertices
int m = (nx+1)*ny;
for (i = 0; i < nx; i++)
{
boundary[i] = new Segment(i, i+1, 1);
boundary[nx+i] = new Segment(m+i+1, m+i, 3);
}
m = nx+1;
for (j = 0; j < ny; j++)
{
boundary[2*nx+j] = new Segment((j+1)*m, j*m, 4);
boundary[2*nx+ny+j] = new Segment(j*m+nx, (j+1)*m+nx, 2);
}
// MarkTriMeshForRefinement(); // done in Finalize(...)
}
else
{
MFEM_ABORT("Unsupported element type.");
}
SetMeshGen();
CheckElementOrientation();
if (generate_edges == 1)
{
el_to_edge = new Table;
NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
GenerateFaces();
CheckBdrElementOrientation();
}
else
{
NumOfEdges = 0;
}
NumOfFaces = 0;
attributes.Append(1);
bdr_attributes.Append(1); bdr_attributes.Append(2);
bdr_attributes.Append(3); bdr_attributes.Append(4);
// Finalize(...) can be called after this method, if needed
}
void Mesh::Make1D(int n, double sx)
{
int j, ind[1];
SetEmpty();
Dim = 1;
spaceDim = 1;
NumOfVertices = n + 1;
NumOfElements = n;
NumOfBdrElements = 2;
vertices.SetSize(NumOfVertices);
elements.SetSize(NumOfElements);
boundary.SetSize(NumOfBdrElements);
// Sets vertices and the corresponding coordinates
for (j = 0; j < n+1; j++)
{
vertices[j](0) = ((double) j / n) * sx;
}
// Sets elements and the corresponding indices of vertices
for (j = 0; j < n; j++)
{
elements[j] = new Segment(j, j+1, 1);
}
// Sets the boundary elements
ind[0] = 0;
boundary[0] = new Point(ind, 1);
ind[0] = n;
boundary[1] = new Point(ind, 2);
NumOfEdges = 0;
NumOfFaces = 0;
SetMeshGen();
GenerateFaces();
attributes.Append(1);
bdr_attributes.Append(1); bdr_attributes.Append(2);
}
Mesh::Mesh(const Mesh &mesh, bool copy_nodes)
{
Dim = mesh.Dim;
spaceDim = mesh.spaceDim;
NumOfVertices = mesh.NumOfVertices;
NumOfElements = mesh.NumOfElements;
NumOfBdrElements = mesh.NumOfBdrElements;
NumOfEdges = mesh.NumOfEdges;
NumOfFaces = mesh.NumOfFaces;
nbInteriorFaces = mesh.nbInteriorFaces;
nbBoundaryFaces = mesh.nbBoundaryFaces;
meshgen = mesh.meshgen;
mesh_geoms = mesh.mesh_geoms;
// Create the new Mesh instance without a record of its refinement history
sequence = 0;
last_operation = Mesh::NONE;
// Duplicate the elements
elements.SetSize(NumOfElements);
for (int i = 0; i < NumOfElements; i++)
{
elements[i] = mesh.elements[i]->Duplicate(this);
}
// Copy the vertices
mesh.vertices.Copy(vertices);
// Duplicate the boundary
boundary.SetSize(NumOfBdrElements);
for (int i = 0; i < NumOfBdrElements; i++)
{
boundary[i] = mesh.boundary[i]->Duplicate(this);
}
// Copy the element-to-face Table, el_to_face
el_to_face = (mesh.el_to_face) ? new Table(*mesh.el_to_face) : NULL;
// Copy the boundary-to-face Array, be_to_face.
mesh.be_to_face.Copy(be_to_face);
// Copy the element-to-edge Table, el_to_edge
el_to_edge = (mesh.el_to_edge) ? new Table(*mesh.el_to_edge) : NULL;
// Copy the boundary-to-edge Table, bel_to_edge (3D)
bel_to_edge = (mesh.bel_to_edge) ? new Table(*mesh.bel_to_edge) : NULL;
// Copy the boundary-to-edge Array, be_to_edge (2D)
mesh.be_to_edge.Copy(be_to_edge);
// Duplicate the faces and faces_info.
faces.SetSize(mesh.faces.Size());
for (int i = 0; i < faces.Size(); i++)
{
Element *face = mesh.faces[i]; // in 1D the faces are NULL
faces[i] = (face) ? face->Duplicate(this) : NULL;
}
mesh.faces_info.Copy(faces_info);
mesh.nc_faces_info.Copy(nc_faces_info);
// Do NOT copy the element-to-element Table, el_to_el
el_to_el = NULL;
// Do NOT copy the face-to-edge Table, face_edge
face_edge = NULL;
// Copy the edge-to-vertex Table, edge_vertex
edge_vertex = (mesh.edge_vertex) ? new Table(*mesh.edge_vertex) : NULL;
// Copy the attributes and bdr_attributes
mesh.attributes.Copy(attributes);
mesh.bdr_attributes.Copy(bdr_attributes);
// Deep copy the NURBSExtension.
#ifdef MFEM_USE_MPI
ParNURBSExtension *pNURBSext =
dynamic_cast<ParNURBSExtension *>(mesh.NURBSext);
if (pNURBSext)
{
NURBSext = new ParNURBSExtension(*pNURBSext);
}
else
#endif
{
NURBSext = mesh.NURBSext ? new NURBSExtension(*mesh.NURBSext) : NULL;
}
// Deep copy the NCMesh.
#ifdef MFEM_USE_MPI
if (dynamic_cast<const ParMesh*>(&mesh))
{
ncmesh = NULL; // skip; will be done in ParMesh copy ctor
}
else
#endif
{
ncmesh = mesh.ncmesh ? new NCMesh(*mesh.ncmesh) : NULL;
}
// Duplicate the Nodes, including the FiniteElementCollection and the
// FiniteElementSpace
if (mesh.Nodes && copy_nodes)
{
FiniteElementSpace *fes = mesh.Nodes->FESpace();
const FiniteElementCollection *fec = fes->FEColl();
FiniteElementCollection *fec_copy =
FiniteElementCollection::New(fec->Name());
FiniteElementSpace *fes_copy =
new FiniteElementSpace(*fes, this, fec_copy);
Nodes = new GridFunction(fes_copy);
Nodes->MakeOwner(fec_copy);
*Nodes = *mesh.Nodes;
own_nodes = 1;
}
else
{
Nodes = mesh.Nodes;
own_nodes = 0;
}
}
Mesh::Mesh(const char *filename, int generate_edges, int refine,
bool fix_orientation)
{
// Initialization as in the default constructor
SetEmpty();
named_ifgzstream imesh(filename);
if (!imesh)
{
// Abort with an error message.
MFEM_ABORT("Mesh file not found: " << filename << '\n');
}
else
{
Load(imesh, generate_edges, refine, fix_orientation);
}
}
Mesh::Mesh(std::istream &input, int generate_edges, int refine,
bool fix_orientation)
{
SetEmpty();
Load(input, generate_edges, refine, fix_orientation);
}
void Mesh::ChangeVertexDataOwnership(double *vertex_data, int len_vertex_data,
bool zerocopy)
{
// A dimension of 3 is now required since we use mfem::Vertex objects as PODs
// and these object have a hardcoded double[3] entry
MFEM_VERIFY(len_vertex_data >= NumOfVertices * 3,
"Not enough vertices in external array : "
"len_vertex_data = "<< len_vertex_data << ", "
"NumOfVertices * 3 = " << NumOfVertices * 3);
// Allow multiple calls to this method with the same vertex_data
if (vertex_data == (double *)(vertices.GetData()))
{
MFEM_ASSERT(!vertices.OwnsData(), "invalid ownership");
return;
}
if (!zerocopy)
{
memcpy(vertex_data, vertices.GetData(),
NumOfVertices * 3 * sizeof(double));
}
// Vertex is POD double[3]
vertices.MakeRef(reinterpret_cast<Vertex*>(vertex_data), NumOfVertices);
}
Mesh::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)
{
if (space_dimension == -1)
{
space_dimension = dimension;
}
InitMesh(dimension, space_dimension, /*num_vertices*/ 0, num_elements,
num_boundary_elements);
int element_index_stride = Geometry::NumVerts[element_type];
int boundary_index_stride = num_boundary_elements > 0 ?
Geometry::NumVerts[boundary_type] : 0;
// assuming Vertex is POD
vertices.MakeRef(reinterpret_cast<Vertex*>(_vertices), num_vertices);
NumOfVertices = num_vertices;
for (int i = 0; i < num_elements; i++)
{
elements[i] = NewElement(element_type);
elements[i]->SetVertices(element_indices + i * element_index_stride);
elements[i]->SetAttribute(element_attributes[i]);
}
NumOfElements = num_elements;
for (int i = 0; i < num_boundary_elements; i++)
{
boundary[i] = NewElement(boundary_type);
boundary[i]->SetVertices(boundary_indices + i * boundary_index_stride);
boundary[i]->SetAttribute(boundary_attributes[i]);
}
NumOfBdrElements = num_boundary_elements;
FinalizeTopology();
}
Element *Mesh::NewElement(int geom)
{
switch (geom)
{
case Geometry::POINT: return (new Point);
case Geometry::SEGMENT: return (new Segment);
case Geometry::TRIANGLE: return (new Triangle);
case Geometry::SQUARE: return (new Quadrilateral);
case Geometry::TETRAHEDRON:
#ifdef MFEM_USE_MEMALLOC
return TetMemory.Alloc();
#else
return (new Tetrahedron);
#endif
case Geometry::CUBE: return (new Hexahedron);
case Geometry::PRISM: return (new Wedge);
default:
MFEM_ABORT("invalid Geometry::Type, geom = " << geom);
}
return NULL;
}
Element *Mesh::ReadElementWithoutAttr(std::istream &input)
{
int geom, nv, *v;
Element *el;
input >> geom;
el = NewElement(geom);
MFEM_VERIFY(el, "Unsupported element type: " << geom);
nv = el->GetNVertices();
v = el->GetVertices();
for (int i = 0; i < nv; i++)
{
input >> v[i];
}
return el;
}
void Mesh::PrintElementWithoutAttr(const Element *el, std::ostream &out)
{
out << el->GetGeometryType();
const int nv = el->GetNVertices();
const int *v = el->GetVertices();
for (int j = 0; j < nv; j++)
{
out << ' ' << v[j];
}
out << '\n';
}
Element *Mesh::ReadElement(std::istream &input)
{
int attr;
Element *el;
input >> attr;
el = ReadElementWithoutAttr(input);
el->SetAttribute(attr);
return el;
}
void Mesh::PrintElement(const Element *el, std::ostream &out)
{
out << el->GetAttribute() << ' ';
PrintElementWithoutAttr(el, out);
}
void Mesh::SetMeshGen()
{
meshgen = mesh_geoms = 0;
for (int i = 0; i < NumOfElements; i++)
{
const Element::Type type = GetElement(i)->GetType();
switch (type)
{
case Element::TETRAHEDRON:
mesh_geoms |= (1 << Geometry::TETRAHEDRON);
case Element::TRIANGLE:
mesh_geoms |= (1 << Geometry::TRIANGLE);
case Element::SEGMENT:
mesh_geoms |= (1 << Geometry::SEGMENT);
case Element::POINT:
mesh_geoms |= (1 << Geometry::POINT);
meshgen |= 1;
break;
case Element::HEXAHEDRON:
mesh_geoms |= (1 << Geometry::CUBE);
case Element::QUADRILATERAL:
mesh_geoms |= (1 << Geometry::SQUARE);
mesh_geoms |= (1 << Geometry::SEGMENT);
mesh_geoms |= (1 << Geometry::POINT);
meshgen |= 2;
break;
case Element::WEDGE:
mesh_geoms |= (1 << Geometry::PRISM);
mesh_geoms |= (1 << Geometry::SQUARE);
mesh_geoms |= (1 << Geometry::TRIANGLE);
mesh_geoms |= (1 << Geometry::SEGMENT);
mesh_geoms |= (1 << Geometry::POINT);
meshgen |= 4;
break;
default:
MFEM_ABORT("invalid element type: " << type);
break;
}
}
}
void Mesh::Loader(std::istream &input, int generate_edges,
std::string parse_tag)
{
int curved = 0, read_gf = 1;
bool finalize_topo = true;
if (!input)
{
MFEM_ABORT("Input stream is not open");
}
Clear();
string mesh_type;
input >> ws;
getline(input, mesh_type);
filter_dos(mesh_type);
// MFEM's native mesh formats
bool mfem_v10 = (mesh_type == "MFEM mesh v1.0");
bool mfem_v11 = (mesh_type == "MFEM mesh v1.1");
bool mfem_v12 = (mesh_type == "MFEM mesh v1.2");
if (mfem_v10 || mfem_v11 || mfem_v12) // MFEM's own mesh formats
{
// Formats mfem_v12 and newer have a tag indicating the end of the mesh
// section in the stream. A user provided parse tag can also be provided
// via the arguments. For example, if this is called from parallel mesh
// object, it can indicate to read until parallel mesh section begins.
if ( mfem_v12 && parse_tag.empty() )
{
parse_tag = "mfem_mesh_end";
}
ReadMFEMMesh(input, mfem_v11, curved);
}
else if (mesh_type == "linemesh") // 1D mesh
{
ReadLineMesh(input);
}
else if (mesh_type == "areamesh2" || mesh_type == "curved_areamesh2")
{
if (mesh_type == "curved_areamesh2")
{
curved = 1;
}
ReadNetgen2DMesh(input, curved);
}
else if (mesh_type == "NETGEN" || mesh_type == "NETGEN_Neutral_Format")
{
ReadNetgen3DMesh(input);
}
else if (mesh_type == "TrueGrid")
{
ReadTrueGridMesh(input);
}
else if (mesh_type == "# vtk DataFile Version 3.0" ||
mesh_type == "# vtk DataFile Version 2.0") // VTK
{
ReadVTKMesh(input, curved, read_gf, finalize_topo);
}
else if (mesh_type == "MFEM NURBS mesh v1.0")
{
ReadNURBSMesh(input, curved, read_gf);
}
else if (mesh_type == "MFEM INLINE mesh v1.0")
{
ReadInlineMesh(input, generate_edges);
return; // done with inline mesh construction
}
else if (mesh_type == "$MeshFormat") // Gmsh
{
ReadGmshMesh(input, curved, read_gf);
}
else if
((mesh_type.size() > 2 &&
mesh_type[0] == 'C' && mesh_type[1] == 'D' && mesh_type[2] == 'F') ||
(mesh_type.size() > 3 &&
mesh_type[1] == 'H' && mesh_type[2] == 'D' && mesh_type[3] == 'F'))
{
named_ifgzstream *mesh_input = dynamic_cast<named_ifgzstream *>(&input);
if (mesh_input)
{
#ifdef MFEM_USE_NETCDF
ReadCubit(mesh_input->filename.c_str(), curved, read_gf);
#else
MFEM_ABORT("NetCDF support requires configuration with"
" MFEM_USE_NETCDF=YES");
return;
#endif
}
else
{
MFEM_ABORT("Can not determine Cubit mesh filename!"
" Use mfem::named_ifgzstream for input.");
return;
}
}
else
{
MFEM_ABORT("Unknown input mesh format: " << mesh_type);
return;
}
// at this point the following should be defined:
// 1) Dim
// 2) NumOfElements, elements
// 3) NumOfBdrElements, boundary
// 4) NumOfVertices, with allocated space in vertices
// 5) curved
// 5a) if curved == 0, vertices must be defined
// 5b) if curved != 0 and read_gf != 0,
// 'input' must point to a GridFunction
// 5c) if curved != 0 and read_gf == 0,
// vertices and Nodes must be defined
// optional:
// 1) el_to_edge may be allocated (as in the case of P2 VTK meshes)
// 2) ncmesh may be allocated
// FinalizeTopology() will:
// - assume that generate_edges is true
// - assume that refine is false
// - does not check the orientation of regular and boundary elements
if (finalize_topo)
{
FinalizeTopology();
}
if (curved && read_gf)
{
Nodes = new GridFunction(this, input);
own_nodes = 1;
spaceDim = Nodes->VectorDim();
if (ncmesh) { ncmesh->spaceDim = spaceDim; }
// Set the 'vertices' from the 'Nodes'
for (int i = 0; i < spaceDim; i++)
{
Vector vert_val;
Nodes->GetNodalValues(vert_val, i+1);
for (int j = 0; j < NumOfVertices; j++)
{
vertices[j](i) = vert_val(j);
}
}
}
// If a parse tag was supplied, keep reading the stream until the tag is
// encountered.
if (mfem_v12)
{
string line;
do
{
skip_comment_lines(input, '#');
MFEM_VERIFY(input.good(), "Required mesh-end tag not found");
getline(input, line);
filter_dos(line);
// mfem v1.2 may not have parse_tag in it, e.g. if trying to read a
// serial mfem v1.2 mesh as parallel with "mfem_serial_mesh_end" as
// parse_tag. That's why, regardless of parse_tag, we stop reading if
// we find "mfem_mesh_end" which is required by mfem v1.2 format.
if (line == "mfem_mesh_end") { break; }
}
while (line != parse_tag);
}
// Finalize(...) should be called after this, if needed.
}
Mesh::Mesh(Mesh *mesh_array[], int num_pieces)
{
int i, j, ie, ib, iv, *v, nv;
Element *el;
Mesh *m;
SetEmpty();
Dim = mesh_array[0]->Dimension();
spaceDim = mesh_array[0]->SpaceDimension();
if (mesh_array[0]->NURBSext)
{
// assuming the pieces form a partition of a NURBS mesh
NURBSext = new NURBSExtension(mesh_array, num_pieces);
NumOfVertices = NURBSext->GetNV();
NumOfElements = NURBSext->GetNE();
NURBSext->GetElementTopo(elements);
// NumOfBdrElements = NURBSext->GetNBE();
// NURBSext->GetBdrElementTopo(boundary);
Array<int> lvert_vert, lelem_elem;
// Here, for visualization purposes, we copy the boundary elements from
// the individual pieces which include the interior boundaries. This
// creates 'boundary' array that is different from the one generated by
// the NURBSExtension which, in particular, makes the boundary-dof table
// invalid. This, in turn, causes GetBdrElementTransformation to not
// function properly.
NumOfBdrElements = 0;
for (i = 0; i < num_pieces; i++)
{
NumOfBdrElements += mesh_array[i]->GetNBE();
}
boundary.SetSize(NumOfBdrElements);
vertices.SetSize(NumOfVertices);
ib = 0;
for (i = 0; i < num_pieces; i++)
{
m = mesh_array[i];
m->NURBSext->GetVertexLocalToGlobal(lvert_vert);
m->NURBSext->GetElementLocalToGlobal(lelem_elem);
// copy the element attributes
for (j = 0; j < m->GetNE(); j++)
{
elements[lelem_elem[j]]->SetAttribute(m->GetAttribute(j));
}
// copy the boundary
for (j = 0; j < m->GetNBE(); j++)
{
el = m->GetBdrElement(j)->Duplicate(this);
v = el->GetVertices();
nv = el->GetNVertices();
for (int k = 0; k < nv; k++)
{
v[k] = lvert_vert[v[k]];
}
boundary[ib++] = el;
}
// copy the vertices
for (j = 0; j < m->GetNV(); j++)
{
vertices[lvert_vert[j]].SetCoords(m->SpaceDimension(),
m->GetVertex(j));
}
}
}
else // not a NURBS mesh
{
NumOfElements = 0;
NumOfBdrElements = 0;
NumOfVertices = 0;
for (i = 0; i < num_pieces; i++)
{
m = mesh_array[i];
NumOfElements += m->GetNE();
NumOfBdrElements += m->GetNBE();
NumOfVertices += m->GetNV();
}
elements.SetSize(NumOfElements);
boundary.SetSize(NumOfBdrElements);
vertices.SetSize(NumOfVertices);
ie = ib = iv = 0;
for (i = 0; i < num_pieces; i++)
{
m = mesh_array[i];
// copy the elements
for (j = 0; j < m->GetNE(); j++)
{
el = m->GetElement(j)->Duplicate(this);
v = el->GetVertices();
nv = el->GetNVertices();
for (int k = 0; k < nv; k++)
{
v[k] += iv;
}
elements[ie++] = el;
}
// copy the boundary elements
for (j = 0; j < m->GetNBE(); j++)
{
el = m->GetBdrElement(j)->Duplicate(this);
v = el->GetVertices();
nv = el->GetNVertices();
for (int k = 0; k < nv; k++)
{
v[k] += iv;
}
boundary[ib++] = el;
}
// copy the vertices
for (j = 0; j < m->GetNV(); j++)
{
vertices[iv++].SetCoords(m->SpaceDimension(), m->GetVertex(j));
}
}
}
FinalizeTopology();
// copy the nodes (curvilinear meshes)
GridFunction *g = mesh_array[0]->GetNodes();
if (g)
{
Array<GridFunction *> gf_array(num_pieces);
for (i = 0; i < num_pieces; i++)
{
gf_array[i] = mesh_array[i]->GetNodes();
}
Nodes = new GridFunction(this, gf_array, num_pieces);
own_nodes = 1;
}
#ifdef MFEM_DEBUG
CheckElementOrientation(false);
CheckBdrElementOrientation(false);
#endif
}
Mesh::Mesh(Mesh *orig_mesh, int ref_factor, int ref_type)
{
Dim = orig_mesh->Dimension();
MFEM_VERIFY(ref_factor >= 1, "the refinement factor must be >= 1");
MFEM_VERIFY(ref_type == BasisType::ClosedUniform ||
ref_type == BasisType::GaussLobatto, "invalid refinement type");
MFEM_VERIFY(Dim == 1 || Dim == 2 || Dim == 3,
"only implemented for Segment, Quadrilateral and Hexahedron "
"elements in 1D/2D/3D");
MFEM_VERIFY(orig_mesh->GetNumGeometries(Dim) <= 1,
"meshes with mixed elements are not supported");
// Construct a scalar H1 FE space of order ref_factor and use its dofs as
// the indices of the new, refined vertices.
H1_FECollection rfec(ref_factor, Dim, ref_type);
FiniteElementSpace rfes(orig_mesh, &rfec);
int r_bndr_factor = pow(ref_factor, Dim - 1);
int r_elem_factor = ref_factor * r_bndr_factor;
int r_num_vert = rfes.GetNDofs();
int r_num_elem = orig_mesh->GetNE() * r_elem_factor;
int r_num_bndr = orig_mesh->GetNBE() * r_bndr_factor;
InitMesh(Dim, orig_mesh->SpaceDimension(), r_num_vert, r_num_elem,
r_num_bndr);
// Set the number of vertices, set the actual coordinates later
NumOfVertices = r_num_vert;
// Add refined elements and set vertex coordinates
Array<int> rdofs;
DenseMatrix phys_pts;
int max_nv = 0;
DenseMatrix node_coordinates(spaceDim*pow(2, Dim), r_num_elem);
H1_FECollection vertex_fec(1, Dim);
for (int el = 0; el < orig_mesh->GetNE(); el++)
{
Geometry::Type geom = orig_mesh->GetElementBaseGeometry(el);
int attrib = orig_mesh->GetAttribute(el);
int nvert = Geometry::NumVerts[geom];
RefinedGeometry &RG = *GlobGeometryRefiner.Refine(geom, ref_factor);
max_nv = std::max(max_nv, nvert);
rfes.GetElementDofs(el, rdofs);
MFEM_ASSERT(rdofs.Size() == RG.RefPts.Size(), "");
const FiniteElement *rfe = rfes.GetFE(el);
orig_mesh->GetElementTransformation(el)->Transform(rfe->GetNodes(),
phys_pts);
const int *c2h_map = rfec.GetDofMap(geom);
const int *vertex_map = vertex_fec.GetDofMap(geom);
for (int i = 0; i < phys_pts.Width(); i++)
{
vertices[rdofs[i]].SetCoords(spaceDim, phys_pts.GetColumn(i));
}
for (int j = 0; j < RG.RefGeoms.Size()/nvert; j++)
{
Element *elem = NewElement(geom);
elem->SetAttribute(attrib);
int *v = elem->GetVertices();
for (int k = 0; k < nvert; k++)
{
int cid = RG.RefGeoms[k+nvert*j]; // local Cartesian index
v[k] = rdofs[c2h_map[cid]];
}
for (int k = 0; k < nvert; k++)
{
for (int j = 0; j < spaceDim; ++j)
{
node_coordinates(k*spaceDim + j, NumOfElements)
= vertices[v[vertex_map[k]]](j);
}
}
AddElement(elem);
}
}
if (orig_mesh->GetNodes())
{
L2_FECollection fec_dg(1, Dim, BasisType::GaussLobatto);
FiniteElementSpace fes_dg(this, &fec_dg, spaceDim, 1);
GridFunction nodes_dg(&fes_dg, node_coordinates.Data());
bool discont = orig_mesh->GetNodalFESpace()->IsDGSpace();
Ordering::Type dof_ordering = orig_mesh->GetNodalFESpace()->GetOrdering();
SetCurvature(1, discont, spaceDim, dof_ordering);
Nodes->ProjectGridFunction(nodes_dg);
}
// Add refined boundary elements
for (int el = 0; el < orig_mesh->GetNBE(); el++)
{
Geometry::Type geom = orig_mesh->GetBdrElementBaseGeometry(el);
int attrib = orig_mesh->GetBdrAttribute(el);
int nvert = Geometry::NumVerts[geom];
RefinedGeometry &RG = *GlobGeometryRefiner.Refine(geom, ref_factor);
rfes.GetBdrElementDofs(el, rdofs);
MFEM_ASSERT(rdofs.Size() == RG.RefPts.Size(), "");
if (Dim == 1)
{
// Dim == 1 is a special case because the boundary elements are
// zero-dimensional points, and therefore don't have a DofMap
for (int j = 0; j < RG.RefGeoms.Size()/nvert; j++)
{
Element *elem = NewElement(geom);
elem->SetAttribute(attrib);
int *v = elem->GetVertices();
v[0] = rdofs[RG.RefGeoms[nvert*j]];
AddBdrElement(elem);
}
}
else
{
const int *c2h_map = rfec.GetDofMap(geom);
for (int j = 0; j < RG.RefGeoms.Size()/nvert; j++)
{
Element *elem = NewElement(geom);
elem->SetAttribute(attrib);
int *v = elem->GetVertices();
for (int k = 0; k < nvert; k++)
{
int cid = RG.RefGeoms[k+nvert*j]; // local Cartesian index
v[k] = rdofs[c2h_map[cid]];
}
AddBdrElement(elem);
}
}
}
FinalizeTopology(false);
sequence = orig_mesh->GetSequence() + 1;
last_operation = Mesh::REFINE;
// Setup the data for the coarse-fine refinement transformations
CoarseFineTr.embeddings.SetSize(GetNE());
if (orig_mesh->GetNE() > 0)
{
const int el = 0;
Geometry::Type geom = orig_mesh->GetElementBaseGeometry(el);
CoarseFineTr.point_matrices[geom].SetSize(Dim, max_nv, r_elem_factor);
int nvert = Geometry::NumVerts[geom];
RefinedGeometry &RG = *GlobGeometryRefiner.Refine(geom, ref_factor);
const int *c2h_map = rfec.GetDofMap(geom);
const IntegrationRule &r_nodes = rfes.GetFE(el)->GetNodes();
for (int j = 0; j < RG.RefGeoms.Size()/nvert; j++)
{
DenseMatrix &Pj = CoarseFineTr.point_matrices[geom](j);
for (int k = 0; k < nvert; k++)
{
int cid = RG.RefGeoms[k+nvert*j]; // local Cartesian index
const IntegrationPoint &ip = r_nodes.IntPoint(c2h_map[cid]);
ip.Get(Pj.GetColumn(k), Dim);
}
}
}
for (int el = 0; el < GetNE(); el++)
{
Embedding &emb = CoarseFineTr.embeddings[el];
emb.parent = el / r_elem_factor;
emb.matrix = el % r_elem_factor;
}
MFEM_ASSERT(CheckElementOrientation(false) == 0, "");
MFEM_ASSERT(CheckBdrElementOrientation(false) == 0, "");
}
void Mesh::KnotInsert(Array<KnotVector *> &kv)
{
if (NURBSext == NULL)
{
mfem_error("Mesh::KnotInsert : Not a NURBS mesh!");
}
if (kv.Size() != NURBSext->GetNKV())
{
mfem_error("Mesh::KnotInsert : KnotVector array size mismatch!");
}
NURBSext->ConvertToPatches(*Nodes);
NURBSext->KnotInsert(kv);
last_operation = Mesh::NONE; // FiniteElementSpace::Update is not supported
sequence++;
UpdateNURBS();
}
void Mesh::KnotInsert(Array<Vector *> &kv)
{
if (NURBSext == NULL)
{
mfem_error("Mesh::KnotInsert : Not a NURBS mesh!");
}
if (kv.Size() != NURBSext->GetNKV())
{
mfem_error("Mesh::KnotInsert : KnotVector array size mismatch!");
}
NURBSext->ConvertToPatches(*Nodes);
NURBSext->KnotInsert(kv);
last_operation = Mesh::NONE; // FiniteElementSpace::Update is not supported
sequence++;
UpdateNURBS();
}
void Mesh::NURBSUniformRefinement()
{
// do not check for NURBSext since this method is protected
NURBSext->ConvertToPatches(*Nodes);
NURBSext->UniformRefinement();
last_operation = Mesh::NONE; // FiniteElementSpace::Update is not supported
sequence++;
UpdateNURBS();
}
void Mesh::DegreeElevate(int rel_degree, int degree)
{
if (NURBSext == NULL)
{
mfem_error("Mesh::DegreeElevate : Not a NURBS mesh!");
}
NURBSext->ConvertToPatches(*Nodes);
NURBSext->DegreeElevate(rel_degree, degree);
last_operation = Mesh::NONE; // FiniteElementSpace::Update is not supported
sequence++;
UpdateNURBS();
}
void Mesh::UpdateNURBS()
{
ResetLazyData();
NURBSext->SetKnotsFromPatches();
Dim = NURBSext->Dimension();
spaceDim = Dim;
if (NumOfElements != NURBSext->GetNE())
{
for (int i = 0; i < elements.Size(); i++)
{
FreeElement(elements[i]);
}
NumOfElements = NURBSext->GetNE();
NURBSext->GetElementTopo(elements);
}
if (NumOfBdrElements != NURBSext->GetNBE())
{
for (int i = 0; i < boundary.Size(); i++)
{
FreeElement(boundary[i]);
}
NumOfBdrElements = NURBSext->GetNBE();
NURBSext->GetBdrElementTopo(boundary);
}
Nodes->FESpace()->Update();
Nodes->Update();
NURBSext->SetCoordsFromPatches(*Nodes);
if (NumOfVertices != NURBSext->GetNV())
{
NumOfVertices = NURBSext->GetNV();
vertices.SetSize(NumOfVertices);
int vd = Nodes->VectorDim();
for (int i = 0; i < vd; i++)
{
Vector vert_val;
Nodes->GetNodalValues(vert_val, i+1);
for (int j = 0; j < NumOfVertices; j++)
{
vertices[j](i) = vert_val(j);
}
}
}
if (el_to_edge)
{
NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
if (Dim == 2)
{
GenerateFaces();
}
}
if (el_to_face)
{
GetElementToFaceTable();
GenerateFaces();
}
}
void Mesh::LoadPatchTopo(std::istream &input, Array<int> &edge_to_knot)
{
SetEmpty();
// Read MFEM NURBS mesh v1.0 format
string ident;
skip_comment_lines(input, '#');
input >> ident; // 'dimension'
input >> Dim;
spaceDim = Dim;
skip_comment_lines(input, '#');
input >> ident; // 'elements'
input >> NumOfElements;
elements.SetSize(NumOfElements);
for (int j = 0; j < NumOfElements; j++)
{
elements[j] = ReadElement(input);
}
skip_comment_lines(input, '#');
input >> ident; // 'boundary'
input >> NumOfBdrElements;
boundary.SetSize(NumOfBdrElements);
for (int j = 0; j < NumOfBdrElements; j++)
{
boundary[j] = ReadElement(input);
}
skip_comment_lines(input, '#');
input >> ident; // 'edges'
input >> NumOfEdges;
edge_vertex = new Table(NumOfEdges, 2);
edge_to_knot.SetSize(NumOfEdges);
for (int j = 0; j < NumOfEdges; j++)
{
int *v = edge_vertex->GetRow(j);
input >> edge_to_knot[j] >> v[0] >> v[1];
if (v[0] > v[1])
{
edge_to_knot[j] = -1 - edge_to_knot[j];
}
}
skip_comment_lines(input, '#');
input >> ident; // 'vertices'
input >> NumOfVertices;
vertices.SetSize(0);
FinalizeTopology();
CheckBdrElementOrientation(); // check and fix boundary element orientation
}
void XYZ_VectorFunction(const Vector &p, Vector &v)
{
if (p.Size() >= v.Size())
{
for (int d = 0; d < v.Size(); d++)
{
v(d) = p(d);
}
}
else
{
int d;
for (d = 0; d < p.Size(); d++)
{
v(d) = p(d);
}
for ( ; d < v.Size(); d++)
{
v(d) = 0.0;
}
}
}
void Mesh::GetNodes(GridFunction &nodes) const
{
if (Nodes == NULL || Nodes->FESpace() != nodes.FESpace())
{
const int newSpaceDim = nodes.FESpace()->GetVDim();
VectorFunctionCoefficient xyz(newSpaceDim, XYZ_VectorFunction);
nodes.ProjectCoefficient(xyz);
}
else
{
nodes = *Nodes;
}
}
void Mesh::SetNodalFESpace(FiniteElementSpace *nfes)
{
GridFunction *nodes = new GridFunction(nfes);
SetNodalGridFunction(nodes, true);
}
void Mesh::EnsureNodes()
{
if (Nodes)
{
const FiniteElementCollection *fec = GetNodalFESpace()->FEColl();
if (dynamic_cast<const H1_FECollection*>(fec)
|| dynamic_cast<const L2_FECollection*>(fec))
{
return;
}
else // Mesh using a legacy FE_Collection
{
const int order = GetNodalFESpace()->GetOrder(0);
SetCurvature(order, false, -1, Ordering::byVDIM);
}
}
else //First order H1 mesh
{
SetCurvature(1, false, -1, Ordering::byVDIM);
}
}
void Mesh::SetNodalGridFunction(GridFunction *nodes, bool make_owner)
{
GetNodes(*nodes);
NewNodes(*nodes, make_owner);
}
const FiniteElementSpace *Mesh::GetNodalFESpace() const
{
return ((Nodes) ? Nodes->FESpace() : NULL);
}
void Mesh::SetCurvature(int order, bool discont, int space_dim, int ordering)
{
space_dim = (space_dim == -1) ? spaceDim : space_dim;
FiniteElementCollection* nfec;
if (discont)
{
const int type = 1; // Gauss-Lobatto points
nfec = new L2_FECollection(order, Dim, type);
}
else
{
nfec = new H1_FECollection(order, Dim);
}
FiniteElementSpace* nfes = new FiniteElementSpace(this, nfec, space_dim,
ordering);
SetNodalFESpace(nfes);
Nodes->MakeOwner(nfec);
}
int Mesh::GetNumFaces() const
{
switch (Dim)
{
case 1: return GetNV();
case 2: return GetNEdges();
case 3: return GetNFaces();
}
return 0;
}
static int CountFacesByType(const Mesh &mesh, const FaceType type)
{
int e1, e2;
int inf1, inf2;
int nf = 0;
for (int f = 0; f < mesh.GetNumFaces(); ++f)
{
mesh.GetFaceElements(f, &e1, &e2);
mesh.GetFaceInfos(f, &inf1, &inf2);
if ((type==FaceType::Interior && (e2>=0 || (e2<0 && inf2>=0))) ||
(type==FaceType::Boundary && e2<0 && inf2<0) ) { nf++; }
}
return nf;
}
int Mesh::GetNFbyType(FaceType type) const
{
const bool isInt = type==FaceType::Interior;
int &nf = isInt ? nbInteriorFaces : nbBoundaryFaces;
if (nf<0) { nf = CountFacesByType(*this, type); }
return nf;
}
#if (!defined(MFEM_USE_MPI) || defined(MFEM_DEBUG))
static const char *fixed_or_not[] = { "fixed", "NOT FIXED" };
#endif
int Mesh::CheckElementOrientation(bool fix_it)
{
int i, j, k, wo = 0, fo = 0, *vi = 0;
double *v[4];
if (Dim == 2 && spaceDim == 2)
{
DenseMatrix J(2, 2);
for (i = 0; i < NumOfElements; i++)
{
if (Nodes == NULL)
{
vi = elements[i]->GetVertices();
for (j = 0; j < 3; j++)
{
v[j] = vertices[vi[j]]();
}
for (j = 0; j < 2; j++)
for (k = 0; k < 2; k++)
{
J(j, k) = v[j+1][k] - v[0][k];
}
}
else
{
// only check the Jacobian at the center of the element
GetElementJacobian(i, J);
}
if (J.Det() < 0.0)
{
if (fix_it)
{
switch (GetElementType(i))
{
case Element::TRIANGLE:
mfem::Swap(vi[0], vi[1]);
break;
case Element::QUADRILATERAL:
mfem::Swap(vi[1], vi[3]);
break;
default:
MFEM_ABORT("Invalid 2D element type \""
<< GetElementType(i) << "\"");
break;
}
fo++;
}
wo++;
}
}
}
if (Dim == 3)
{
DenseMatrix J(3, 3);
for (i = 0; i < NumOfElements; i++)
{
vi = elements[i]->GetVertices();
switch (GetElementType(i))
{
case Element::TETRAHEDRON:
if (Nodes == NULL)
{
for (j = 0; j < 4; j++)
{
v[j] = vertices[vi[j]]();
}
for (j = 0; j < 3; j++)
for (k = 0; k < 3; k++)
{
J(j, k) = v[j+1][k] - v[0][k];
}
}
else
{
// only check the Jacobian at the center of the element
GetElementJacobian(i, J);
}
if (J.Det() < 0.0)
{
wo++;
if (fix_it)
{
mfem::Swap(vi[0], vi[1]);
fo++;
}
}
break;
case Element::WEDGE:
// only check the Jacobian at the center of the element
GetElementJacobian(i, J);
if (J.Det() < 0.0)
{
wo++;
if (fix_it)
{
// how?
}
}
break;
case Element::HEXAHEDRON:
// only check the Jacobian at the center of the element
GetElementJacobian(i, J);
if (J.Det() < 0.0)
{
wo++;
if (fix_it)
{
// how?
}
}
break;
default:
MFEM_ABORT("Invalid 3D element type \""
<< GetElementType(i) << "\"");
break;
}
}
}
#if (!defined(MFEM_USE_MPI) || defined(MFEM_DEBUG))
if (wo > 0)
{
mfem::out << "Elements with wrong orientation: " << wo << " / "
<< NumOfElements << " (" << fixed_or_not[(wo == fo) ? 0 : 1]
<< ")" << endl;
}
#endif
return wo;
}
int Mesh::GetTriOrientation(const int *base, const int *test)
{
// Static method.
// This function computes the index 'j' of the permutation that transforms
// test into base: test[tri_orientation[j][i]]=base[i].
// tri_orientation = Geometry::Constants<Geometry::TRIANGLE>::Orient
int orient;
if (test[0] == base[0])
if (test[1] == base[1])
{
orient = 0; // (0, 1, 2)
}
else
{
orient = 5; // (0, 2, 1)
}
else if (test[0] == base[1])
if (test[1] == base[0])
{
orient = 1; // (1, 0, 2)
}
else
{
orient = 2; // (1, 2, 0)
}
else // test[0] == base[2]
if (test[1] == base[0])
{
orient = 4; // (2, 0, 1)
}
else
{
orient = 3; // (2, 1, 0)
}
#ifdef MFEM_DEBUG
const int *aor = tri_t::Orient[orient];
for (int j = 0; j < 3; j++)
if (test[aor[j]] != base[j])
{
mfem_error("Mesh::GetTriOrientation(...)");
}
#endif
return orient;
}
int Mesh::GetQuadOrientation(const int *base, const int *test)
{
int i;
for (i = 0; i < 4; i++)
if (test[i] == base[0])
{
break;
}
#ifdef MFEM_DEBUG
int orient;
if (test[(i+1)%4] == base[1])
{
orient = 2*i;
}
else
{
orient = 2*i+1;
}
const int *aor = quad_t::Orient[orient];
for (int j = 0; j < 4; j++)
if (test[aor[j]] != base[j])
{
mfem::err << "Mesh::GetQuadOrientation(...)" << endl;
mfem::err << " base = [";
for (int k = 0; k < 4; k++)
{
mfem::err << " " << base[k];
}
mfem::err << " ]\n test = [";
for (int k = 0; k < 4; k++)
{
mfem::err << " " << test[k];
}
mfem::err << " ]" << endl;
mfem_error();
}
#endif
if (test[(i+1)%4] == base[1])
{
return 2*i;
}
return 2*i+1;
}
int Mesh::GetTetOrientation(const int *base, const int *test)
{
// Static method.
// This function computes the index 'j' of the permutation that transforms
// test into base: test[tet_orientation[j][i]]=base[i].
// tet_orientation = Geometry::Constants<Geometry::TETRAHEDRON>::Orient
int orient;
if (test[0] == base[0])
if (test[1] == base[1])
if (test[2] == base[2])
{
orient = 0; // (0, 1, 2, 3)
}
else
{
orient = 1; // (0, 1, 3, 2)
}
else if (test[2] == base[1])
if (test[3] == base[2])
{
orient = 2; // (0, 2, 3, 1)
}
else
{
orient = 3; // (0, 2, 1, 3)
}
else // test[3] == base[1]
if (test[1] == base[2])
{
orient = 4; // (0, 3, 1, 2)
}
else
{
orient = 5; // (0, 3, 2, 1)
}
else if (test[1] == base[0])
if (test[2] == base[1])
if (test[0] == base[2])
{
orient = 6; // (1, 2, 0, 3)
}
else
{
orient = 7; // (1, 2, 3, 0)
}
else if (test[3] == base[1])
if (test[2] == base[2])
{
orient = 8; // (1, 3, 2, 0)
}
else
{
orient = 9; // (1, 3, 0, 2)
}
else // test[0] == base[1]
if (test[3] == base[2])
{
orient = 10; // (1, 0, 3, 2)
}
else
{
orient = 11; // (1, 0, 2, 3)
}
else if (test[2] == base[0])
if (test[3] == base[1])
if (test[0] == base[2])
{
orient = 12; // (2, 3, 0, 1)
}
else
{
orient = 13; // (2, 3, 1, 0)
}
else if (test[0] == base[1])
if (test[1] == base[2])
{
orient = 14; // (2, 0, 1, 3)
}
else
{
orient = 15; // (2, 0, 3, 1)
}
else // test[1] == base[1]
if (test[3] == base[2])
{
orient = 16; // (2, 1, 3, 0)
}
else
{
orient = 17; // (2, 1, 0, 3)
}
else // (test[3] == base[0])
if (test[0] == base[1])
if (test[2] == base[2])
{
orient = 18; // (3, 0, 2, 1)
}
else
{
orient = 19; // (3, 0, 1, 2)
}
else if (test[1] == base[1])
if (test[0] == base[2])
{
orient = 20; // (3, 1, 0, 2)
}
else
{
orient = 21; // (3, 1, 2, 0)
}
else // test[2] == base[1]
if (test[1] == base[2])
{
orient = 22; // (3, 2, 1, 0)
}
else
{
orient = 23; // (3, 2, 0, 1)
}
#ifdef MFEM_DEBUG
const int *aor = tet_t::Orient[orient];
for (int j = 0; j < 4; j++)
if (test[aor[j]] != base[j])
{
mfem_error("Mesh::GetTetOrientation(...)");
}
#endif
return orient;
}
int Mesh::CheckBdrElementOrientation(bool fix_it)
{
int wo = 0; // count wrong orientations
if (Dim == 2)
{
if (el_to_edge == NULL) // edges were not generated
{
el_to_edge = new Table;
NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
GenerateFaces(); // 'Faces' in 2D refers to the edges
}
for (int i = 0; i < NumOfBdrElements; i++)
{
if (faces_info[be_to_edge[i]].Elem2No < 0) // boundary face
{
int *bv = boundary[i]->GetVertices();
int *fv = faces[be_to_edge[i]]->GetVertices();
if (bv[0] != fv[0])
{
if (fix_it)
{
mfem::Swap<int>(bv[0], bv[1]);
}
wo++;
}
}
}
}
if (Dim == 3)
{
for (int i = 0; i < NumOfBdrElements; i++)
{
const int fi = be_to_face[i];
if (faces_info[fi].Elem2No >= 0) { continue; }
// boundary face
int *bv = boundary[i]->GetVertices();
// Make sure the 'faces' are generated:
MFEM_ASSERT(fi < faces.Size(), "internal error");
const int *fv = faces[fi]->GetVertices();
int orientation; // orientation of the bdr. elem. w.r.t. the
// corresponding face element (that's the base)
const Element::Type bdr_type = GetBdrElementType(i);
switch (bdr_type)
{
case Element::TRIANGLE:
{
orientation = GetTriOrientation(fv, bv);
break;
}
case Element::QUADRILATERAL:
{
orientation = GetQuadOrientation(fv, bv);
break;
}
default:
MFEM_ABORT("Invalid 2D boundary element type \""
<< bdr_type << "\"");
orientation = 0; // suppress a warning
break;
}
if (orientation % 2 == 0) { continue; }
wo++;
if (!fix_it) { continue; }
switch (bdr_type)
{
case Element::TRIANGLE:
{
// swap vertices 0 and 1 so that we don't change the marked edge:
// (0,1,2) -> (1,0,2)
mfem::Swap<int>(bv[0], bv[1]);
if (bel_to_edge)
{
int *be = bel_to_edge->GetRow(i);
mfem::Swap<int>(be[1], be[2]);
}
break;
}
case Element::QUADRILATERAL:
{
mfem::Swap<int>(bv[0], bv[2]);
if (bel_to_edge)
{
int *be = bel_to_edge->GetRow(i);
mfem::Swap<int>(be[0], be[1]);
mfem::Swap<int>(be[2], be[3]);
}
break;
}
default: // unreachable
break;
}
}
}
// #if (!defined(MFEM_USE_MPI) || defined(MFEM_DEBUG))
#ifdef MFEM_DEBUG
if (wo > 0)
{
mfem::out << "Boundary elements with wrong orientation: " << wo << " / "
<< NumOfBdrElements << " (" << fixed_or_not[fix_it ? 0 : 1]
<< ")" << endl;
}
#endif
return wo;
}
int Mesh::GetNumGeometries(int dim) const
{
MFEM_ASSERT(0 <= dim && dim <= Dim, "invalid dim: " << dim);
int num_geoms = 0;
for (int g = Geometry::DimStart[dim]; g < Geometry::DimStart[dim+1]; g++)
{
if (HasGeometry(Geometry::Type(g))) { num_geoms++; }
}
return num_geoms;
}
void Mesh::GetGeometries(int dim, Array<Geometry::Type> &el_geoms) const
{
MFEM_ASSERT(0 <= dim && dim <= Dim, "invalid dim: " << dim);
el_geoms.SetSize(0);
for (int g = Geometry::DimStart[dim]; g < Geometry::DimStart[dim+1]; g++)
{
if (HasGeometry(Geometry::Type(g)))
{
el_geoms.Append(Geometry::Type(g));
}
}
}
void Mesh::GetElementEdges(int i, Array<int> &edges, Array<int> &cor) const
{
if (el_to_edge)
{
el_to_edge->GetRow(i, edges);
}
else
{
mfem_error("Mesh::GetElementEdges(...) element to edge table "
"is not generated.");
}
const int *v = elements[i]->GetVertices();
const int ne = elements[i]->GetNEdges();
cor.SetSize(ne);
for (int j = 0; j < ne; j++)
{
const int *e = elements[i]->GetEdgeVertices(j);
cor[j] = (v[e[0]] < v[e[1]]) ? (1) : (-1);
}
}
void Mesh::GetBdrElementEdges(int i, Array<int> &edges, Array<int> &cor) const
{
if (Dim == 2)
{
edges.SetSize(1);
cor.SetSize(1);
edges[0] = be_to_edge[i];
const int *v = boundary[i]->GetVertices();
cor[0] = (v[0] < v[1]) ? (1) : (-1);
}
else if (Dim == 3)
{
if (bel_to_edge)
{
bel_to_edge->GetRow(i, edges);
}
else
{
mfem_error("Mesh::GetBdrElementEdges(...)");
}
const int *v = boundary[i]->GetVertices();
const int ne = boundary[i]->GetNEdges();
cor.SetSize(ne);
for (int j = 0; j < ne; j++)
{
const int *e = boundary[i]->GetEdgeVertices(j);
cor[j] = (v[e[0]] < v[e[1]]) ? (1) : (-1);
}
}
}
void Mesh::GetFaceEdges(int i, Array<int> &edges, Array<int> &o) const
{
if (Dim == 2)
{
edges.SetSize(1);
edges[0] = i;
o.SetSize(1);
const int *v = faces[i]->GetVertices();
o[0] = (v[0] < v[1]) ? (1) : (-1);
}
if (Dim != 3)
{
return;
}
GetFaceEdgeTable(); // generate face_edge Table (if not generated)
face_edge->GetRow(i, edges);
const int *v = faces[i]->GetVertices();
const int ne = faces[i]->GetNEdges();
o.SetSize(ne);
for (int j = 0; j < ne; j++)
{
const int *e = faces[i]->GetEdgeVertices(j);
o[j] = (v[e[0]] < v[e[1]]) ? (1) : (-1);
}
}
void Mesh::GetEdgeVertices(int i, Array<int> &vert) const
{
// the two vertices are sorted: vert[0] < vert[1]
// this is consistent with the global edge orientation
// generate edge_vertex Table (if not generated)
if (!edge_vertex) { GetEdgeVertexTable(); }
edge_vertex->GetRow(i, vert);
}
Table *Mesh::GetFaceEdgeTable() const
{
if (face_edge)
{
return face_edge;
}
if (Dim != 3)
{
return NULL;
}
#ifdef MFEM_DEBUG
if (faces.Size() != NumOfFaces)
{
mfem_error("Mesh::GetFaceEdgeTable : faces were not generated!");
}
#endif
DSTable v_to_v(NumOfVertices);
GetVertexToVertexTable(v_to_v);
face_edge = new Table;
GetElementArrayEdgeTable(faces, v_to_v, *face_edge);
return (face_edge);
}
Table *Mesh::GetEdgeVertexTable() const
{
if (edge_vertex)
{
return edge_vertex;
}
DSTable v_to_v(NumOfVertices);
GetVertexToVertexTable(v_to_v);
int nedges = v_to_v.NumberOfEntries();
edge_vertex = new Table(nedges, 2);
for (int i = 0; i < NumOfVertices; i++)
{
for (DSTable::RowIterator it(v_to_v, i); !it; ++it)
{
int j = it.Index();
edge_vertex->Push(j, i);
edge_vertex->Push(j, it.Column());
}
}
edge_vertex->Finalize();
return edge_vertex;
}
Table *Mesh::GetVertexToElementTable()
{
int i, j, nv, *v;
Table *vert_elem = new Table;
vert_elem->MakeI(NumOfVertices);
for (i = 0; i < NumOfElements; i++)
{
nv = elements[i]->GetNVertices();
v = elements[i]->GetVertices();
for (j = 0; j < nv; j++)
{
vert_elem->AddAColumnInRow(v[j]);
}
}
vert_elem->MakeJ();
for (i = 0; i < NumOfElements; i++)
{
nv = elements[i]->GetNVertices();
v = elements[i]->GetVertices();
for (j = 0; j < nv; j++)
{
vert_elem->AddConnection(v[j], i);
}
}
vert_elem->ShiftUpI();
return vert_elem;
}
Table *Mesh::GetFaceToElementTable() const
{
Table *face_elem = new Table;
face_elem->MakeI(faces_info.Size());
for (int i = 0; i < faces_info.Size(); i++)
{
if (faces_info[i].Elem2No >= 0)
{
face_elem->AddColumnsInRow(i, 2);
}
else
{
face_elem->AddAColumnInRow(i);
}
}
face_elem->MakeJ();
for (int i = 0; i < faces_info.Size(); i++)
{
face_elem->AddConnection(i, faces_info[i].Elem1No);
if (faces_info[i].Elem2No >= 0)
{
face_elem->AddConnection(i, faces_info[i].Elem2No);
}
}
face_elem->ShiftUpI();
return face_elem;
}
void Mesh::GetElementFaces(int i, Array<int> &fcs, Array<int> &cor)
const
{
int n, j;
if (el_to_face)
{
el_to_face->GetRow(i, fcs);
}
else
{
mfem_error("Mesh::GetElementFaces(...) : el_to_face not generated.");
}
n = fcs.Size();
cor.SetSize(n);
for (j = 0; j < n; j++)
if (faces_info[fcs[j]].Elem1No == i)
{
cor[j] = faces_info[fcs[j]].Elem1Inf % 64;
}
#ifdef MFEM_DEBUG
else if (faces_info[fcs[j]].Elem2No == i)
{
cor[j] = faces_info[fcs[j]].Elem2Inf % 64;
}
else
{
mfem_error("Mesh::GetElementFaces(...) : 2");
}
#else
else
{
cor[j] = faces_info[fcs[j]].Elem2Inf % 64;
}
#endif
}
void Mesh::GetBdrElementFace(int i, int *f, int *o) const
{
const int *bv, *fv;
*f = be_to_face[i];
bv = boundary[i]->GetVertices();
fv = faces[be_to_face[i]]->GetVertices();
// find the orientation of the bdr. elem. w.r.t.
// the corresponding face element (that's the base)
switch (GetBdrElementType(i))
{
case Element::TRIANGLE:
*o = GetTriOrientation(fv, bv);
break;
case Element::QUADRILATERAL:
*o = GetQuadOrientation(fv, bv);
break;
default:
mfem_error("Mesh::GetBdrElementFace(...) 2");
}
}
int Mesh::GetBdrElementEdgeIndex(int i) const
{
switch (Dim)
{
case 1: return boundary[i]->GetVertices()[0];
case 2: return be_to_edge[i];
case 3: return be_to_face[i];
default: mfem_error("Mesh::GetBdrElementEdgeIndex: invalid dimension!");
}
return -1;
}
void Mesh::GetBdrElementAdjacentElement(int bdr_el, int &el, int &info) const
{
int fid = GetBdrElementEdgeIndex(bdr_el);
const FaceInfo &fi = faces_info[fid];
MFEM_ASSERT(fi.Elem1Inf%64 == 0, "internal error"); // orientation == 0
const int *fv = (Dim > 1) ? faces[fid]->GetVertices() : NULL;
const int *bv = boundary[bdr_el]->GetVertices();
int ori;
switch (GetBdrElementBaseGeometry(bdr_el))
{
case Geometry::POINT: ori = 0; break;
case Geometry::SEGMENT: ori = (fv[0] == bv[0]) ? 0 : 1; break;
case Geometry::TRIANGLE: ori = GetTriOrientation(fv, bv); break;
case Geometry::SQUARE: ori = GetQuadOrientation(fv, bv); break;
default: MFEM_ABORT("boundary element type not implemented"); ori = 0;
}
el = fi.Elem1No;
info = fi.Elem1Inf + ori;
}
Element::Type Mesh::GetElementType(int i) const
{
return elements[i]->GetType();
}
Element::Type Mesh::GetBdrElementType(int i) const
{
return boundary[i]->GetType();
}
void Mesh::GetPointMatrix(int i, DenseMatrix &pointmat) const
{
int k, j, nv;
const int *v;
v = elements[i]->GetVertices();
nv = elements[i]->GetNVertices();
pointmat.SetSize(spaceDim, nv);
for (k = 0; k < spaceDim; k++)
{
for (j = 0; j < nv; j++)
{
pointmat(k, j) = vertices[v[j]](k);
}
}
}
void Mesh::GetBdrPointMatrix(int i,DenseMatrix &pointmat) const
{
int k, j, nv;
const int *v;
v = boundary[i]->GetVertices();
nv = boundary[i]->GetNVertices();
pointmat.SetSize(spaceDim, nv);
for (k = 0; k < spaceDim; k++)
for (j = 0; j < nv; j++)
{
pointmat(k, j) = vertices[v[j]](k);
}
}
double Mesh::GetLength(int i, int j) const
{
const double *vi = vertices[i]();
const double *vj = vertices[j]();
double length = 0.;
for (int k = 0; k < spaceDim; k++)
{
length += (vi[k]-vj[k])*(vi[k]-vj[k]);
}
return sqrt(length);
}
// static method
void Mesh::GetElementArrayEdgeTable(const Array<Element*> &elem_array,
const DSTable &v_to_v, Table &el_to_edge)
{
el_to_edge.MakeI(elem_array.Size());
for (int i = 0; i < elem_array.Size(); i++)
{
el_to_edge.AddColumnsInRow(i, elem_array[i]->GetNEdges());
}
el_to_edge.MakeJ();
for (int i = 0; i < elem_array.Size(); i++)
{
const int *v = elem_array[i]->GetVertices();
const int ne = elem_array[i]->GetNEdges();
for (int j = 0; j < ne; j++)
{
const int *e = elem_array[i]->GetEdgeVertices(j);
el_to_edge.AddConnection(i, v_to_v(v[e[0]], v[e[1]]));
}
}
el_to_edge.ShiftUpI();
}
void Mesh::GetVertexToVertexTable(DSTable &v_to_v) const
{
if (edge_vertex)
{
for (int i = 0; i < edge_vertex->Size(); i++)
{
const int *v = edge_vertex->GetRow(i);
v_to_v.Push(v[0], v[1]);
}
}
else
{
for (int i = 0; i < NumOfElements; i++)
{
const int *v = elements[i]->GetVertices();
const int ne = elements[i]->GetNEdges();
for (int j = 0; j < ne; j++)
{
const int *e = elements[i]->GetEdgeVertices(j);
v_to_v.Push(v[e[0]], v[e[1]]);
}
}
}
}
int Mesh::GetElementToEdgeTable(Table & e_to_f, Array<int> &be_to_f)
{
int i, NumberOfEdges;
DSTable v_to_v(NumOfVertices);
GetVertexToVertexTable(v_to_v);
NumberOfEdges = v_to_v.NumberOfEntries();
// Fill the element to edge table
GetElementArrayEdgeTable(elements, v_to_v, e_to_f);
if (Dim == 2)
{
// Initialize the indices for the boundary elements.
be_to_f.SetSize(NumOfBdrElements);
for (i = 0; i < NumOfBdrElements; i++)
{
const int *v = boundary[i]->GetVertices();
be_to_f[i] = v_to_v(v[0], v[1]);
}
}
else if (Dim == 3)
{
if (bel_to_edge == NULL)
{
bel_to_edge = new Table;
}
GetElementArrayEdgeTable(boundary, v_to_v, *bel_to_edge);
}
else
{
mfem_error("1D GetElementToEdgeTable is not yet implemented.");
}
// Return the number of edges
return NumberOfEdges;
}
const Table & Mesh::ElementToElementTable()
{
if (el_to_el)
{
return *el_to_el;
}
// Note that, for ParNCMeshes, faces_info will contain also the ghost faces
MFEM_ASSERT(faces_info.Size() >= GetNumFaces(), "faces were not generated!");
Array<Connection> conn;
conn.Reserve(2*faces_info.Size());
for (int i = 0; i < faces_info.Size(); i++)
{
const FaceInfo &fi = faces_info[i];
if (fi.Elem2No >= 0)
{
conn.Append(Connection(fi.Elem1No, fi.Elem2No));
conn.Append(Connection(fi.Elem2No, fi.Elem1No));
}
else if (fi.Elem2Inf >= 0)
{
int nbr_elem_idx = NumOfElements - 1 - fi.Elem2No;
conn.Append(Connection(fi.Elem1No, nbr_elem_idx));
conn.Append(Connection(nbr_elem_idx, fi.Elem1No));
}
}
conn.Sort();
conn.Unique();
el_to_el = new Table(NumOfElements, conn);
return *el_to_el;
}
const Table & Mesh::ElementToFaceTable() const
{
if (el_to_face == NULL)
{
mfem_error("Mesh::ElementToFaceTable()");
}
return *el_to_face;
}
const Table & Mesh::ElementToEdgeTable() const
{
if (el_to_edge == NULL)
{
mfem_error("Mesh::ElementToEdgeTable()");
}
return *el_to_edge;
}
void Mesh::AddPointFaceElement(int lf, int gf, int el)
{
if (faces_info[gf].Elem1No == -1) // this will be elem1
{
// faces[gf] = new Point(&gf);
faces_info[gf].Elem1No = el;
faces_info[gf].Elem1Inf = 64 * lf; // face lf with orientation 0
faces_info[gf].Elem2No = -1; // in case there's no other side
faces_info[gf].Elem2Inf = -1; // face is not shared
}
else // this will be elem2
{
faces_info[gf].Elem2No = el;
faces_info[gf].Elem2Inf = 64 * lf + 1;
}
}
void Mesh::AddSegmentFaceElement(int lf, int gf, int el, int v0, int v1)
{
if (faces[gf] == NULL) // this will be elem1
{
faces[gf] = new Segment(v0, v1);
faces_info[gf].Elem1No = el;
faces_info[gf].Elem1Inf = 64 * lf; // face lf with orientation 0
faces_info[gf].Elem2No = -1; // in case there's no other side
faces_info[gf].Elem2Inf = -1; // face is not shared
}
else // this will be elem2
{
int *v = faces[gf]->GetVertices();
faces_info[gf].Elem2No = el;
if ( v[1] == v0 && v[0] == v1 )
{
faces_info[gf].Elem2Inf = 64 * lf + 1;
}
else if ( v[0] == v0 && v[1] == v1 )
{
// Temporarily allow even edge orientations: see the remark in
// AddTriangleFaceElement().
// Also, in a non-orientable surface mesh, the orientation will be even
// for edges that connect elements with opposite orientations.
faces_info[gf].Elem2Inf = 64 * lf;
}
else
{
MFEM_ABORT("internal error");
}
}
}
void Mesh::AddTriangleFaceElement(int lf, int gf, int el,
int v0, int v1, int v2)
{
if (faces[gf] == NULL) // this will be elem1
{
faces[gf] = new Triangle(v0, v1, v2);
faces_info[gf].Elem1No = el;
faces_info[gf].Elem1Inf = 64 * lf; // face lf with orientation 0
faces_info[gf].Elem2No = -1; // in case there's no other side
faces_info[gf].Elem2Inf = -1; // face is not shared
}
else // this will be elem2
{
int orientation, vv[3] = { v0, v1, v2 };
orientation = GetTriOrientation(faces[gf]->GetVertices(), vv);
// In a valid mesh, we should have (orientation % 2 != 0), however, if
// one of the adjacent elements has wrong orientation, both face
// orientations can be even, until the element orientations are fixed.
// MFEM_ASSERT(orientation % 2 != 0, "");
faces_info[gf].Elem2No = el;
faces_info[gf].Elem2Inf = 64 * lf + orientation;
}
}
void Mesh::AddQuadFaceElement(int lf, int gf, int el,
int v0, int v1, int v2, int v3)
{
if (faces_info[gf].Elem1No < 0) // this will be elem1
{
faces[gf] = new Quadrilateral(v0, v1, v2, v3);
faces_info[gf].Elem1No = el;
faces_info[gf].Elem1Inf = 64 * lf; // face lf with orientation 0
faces_info[gf].Elem2No = -1; // in case there's no other side
faces_info[gf].Elem2Inf = -1; // face is not shared
}
else // this will be elem2
{
int vv[4] = { v0, v1, v2, v3 };
int oo = GetQuadOrientation(faces[gf]->GetVertices(), vv);
// Temporarily allow even face orientations: see the remark in
// AddTriangleFaceElement().
// MFEM_ASSERT(oo % 2 != 0, "");
faces_info[gf].Elem2No = el;
faces_info[gf].Elem2Inf = 64 * lf + oo;
}
}
void Mesh::GenerateFaces()
{
int i, nfaces = GetNumFaces();
for (i = 0; i < faces.Size(); i++)
{
FreeElement(faces[i]);
}
// (re)generate the interior faces and the info for them
faces.SetSize(nfaces);
faces_info.SetSize(nfaces);
for (i = 0; i < nfaces; i++)
{
faces[i] = NULL;
faces_info[i].Elem1No = -1;
faces_info[i].NCFace = -1;
}
for (i = 0; i < NumOfElements; i++)
{
const int *v = elements[i]->GetVertices();
const int *ef;
if (Dim == 1)
{
AddPointFaceElement(0, v[0], i);
AddPointFaceElement(1, v[1], i);
}
else if (Dim == 2)
{
ef = el_to_edge->GetRow(i);
const int ne = elements[i]->GetNEdges();
for (int j = 0; j < ne; j++)
{
const int *e = elements[i]->GetEdgeVertices(j);
AddSegmentFaceElement(j, ef[j], i, v[e[0]], v[e[1]]);
}
}
else
{
ef = el_to_face->GetRow(i);
switch (GetElementType(i))
{
case Element::TETRAHEDRON:
{
for (int j = 0; j < 4; j++)
{
const int *fv = tet_t::FaceVert[j];
AddTriangleFaceElement(j, ef[j], i,
v[fv[0]], v[fv[1]], v[fv[2]]);
}
break;
}
case Element::WEDGE:
{
for (int j = 0; j < 2; j++)
{
const int *fv = pri_t::FaceVert[j];
AddTriangleFaceElement(j, ef[j], i,
v[fv[0]], v[fv[1]], v[fv[2]]);
}
for (int j = 2; j < 5; j++)
{
const int *fv = pri_t::FaceVert[j];
AddQuadFaceElement(j, ef[j], i,
v[fv[0]], v[fv[1]], v[fv[2]], v[fv[3]]);
}
break;
}
case Element::HEXAHEDRON:
{
for (int j = 0; j < 6; j++)
{
const int *fv = hex_t::FaceVert[j];
AddQuadFaceElement(j, ef[j], i,
v[fv[0]], v[fv[1]], v[fv[2]], v[fv[3]]);
}
break;
}
default:
MFEM_ABORT("Unexpected type of Element.");
}
}
}
}
void Mesh::GenerateNCFaceInfo()
{
MFEM_VERIFY(ncmesh, "missing NCMesh.");
for (int i = 0; i < faces_info.Size(); i++)
{
faces_info[i].NCFace = -1;
}
const NCMesh::NCList &list =
(Dim == 2) ? ncmesh->GetEdgeList() : ncmesh->GetFaceList();
nc_faces_info.SetSize(0);
nc_faces_info.Reserve(list.masters.size() + list.slaves.size());
int nfaces = GetNumFaces();
// add records for master faces
for (unsigned i = 0; i < list.masters.size(); i++)
{
const NCMesh::Master &master = list.masters[i];
if (master.index >= nfaces) { continue; }
faces_info[master.index].NCFace = nc_faces_info.Size();
nc_faces_info.Append(NCFaceInfo(false, master.local, NULL));
// NOTE: one of the unused members stores local face no. to be used below
}
// add records for slave faces
for (unsigned i = 0; i < list.slaves.size(); i++)
{
const NCMesh::Slave &slave = list.slaves[i];
if (slave.index < 0 || // degenerate slave face
slave.index >= nfaces || // ghost slave
slave.master >= nfaces) // has ghost master
{
continue;
}
FaceInfo &slave_fi = faces_info[slave.index];
FaceInfo &master_fi = faces_info[slave.master];
NCFaceInfo &master_nc = nc_faces_info[master_fi.NCFace];
slave_fi.NCFace = nc_faces_info.Size();
nc_faces_info.Append(NCFaceInfo(true, slave.master, &slave.point_matrix));
slave_fi.Elem2No = master_fi.Elem1No;
slave_fi.Elem2Inf = 64 * master_nc.MasterFace; // get lf no. stored above
// NOTE: orientation part of Elem2Inf is encoded in the point matrix
}
}
STable3D *Mesh::GetFacesTable()
{
STable3D *faces_tbl = new STable3D(NumOfVertices);
for (int i = 0; i < NumOfElements; i++)
{
const int *v = elements[i]->GetVertices();
switch (GetElementType(i))
{
case Element::TETRAHEDRON:
{
for (int j = 0; j < 4; j++)
{
const int *fv = tet_t::FaceVert[j];
faces_tbl->Push(v[fv[0]], v[fv[1]], v[fv[2]]);
}
break;
}
case Element::WEDGE:
{
for (int j = 0; j < 2; j++)
{
const int *fv = pri_t::FaceVert[j];
faces_tbl->Push(v[fv[0]], v[fv[1]], v[fv[2]]);
}
for (int j = 2; j < 5; j++)
{
const int *fv = pri_t::FaceVert[j];
faces_tbl->Push4(v[fv[0]], v[fv[1]], v[fv[2]], v[fv[3]]);
}
break;
}
case Element::HEXAHEDRON:
{
// find the face by the vertices with the smallest 3 numbers
// z = 0, y = 0, x = 1, y = 1, x = 0, z = 1
for (int j = 0; j < 6; j++)
{
const int *fv = hex_t::FaceVert[j];
faces_tbl->Push4(v[fv[0]], v[fv[1]], v[fv[2]], v[fv[3]]);
}
break;
}
default:
MFEM_ABORT("Unexpected type of Element.");
}
}
return faces_tbl;
}
STable3D *Mesh::GetElementToFaceTable(int ret_ftbl)
{
int i, *v;
STable3D *faces_tbl;
if (el_to_face != NULL)
{
delete el_to_face;
}
el_to_face = new Table(NumOfElements, 6); // must be 6 for hexahedra
faces_tbl = new STable3D(NumOfVertices);
for (i = 0; i < NumOfElements; i++)
{
v = elements[i]->GetVertices();
switch (GetElementType(i))
{
case Element::TETRAHEDRON:
{
for (int j = 0; j < 4; j++)
{
const int *fv = tet_t::FaceVert[j];
el_to_face->Push(
i, faces_tbl->Push(v[fv[0]], v[fv[1]], v[fv[2]]));
}
break;
}
case Element::WEDGE:
{
for (int j = 0; j < 2; j++)
{
const int *fv = pri_t::FaceVert[j];
el_to_face->Push(
i, faces_tbl->Push(v[fv[0]], v[fv[1]], v[fv[2]]));
}
for (int j = 2; j < 5; j++)
{
const int *fv = pri_t::FaceVert[j];
el_to_face->Push(
i, faces_tbl->Push4(v[fv[0]], v[fv[1]], v[fv[2]], v[fv[3]]));
}
break;
}
case Element::HEXAHEDRON:
{
// find the face by the vertices with the smallest 3 numbers
// z = 0, y = 0, x = 1, y = 1, x = 0, z = 1
for (int j = 0; j < 6; j++)
{
const int *fv = hex_t::FaceVert[j];
el_to_face->Push(
i, faces_tbl->Push4(v[fv[0]], v[fv[1]], v[fv[2]], v[fv[3]]));
}
break;
}
default:
MFEM_ABORT("Unexpected type of Element.");
}
}
el_to_face->Finalize();
NumOfFaces = faces_tbl->NumberOfElements();
be_to_face.SetSize(NumOfBdrElements);
for (i = 0; i < NumOfBdrElements; i++)
{
v = boundary[i]->GetVertices();
switch (GetBdrElementType(i))
{
case Element::TRIANGLE:
{
be_to_face[i] = (*faces_tbl)(v[0], v[1], v[2]);
break;
}
case Element::QUADRILATERAL:
{
be_to_face[i] = (*faces_tbl)(v[0], v[1], v[2], v[3]);
break;
}
default:
MFEM_ABORT("Unexpected type of boundary Element.");
}
}
if (ret_ftbl)
{
return faces_tbl;
}
delete faces_tbl;
return NULL;
}
// shift cyclically 3 integers so that the smallest is first
static inline
void Rotate3(int &a, int &b, int &c)
{
if (a < b)
{
if (a > c)
{
ShiftRight(a, b, c);
}
}
else
{
if (b < c)
{
ShiftRight(c, b, a);
}
else
{
ShiftRight(a, b, c);
}
}
}
void Mesh::ReorientTetMesh()
{
if (Dim != 3 || !(meshgen & 1))
{
return;
}
ResetLazyData();
DSTable *old_v_to_v = NULL;
Table *old_elem_vert = NULL;
if (Nodes)
{
PrepareNodeReorder(&old_v_to_v, &old_elem_vert);
}
for (int i = 0; i < NumOfElements; i++)
{
if (GetElementType(i) == Element::TETRAHEDRON)
{
int *v = elements[i]->GetVertices();
Rotate3(v[0], v[1], v[2]);
if (v[0] < v[3])
{
Rotate3(v[1], v[2], v[3]);
}
else
{
ShiftRight(v[0], v[1], v[3]);
}
}
}
for (int i = 0; i < NumOfBdrElements; i++)
{
if (GetBdrElementType(i) == Element::TRIANGLE)
{
int *v = boundary[i]->GetVertices();
Rotate3(v[0], v[1], v[2]);
}
}
if (!Nodes)
{
GetElementToFaceTable();
GenerateFaces();
if (el_to_edge)
{
NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
}
}
else
{
DoNodeReorder(old_v_to_v, old_elem_vert);
delete old_elem_vert;
delete old_v_to_v;
}
}
int *Mesh::CartesianPartitioning(int nxyz[])
{
int *partitioning;
double pmin[3] = { infinity(), infinity(), infinity() };
double pmax[3] = { -infinity(), -infinity(), -infinity() };
// find a bounding box using the vertices
for (int vi = 0; vi < NumOfVertices; vi++)
{
const double *p = vertices[vi]();
for (int i = 0; i < spaceDim; i++)
{
if (p[i] < pmin[i]) { pmin[i] = p[i]; }
if (p[i] > pmax[i]) { pmax[i] = p[i]; }
}
}
partitioning = new int[NumOfElements];
// determine the partitioning using the centers of the elements
double ppt[3];
Vector pt(ppt, spaceDim);
for (int el = 0; el < NumOfElements; el++)
{
GetElementTransformation(el)->Transform(
Geometries.GetCenter(GetElementBaseGeometry(el)), pt);
int part = 0;
for (int i = spaceDim-1; i >= 0; i--)
{
int idx = (int)floor(nxyz[i]*((pt(i) - pmin[i])/(pmax[i] - pmin[i])));
if (idx < 0) { idx = 0; }
if (idx >= nxyz[i]) { idx = nxyz[i]-1; }
part = part * nxyz[i] + idx;
}
partitioning[el] = part;
}
return partitioning;
}
int *Mesh::GeneratePartitioning(int nparts, int part_method)
{
#ifdef MFEM_USE_METIS
int print_messages = 1;
// If running in parallel, print messages only from rank 0.
#ifdef MFEM_USE_MPI
int init_flag, fin_flag;
MPI_Initialized(&init_flag);
MPI_Finalized(&fin_flag);
if (init_flag && !fin_flag)
{
int rank;
MPI_Comm_rank(GetGlobalMPI_Comm(), &rank);
if (rank != 0) { print_messages = 0; }
}
#endif
int i, *partitioning;
ElementToElementTable();
partitioning = new int[NumOfElements];
if (nparts == 1)
{
for (i = 0; i < NumOfElements; i++)
{
partitioning[i] = 0;
}
}
else if (NumOfElements <= nparts)
{
for (i = 0; i < NumOfElements; i++)
{
partitioning[i] = i;
}
}
else
{
idx_t *I, *J, n;
#ifndef MFEM_USE_METIS_5
idx_t wgtflag = 0;
idx_t numflag = 0;
idx_t options[5];
#else
idx_t ncon = 1;
idx_t err;
idx_t options[40];
#endif
idx_t edgecut;
// In case METIS have been compiled with 64bit indices
bool freedata = false;
idx_t mparts = (idx_t) nparts;
idx_t *mpartitioning;
n = NumOfElements;
if (sizeof(idx_t) == sizeof(int))
{
I = (idx_t*) el_to_el->GetI();
J = (idx_t*) el_to_el->GetJ();
mpartitioning = (idx_t*) partitioning;
}
else
{
int *iI = el_to_el->GetI();
int *iJ = el_to_el->GetJ();
int m = iI[n];
I = new idx_t[n+1];
J = new idx_t[m];
for (int k = 0; k < n+1; k++) { I[k] = iI[k]; }
for (int k = 0; k < m; k++) { J[k] = iJ[k]; }
mpartitioning = new idx_t[n];
freedata = true;
}
#ifndef MFEM_USE_METIS_5
options[0] = 0;
#else
METIS_SetDefaultOptions(options);
options[METIS_OPTION_CONTIG] = 1; // set METIS_OPTION_CONTIG
#endif
// Sort the neighbor lists
if (part_method >= 0 && part_method <= 2)
{
for (i = 0; i < n; i++)
{
// Sort in increasing order.
// std::sort(J+I[i], J+I[i+1]);
// Sort in decreasing order, as in previous versions of MFEM.
std::sort(J+I[i], J+I[i+1], std::greater<idx_t>());
}
}
// This function should be used to partition a graph into a small
// number of partitions (less than 8).
if (part_method == 0 || part_method == 3)
{
#ifndef MFEM_USE_METIS_5
METIS_PartGraphRecursive(&n,
I,
J,
NULL,
NULL,
&wgtflag,
&numflag,
&mparts,
options,
&edgecut,
mpartitioning);
#else
err = METIS_PartGraphRecursive(&n,
&ncon,
I,
J,
NULL,
NULL,
NULL,
&mparts,
NULL,
NULL,
options,
&edgecut,
mpartitioning);
if (err != 1)
{
mfem_error("Mesh::GeneratePartitioning: "
" error in METIS_PartGraphRecursive!");
}
#endif
}
// This function should be used to partition a graph into a large
// number of partitions (greater than 8).
if (part_method == 1 || part_method == 4)
{
#ifndef MFEM_USE_METIS_5
METIS_PartGraphKway(&n,
I,
J,
NULL,
NULL,
&wgtflag,
&numflag,
&mparts,
options,
&edgecut,
mpartitioning);
#else
err = METIS_PartGraphKway(&n,
&ncon,
I,
J,
NULL,
NULL,
NULL,
&mparts,
NULL,
NULL,
options,
&edgecut,
mpartitioning);
if (err != 1)
{
mfem_error("Mesh::GeneratePartitioning: "
" error in METIS_PartGraphKway!");
}
#endif
}
// The objective of this partitioning is to minimize the total
// communication volume
if (part_method == 2 || part_method == 5)
{
#ifndef MFEM_USE_METIS_5
METIS_PartGraphVKway(&n,
I,
J,
NULL,
NULL,
&wgtflag,
&numflag,
&mparts,
options,
&edgecut,
mpartitioning);
#else
options[METIS_OPTION_OBJTYPE] = METIS_OBJTYPE_VOL;
err = METIS_PartGraphKway(&n,
&ncon,
I,
J,
NULL,
NULL,
NULL,
&mparts,
NULL,
NULL,
options,
&edgecut,
mpartitioning);
if (err != 1)
{
mfem_error("Mesh::GeneratePartitioning: "
" error in METIS_PartGraphKway!");
}
#endif
}
#ifdef MFEM_DEBUG
if (print_messages)
{
mfem::out << "Mesh::GeneratePartitioning(...): edgecut = "
<< edgecut << endl;
}
#endif
nparts = (int) mparts;
if (mpartitioning != (idx_t*)partitioning)
{
for (int k = 0; k<NumOfElements; k++)
{
partitioning[k] = mpartitioning[k];
}
}
if (freedata)
{
delete[] I;
delete[] J;
delete[] mpartitioning;
}
}
delete el_to_el;
el_to_el = NULL;
// Check for empty partitionings (a "feature" in METIS)
{
Array< Pair<int,int> > psize(nparts);
for (i = 0; i < nparts; i++)
{
psize[i].one = 0;
psize[i].two = i;
}
for (i = 0; i < NumOfElements; i++)
{
psize[partitioning[i]].one++;
}
int empty_parts = 0;
for (i = 0; i < nparts; i++)
{
if (psize[i].one == 0) { empty_parts++; }
}
// This code just split the largest partitionings in two.
// Do we need to replace it with something better?
if (empty_parts)
{
if (print_messages)
{
mfem::err << "Mesh::GeneratePartitioning(...): METIS returned "
<< empty_parts << " empty parts!"
<< " Applying a simple fix ..." << endl;
}
SortPairs<int,int>(psize, nparts);
for (i = nparts-1; i > nparts-1-empty_parts; i--)
{
psize[i].one /= 2;
}
for (int j = 0; j < NumOfElements; j++)
{
for (i = nparts-1; i > nparts-1-empty_parts; i--)
{
if (psize[i].one == 0 || partitioning[j] != psize[i].two)
{
continue;
}
else
{
partitioning[j] = psize[nparts-1-i].two;
psize[i].one--;
}
}
}
}
}
return partitioning;
#else
mfem_error("Mesh::GeneratePartitioning(...): "
"MFEM was compiled without Metis.");
return NULL;
#endif
}
/* required: 0 <= partitioning[i] < num_part */
void FindPartitioningComponents(Table &elem_elem,
const Array<int> &partitioning,
Array<int> &component,
Array<int> &num_comp)
{
int i, j, k;
int num_elem, *i_elem_elem, *j_elem_elem;
num_elem = elem_elem.Size();
i_elem_elem = elem_elem.GetI();
j_elem_elem = elem_elem.GetJ();
component.SetSize(num_elem);
Array<int> elem_stack(num_elem);
int stack_p, stack_top_p, elem;
int num_part;
num_part = -1;
for (i = 0; i < num_elem; i++)
{
if (partitioning[i] > num_part)
{
num_part = partitioning[i];
}
component[i] = -1;
}
num_part++;
num_comp.SetSize(num_part);
for (i = 0; i < num_part; i++)
{
num_comp[i] = 0;
}
stack_p = 0;
stack_top_p = 0; // points to the first unused element in the stack
for (elem = 0; elem < num_elem; elem++)
{
if (component[elem] >= 0)
{
continue;
}
component[elem] = num_comp[partitioning[elem]]++;
elem_stack[stack_top_p++] = elem;
for ( ; stack_p < stack_top_p; stack_p++)
{
i = elem_stack[stack_p];
for (j = i_elem_elem[i]; j < i_elem_elem[i+1]; j++)
{
k = j_elem_elem[j];
if (partitioning[k] == partitioning[i])
{
if (component[k] < 0)
{
component[k] = component[i];
elem_stack[stack_top_p++] = k;
}
else if (component[k] != component[i])
{
mfem_error("FindPartitioningComponents");
}
}
}
}
}
}
void Mesh::CheckPartitioning(int *partitioning)
{
int i, n_empty, n_mcomp;
Array<int> component, num_comp;
const Array<int> _partitioning(partitioning, GetNE());
ElementToElementTable();
FindPartitioningComponents(*el_to_el, _partitioning, component, num_comp);
n_empty = n_mcomp = 0;
for (i = 0; i < num_comp.Size(); i++)
if (num_comp[i] == 0)
{
n_empty++;
}
else if (num_comp[i] > 1)
{
n_mcomp++;
}
if (n_empty > 0)
{
mfem::out << "Mesh::CheckPartitioning(...) :\n"
<< "The following subdomains are empty :\n";
for (i = 0; i < num_comp.Size(); i++)
if (num_comp[i] == 0)
{
mfem::out << ' ' << i;
}
mfem::out << endl;
}
if (n_mcomp > 0)
{
mfem::out << "Mesh::CheckPartitioning(...) :\n"
<< "The following subdomains are NOT connected :\n";
for (i = 0; i < num_comp.Size(); i++)
if (num_comp[i] > 1)
{
mfem::out << ' ' << i;
}
mfem::out << endl;
}
if (n_empty == 0 && n_mcomp == 0)
mfem::out << "Mesh::CheckPartitioning(...) : "
"All subdomains are connected." << endl;
if (el_to_el)
{
delete el_to_el;
}
el_to_el = NULL;
}
// compute the coefficients of the polynomial in t:
// c(0)+c(1)*t+...+c(d)*t^d = det(A+t*B)
// where A, B are (d x d), d=2,3
void DetOfLinComb(const DenseMatrix &A, const DenseMatrix &B, Vector &c)
{
const double *a = A.Data();
const double *b = B.Data();
c.SetSize(A.Width()+1);
switch (A.Width())
{
case 2:
{
// det(A+t*B) = |a0 a2| / |a0 b2| + |b0 a2| \ |b0 b2|
// |a1 a3| + \ |a1 b3| |b1 a3| / * t + |b1 b3| * t^2
c(0) = a[0]*a[3]-a[1]*a[2];
c(1) = a[0]*b[3]-a[1]*b[2]+b[0]*a[3]-b[1]*a[2];
c(2) = b[0]*b[3]-b[1]*b[2];
}
break;
case 3:
{
/* |a0 a3 a6|
* det(A+t*B) = |a1 a4 a7| +
* |a2 a5 a8|
* / |b0 a3 a6| |a0 b3 a6| |a0 a3 b6| \
* + | |b1 a4 a7| + |a1 b4 a7| + |a1 a4 b7| | * t +
* \ |b2 a5 a8| |a2 b5 a8| |a2 a5 b8| /
* / |a0 b3 b6| |b0 a3 b6| |b0 b3 a6| \
* + | |a1 b4 b7| + |b1 a4 b7| + |b1 b4 a7| | * t^2 +
* \ |a2 b5 b8| |b2 a5 b8| |b2 b5 a8| /
* |b0 b3 b6|
* + |b1 b4 b7| * t^3
* |b2 b5 b8| */
c(0) = (a[0] * (a[4] * a[8] - a[5] * a[7]) +
a[1] * (a[5] * a[6] - a[3] * a[8]) +
a[2] * (a[3] * a[7] - a[4] * a[6]));
c(1) = (b[0] * (a[4] * a[8] - a[5] * a[7]) +
b[1] * (a[5] * a[6] - a[3] * a[8]) +
b[2] * (a[3] * a[7] - a[4] * a[6]) +
a[0] * (b[4] * a[8] - b[5] * a[7]) +
a[1] * (b[5] * a[6] - b[3] * a[8]) +
a[2] * (b[3] * a[7] - b[4] * a[6]) +
a[0] * (a[4] * b[8] - a[5] * b[7]) +
a[1] * (a[5] * b[6] - a[3] * b[8]) +
a[2] * (a[3] * b[7] - a[4] * b[6]));
c(2) = (a[0] * (b[4] * b[8] - b[5] * b[7]) +
a[1] * (b[5] * b[6] - b[3] * b[8]) +
a[2] * (b[3] * b[7] - b[4] * b[6]) +
b[0] * (a[4] * b[8] - a[5] * b[7]) +
b[1] * (a[5] * b[6] - a[3] * b[8]) +
b[2] * (a[3] * b[7] - a[4] * b[6]) +
b[0] * (b[4] * a[8] - b[5] * a[7]) +
b[1] * (b[5] * a[6] - b[3] * a[8]) +
b[2] * (b[3] * a[7] - b[4] * a[6]));
c(3) = (b[0] * (b[4] * b[8] - b[5] * b[7]) +
b[1] * (b[5] * b[6] - b[3] * b[8]) +
b[2] * (b[3] * b[7] - b[4] * b[6]));
}
break;
default:
mfem_error("DetOfLinComb(...)");
}
}
// compute the real roots of
// z(0)+z(1)*x+...+z(d)*x^d = 0, d=2,3;
// the roots are returned in x, sorted in increasing order;
// it is assumed that x is at least of size d;
// return the number of roots counting multiplicity;
// return -1 if all z(i) are 0.
int FindRoots(const Vector &z, Vector &x)
{
int d = z.Size()-1;
if (d > 3 || d < 0)
{
mfem_error("FindRoots(...)");
}
while (z(d) == 0.0)
{
if (d == 0)
{
return (-1);
}
d--;
}
switch (d)
{
case 0:
{
return 0;
}
case 1:
{
x(0) = -z(0)/z(1);
return 1;
}
case 2:
{
double a = z(2), b = z(1), c = z(0);
double D = b*b-4*a*c;
if (D < 0.0)
{
return 0;
}
if (D == 0.0)
{
x(0) = x(1) = -0.5 * b / a;
return 2; // root with multiplicity 2
}
if (b == 0.0)
{
x(0) = -(x(1) = fabs(0.5 * sqrt(D) / a));
return 2;
}
else
{
double t;
if (b > 0.0)
{
t = -0.5 * (b + sqrt(D));
}
else
{
t = -0.5 * (b - sqrt(D));
}
x(0) = t / a;
x(1) = c / t;
if (x(0) > x(1))
{
Swap<double>(x(0), x(1));
}
return 2;
}
}
case 3:
{
double a = z(2)/z(3), b = z(1)/z(3), c = z(0)/z(3);
// find the real roots of x^3 + a x^2 + b x + c = 0
double Q = (a * a - 3 * b) / 9;
double R = (2 * a * a * a - 9 * a * b + 27 * c) / 54;
double Q3 = Q * Q * Q;
double R2 = R * R;
if (R2 == Q3)
{
if (Q == 0)
{
x(0) = x(1) = x(2) = - a / 3;
}
else
{
double sqrtQ = sqrt(Q);
if (R > 0)
{
x(0) = -2 * sqrtQ - a / 3;
x(1) = x(2) = sqrtQ - a / 3;
}
else
{
x(0) = x(1) = - sqrtQ - a / 3;
x(2) = 2 * sqrtQ - a / 3;
}
}
return 3;
}
else if (R2 < Q3)
{
double theta = acos(R / sqrt(Q3));
double A = -2 * sqrt(Q);
double x0, x1, x2;
x0 = A * cos(theta / 3) - a / 3;
x1 = A * cos((theta + 2.0 * M_PI) / 3) - a / 3;
x2 = A * cos((theta - 2.0 * M_PI) / 3) - a / 3;
/* Sort x0, x1, x2 */
if (x0 > x1)
{
Swap<double>(x0, x1);
}
if (x1 > x2)
{
Swap<double>(x1, x2);
if (x0 > x1)
{
Swap<double>(x0, x1);
}
}
x(0) = x0;
x(1) = x1;
x(2) = x2;
return 3;
}
else
{
double A;
if (R >= 0.0)
{
A = -pow(sqrt(R2 - Q3) + R, 1.0/3.0);
}
else
{
A = pow(sqrt(R2 - Q3) - R, 1.0/3.0);
}
x(0) = A + Q / A - a / 3;
return 1;
}
}
}
return 0;
}
void FindTMax(Vector &c, Vector &x, double &tmax,
const double factor, const int Dim)
{
const double c0 = c(0);
c(0) = c0 * (1.0 - pow(factor, -Dim));
int nr = FindRoots(c, x);
for (int j = 0; j < nr; j++)
{
if (x(j) > tmax)
{
break;
}
if (x(j) >= 0.0)
{
tmax = x(j);
break;
}
}
c(0) = c0 * (1.0 - pow(factor, Dim));
nr = FindRoots(c, x);
for (int j = 0; j < nr; j++)
{
if (x(j) > tmax)
{
break;
}
if (x(j) >= 0.0)
{
tmax = x(j);
break;
}
}
}
void Mesh::CheckDisplacements(const Vector &displacements, double &tmax)
{
int nvs = vertices.Size();
DenseMatrix P, V, DS, PDS(spaceDim), VDS(spaceDim);
Vector c(spaceDim+1), x(spaceDim);
const double factor = 2.0;
// check for tangling assuming constant speed
if (tmax < 1.0)
{
tmax = 1.0;
}
for (int i = 0; i < NumOfElements; i++)
{
Element *el = elements[i];
int nv = el->GetNVertices();
int *v = el->GetVertices();
P.SetSize(spaceDim, nv);
V.SetSize(spaceDim, nv);
for (int j = 0; j < spaceDim; j++)
for (int k = 0; k < nv; k++)
{
P(j, k) = vertices[v[k]](j);
V(j, k) = displacements(v[k]+j*nvs);
}
DS.SetSize(nv, spaceDim);
const FiniteElement *fe =
GetTransformationFEforElementType(el->GetType());
// check if det(P.DShape+t*V.DShape) > 0 for all x and 0<=t<=1
switch (el->GetType())
{
case Element::TRIANGLE:
case Element::TETRAHEDRON:
{
// DS is constant
fe->CalcDShape(Geometries.GetCenter(fe->GetGeomType()), DS);
Mult(P, DS, PDS);
Mult(V, DS, VDS);
DetOfLinComb(PDS, VDS, c);
if (c(0) <= 0.0)
{
tmax = 0.0;
}
else
{
FindTMax(c, x, tmax, factor, Dim);
}
}
break;
case Element::QUADRILATERAL:
{
const IntegrationRule &ir = fe->GetNodes();
for (int j = 0; j < nv; j++)
{
fe->CalcDShape(ir.IntPoint(j), DS);
Mult(P, DS, PDS);
Mult(V, DS, VDS);
DetOfLinComb(PDS, VDS, c);
if (c(0) <= 0.0)
{
tmax = 0.0;
}
else
{
FindTMax(c, x, tmax, factor, Dim);
}
}
}
break;
default:
mfem_error("Mesh::CheckDisplacements(...)");
}
}
}
void Mesh::MoveVertices(const Vector &displacements)
{
for (int i = 0, nv = vertices.Size(); i < nv; i++)
for (int j = 0; j < spaceDim; j++)
{
vertices[i](j) += displacements(j*nv+i);
}
}
void Mesh::GetVertices(Vector &vert_coord) const
{
int nv = vertices.Size();
vert_coord.SetSize(nv*spaceDim);
for (int i = 0; i < nv; i++)
for (int j = 0; j < spaceDim; j++)
{
vert_coord(j*nv+i) = vertices[i](j);
}
}
void Mesh::SetVertices(const Vector &vert_coord)
{
for (int i = 0, nv = vertices.Size(); i < nv; i++)
for (int j = 0; j < spaceDim; j++)
{
vertices[i](j) = vert_coord(j*nv+i);
}
}
void Mesh::GetNode(int i, double *coord) const
{
if (Nodes)
{
FiniteElementSpace *fes = Nodes->FESpace();
for (int j = 0; j < spaceDim; j++)
{
coord[j] = (*Nodes)(fes->DofToVDof(i, j));
}
}
else
{
for (int j = 0; j < spaceDim; j++)
{
coord[j] = vertices[i](j);
}
}
}
void Mesh::SetNode(int i, const double *coord)
{
if (Nodes)
{
FiniteElementSpace *fes = Nodes->FESpace();
for (int j = 0; j < spaceDim; j++)
{
(*Nodes)(fes->DofToVDof(i, j)) = coord[j];
}
}
else
{
for (int j = 0; j < spaceDim; j++)
{
vertices[i](j) = coord[j];
}
}
}
void Mesh::MoveNodes(const Vector &displacements)
{
if (Nodes)
{
(*Nodes) += displacements;
}
else
{
MoveVertices(displacements);
}
}
void Mesh::GetNodes(Vector &node_coord) const
{
if (Nodes)
{
node_coord = (*Nodes);
}
else
{
GetVertices(node_coord);
}
}
void Mesh::SetNodes(const Vector &node_coord)
{
if (Nodes)
{
(*Nodes) = node_coord;
}
else
{
SetVertices(node_coord);
}
}
void Mesh::NewNodes(GridFunction &nodes, bool make_owner)
{
if (own_nodes) { delete Nodes; }
Nodes = &nodes;
spaceDim = Nodes->FESpace()->GetVDim();
own_nodes = (int)make_owner;
if (NURBSext != nodes.FESpace()->GetNURBSext())
{
delete NURBSext;
NURBSext = nodes.FESpace()->StealNURBSext();
}
}
void Mesh::SwapNodes(GridFunction *&nodes, int &own_nodes_)
{
mfem::Swap<GridFunction*>(Nodes, nodes);
mfem::Swap<int>(own_nodes, own_nodes_);
// TODO:
// if (nodes)
// nodes->FESpace()->MakeNURBSextOwner();
// NURBSext = (Nodes) ? Nodes->FESpace()->StealNURBSext() : NULL;
}
void Mesh::AverageVertices(const int *indexes, int n, int result)
{
int j, k;
for (k = 0; k < spaceDim; k++)
{
vertices[result](k) = vertices[indexes[0]](k);
}
for (j = 1; j < n; j++)
for (k = 0; k < spaceDim; k++)
{
vertices[result](k) += vertices[indexes[j]](k);
}
for (k = 0; k < spaceDim; k++)
{
vertices[result](k) *= (1.0 / n);
}
}
void Mesh::UpdateNodes()
{
if (Nodes)
{
Nodes->FESpace()->Update();
Nodes->Update();
}
}
void Mesh::UniformRefinement2D_base(bool update_nodes)
{
ResetLazyData();
if (el_to_edge == NULL)
{
el_to_edge = new Table;
NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
}
int quad_counter = 0;
for (int i = 0; i < NumOfElements; i++)
{
if (elements[i]->GetType() == Element::QUADRILATERAL)
{
quad_counter++;
}
}
const int oedge = NumOfVertices;
const int oelem = oedge + NumOfEdges;
Array<Element*> new_elements;
Array<Element*> new_boundary;
vertices.SetSize(oelem + quad_counter);
new_elements.SetSize(4 * NumOfElements);
quad_counter = 0;
for (int i = 0, j = 0; i < NumOfElements; i++)
{
const Element::Type el_type = elements[i]->GetType();
const int attr = elements[i]->GetAttribute();
int *v = elements[i]->GetVertices();
const int *e = el_to_edge->GetRow(i);
int vv[2];
if (el_type == Element::TRIANGLE)
{
for (int ei = 0; ei < 3; ei++)
{
for (int k = 0; k < 2; k++)
{
vv[k] = v[tri_t::Edges[ei][k]];
}
AverageVertices(vv, 2, oedge+e[ei]);
}
new_elements[j++] =
new Triangle(v[0], oedge+e[0], oedge+e[2], attr);
new_elements[j++] =
new Triangle(oedge+e[1], oedge+e[2], oedge+e[0], attr);
new_elements[j++] =
new Triangle(oedge+e[0], v[1], oedge+e[1], attr);
new_elements[j++] =
new Triangle(oedge+e[2], oedge+e[1], v[2], attr);
}
else if (el_type == Element::QUADRILATERAL)
{
const int qe = quad_counter;
quad_counter++;
AverageVertices(v, 4, oelem+qe);
for (int ei = 0; ei < 4; ei++)
{
for (int k = 0; k < 2; k++)
{
vv[k] = v[quad_t::Edges[ei][k]];
}
AverageVertices(vv, 2, oedge+e[ei]);
}
new_elements[j++] =
new Quadrilateral(v[0], oedge+e[0], oelem+qe, oedge+e[3], attr);
new_elements[j++] =
new Quadrilateral(oedge+e[0], v[1], oedge+e[1], oelem+qe, attr);
new_elements[j++] =
new Quadrilateral(oelem+qe, oedge+e[1], v[2], oedge+e[2], attr);
new_elements[j++] =
new Quadrilateral(oedge+e[3], oelem+qe, oedge+e[2], v[3], attr);
}
else
{
MFEM_ABORT("unknown element type: " << el_type);
}
FreeElement(elements[i]);
}
mfem::Swap(elements, new_elements);
// refine boundary elements
new_boundary.SetSize(2 * NumOfBdrElements);
for (int i = 0, j = 0; i < NumOfBdrElements; i++)
{
const int attr = boundary[i]->GetAttribute();
int *v = boundary[i]->GetVertices();
new_boundary[j++] = new Segment(v[0], oedge+be_to_edge[i], attr);
new_boundary[j++] = new Segment(oedge+be_to_edge[i], v[1], attr);
FreeElement(boundary[i]);
}
mfem::Swap(boundary, new_boundary);
static const double A = 0.0, B = 0.5, C = 1.0;
static double tri_children[2*3*4] =
{
A,A, B,A, A,B,
B,B, A,B, B,A,
B,A, C,A, B,B,
A,B, B,B, A,C
};
static double quad_children[2*4*4] =
{
A,A, B,A, B,B, A,B, // lower-left
B,A, C,A, C,B, B,B, // lower-right
B,B, C,B, C,C, B,C, // upper-right
A,B, B,B, B,C, A,C // upper-left
};
CoarseFineTr.point_matrices[Geometry::TRIANGLE]
.UseExternalData(tri_children, 2, 3, 4);
CoarseFineTr.point_matrices[Geometry::SQUARE]
.UseExternalData(quad_children, 2, 4, 4);
CoarseFineTr.embeddings.SetSize(elements.Size());
for (int i = 0; i < elements.Size(); i++)
{
Embedding &emb = CoarseFineTr.embeddings[i];
emb.parent = i / 4;
emb.matrix = i % 4;
}
NumOfVertices = vertices.Size();
NumOfElements = 4 * NumOfElements;
NumOfBdrElements = 2 * NumOfBdrElements;
NumOfFaces = 0;
NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
GenerateFaces();
last_operation = Mesh::REFINE;
sequence++;
if (update_nodes) { UpdateNodes(); }
#ifdef MFEM_DEBUG
if (!Nodes || update_nodes)
{
CheckElementOrientation(false);
}
CheckBdrElementOrientation(false);
#endif
}
static inline double sqr(const double &x)
{
return x*x;
}
void Mesh::UniformRefinement3D_base(Array<int> *f2qf_ptr, DSTable *v_to_v_p,
bool update_nodes)
{
ResetLazyData();
if (el_to_edge == NULL)
{
el_to_edge = new Table;
NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
}
if (el_to_face == NULL)
{
GetElementToFaceTable();
}
Array<int> f2qf_loc;
Array<int> &f2qf = f2qf_ptr ? *f2qf_ptr : f2qf_loc;
f2qf.SetSize(0);
int NumOfQuadFaces = 0;
if (HasGeometry(Geometry::SQUARE))
{
if (HasGeometry(Geometry::TRIANGLE))
{
f2qf.SetSize(faces.Size());
for (int i = 0; i < faces.Size(); i++)
{
if (faces[i]->GetType() == Element::QUADRILATERAL)
{
f2qf[i] = NumOfQuadFaces;
NumOfQuadFaces++;
}
}
}
else
{
NumOfQuadFaces = faces.Size();
}
}
int hex_counter = 0;
if (HasGeometry(Geometry::CUBE))
{
for (int i = 0; i < elements.Size(); i++)
{
if (elements[i]->GetType() == Element::HEXAHEDRON)
{
hex_counter++;
}
}
}
// Map from edge-index to vertex-index, needed for ReorientTetMesh() for
// parallel meshes.
Array<int> e2v;
if (HasGeometry(Geometry::TETRAHEDRON))
{
e2v.SetSize(NumOfEdges);
DSTable *v_to_v_ptr = v_to_v_p;
if (!v_to_v_p)
{
v_to_v_ptr = new DSTable(NumOfVertices);
GetVertexToVertexTable(*v_to_v_ptr);
}
Array<Pair<int,int> > J_v2v(NumOfEdges); // (second vertex id, edge id)
J_v2v.SetSize(0);
for (int i = 0; i < NumOfVertices; i++)
{
Pair<int,int> *row_start = J_v2v.end();
for (DSTable::RowIterator it(*v_to_v_ptr, i); !it; ++it)
{
J_v2v.Append(Pair<int,int>(it.Column(), it.Index()));
}
std::sort(row_start, J_v2v.end());
}
for (int i = 0; i < J_v2v.Size(); i++)
{
e2v[J_v2v[i].two] = i;
}
if (!v_to_v_p)
{
delete v_to_v_ptr;
}
else
{
for (int i = 0; i < NumOfVertices; i++)
{
for (DSTable::RowIterator it(*v_to_v_ptr, i); !it; ++it)
{
it.SetIndex(e2v[it.Index()]);
}
}
}
}
// Offsets for new vertices from edges, faces (quads only), and elements
// (hexes only); each of these entities generates one new vertex.
const int oedge = NumOfVertices;
const int oface = oedge + NumOfEdges;
const int oelem = oface + NumOfQuadFaces;
Array<Element*> new_elements;
Array<Element*> new_boundary;
vertices.SetSize(oelem + hex_counter);
new_elements.SetSize(8 * NumOfElements);
CoarseFineTr.embeddings.SetSize(new_elements.Size());
hex_counter = 0;
for (int i = 0, j = 0; i < NumOfElements; i++)
{
const Element::Type el_type = elements[i]->GetType();
const int attr = elements[i]->GetAttribute();
int *v = elements[i]->GetVertices();
const int *e = el_to_edge->GetRow(i);
int vv[4], ev[12];
if (e2v.Size())
{
const int ne = el_to_edge->RowSize(i);
for (int k = 0; k < ne; k++) { ev[k] = e2v[e[k]]; }
e = ev;
}
switch (el_type)
{
case Element::TETRAHEDRON:
{
for (int ei = 0; ei < 6; ei++)
{
for (int k = 0; k < 2; k++)
{
vv[k] = v[tet_t::Edges[ei][k]];
}
AverageVertices(vv, 2, oedge+e[ei]);
}
// Algorithm for choosing refinement type:
// 0: smallest octahedron diagonal
// 1: best aspect ratio
const int rt_algo = 1;
// Refinement type:
// 0: (v0,v1)-(v2,v3), 1: (v0,v2)-(v1,v3), 2: (v0,v3)-(v1,v2)
// 0: e0-e5, 1: e1-e4, 2: e2-e3
int rt;
ElementTransformation *T = GetElementTransformation(i);
T->SetIntPoint(&Geometries.GetCenter(Geometry::TETRAHEDRON));
const DenseMatrix &J = T->Jacobian();
if (rt_algo == 0)
{
// smallest octahedron diagonal
double len_sqr, min_len;
min_len = sqr(J(0,0)-J(0,1)-J(0,2)) +
sqr(J(1,0)-J(1,1)-J(1,2)) +
sqr(J(2,0)-J(2,1)-J(2,2));
rt = 0;
len_sqr = sqr(J(0,1)-J(0,0)-J(0,2)) +
sqr(J(1,1)-J(1,0)-J(1,2)) +
sqr(J(2,1)-J(2,0)-J(2,2));
if (len_sqr < min_len) { min_len = len_sqr; rt = 1; }
len_sqr = sqr(J(0,2)-J(0,0)-J(0,1)) +
sqr(J(1,2)-J(1,0)-J(1,1)) +
sqr(J(2,2)-J(2,0)-J(2,1));
if (len_sqr < min_len) { rt = 2; }
}
else
{
// best aspect ratio
double Em_data[18], Js_data[9], Jp_data[9];
DenseMatrix Em(Em_data, 3, 6);
DenseMatrix Js(Js_data, 3, 3), Jp(Jp_data, 3, 3);
double ar1, ar2, kappa, kappa_min;
for (int s = 0; s < 3; s++)
{
for (int t = 0; t < 3; t++)
{
Em(t,s) = 0.5*J(t,s);
}
}
for (int t = 0; t < 3; t++)
{
Em(t,3) = 0.5*(J(t,0)+J(t,1));
Em(t,4) = 0.5*(J(t,0)+J(t,2));
Em(t,5) = 0.5*(J(t,1)+J(t,2));
}
// rt = 0; Em: {0,5,1,2}, {0,5,2,4}
for (int t = 0; t < 3; t++)
{
Js(t,0) = Em(t,5)-Em(t,0);
Js(t,1) = Em(t,1)-Em(t,0);
Js(t,2) = Em(t,2)-Em(t,0);
}
Geometries.JacToPerfJac(Geometry::TETRAHEDRON, Js, Jp);
ar1 = Jp.CalcSingularvalue(0)/Jp.CalcSingularvalue(2);
for (int t = 0; t < 3; t++)
{
Js(t,0) = Em(t,5)-Em(t,0);
Js(t,1) = Em(t,2)-Em(t,0);
Js(t,2) = Em(t,4)-Em(t,0);
}
Geometries.JacToPerfJac(Geometry::TETRAHEDRON, Js, Jp);
ar2 = Jp.CalcSingularvalue(0)/Jp.CalcSingularvalue(2);
kappa_min = std::max(ar1, ar2);
rt = 0;
// rt = 1; Em: {1,0,4,2}, {1,2,4,5}
for (int t = 0; t < 3; t++)
{
Js(t,0) = Em(t,0)-Em(t,1);
Js(t,1) = Em(t,4)-Em(t,1);
Js(t,2) = Em(t,2)-Em(t,1);
}
Geometries.JacToPerfJac(Geometry::TETRAHEDRON, Js, Jp);
ar1 = Jp.CalcSingularvalue(0)/Jp.CalcSingularvalue(2);
for (int t = 0; t < 3; t++)
{
Js(t,0) = Em(t,2)-Em(t,1);
Js(t,1) = Em(t,4)-Em(t,1);
Js(t,2) = Em(t,5)-Em(t,1);
}
Geometries.JacToPerfJac(Geometry::TETRAHEDRON, Js, Jp);
ar2 = Jp.CalcSingularvalue(0)/Jp.CalcSingularvalue(2);
kappa = std::max(ar1, ar2);
if (kappa < kappa_min) { kappa_min = kappa; rt = 1; }
// rt = 2; Em: {2,0,1,3}, {2,1,5,3}
for (int t = 0; t < 3; t++)
{
Js(t,0) = Em(t,0)-Em(t,2);
Js(t,1) = Em(t,1)-Em(t,2);
Js(t,2) = Em(t,3)-Em(t,2);
}
Geometries.JacToPerfJac(Geometry::TETRAHEDRON, Js, Jp);
ar1 = Jp.CalcSingularvalue(0)/Jp.CalcSingularvalue(2);
for (int t = 0; t < 3; t++)
{
Js(t,0) = Em(t,1)-Em(t,2);
Js(t,1) = Em(t,5)-Em(t,2);
Js(t,2) = Em(t,3)-Em(t,2);
}
Geometries.JacToPerfJac(Geometry::TETRAHEDRON, Js, Jp);
ar2 = Jp.CalcSingularvalue(0)/Jp.CalcSingularvalue(2);
kappa = std::max(ar1, ar2);
if (kappa < kappa_min) { rt = 2; }
}
static const int mv_all[3][4][4] =
{
{ {0,5,1,2}, {0,5,2,4}, {0,5,4,3}, {0,5,3,1} }, // rt = 0
{ {1,0,4,2}, {1,2,4,5}, {1,5,4,3}, {1,3,4,0} }, // rt = 1
{ {2,0,1,3}, {2,1,5,3}, {2,5,4,3}, {2,4,0,3} } // rt = 2
};
const int (&mv)[4][4] = mv_all[rt];
#ifndef MFEM_USE_MEMALLOC
new_elements[j+0] =
new Tetrahedron(v[0], oedge+e[0], oedge+e[1], oedge+e[2], attr);
new_elements[j+1] =
new Tetrahedron(oedge+e[0], v[1], oedge+e[3], oedge+e[4], attr);
new_elements[j+2] =
new Tetrahedron(oedge+e[1], oedge+e[3], v[2], oedge+e[5], attr);
new_elements[j+3] =
new Tetrahedron(oedge+e[2], oedge+e[4], oedge+e[5], v[3], attr);
for (int k = 0; k < 4; k++)
{
new_elements[j+4+k] =
new Tetrahedron(oedge+e[mv[k][0]], oedge+e[mv[k][1]],
oedge+e[mv[k][2]], oedge+e[mv[k][3]], attr);
}
#else
Tetrahedron *tet;
new_elements[j+0] = tet = TetMemory.Alloc();
tet->Init(v[0], oedge+e[0], oedge+e[1], oedge+e[2], attr);
new_elements[j+1] = tet = TetMemory.Alloc();
tet->Init(oedge+e[0], v[1], oedge+e[3], oedge+e[4], attr);
new_elements[j+2] = tet = TetMemory.Alloc();
tet->Init(oedge+e[1], oedge+e[3], v[2], oedge+e[5], attr);
new_elements[j+3] = tet = TetMemory.Alloc();
tet->Init(oedge+e[2], oedge+e[4], oedge+e[5], v[3], attr);
for (int k = 0; k < 4; k++)
{
new_elements[j+4+k] = tet = TetMemory.Alloc();
tet->Init(oedge+e[mv[k][0]], oedge+e[mv[k][1]],
oedge+e[mv[k][2]], oedge+e[mv[k][3]], attr);
}
#endif
for (int k = 0; k < 4; k++)
{
CoarseFineTr.embeddings[j+k].parent = i;
CoarseFineTr.embeddings[j+k].matrix = k;
}
for (int k = 0; k < 4; k++)
{
CoarseFineTr.embeddings[j+4+k].parent = i;
CoarseFineTr.embeddings[j+4+k].matrix = 4*(rt+1)+k;
}
j += 8;
}
break;
case Element::WEDGE:
{
const int *f = el_to_face->GetRow(i);
for (int fi = 2; fi < 5; fi++)
{
for (int k = 0; k < 4; k++)
{
vv[k] = v[pri_t::FaceVert[fi][k]];
}
AverageVertices(vv, 4, oface + f2qf[f[fi]]);
}
for (int ei = 0; ei < 9; ei++)
{
for (int k = 0; k < 2; k++)
{
vv[k] = v[pri_t::Edges[ei][k]];
}
AverageVertices(vv, 2, oedge+e[ei]);
}
const int qf2 = f2qf[f[2]];
const int qf3 = f2qf[f[3]];
const int qf4 = f2qf[f[4]];
new_elements[j++] =
new Wedge(v[0], oedge+e[0], oedge+e[2],
oedge+e[6], oface+qf2, oface+qf4, attr);
new_elements[j++] =
new Wedge(oedge+e[1], oedge+e[2], oedge+e[0],
oface+qf3, oface+qf4, oface+qf2, attr);
new_elements[j++] =
new Wedge(oedge+e[0], v[1], oedge+e[1],
oface+qf2, oedge+e[7], oface+qf3, attr);
new_elements[j++] =
new Wedge(oedge+e[2], oedge+e[1], v[2],
oface+qf4, oface+qf3, oedge+e[8], attr);
new_elements[j++] =
new Wedge(oedge+e[6], oface+qf2, oface+qf4,
v[3], oedge+e[3], oedge+e[5], attr);
new_elements[j++] =
new Wedge(oface+qf3, oface+qf4, oface+qf2,
oedge+e[4], oedge+e[5], oedge+e[3], attr);
new_elements[j++] =
new Wedge(oface+qf2, oedge+e[7], oface+qf3,
oedge+e[3], v[4], oedge+e[4], attr);
new_elements[j++] =
new Wedge(oface+qf4, oface+qf3, oedge+e[8],
oedge+e[5], oedge+e[4], v[5], attr);
}
break;
case Element::HEXAHEDRON:
{
const int *f = el_to_face->GetRow(i);
const int he = hex_counter;
hex_counter++;
const int *qf;
int qf_data[6];
if (f2qf.Size() == 0)
{
qf = f;
}
else
{
for (int k = 0; k < 6; k++) { qf_data[k] = f2qf[f[k]]; }
qf = qf_data;
}
AverageVertices(v, 8, oelem+he);
for (int fi = 0; fi < 6; fi++)
{
for (int k = 0; k < 4; k++)
{
vv[k] = v[hex_t::FaceVert[fi][k]];
}
AverageVertices(vv, 4, oface + qf[fi]);
}
for (int ei = 0; ei < 12; ei++)
{
for (int k = 0; k < 2; k++)
{
vv[k] = v[hex_t::Edges[ei][k]];
}
AverageVertices(vv, 2, oedge+e[ei]);
}
new_elements[j++] =
new Hexahedron(v[0], oedge+e[0], oface+qf[0],
oedge+e[3], oedge+e[8], oface+qf[1],
oelem+he, oface+qf[4], attr);
new_elements[j++] =
new Hexahedron(oedge+e[0], v[1], oedge+e[1],
oface+qf[0], oface+qf[1], oedge+e[9],
oface+qf[2], oelem+he, attr);
new_elements[j++] =
new Hexahedron(oface+qf[0], oedge+e[1], v[2],
oedge+e[2], oelem+he, oface+qf[2],
oedge+e[10], oface+qf[3], attr);
new_elements[j++] =
new Hexahedron(oedge+e[3], oface+qf[0], oedge+e[2],
v[3], oface+qf[4], oelem+he,
oface+qf[3], oedge+e[11], attr);
new_elements[j++] =
new Hexahedron(oedge+e[8], oface+qf[1], oelem+he,
oface+qf[4], v[4], oedge+e[4],
oface+qf[5], oedge+e[7], attr);
new_elements[j++] =
new Hexahedron(oface+qf[1], oedge+e[9], oface+qf[2],
oelem+he, oedge+e[4], v[5],
oedge+e[5], oface+qf[5], attr);
new_elements[j++] =
new Hexahedron(oelem+he, oface+qf[2], oedge+e[10],
oface+qf[3], oface+qf[5], oedge+e[5],
v[6], oedge+e[6], attr);
new_elements[j++] =
new Hexahedron(oface+qf[4], oelem+he, oface+qf[3],
oedge+e[11], oedge+e[7], oface+qf[5],
oedge+e[6], v[7], attr);
}
break;
default:
MFEM_ABORT("Unknown 3D element type \"" << el_type << "\"");
break;
}
FreeElement(elements[i]);
}
mfem::Swap(elements, new_elements);
// refine boundary elements
new_boundary.SetSize(4 * NumOfBdrElements);
for (int i = 0, j = 0; i < NumOfBdrElements; i++)
{
const Element::Type bdr_el_type = boundary[i]->GetType();
const int attr = boundary[i]->GetAttribute();
int *v = boundary[i]->GetVertices();
const int *e = bel_to_edge->GetRow(i);
int ev[4];
if (e2v.Size())
{
const int ne = bel_to_edge->RowSize(i);
for (int k = 0; k < ne; k++) { ev[k] = e2v[e[k]]; }
e = ev;
}
if (bdr_el_type == Element::TRIANGLE)
{
new_boundary[j++] =
new Triangle(v[0], oedge+e[0], oedge+e[2], attr);
new_boundary[j++] =
new Triangle(oedge+e[1], oedge+e[2], oedge+e[0], attr);
new_boundary[j++] =
new Triangle(oedge+e[0], v[1], oedge+e[1], attr);
new_boundary[j++] =
new Triangle(oedge+e[2], oedge+e[1], v[2], attr);
}
else if (bdr_el_type == Element::QUADRILATERAL)
{
const int qf =
(f2qf.Size() == 0) ? be_to_face[i] : f2qf[be_to_face[i]];
new_boundary[j++] =
new Quadrilateral(v[0], oedge+e[0], oface+qf, oedge+e[3], attr);
new_boundary[j++] =
new Quadrilateral(oedge+e[0], v[1], oedge+e[1], oface+qf, attr);
new_boundary[j++] =
new Quadrilateral(oface+qf, oedge+e[1], v[2], oedge+e[2], attr);
new_boundary[j++] =
new Quadrilateral(oedge+e[3], oface+qf, oedge+e[2], v[3], attr);
}
else
{
MFEM_ABORT("boundary Element is not a triangle or a quad!");
}
FreeElement(boundary[i]);
}
mfem::Swap(boundary, new_boundary);
static const double A = 0.0, B = 0.5, C = 1.0;
static double tet_children[3*4*16] =
{
A,A,A, B,A,A, A,B,A, A,A,B,
B,A,A, C,A,A, B,B,A, B,A,B,
A,B,A, B,B,A, A,C,A, A,B,B,
A,A,B, B,A,B, A,B,B, A,A,C,
// edge coordinates:
// 0 -> B,A,A 1 -> A,B,A 2 -> A,A,B
// 3 -> B,B,A 4 -> B,A,B 5 -> A,B,B
// rt = 0: {0,5,1,2}, {0,5,2,4}, {0,5,4,3}, {0,5,3,1}
B,A,A, A,B,B, A,B,A, A,A,B,
B,A,A, A,B,B, A,A,B, B,A,B,
B,A,A, A,B,B, B,A,B, B,B,A,
B,A,A, A,B,B, B,B,A, A,B,A,
// rt = 1: {1,0,4,2}, {1,2,4,5}, {1,5,4,3}, {1,3,4,0}
A,B,A, B,A,A, B,A,B, A,A,B,
A,B,A, A,A,B, B,A,B, A,B,B,
A,B,A, A,B,B, B,A,B, B,B,A,
A,B,A, B,B,A, B,A,B, B,A,A,
// rt = 2: {2,0,1,3}, {2,1,5,3}, {2,5,4,3}, {2,4,0,3}
A,A,B, B,A,A, A,B,A, B,B,A,
A,A,B, A,B,A, A,B,B, B,B,A,
A,A,B, A,B,B, B,A,B, B,B,A,
A,A,B, B,A,B, B,A,A, B,B,A
};
static double pri_children[3*6*8] =
{
A,A,A, B,A,A, A,B,A, A,A,B, B,A,B, A,B,B,
B,B,A, A,B,A, B,A,A, B,B,B, A,B,B, B,A,B,
B,A,A, C,A,A, B,B,A, B,A,B, C,A,B, B,B,B,
A,B,A, B,B,A, A,C,A, A,B,B, B,B,B, A,C,B,
A,A,B, B,A,B, A,B,B, A,A,C, B,A,C, A,B,C,
B,B,B, A,B,B, B,A,B, B,B,C, A,B,C, B,A,C,
B,A,B, C,A,B, B,B,B, B,A,C, C,A,C, B,B,C,
A,B,B, B,B,B, A,C,B, A,B,C, B,B,C, A,C,C
};
static double hex_children[3*8*8] =
{
A,A,A, B,A,A, B,B,A, A,B,A, A,A,B, B,A,B, B,B,B, A,B,B,
B,A,A, C,A,A, C,B,A, B,B,A, B,A,B, C,A,B, C,B,B, B,B,B,
B,B,A, C,B,A, C,C,A, B,C,A, B,B,B, C,B,B, C,C,B, B,C,B,
A,B,A, B,B,A, B,C,A, A,C,A, A,B,B, B,B,B, B,C,B, A,C,B,
A,A,B, B,A,B, B,B,B, A,B,B, A,A,C, B,A,C, B,B,C, A,B,C,
B,A,B, C,A,B, C,B,B, B,B,B, B,A,C, C,A,C, C,B,C, B,B,C,
B,B,B, C,B,B, C,C,B, B,C,B, B,B,C, C,B,C, C,C,C, B,C,C,
A,B,B, B,B,B, B,C,B, A,C,B, A,B,C, B,B,C, B,C,C, A,C,C
};
CoarseFineTr.point_matrices[Geometry::TETRAHEDRON]
.UseExternalData(tet_children, 3, 4, 16);
CoarseFineTr.point_matrices[Geometry::PRISM]
.UseExternalData(pri_children, 3, 6, 8);
CoarseFineTr.point_matrices[Geometry::CUBE]
.UseExternalData(hex_children, 3, 8, 8);
for (int i = 0; i < elements.Size(); i++)
{
// tetrahedron elements are handled above:
if (elements[i]->GetType() == Element::TETRAHEDRON) { continue; }
Embedding &emb = CoarseFineTr.embeddings[i];
emb.parent = i / 8;
emb.matrix = i % 8;
}
NumOfVertices = vertices.Size();
NumOfElements = 8 * NumOfElements;
NumOfBdrElements = 4 * NumOfBdrElements;
GetElementToFaceTable();
GenerateFaces();
#ifdef MFEM_DEBUG
CheckBdrElementOrientation(false);
#endif
NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
last_operation = Mesh::REFINE;
sequence++;
if (update_nodes) { UpdateNodes(); }
}
void Mesh::LocalRefinement(const Array<int> &marked_el, int type)
{
int i, j, ind, nedges;
Array<int> v;
ResetLazyData();
if (ncmesh)
{
MFEM_ABORT("Local and nonconforming refinements cannot be mixed.");
}
InitRefinementTransforms();
if (Dim == 1) // --------------------------------------------------------
{
int cne = NumOfElements, cnv = NumOfVertices;
NumOfVertices += marked_el.Size();
NumOfElements += marked_el.Size();
vertices.SetSize(NumOfVertices);
elements.SetSize(NumOfElements);
CoarseFineTr.embeddings.SetSize(NumOfElements);
for (j = 0; j < marked_el.Size(); j++)
{
i = marked_el[j];
Segment *c_seg = (Segment *)elements[i];
int *vert = c_seg->GetVertices(), attr = c_seg->GetAttribute();
int new_v = cnv + j, new_e = cne + j;
AverageVertices(vert, 2, new_v);
elements[new_e] = new Segment(new_v, vert[1], attr);
vert[1] = new_v;
CoarseFineTr.embeddings[i] = Embedding(i, 1);
CoarseFineTr.embeddings[new_e] = Embedding(i, 2);
}
static double seg_children[3*2] = { 0.0,1.0, 0.0,0.5, 0.5,1.0 };
CoarseFineTr.point_matrices[Geometry::SEGMENT].
UseExternalData(seg_children, 1, 2, 3);
GenerateFaces();
} // end of 'if (Dim == 1)'
else if (Dim == 2) // ---------------------------------------------------
{
// 1. Get table of vertex to vertex connections.
DSTable v_to_v(NumOfVertices);
GetVertexToVertexTable(v_to_v);
// 2. Get edge to element connections in arrays edge1 and edge2
nedges = v_to_v.NumberOfEntries();
int *edge1 = new int[nedges];
int *edge2 = new int[nedges];
int *middle = new int[nedges];
for (i = 0; i < nedges; i++)
{
edge1[i] = edge2[i] = middle[i] = -1;
}
for (i = 0; i < NumOfElements; i++)
{
elements[i]->GetVertices(v);
for (j = 1; j < v.Size(); j++)
{
ind = v_to_v(v[j-1], v[j]);
(edge1[ind] == -1) ? (edge1[ind] = i) : (edge2[ind] = i);
}
ind = v_to_v(v[0], v[v.Size()-1]);
(edge1[ind] == -1) ? (edge1[ind] = i) : (edge2[ind] = i);
}
// 3. Do the red refinement.
for (i = 0; i < marked_el.Size(); i++)
{
RedRefinement(marked_el[i], v_to_v, edge1, edge2, middle);
}
// 4. Do the green refinement (to get conforming mesh).
int need_refinement;
do
{
need_refinement = 0;
for (i = 0; i < nedges; i++)
{
if (middle[i] != -1 && edge1[i] != -1)
{
need_refinement = 1;
GreenRefinement(edge1[i], v_to_v, edge1, edge2, middle);
}
}
}
while (need_refinement == 1);
// 5. Update the boundary elements.
int v1[2], v2[2], bisect, temp;
temp = NumOfBdrElements;
for (i = 0; i < temp; i++)
{
boundary[i]->GetVertices(v);
bisect = v_to_v(v[0], v[1]);
if (middle[bisect] != -1) // the element was refined (needs updating)
{
if (boundary[i]->GetType() == Element::SEGMENT)
{
v1[0] = v[0]; v1[1] = middle[bisect];
v2[0] = middle[bisect]; v2[1] = v[1];
boundary[i]->SetVertices(v1);
boundary.Append(new Segment(v2, boundary[i]->GetAttribute()));
}
else
mfem_error("Only bisection of segment is implemented"
" for bdr elem.");
}
}
NumOfBdrElements = boundary.Size();
// 6. Free the allocated memory.
delete [] edge1;
delete [] edge2;
delete [] middle;
if (el_to_edge != NULL)
{
NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
GenerateFaces();
}
}
else if (Dim == 3) // ---------------------------------------------------
{
// 1. Hash table of vertex to vertex connections corresponding to refined
// edges.
HashTable<Hashed2> v_to_v;
MFEM_VERIFY(GetNE() == 0 ||
((Tetrahedron*)elements[0])->GetRefinementFlag() != 0,
"tetrahedral mesh is not marked for refinement:"
" call Finalize(true)");
// 2. Do the red refinement.
int ii;
switch (type)
{
case 1:
for (i = 0; i < marked_el.Size(); i++)
{
Bisection(marked_el[i], v_to_v);
}
break;
case 2:
for (i = 0; i < marked_el.Size(); i++)
{
Bisection(marked_el[i], v_to_v);
Bisection(NumOfElements - 1, v_to_v);
Bisection(marked_el[i], v_to_v);
}
break;
case 3:
for (i = 0; i < marked_el.Size(); i++)
{
Bisection(marked_el[i], v_to_v);
ii = NumOfElements - 1;
Bisection(ii, v_to_v);
Bisection(NumOfElements - 1, v_to_v);
Bisection(ii, v_to_v);
Bisection(marked_el[i], v_to_v);
Bisection(NumOfElements-1, v_to_v);
Bisection(marked_el[i], v_to_v);
}
break;
}
// 3. Do the green refinement (to get conforming mesh).
int need_refinement;
// int need_refinement, onoe, max_gen = 0;
do
{
// int redges[2], type, flag;
need_refinement = 0;
// onoe = NumOfElements;
// for (i = 0; i < onoe; i++)
for (i = 0; i < NumOfElements; i++)
{
// ((Tetrahedron *)elements[i])->
// ParseRefinementFlag(redges, type, flag);
// if (flag > max_gen) max_gen = flag;
if (elements[i]->NeedRefinement(v_to_v))
{
need_refinement = 1;
Bisection(i, v_to_v);
}
}
}
while (need_refinement == 1);
// mfem::out << "Maximum generation: " << max_gen << endl;
// 4. Update the boundary elements.
do
{
need_refinement = 0;
for (i = 0; i < NumOfBdrElements; i++)
if (boundary[i]->NeedRefinement(v_to_v))
{
need_refinement = 1;
BdrBisection(i, v_to_v);
}
}
while (need_refinement == 1);
NumOfVertices = vertices.Size();
NumOfBdrElements = boundary.Size();
// 5. Update element-to-edge and element-to-face relations.
if (el_to_edge != NULL)
{
NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
}
if (el_to_face != NULL)
{
GetElementToFaceTable();
GenerateFaces();
}
} // end 'if (Dim == 3)'
last_operation = Mesh::REFINE;
sequence++;
UpdateNodes();
#ifdef MFEM_DEBUG
CheckElementOrientation(false);
#endif
}
void Mesh::NonconformingRefinement(const Array<Refinement> &refinements,
int nc_limit)
{
MFEM_VERIFY(!NURBSext, "Nonconforming refinement of NURBS meshes is "
"not supported. Project the NURBS to Nodes first.");
ResetLazyData();
if (!ncmesh)
{
// start tracking refinement hierarchy
ncmesh = new NCMesh(this);
}
if (!refinements.Size())
{
last_operation = Mesh::NONE;
return;
}
// do the refinements
ncmesh->MarkCoarseLevel();
ncmesh->Refine(refinements);
if (nc_limit > 0)
{
ncmesh->LimitNCLevel(nc_limit);
}
// create a second mesh containing the finest elements from 'ncmesh'
Mesh* mesh2 = new Mesh(*ncmesh);
ncmesh->OnMeshUpdated(mesh2);
// now swap the meshes, the second mesh will become the old coarse mesh
// and this mesh will be the new fine mesh
Swap(*mesh2, false);
delete mesh2;
GenerateNCFaceInfo();
last_operation = Mesh::REFINE;
sequence++;
if (Nodes) // update/interpolate curved mesh
{
Nodes->FESpace()->Update();
Nodes->Update();
}
}
double Mesh::AggregateError(const Array<double> &elem_error,
const int *fine, int nfine, int op)
{
double error = 0.0;
for (int i = 0; i < nfine; i++)
{
MFEM_VERIFY(fine[i] < elem_error.Size(), "");
double err_fine = elem_error[fine[i]];
switch (op)
{
case 0: error = std::min(error, err_fine); break;
case 1: error += err_fine; break;
case 2: error = std::max(error, err_fine); break;
}
}
return error;
}
bool Mesh::NonconformingDerefinement(Array<double> &elem_error,
double threshold, int nc_limit, int op)
{
MFEM_VERIFY(ncmesh, "Only supported for non-conforming meshes.");
MFEM_VERIFY(!NURBSext, "Derefinement of NURBS meshes is not supported. "
"Project the NURBS to Nodes first.");
ResetLazyData();
const Table &dt = ncmesh->GetDerefinementTable();
Array<int> level_ok;
if (nc_limit > 0)
{
ncmesh->CheckDerefinementNCLevel(dt, level_ok, nc_limit);
}
Array<int> derefs;
for (int i = 0; i < dt.Size(); i++)
{
if (nc_limit > 0 && !level_ok[i]) { continue; }
double error =
AggregateError(elem_error, dt.GetRow(i), dt.RowSize(i), op);
if (error < threshold) { derefs.Append(i); }
}
if (!derefs.Size()) { return false; }
ncmesh->Derefine(derefs);
Mesh* mesh2 = new Mesh(*ncmesh);
ncmesh->OnMeshUpdated(mesh2);
Swap(*mesh2, false);
delete mesh2;
GenerateNCFaceInfo();
last_operation = Mesh::DEREFINE;
sequence++;
UpdateNodes();
return true;
}
bool Mesh::DerefineByError(Array<double> &elem_error, double threshold,
int nc_limit, int op)
{
// NOTE: the error array is not const because it will be expanded in parallel
// by ghost element errors
if (Nonconforming())
{
return NonconformingDerefinement(elem_error, threshold, nc_limit, op);
}
else
{
MFEM_ABORT("Derefinement is currently supported for non-conforming "
"meshes only.");
return false;
}
}
bool Mesh::DerefineByError(const Vector &elem_error, double threshold,
int nc_limit, int op)
{
Array<double> tmp(elem_error.Size());
for (int i = 0; i < tmp.Size(); i++)
{
tmp[i] = elem_error(i);
}
return DerefineByError(tmp, threshold, nc_limit, op);
}
void Mesh::InitFromNCMesh(const NCMesh &ncmesh)
{
Dim = ncmesh.Dimension();
spaceDim = ncmesh.SpaceDimension();
DeleteTables();
ncmesh.GetMeshComponents(*this);
NumOfVertices = vertices.Size();
NumOfElements = elements.Size();
NumOfBdrElements = boundary.Size();
SetMeshGen(); // set the mesh type: 'meshgen', ...
NumOfEdges = NumOfFaces = 0;
nbInteriorFaces = nbBoundaryFaces = -1;
if (Dim > 1)
{
el_to_edge = new Table;
NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
}
if (Dim > 2)
{
GetElementToFaceTable();
}
GenerateFaces();
#ifdef MFEM_DEBUG
CheckBdrElementOrientation(false);
#endif
// NOTE: ncmesh->OnMeshUpdated() and GenerateNCFaceInfo() should be called
// outside after this method.
}
Mesh::Mesh(const NCMesh &ncmesh)
{
Init();
InitTables();
InitFromNCMesh(ncmesh);
SetAttributes();
}
void Mesh::Swap(Mesh& other, bool non_geometry)
{
mfem::Swap(Dim, other.Dim);
mfem::Swap(spaceDim, other.spaceDim);
mfem::Swap(NumOfVertices, other.NumOfVertices);
mfem::Swap(NumOfElements, other.NumOfElements);
mfem::Swap(NumOfBdrElements, other.NumOfBdrElements);
mfem::Swap(NumOfEdges, other.NumOfEdges);
mfem::Swap(NumOfFaces, other.NumOfFaces);
mfem::Swap(meshgen, other.meshgen);
mfem::Swap(mesh_geoms, other.mesh_geoms);
mfem::Swap(elements, other.elements);
mfem::Swap(vertices, other.vertices);
mfem::Swap(boundary, other.boundary);
mfem::Swap(faces, other.faces);
mfem::Swap(faces_info, other.faces_info);
mfem::Swap(nc_faces_info, other.nc_faces_info);
mfem::Swap(el_to_edge, other.el_to_edge);
mfem::Swap(el_to_face, other.el_to_face);
mfem::Swap(el_to_el, other.el_to_el);
mfem::Swap(be_to_edge, other.be_to_edge);
mfem::Swap(bel_to_edge, other.bel_to_edge);
mfem::Swap(be_to_face, other.be_to_face);
mfem::Swap(face_edge, other.face_edge);
mfem::Swap(edge_vertex, other.edge_vertex);
mfem::Swap(attributes, other.attributes);
mfem::Swap(bdr_attributes, other.bdr_attributes);
mfem::Swap(geom_factors, other.geom_factors);
#ifdef MFEM_USE_MEMALLOC
TetMemory.Swap(other.TetMemory);
#endif
if (non_geometry)
{
mfem::Swap(NURBSext, other.NURBSext);
mfem::Swap(ncmesh, other.ncmesh);
mfem::Swap(Nodes, other.Nodes);
mfem::Swap(own_nodes, other.own_nodes);
}
}
void Mesh::GetElementData(const Array<Element*> &elem_array, int geom,
Array<int> &elem_vtx, Array<int> &attr) const
{
// protected method
const int nv = Geometry::NumVerts[geom];
int num_elems = 0;
for (int i = 0; i < elem_array.Size(); i++)
{
if (elem_array[i]->GetGeometryType() == geom)
{
num_elems++;
}
}
elem_vtx.SetSize(nv*num_elems);
attr.SetSize(num_elems);
elem_vtx.SetSize(0);
attr.SetSize(0);
for (int i = 0; i < elem_array.Size(); i++)
{
Element *el = elem_array[i];
if (el->GetGeometryType() != geom) { continue; }
Array<int> loc_vtx(el->GetVertices(), nv);
elem_vtx.Append(loc_vtx);
attr.Append(el->GetAttribute());
}
}
static Array<int>& AllElements(Array<int> &list, int nelem)
{
list.SetSize(nelem);
for (int i = 0; i < nelem; i++) { list[i] = i; }
return list;
}
void Mesh::UniformRefinement(int ref_algo)
{
Array<int> list;
if (NURBSext)
{
NURBSUniformRefinement();
}
else if (ncmesh)
{
GeneralRefinement(AllElements(list, GetNE()));
}
else if (ref_algo == 1 && meshgen == 1 && Dim == 3)
{
// algorithm "B" for an all-tet mesh
LocalRefinement(AllElements(list, GetNE()));
}
else
{
switch (Dim)
{
case 1: LocalRefinement(AllElements(list, GetNE())); break;
case 2: UniformRefinement2D(); break;
case 3: UniformRefinement3D(); break;
default: MFEM_ABORT("internal error");
}
}
}
void Mesh::GeneralRefinement(const Array<Refinement> &refinements,
int nonconforming, int nc_limit)
{
if (ncmesh)
{
nonconforming = 1;
}
else if (Dim == 1 || (Dim == 3 && (meshgen & 1)))
{
nonconforming = 0;
}
else if (nonconforming < 0)
{
// determine if nonconforming refinement is suitable
if ((meshgen & 2) || (meshgen & 4))
{
nonconforming = 1; // tensor product elements and wedges
}
else
{
nonconforming = 0; // simplices
}
}
if (nonconforming)
{
// non-conforming refinement (hanging nodes)
NonconformingRefinement(refinements, nc_limit);
}
else
{
Array<int> el_to_refine(refinements.Size());
for (int i = 0; i < refinements.Size(); i++)
{
el_to_refine[i] = refinements[i].index;
}
// infer 'type' of local refinement from first element's 'ref_type'
int type, rt = (refinements.Size() ? refinements[0].ref_type : 7);
if (rt == 1 || rt == 2 || rt == 4)
{
type = 1; // bisection
}
else if (rt == 3 || rt == 5 || rt == 6)
{
type = 2; // quadrisection
}
else
{
type = 3; // octasection
}
// red-green refinement and bisection, no hanging nodes
LocalRefinement(el_to_refine, type);
}
}
void Mesh::GeneralRefinement(const Array<int> &el_to_refine, int nonconforming,
int nc_limit)
{
Array<Refinement> refinements(el_to_refine.Size());
for (int i = 0; i < el_to_refine.Size(); i++)
{
refinements[i] = Refinement(el_to_refine[i]);
}
GeneralRefinement(refinements, nonconforming, nc_limit);
}
void Mesh::EnsureNCMesh(bool simplices_nonconforming)
{
MFEM_VERIFY(!NURBSext, "Cannot convert a NURBS mesh to an NC mesh. "
"Project the NURBS to Nodes first.");
if (!ncmesh)
{
if ((meshgen & 0x2) /* quads/hexes */ ||
(meshgen & 0x4) /* wedges */ ||
(simplices_nonconforming && (meshgen & 0x1)) /* simplices */)
{
ncmesh = new NCMesh(this);
ncmesh->OnMeshUpdated(this);
GenerateNCFaceInfo();
}
}
}
void Mesh::RandomRefinement(double prob, bool aniso, int nonconforming,
int nc_limit)
{
Array<Refinement> refs;
for (int i = 0; i < GetNE(); i++)
{
if ((double) rand() / RAND_MAX < prob)
{
int type = 7;
if (aniso)
{
type = (Dim == 3) ? (rand() % 7 + 1) : (rand() % 3 + 1);
}
refs.Append(Refinement(i, type));
}
}
GeneralRefinement(refs, nonconforming, nc_limit);
}
void Mesh::RefineAtVertex(const Vertex& vert, double eps, int nonconforming)
{
Array<int> v;
Array<Refinement> refs;
for (int i = 0; i < GetNE(); i++)
{
GetElementVertices(i, v);
bool refine = false;
for (int j = 0; j < v.Size(); j++)
{
double dist = 0.0;
for (int l = 0; l < spaceDim; l++)
{
double d = vert(l) - vertices[v[j]](l);
dist += d*d;
}
if (dist <= eps*eps) { refine = true; break; }
}
if (refine)
{
refs.Append(Refinement(i));
}
}
GeneralRefinement(refs, nonconforming);
}
bool Mesh::RefineByError(const Array<double> &elem_error, double threshold,
int nonconforming, int nc_limit)
{
MFEM_VERIFY(elem_error.Size() == GetNE(), "");
Array<Refinement> refs;
for (int i = 0; i < GetNE(); i++)
{
if (elem_error[i] > threshold)
{
refs.Append(Refinement(i));
}
}
if (ReduceInt(refs.Size()))
{
GeneralRefinement(refs, nonconforming, nc_limit);
return true;
}
return false;
}
bool Mesh::RefineByError(const Vector &elem_error, double threshold,
int nonconforming, int nc_limit)
{
Array<double> tmp(const_cast<double*>(elem_error.GetData()),
elem_error.Size());
return RefineByError(tmp, threshold, nonconforming, nc_limit);
}
void Mesh::Bisection(int i, const DSTable &v_to_v,
int *edge1, int *edge2, int *middle)
{
int *vert;
int v[2][4], v_new, bisect, t;
Element *el = elements[i];
Vertex V;
t = el->GetType();
if (t == Element::TRIANGLE)
{
Triangle *tri = (Triangle *) el;
vert = tri->GetVertices();
// 1. Get the index for the new vertex in v_new.
bisect = v_to_v(vert[0], vert[1]);
MFEM_ASSERT(bisect >= 0, "");
if (middle[bisect] == -1)
{
v_new = NumOfVertices++;
for (int d = 0; d < spaceDim; d++)
{
V(d) = 0.5 * (vertices[vert[0]](d) + vertices[vert[1]](d));
}
vertices.Append(V);
// Put the element that may need refinement (because of this
// bisection) in edge1, or -1 if no more refinement is needed.
if (edge1[bisect] == i)
{
edge1[bisect] = edge2[bisect];
}
middle[bisect] = v_new;
}
else
{
v_new = middle[bisect];
// This edge will require no more refinement.
edge1[bisect] = -1;
}
// 2. Set the node indices for the new elements in v[0] and v[1] so that
// the edge marked for refinement is between the first two nodes.
v[0][0] = vert[2]; v[0][1] = vert[0]; v[0][2] = v_new;
v[1][0] = vert[1]; v[1][1] = vert[2]; v[1][2] = v_new;
tri->SetVertices(v[0]); // changes vert[0..2] !!!
Triangle* tri_new = new Triangle(v[1], tri->GetAttribute());
elements.Append(tri_new);
int tr = tri->GetTransform();
tri_new->ResetTransform(tr);
// record the sequence of refinements
tri->PushTransform(4);
tri_new->PushTransform(5);
int coarse = FindCoarseElement(i);
CoarseFineTr.embeddings[i].parent = coarse;
CoarseFineTr.embeddings.Append(Embedding(coarse));
// 3. edge1 and edge2 may have to be changed for the second triangle.
if (v[1][0] < v_to_v.NumberOfRows() && v[1][1] < v_to_v.NumberOfRows())
{
bisect = v_to_v(v[1][0], v[1][1]);
MFEM_ASSERT(bisect >= 0, "");
if (edge1[bisect] == i)
{
edge1[bisect] = NumOfElements;
}
else if (edge2[bisect] == i)
{
edge2[bisect] = NumOfElements;
}
}
NumOfElements++;
}
else
{
MFEM_ABORT("Bisection for now works only for triangles.");
}
}
void Mesh::Bisection(int i, HashTable<Hashed2> &v_to_v)
{
int *vert;
int v[2][4], v_new, bisect, t;
Element *el = elements[i];
Vertex V;
t = el->GetType();
if (t == Element::TETRAHEDRON)
{
int j, type, new_type, old_redges[2], new_redges[2][2], flag;
Tetrahedron *tet = (Tetrahedron *) el;
MFEM_VERIFY(tet->GetRefinementFlag() != 0,
"TETRAHEDRON element is not marked for refinement.");
vert = tet->GetVertices();
// 1. Get the index for the new vertex in v_new.
bisect = v_to_v.FindId(vert[0], vert[1]);
if (bisect == -1)
{
v_new = NumOfVertices + v_to_v.GetId(vert[0],vert[1]);
for (j = 0; j < 3; j++)
{
V(j) = 0.5 * (vertices[vert[0]](j) + vertices[vert[1]](j));
}
vertices.Append(V);
}
else
{
v_new = NumOfVertices + bisect;
}
// 2. Set the node indices for the new elements in v[2][4] so that
// the edge marked for refinement is between the first two nodes.
tet->ParseRefinementFlag(old_redges, type, flag);
v[0][3] = v_new;
v[1][3] = v_new;
new_redges[0][0] = 2;
new_redges[0][1] = 1;
new_redges[1][0] = 2;
new_redges[1][1] = 1;
int tr1 = -1, tr2 = -1;
switch (old_redges[0])
{
case 2:
v[0][0] = vert[0]; v[0][1] = vert[2]; v[0][2] = vert[3];
if (type == Tetrahedron::TYPE_PF) { new_redges[0][1] = 4; }
tr1 = 0;
break;
case 3:
v[0][0] = vert[3]; v[0][1] = vert[0]; v[0][2] = vert[2];
tr1 = 2;
break;
case 5:
v[0][0] = vert[2]; v[0][1] = vert[3]; v[0][2] = vert[0];
tr1 = 4;
}
switch (old_redges[1])
{
case 1:
v[1][0] = vert[2]; v[1][1] = vert[1]; v[1][2] = vert[3];
if (type == Tetrahedron::TYPE_PF) { new_redges[1][0] = 3; }
tr2 = 1;
break;
case 4:
v[1][0] = vert[1]; v[1][1] = vert[3]; v[1][2] = vert[2];
tr2 = 3;
break;
case 5:
v[1][0] = vert[3]; v[1][1] = vert[2]; v[1][2] = vert[1];
tr2 = 5;
}
int attr = tet->GetAttribute();
tet->SetVertices(v[0]);
#ifdef MFEM_USE_MEMALLOC
Tetrahedron *tet2 = TetMemory.Alloc();
tet2->SetVertices(v[1]);
tet2->SetAttribute(attr);
#else
Tetrahedron *tet2 = new Tetrahedron(v[1], attr);
#endif
tet2->ResetTransform(tet->GetTransform());
elements.Append(tet2);
// record the sequence of refinements
tet->PushTransform(tr1);
tet2->PushTransform(tr2);
int coarse = FindCoarseElement(i);
CoarseFineTr.embeddings[i].parent = coarse;
CoarseFineTr.embeddings.Append(Embedding(coarse));
// 3. Set the bisection flag
switch (type)
{
case Tetrahedron::TYPE_PU:
new_type = Tetrahedron::TYPE_PF; break;
case Tetrahedron::TYPE_PF:
new_type = Tetrahedron::TYPE_A; break;
default:
new_type = Tetrahedron::TYPE_PU;
}
tet->CreateRefinementFlag(new_redges[0], new_type, flag+1);
tet2->CreateRefinementFlag(new_redges[1], new_type, flag+1);
NumOfElements++;
}
else
{
MFEM_ABORT("Bisection with HashTable for now works only for tetrahedra.");
}
}
void Mesh::BdrBisection(int i, const HashTable<Hashed2> &v_to_v)
{
int *vert;
int v[2][3], v_new, bisect, t;
Element *bdr_el = boundary[i];
t = bdr_el->GetType();
if (t == Element::TRIANGLE)
{
Triangle *tri = (Triangle *) bdr_el;
vert = tri->GetVertices();
// 1. Get the index for the new vertex in v_new.
bisect = v_to_v.FindId(vert[0], vert[1]);
MFEM_ASSERT(bisect >= 0, "");
v_new = NumOfVertices + bisect;
MFEM_ASSERT(v_new != -1, "");
// 2. Set the node indices for the new elements in v[0] and v[1] so that
// the edge marked for refinement is between the first two nodes.
v[0][0] = vert[2]; v[0][1] = vert[0]; v[0][2] = v_new;
v[1][0] = vert[1]; v[1][1] = vert[2]; v[1][2] = v_new;
tri->SetVertices(v[0]);
boundary.Append(new Triangle(v[1], tri->GetAttribute()));
NumOfBdrElements++;
}
else
{
MFEM_ABORT("Bisection of boundary elements with HashTable works only for"
" triangles!");
}
}
void Mesh::UniformRefinement(int i, const DSTable &v_to_v,
int *edge1, int *edge2, int *middle)
{
Array<int> v;
int j, v1[3], v2[3], v3[3], v4[3], v_new[3], bisect[3];
Vertex V;
if (elements[i]->GetType() == Element::TRIANGLE)
{
Triangle *tri0 = (Triangle*) elements[i];
tri0->GetVertices(v);
// 1. Get the indeces for the new vertices in array v_new
bisect[0] = v_to_v(v[0],v[1]);
bisect[1] = v_to_v(v[1],v[2]);
bisect[2] = v_to_v(v[0],v[2]);
MFEM_ASSERT(bisect[0] >= 0 && bisect[1] >= 0 && bisect[2] >= 0, "");
for (j = 0; j < 3; j++) // for the 3 edges fix v_new
{
if (middle[bisect[j]] == -1)
{
v_new[j] = NumOfVertices++;
for (int d = 0; d < spaceDim; d++)
{
V(d) = (vertices[v[j]](d) + vertices[v[(j+1)%3]](d))/2.;
}
vertices.Append(V);
// Put the element that may need refinement (because of this
// bisection) in edge1, or -1 if no more refinement is needed.
if (edge1[bisect[j]] == i)
{
edge1[bisect[j]] = edge2[bisect[j]];
}
middle[bisect[j]] = v_new[j];
}
else
{
v_new[j] = middle[bisect[j]];
// This edge will require no more refinement.
edge1[bisect[j]] = -1;
}
}
// 2. Set the node indeces for the new elements in v1, v2, v3 & v4 so that
// the edges marked for refinement be between the first two nodes.
v1[0] = v[0]; v1[1] = v_new[0]; v1[2] = v_new[2];
v2[0] = v_new[0]; v2[1] = v[1]; v2[2] = v_new[1];
v3[0] = v_new[2]; v3[1] = v_new[1]; v3[2] = v[2];
v4[0] = v_new[1]; v4[1] = v_new[2]; v4[2] = v_new[0];
Triangle* tri1 = new Triangle(v1, tri0->GetAttribute());
Triangle* tri2 = new Triangle(v2, tri0->GetAttribute());
Triangle* tri3 = new Triangle(v3, tri0->GetAttribute());
elements.Append(tri1);
elements.Append(tri2);
elements.Append(tri3);
tri0->SetVertices(v4);
// record the sequence of refinements
unsigned code = tri0->GetTransform();
tri1->ResetTransform(code);
tri2->ResetTransform(code);
tri3->ResetTransform(code);
tri0->PushTransform(3);
tri1->PushTransform(0);
tri2->PushTransform(1);
tri3->PushTransform(2);
// set parent indices
int coarse = FindCoarseElement(i);
CoarseFineTr.embeddings[i] = Embedding(coarse);
CoarseFineTr.embeddings.Append(Embedding(coarse));
CoarseFineTr.embeddings.Append(Embedding(coarse));
CoarseFineTr.embeddings.Append(Embedding(coarse));
NumOfElements += 3;
}
else
{
MFEM_ABORT("Uniform refinement for now works only for triangles.");
}
}
void Mesh::InitRefinementTransforms()
{
// initialize CoarseFineTr
CoarseFineTr.Clear();
CoarseFineTr.embeddings.SetSize(NumOfElements);
for (int i = 0; i < NumOfElements; i++)
{
elements[i]->ResetTransform(0);
CoarseFineTr.embeddings[i] = Embedding(i);
}
}
int Mesh::FindCoarseElement(int i)
{
int coarse;
while ((coarse = CoarseFineTr.embeddings[i].parent) != i)
{
i = coarse;
}
return coarse;
}
const CoarseFineTransformations& Mesh::GetRefinementTransforms()
{
MFEM_VERIFY(GetLastOperation() == Mesh::REFINE, "");
if (ncmesh)
{
return ncmesh->GetRefinementTransforms();
}
Mesh::GeometryList elem_geoms(*this);
for (int i = 0; i < elem_geoms.Size(); i++)
{
const Geometry::Type geom = elem_geoms[i];
if (CoarseFineTr.point_matrices[geom].SizeK()) { continue; }
if (geom == Geometry::TRIANGLE ||
geom == Geometry::TETRAHEDRON)
{
std::map<unsigned, int> mat_no;
mat_no[0] = 1; // identity
// assign matrix indices to element transformations
for (int i = 0; i < elements.Size(); i++)
{
int index = 0;
unsigned code = elements[i]->GetTransform();
if (code)
{
int &matrix = mat_no[code];
if (!matrix) { matrix = mat_no.size(); }
index = matrix-1;
}
CoarseFineTr.embeddings[i].matrix = index;
}
DenseTensor &pmats = CoarseFineTr.point_matrices[geom];
pmats.SetSize(Dim, Dim+1, mat_no.size());
// calculate the point matrices used
std::map<unsigned, int>::iterator it;
for (it = mat_no.begin(); it != mat_no.end(); ++it)
{
if (geom == Geometry::TRIANGLE)
{
Triangle::GetPointMatrix(it->first, pmats(it->second-1));
}
else
{
Tetrahedron::GetPointMatrix(it->first, pmats(it->second-1));
}
}
}
else
{
MFEM_ABORT("Don't know how to construct CoarseFineTransformations for"
" geom = " << geom);
}
}
// NOTE: quads and hexes already have trivial transformations ready
return CoarseFineTr;
}
void Mesh::PrintXG(std::ostream &out) const
{
MFEM_ASSERT(Dim==spaceDim, "2D Manifold meshes not supported");
int i, j;
Array<int> v;
if (Dim == 2)
{
// Print the type of the mesh.
if (Nodes == NULL)
{
out << "areamesh2\n\n";
}
else
{
out << "curved_areamesh2\n\n";
}
// Print the boundary elements.
out << NumOfBdrElements << '\n';
for (i = 0; i < NumOfBdrElements; i++)
{
boundary[i]->GetVertices(v);
out << boundary[i]->GetAttribute();
for (j = 0; j < v.Size(); j++)
{
out << ' ' << v[j] + 1;
}
out << '\n';
}
// Print the elements.
out << NumOfElements << '\n';
for (i = 0; i < NumOfElements; i++)
{
elements[i]->GetVertices(v);
out << elements[i]->GetAttribute() << ' ' << v.Size();
for (j = 0; j < v.Size(); j++)
{
out << ' ' << v[j] + 1;
}
out << '\n';
}
if (Nodes == NULL)
{
// Print the vertices.
out << NumOfVertices << '\n';
for (i = 0; i < NumOfVertices; i++)
{
out << vertices[i](0);
for (j = 1; j < Dim; j++)
{
out << ' ' << vertices[i](j);
}
out << '\n';
}
}
else
{
out << NumOfVertices << '\n';
Nodes->Save(out);
}
}
else // ===== Dim != 2 =====
{
if (Nodes)
{
mfem_error("Mesh::PrintXG(...) : Curved mesh in 3D");
}
if (meshgen == 1)
{
int nv;
const int *ind;
out << "NETGEN_Neutral_Format\n";
// print the vertices
out << NumOfVertices << '\n';
for (i = 0; i < NumOfVertices; i++)
{
for (j = 0; j < Dim; j++)
{
out << ' ' << vertices[i](j);
}
out << '\n';
}
// print the elements
out << NumOfElements << '\n';
for (i = 0; i < NumOfElements; i++)
{
nv = elements[i]->GetNVertices();
ind = elements[i]->GetVertices();
out << elements[i]->GetAttribute();
for (j = 0; j < nv; j++)
{
out << ' ' << ind[j]+1;
}
out << '\n';
}
// print the boundary information.
out << NumOfBdrElements << '\n';
for (i = 0; i < NumOfBdrElements; i++)
{
nv = boundary[i]->GetNVertices();
ind = boundary[i]->GetVertices();
out << boundary[i]->GetAttribute();
for (j = 0; j < nv; j++)
{
out << ' ' << ind[j]+1;
}
out << '\n';
}
}
else if (meshgen == 2) // TrueGrid
{
int nv;
const int *ind;
out << "TrueGrid\n"
<< "1 " << NumOfVertices << " " << NumOfElements
<< " 0 0 0 0 0 0 0\n"
<< "0 0 0 1 0 0 0 0 0 0 0\n"
<< "0 0 " << NumOfBdrElements << " 0 0 0 0 0 0 0 0 0 0 0 0 0\n"
<< "0.0 0.0 0.0 0 0 0.0 0.0 0 0.0\n"
<< "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n";
for (i = 0; i < NumOfVertices; i++)
out << i+1 << " 0.0 " << vertices[i](0) << ' ' << vertices[i](1)
<< ' ' << vertices[i](2) << " 0.0\n";
for (i = 0; i < NumOfElements; i++)
{
nv = elements[i]->GetNVertices();
ind = elements[i]->GetVertices();
out << i+1 << ' ' << elements[i]->GetAttribute();
for (j = 0; j < nv; j++)
{
out << ' ' << ind[j]+1;
}
out << '\n';
}
for (i = 0; i < NumOfBdrElements; i++)
{
nv = boundary[i]->GetNVertices();
ind = boundary[i]->GetVertices();
out << boundary[i]->GetAttribute();
for (j = 0; j < nv; j++)
{
out << ' ' << ind[j]+1;
}
out << " 1.0 1.0 1.0 1.0\n";
}
}
}
out << flush;
}
void Mesh::Printer(std::ostream &out, std::string section_delimiter) const
{
int i, j;
if (NURBSext)
{
// general format
NURBSext->Print(out);
out << '\n';
Nodes->Save(out);
// patch-wise format
// NURBSext->ConvertToPatches(*Nodes);
// NURBSext->Print(out);
return;
}
out << (ncmesh ? "MFEM mesh v1.1\n" :
section_delimiter.empty() ? "MFEM mesh v1.0\n" :
"MFEM mesh v1.2\n");
// optional
out <<
"\n#\n# MFEM Geometry Types (see mesh/geom.hpp):\n#\n"
"# POINT = 0\n"
"# SEGMENT = 1\n"
"# TRIANGLE = 2\n"
"# SQUARE = 3\n"
"# TETRAHEDRON = 4\n"
"# CUBE = 5\n"
"# PRISM = 6\n"
"#\n";
out << "\ndimension\n" << Dim
<< "\n\nelements\n" << NumOfElements << '\n';
for (i = 0; i < NumOfElements; i++)
{
PrintElement(elements[i], out);
}
out << "\nboundary\n" << NumOfBdrElements << '\n';
for (i = 0; i < NumOfBdrElements; i++)
{
PrintElement(boundary[i], out);
}
if (ncmesh)
{
out << "\nvertex_parents\n";
ncmesh->PrintVertexParents(out);
out << "\ncoarse_elements\n";
ncmesh->PrintCoarseElements(out);
}
out << "\nvertices\n" << NumOfVertices << '\n';
if (Nodes == NULL)
{
out << spaceDim << '\n';
for (i = 0; i < NumOfVertices; i++)
{
out << vertices[i](0);
for (j = 1; j < spaceDim; j++)
{
out << ' ' << vertices[i](j);
}
out << '\n';
}
out.flush();
}
else
{
out << "\nnodes\n";
Nodes->Save(out);
}
if (!ncmesh && !section_delimiter.empty())
{
out << section_delimiter << endl; // only with format v1.2
}
}
void Mesh::PrintTopo(std::ostream &out,const Array<int> &e_to_k) const
{
int i;
Array<int> vert;
out << "MFEM NURBS mesh v1.0\n";
// optional
out <<
"\n#\n# MFEM Geometry Types (see mesh/geom.hpp):\n#\n"
"# SEGMENT = 1\n"
"# SQUARE = 3\n"
"# CUBE = 5\n"
"#\n";
out << "\ndimension\n" << Dim
<< "\n\nelements\n" << NumOfElements << '\n';
for (i = 0; i < NumOfElements; i++)
{
PrintElement(elements[i], out);
}
out << "\nboundary\n" << NumOfBdrElements << '\n';
for (i = 0; i < NumOfBdrElements; i++)
{
PrintElement(boundary[i], out);
}
out << "\nedges\n" << NumOfEdges << '\n';
for (i = 0; i < NumOfEdges; i++)
{
edge_vertex->GetRow(i, vert);
int ki = e_to_k[i];
if (ki < 0)
{
ki = -1 - ki;
}
out << ki << ' ' << vert[0] << ' ' << vert[1] << '\n';
}
out << "\nvertices\n" << NumOfVertices << '\n';
}
#ifdef MFEM_USE_ADIOS2
void Mesh::Print(adios2stream &out) const
{
out.Print(*this);
}
#endif
void Mesh::PrintVTK(std::ostream &out)
{
out <<
"# vtk DataFile Version 3.0\n"
"Generated by MFEM\n"
"ASCII\n"
"DATASET UNSTRUCTURED_GRID\n";
if (Nodes == NULL)
{
out << "POINTS " << NumOfVertices << " double\n";
for (int i = 0; i < NumOfVertices; i++)
{
out << vertices[i](0);
int j;
for (j = 1; j < spaceDim; j++)
{
out << ' ' << vertices[i](j);
}
for ( ; j < 3; j++)
{
out << ' ' << 0.0;
}
out << '\n';
}
}
else
{
Array<int> vdofs(3);
out << "POINTS " << Nodes->FESpace()->GetNDofs() << " double\n";
for (int i = 0; i < Nodes->FESpace()->GetNDofs(); i++)
{
vdofs.SetSize(1);
vdofs[0] = i;
Nodes->FESpace()->DofsToVDofs(vdofs);
out << (*Nodes)(vdofs[0]);
int j;
for (j = 1; j < spaceDim; j++)
{
out << ' ' << (*Nodes)(vdofs[j]);
}
for ( ; j < 3; j++)
{
out << ' ' << 0.0;
}
out << '\n';
}
}
int order = -1;
if (Nodes == NULL)
{
int size = 0;
for (int i = 0; i < NumOfElements; i++)
{
size += elements[i]->GetNVertices() + 1;
}
out << "CELLS " << NumOfElements << ' ' << size << '\n';
for (int i = 0; i < NumOfElements; i++)
{
const int *v = elements[i]->GetVertices();
const int nv = elements[i]->GetNVertices();
out << nv;
for (int j = 0; j < nv; j++)
{
out << ' ' << v[j];
}
out << '\n';
}
order = 1;
}
else
{
Array<int> dofs;
int size = 0;
for (int i = 0; i < NumOfElements; i++)
{
Nodes->FESpace()->GetElementDofs(i, dofs);
MFEM_ASSERT(Dim != 0 || dofs.Size() == 1,
"Point meshes should have a single dof per element");
size += dofs.Size() + 1;
}
out << "CELLS " << NumOfElements << ' ' << size << '\n';
const char *fec_name = Nodes->FESpace()->FEColl()->Name();
if (!strcmp(fec_name, "Linear") ||
!strcmp(fec_name, "H1_0D_P1") ||
!strcmp(fec_name, "H1_1D_P1") ||
!strcmp(fec_name, "H1_2D_P1") ||
!strcmp(fec_name, "H1_3D_P1"))
{
order = 1;
}
else if (!strcmp(fec_name, "Quadratic") ||
!strcmp(fec_name, "H1_1D_P2") ||
!strcmp(fec_name, "H1_2D_P2") ||
!strcmp(fec_name, "H1_3D_P2"))
{
order = 2;
}
if (order == -1)
{
mfem::err << "Mesh::PrintVTK : can not save '"
<< fec_name << "' elements!" << endl;
mfem_error();
}
for (int i = 0; i < NumOfElements; i++)
{
Nodes->FESpace()->GetElementDofs(i, dofs);
out << dofs.Size();
if (order == 1)
{
for (int j = 0; j < dofs.Size(); j++)
{
out << ' ' << dofs[j];
}
}
else if (order == 2)
{
const int *vtk_mfem;
switch (elements[i]->GetGeometryType())
{
case Geometry::SEGMENT:
case Geometry::TRIANGLE:
case Geometry::SQUARE:
vtk_mfem = vtk_quadratic_hex; break; // identity map
case Geometry::TETRAHEDRON:
vtk_mfem = vtk_quadratic_tet; break;
case Geometry::PRISM:
vtk_mfem = vtk_quadratic_wedge; break;
case Geometry::CUBE:
default:
vtk_mfem = vtk_quadratic_hex; break;
}
for (int j = 0; j < dofs.Size(); j++)
{
out << ' ' << dofs[vtk_mfem[j]];
}
}
out << '\n';
}
}
out << "CELL_TYPES " << NumOfElements << '\n';
for (int i = 0; i < NumOfElements; i++)
{
int vtk_cell_type = 5;
Geometry::Type geom_type = GetElement(i)->GetGeometryType();
if (order == 1)
{
switch (geom_type)
{
case Geometry::POINT: vtk_cell_type = 1; break;
case Geometry::SEGMENT: vtk_cell_type = 3; break;
case Geometry::TRIANGLE: vtk_cell_type = 5; break;
case Geometry::SQUARE: vtk_cell_type = 9; break;
case Geometry::TETRAHEDRON: vtk_cell_type = 10; break;
case Geometry::CUBE: vtk_cell_type = 12; break;
case Geometry::PRISM: vtk_cell_type = 13; break;
default: break;
}
}
else if (order == 2)
{
switch (geom_type)
{
case Geometry::SEGMENT: vtk_cell_type = 21; break;
case Geometry::TRIANGLE: vtk_cell_type = 22; break;
case Geometry::SQUARE: vtk_cell_type = 28; break;
case Geometry::TETRAHEDRON: vtk_cell_type = 24; break;
case Geometry::CUBE: vtk_cell_type = 29; break;
case Geometry::PRISM: vtk_cell_type = 32; break;
default: break;
}
}
out << vtk_cell_type << '\n';
}
// write attributes
out << "CELL_DATA " << NumOfElements << '\n'
<< "SCALARS material int\n"
<< "LOOKUP_TABLE default\n";
for (int i = 0; i < NumOfElements; i++)
{
out << elements[i]->GetAttribute() << '\n';
}
out.flush();
}
void Mesh::PrintVTU(std::string fname,
VTKFormat format,
bool high_order_output,
int compression_level)
{
int ref = (high_order_output && Nodes) ? Nodes->FESpace()->GetOrder(0) : 1;
fname = fname + ".vtu";
std::fstream out(fname.c_str(),std::ios::out);
out << "<VTKFile type=\"UnstructuredGrid\" version=\"0.1\"";
if (compression_level != 0)
{
out << " compressor=\"vtkZLibDataCompressor\"";
}
out << " byte_order=\"" << VTKByteOrder() << "\">\n";
out << "<UnstructuredGrid>\n";
PrintVTU(out, ref, format, high_order_output, compression_level);
out << "</Piece>\n"; // need to close the piece open in the PrintVTU method
out << "</UnstructuredGrid>\n";
out << "</VTKFile>" << std::endl;
out.close();
}
template <typename T>
void WriteBinaryOrASCII(std::ostream &out, std::vector<char> &buf, const T &val,
const char *suffix, VTKFormat format)
{
if (format == VTKFormat::ASCII) { out << val << suffix; }
else { bin_io::AppendBytes(buf, val); }
}
// Ensure ASCII output of uint8_t to stream is integer rather than character
template <>
void WriteBinaryOrASCII<uint8_t>(std::ostream &out, std::vector<char> &buf,
const uint8_t &val, const char *suffix,
VTKFormat format)
{
if (format == VTKFormat::ASCII) { out << static_cast<int>(val) << suffix; }
else { bin_io::AppendBytes(buf, val); }
}
template <>
void WriteBinaryOrASCII<double>(std::ostream &out, std::vector<char> &buf,
const double &val, const char *suffix,
VTKFormat format)
{
if (format == VTKFormat::BINARY32)
{
bin_io::AppendBytes<float>(buf, float(val));
}
else if (format == VTKFormat::BINARY)
{
bin_io::AppendBytes(buf, val);
}
else
{
out << val << suffix;
}
}
template <>
void WriteBinaryOrASCII<float>(std::ostream &out, std::vector<char> &buf,
const float &val, const char *suffix,
VTKFormat format)
{
if (format == VTKFormat::BINARY) { bin_io::AppendBytes<double>(buf, val); }
else if (format == VTKFormat::BINARY32) { bin_io::AppendBytes(buf, val); }
else { out << val << suffix; }
}
void WriteBase64WithSizeAndClear(std::ostream &out, std::vector<char> &buf,
int compression_level)
{
WriteVTKEncodedCompressed(out, buf.data(), buf.size(), compression_level);
out << '\n';
buf.clear();
}
void Mesh::PrintVTU(std::ostream &out, int ref, VTKFormat format,
bool high_order_output, int compression_level)
{
RefinedGeometry *RefG;
DenseMatrix pmat;
const char *fmt_str = (format == VTKFormat::ASCII) ? "ascii" : "binary";
const char *type_str = (format != VTKFormat::BINARY32) ? "Float64" : "Float32";
std::vector<char> buf;
// count the points, cells, size
int np = 0, nc_ref = 0, size = 0;
for (int i = 0; i < GetNE(); i++)
{
Geometry::Type geom = GetElementBaseGeometry(i);
int nv = Geometries.GetVertices(geom)->GetNPoints();
RefG = GlobGeometryRefiner.Refine(geom, ref, 1);
np += RefG->RefPts.GetNPoints();
nc_ref += RefG->RefGeoms.Size() / nv;
size += (RefG->RefGeoms.Size() / nv) * (nv + 1);
}
out << "<Piece NumberOfPoints=\"" << np << "\" NumberOfCells=\""
<< (high_order_output ? GetNE() : nc_ref) << "\">\n";
// print out the points
out << "<Points>\n";
out << "<DataArray type=\"" << type_str
<< "\" NumberOfComponents=\"3\" format=\"" << fmt_str << "\">\n";
for (int i = 0; i < GetNE(); i++)
{
RefG = GlobGeometryRefiner.Refine(
GetElementBaseGeometry(i), ref, 1);
GetElementTransformation(i)->Transform(RefG->RefPts, pmat);
for (int j = 0; j < pmat.Width(); j++)
{
WriteBinaryOrASCII(out, buf, pmat(0,j), " ", format);
if (pmat.Height() > 1)
{
WriteBinaryOrASCII(out, buf, pmat(1,j), " ", format);
}
else
{
WriteBinaryOrASCII(out, buf, 0.0, " ", format);
}
if (pmat.Height() > 2)
{
WriteBinaryOrASCII(out, buf, pmat(2,j), "", format);
}
else
{
WriteBinaryOrASCII(out, buf, 0.0, "", format);
}
if (format == VTKFormat::ASCII) { out << '\n'; }
}
}
if (format != VTKFormat::ASCII)
{
WriteBase64WithSizeAndClear(out, buf, compression_level);
}
out << "</DataArray>" << std::endl;
out << "</Points>" << std::endl;
out << "<Cells>" << std::endl;
out << "<DataArray type=\"Int32\" Name=\"connectivity\" format=\""
<< fmt_str << "\">" << std::endl;
// connectivity
std::vector<int> offset;
np = 0;
if (high_order_output)
{
Array<int> local_connectivity;
for (int iel = 0; iel < GetNE(); iel++)
{
Geometry::Type geom = GetElementBaseGeometry(iel);
CreateVTKElementConnectivity(local_connectivity, geom, ref);
int nnodes = local_connectivity.Size();
for (int i=0; i<nnodes; ++i)
{
WriteBinaryOrASCII(out, buf, np+local_connectivity[i], " ", format);
}
if (format == VTKFormat::ASCII) { out << '\n'; }
np += nnodes;
offset.push_back(np);
}
}
else
{
int coff = 0;
for (int i = 0; i < GetNE(); i++)
{
Geometry::Type geom = GetElementBaseGeometry(i);
int nv = Geometries.GetVertices(geom)->GetNPoints();
RefG = GlobGeometryRefiner.Refine(geom, ref, 1);
Array<int> &RG = RefG->RefGeoms;
for (int j = 0; j < RG.Size(); )
{
// out << nv;
coff = coff+nv;
offset.push_back(coff);
for (int k = 0; k < nv; k++, j++)
{
WriteBinaryOrASCII(out, buf, np + RG[j], " ", format);
}
if (format == VTKFormat::ASCII) { out << '\n'; }
}
np += RefG->RefPts.GetNPoints();
}
}
if (format != VTKFormat::ASCII)
{
WriteBase64WithSizeAndClear(out, buf, compression_level);
}
out << "</DataArray>" << std::endl;
out << "<DataArray type=\"Int32\" Name=\"offsets\" format=\""
<< fmt_str << "\">" << std::endl;
// offsets
for (size_t ii=0; ii<offset.size(); ii++)
{
WriteBinaryOrASCII(out, buf, offset[ii], "\n", format);
}
if (format != VTKFormat::ASCII)
{
WriteBase64WithSizeAndClear(out, buf, compression_level);
}
out << "</DataArray>" << std::endl;
out << "<DataArray type=\"UInt8\" Name=\"types\" format=\""
<< fmt_str << "\">" << std::endl;
// cell types
for (int i = 0; i < GetNE(); i++)
{
Geometry::Type geom = GetElementBaseGeometry(i);
uint8_t vtk_cell_type = 5;
// VTK element types defined at: https://git.io/JvZLm
switch (geom)
{
case Geometry::POINT:
vtk_cell_type = 1;
break;
case Geometry::SEGMENT:
vtk_cell_type = high_order_output ? 68 : 3;
break;
case Geometry::TRIANGLE:
vtk_cell_type = high_order_output ? 69 : 5;
break;
case Geometry::SQUARE:
vtk_cell_type = high_order_output ? 70 : 9;
break;
case Geometry::TETRAHEDRON:
vtk_cell_type = high_order_output ? 71 : 10;
break;
case Geometry::CUBE:
vtk_cell_type = high_order_output ? 72 : 12;
break;
case Geometry::PRISM:
vtk_cell_type = high_order_output ? 73 : 13;
break;
default:
MFEM_ABORT("Unrecognized VTK element type \"" << geom << "\"");
break;
}
if (high_order_output)
{
WriteBinaryOrASCII(out, buf, vtk_cell_type, "\n", format);
}
else
{
int nv = Geometries.GetVertices(geom)->GetNPoints();
RefG = GlobGeometryRefiner.Refine(geom, ref, 1);
Array<int> &RG = RefG->RefGeoms;
for (int j = 0; j < RG.Size(); j += nv)
{
WriteBinaryOrASCII(out, buf, vtk_cell_type, "\n", format);
}
}
}
if (format != VTKFormat::ASCII)
{
WriteBase64WithSizeAndClear(out, buf, compression_level);
}
out << "</DataArray>" << std::endl;
out << "</Cells>" << std::endl;
out << "<CellData Scalars=\"material\">" << std::endl;
out << "<DataArray type=\"Int32\" Name=\"material\" format=\""
<< fmt_str << "\">" << std::endl;
for (int i = 0; i < GetNE(); i++)
{
int attr = GetAttribute(i);
if (high_order_output)
{
WriteBinaryOrASCII(out, buf, attr, "\n", format);
}
else
{
Geometry::Type geom = GetElementBaseGeometry(i);
int nv = Geometries.GetVertices(geom)->GetNPoints();
RefG = GlobGeometryRefiner.Refine(geom, ref, 1);
for (int j = 0; j < RefG->RefGeoms.Size(); j += nv)
{
WriteBinaryOrASCII(out, buf, attr, "\n", format);
}
}
}
if (format != VTKFormat::ASCII)
{
WriteBase64WithSizeAndClear(out, buf, compression_level);
}
out << "</DataArray>" << std::endl;
out << "</CellData>" << std::endl;
}
void Mesh::PrintVTK(std::ostream &out, int ref, int field_data)
{
int np, nc, size;
RefinedGeometry *RefG;
DenseMatrix pmat;
out <<
"# vtk DataFile Version 3.0\n"
"Generated by MFEM\n"
"ASCII\n"
"DATASET UNSTRUCTURED_GRID\n";
// additional dataset information
if (field_data)
{
out << "FIELD FieldData 1\n"
<< "MaterialIds " << 1 << " " << attributes.Size() << " int\n";
for (int i = 0; i < attributes.Size(); i++)
{
out << ' ' << attributes[i];
}
out << '\n';
}
// count the points, cells, size
np = nc = size = 0;
for (int i = 0; i < GetNE(); i++)
{
Geometry::Type geom = GetElementBaseGeometry(i);
int nv = Geometries.GetVertices(geom)->GetNPoints();
RefG = GlobGeometryRefiner.Refine(geom, ref, 1);
np += RefG->RefPts.GetNPoints();
nc += RefG->RefGeoms.Size() / nv;
size += (RefG->RefGeoms.Size() / nv) * (nv + 1);
}
out << "POINTS " << np << " double\n";
// write the points
for (int i = 0; i < GetNE(); i++)
{
RefG = GlobGeometryRefiner.Refine(
GetElementBaseGeometry(i), ref, 1);
GetElementTransformation(i)->Transform(RefG->RefPts, pmat);
for (int j = 0; j < pmat.Width(); j++)
{
out << pmat(0, j) << ' ';
if (pmat.Height() > 1)
{
out << pmat(1, j) << ' ';
if (pmat.Height() > 2)
{
out << pmat(2, j);
}
else
{
out << 0.0;
}
}
else
{
out << 0.0 << ' ' << 0.0;
}
out << '\n';
}
}
// write the cells
out << "CELLS " << nc << ' ' << size << '\n';
np = 0;
for (int i = 0; i < GetNE(); i++)
{
Geometry::Type geom = GetElementBaseGeometry(i);
int nv = Geometries.GetVertices(geom)->GetNPoints();
RefG = GlobGeometryRefiner.Refine(geom, ref, 1);
Array<int> &RG = RefG->RefGeoms;
for (int j = 0; j < RG.Size(); )
{
out << nv;
for (int k = 0; k < nv; k++, j++)
{
out << ' ' << np + RG[j];
}
out << '\n';
}
np += RefG->RefPts.GetNPoints();
}
out << "CELL_TYPES " << nc << '\n';
for (int i = 0; i < GetNE(); i++)
{
Geometry::Type geom = GetElementBaseGeometry(i);
int nv = Geometries.GetVertices(geom)->GetNPoints();
RefG = GlobGeometryRefiner.Refine(geom, ref, 1);
Array<int> &RG = RefG->RefGeoms;
int vtk_cell_type = 5;
switch (geom)
{
case Geometry::POINT: vtk_cell_type = 1; break;
case Geometry::SEGMENT: vtk_cell_type = 3; break;
case Geometry::TRIANGLE: vtk_cell_type = 5; break;
case Geometry::SQUARE: vtk_cell_type = 9; break;
case Geometry::TETRAHEDRON: vtk_cell_type = 10; break;
case Geometry::CUBE: vtk_cell_type = 12; break;
case Geometry::PRISM: vtk_cell_type = 13; break;
default:
MFEM_ABORT("Unrecognized VTK element type \"" << geom << "\"");
break;
}
for (int j = 0; j < RG.Size(); j += nv)
{
out << vtk_cell_type << '\n';
}
}
// write attributes (materials)
out << "CELL_DATA " << nc << '\n'
<< "SCALARS material int\n"
<< "LOOKUP_TABLE default\n";
for (int i = 0; i < GetNE(); i++)
{
Geometry::Type geom = GetElementBaseGeometry(i);
int nv = Geometries.GetVertices(geom)->GetNPoints();
RefG = GlobGeometryRefiner.Refine(geom, ref, 1);
int attr = GetAttribute(i);
for (int j = 0; j < RefG->RefGeoms.Size(); j += nv)
{
out << attr << '\n';
}
}
if (Dim > 1)
{
Array<int> coloring;
srand((unsigned)time(0));
double a = double(rand()) / (double(RAND_MAX) + 1.);
int el0 = (int)floor(a * GetNE());
GetElementColoring(coloring, el0);
out << "SCALARS element_coloring int\n"
<< "LOOKUP_TABLE default\n";
for (int i = 0; i < GetNE(); i++)
{
Geometry::Type geom = GetElementBaseGeometry(i);
int nv = Geometries.GetVertices(geom)->GetNPoints();
RefG = GlobGeometryRefiner.Refine(geom, ref, 1);
for (int j = 0; j < RefG->RefGeoms.Size(); j += nv)
{
out << coloring[i] + 1 << '\n';
}
}
}
// prepare to write data
out << "POINT_DATA " << np << '\n' << flush;
}
void Mesh::GetElementColoring(Array<int> &colors, int el0)
{
int delete_el_to_el = (el_to_el) ? (0) : (1);
const Table &el_el = ElementToElementTable();
int num_el = GetNE(), stack_p, stack_top_p, max_num_col;
Array<int> el_stack(num_el);
const int *i_el_el = el_el.GetI();
const int *j_el_el = el_el.GetJ();
colors.SetSize(num_el);
colors = -2;
max_num_col = 1;
stack_p = stack_top_p = 0;
for (int el = el0; stack_top_p < num_el; el=(el+1)%num_el)
{
if (colors[el] != -2)
{
continue;
}
colors[el] = -1;
el_stack[stack_top_p++] = el;
for ( ; stack_p < stack_top_p; stack_p++)
{
int i = el_stack[stack_p];
int num_nb = i_el_el[i+1] - i_el_el[i];
if (max_num_col < num_nb + 1)
{
max_num_col = num_nb + 1;
}
for (int j = i_el_el[i]; j < i_el_el[i+1]; j++)
{
int k = j_el_el[j];
if (colors[k] == -2)
{
colors[k] = -1;
el_stack[stack_top_p++] = k;
}
}
}
}
Array<int> col_marker(max_num_col);
for (stack_p = 0; stack_p < stack_top_p; stack_p++)
{
int i = el_stack[stack_p], col;
col_marker = 0;
for (int j = i_el_el[i]; j < i_el_el[i+1]; j++)
{
col = colors[j_el_el[j]];
if (col != -1)
{
col_marker[col] = 1;
}
}
for (col = 0; col < max_num_col; col++)
if (col_marker[col] == 0)
{
break;
}
colors[i] = col;
}
if (delete_el_to_el)
{
delete el_to_el;
el_to_el = NULL;
}
}
void Mesh::PrintWithPartitioning(int *partitioning, std::ostream &out,
int elem_attr) const
{
if (Dim != 3 && Dim != 2) { return; }
int i, j, k, l, nv, nbe, *v;
out << "MFEM mesh v1.0\n";
// optional
out <<
"\n#\n# MFEM Geometry Types (see mesh/geom.hpp):\n#\n"
"# POINT = 0\n"
"# SEGMENT = 1\n"
"# TRIANGLE = 2\n"
"# SQUARE = 3\n"
"# TETRAHEDRON = 4\n"
"# CUBE = 5\n"
"# PRISM = 6\n"
"#\n";
out << "\ndimension\n" << Dim
<< "\n\nelements\n" << NumOfElements << '\n';
for (i = 0; i < NumOfElements; i++)
{
out << int((elem_attr) ? partitioning[i]+1 : elements[i]->GetAttribute())
<< ' ' << elements[i]->GetGeometryType();
nv = elements[i]->GetNVertices();
v = elements[i]->GetVertices();
for (j = 0; j < nv; j++)
{
out << ' ' << v[j];
}
out << '\n';
}
nbe = 0;
for (i = 0; i < faces_info.Size(); i++)
{
if ((l = faces_info[i].Elem2No) >= 0)
{
k = partitioning[faces_info[i].Elem1No];
l = partitioning[l];
if (k != l)
{
nbe++;
if (!Nonconforming() || !IsSlaveFace(faces_info[i]))
{
nbe++;
}
}
}
else
{
nbe++;
}
}
out << "\nboundary\n" << nbe << '\n';
for (i = 0; i < faces_info.Size(); i++)
{
if ((l = faces_info[i].Elem2No) >= 0)
{
k = partitioning[faces_info[i].Elem1No];
l = partitioning[l];
if (k != l)
{
nv = faces[i]->GetNVertices();
v = faces[i]->GetVertices();
out << k+1 << ' ' << faces[i]->GetGeometryType();
for (j = 0; j < nv; j++)
{
out << ' ' << v[j];
}
out << '\n';
if (!Nonconforming() || !IsSlaveFace(faces_info[i]))
{
out << l+1 << ' ' << faces[i]->GetGeometryType();
for (j = nv-1; j >= 0; j--)
{
out << ' ' << v[j];
}
out << '\n';
}
}
}
else
{
k = partitioning[faces_info[i].Elem1No];
nv = faces[i]->GetNVertices();
v = faces[i]->GetVertices();
out << k+1 << ' ' << faces[i]->GetGeometryType();
for (j = 0; j < nv; j++)
{
out << ' ' << v[j];
}
out << '\n';
}
}
out << "\nvertices\n" << NumOfVertices << '\n';
if (Nodes == NULL)
{
out << spaceDim << '\n';
for (i = 0; i < NumOfVertices; i++)
{
out << vertices[i](0);
for (j = 1; j < spaceDim; j++)
{
out << ' ' << vertices[i](j);
}
out << '\n';
}
out.flush();
}
else
{
out << "\nnodes\n";
Nodes->Save(out);
}
}
void Mesh::PrintElementsWithPartitioning(int *partitioning,
std::ostream &out,
int interior_faces)
{
MFEM_ASSERT(Dim == spaceDim, "2D Manifolds not supported\n");
if (Dim != 3 && Dim != 2) { return; }
int i, j, k, l, s;
int nv;
const int *ind;
int *vcount = new int[NumOfVertices];
for (i = 0; i < NumOfVertices; i++)
{
vcount[i] = 0;
}
for (i = 0; i < NumOfElements; i++)
{
nv = elements[i]->GetNVertices();
ind = elements[i]->GetVertices();
for (j = 0; j < nv; j++)
{
vcount[ind[j]]++;
}
}
int *voff = new int[NumOfVertices+1];
voff[0] = 0;
for (i = 1; i <= NumOfVertices; i++)
{
voff[i] = vcount[i-1] + voff[i-1];
}
int **vown = new int*[NumOfVertices];
for (i = 0; i < NumOfVertices; i++)
{
vown[i] = new int[vcount[i]];
}
// 2D
if (Dim == 2)
{
int nv, nbe;
int *ind;
Table edge_el;
Transpose(ElementToEdgeTable(), edge_el);
// Fake printing of the elements.
for (i = 0; i < NumOfElements; i++)
{
nv = elements[i]->GetNVertices();
ind = elements[i]->GetVertices();
for (j = 0; j < nv; j++)
{
vcount[ind[j]]--;
vown[ind[j]][vcount[ind[j]]] = i;
}
}
for (i = 0; i < NumOfVertices; i++)
{
vcount[i] = voff[i+1] - voff[i];
}
nbe = 0;
for (i = 0; i < edge_el.Size(); i++)
{
const int *el = edge_el.GetRow(i);
if (edge_el.RowSize(i) > 1)
{
k = partitioning[el[0]];
l = partitioning[el[1]];
if (interior_faces || k != l)
{
nbe += 2;
}
}
else
{
nbe++;
}
}
// Print the type of the mesh and the boundary elements.
out << "areamesh2\n\n" << nbe << '\n';
for (i = 0; i < edge_el.Size(); i++)
{
const int *el = edge_el.GetRow(i);
if (edge_el.RowSize(i) > 1)
{
k = partitioning[el[0]];
l = partitioning[el[1]];
if (interior_faces || k != l)
{
Array<int> ev;
GetEdgeVertices(i,ev);
out << k+1; // attribute
for (j = 0; j < 2; j++)
for (s = 0; s < vcount[ev[j]]; s++)
if (vown[ev[j]][s] == el[0])
{
out << ' ' << voff[ev[j]]+s+1;
}
out << '\n';
out << l+1; // attribute
for (j = 1; j >= 0; j--)
for (s = 0; s < vcount[ev[j]]; s++)
if (vown[ev[j]][s] == el[1])
{
out << ' ' << voff[ev[j]]+s+1;
}
out << '\n';
}
}
else
{
k = partitioning[el[0]];
Array<int> ev;
GetEdgeVertices(i,ev);
out << k+1; // attribute
for (j = 0; j < 2; j++)
for (s = 0; s < vcount[ev[j]]; s++)
if (vown[ev[j]][s] == el[0])
{
out << ' ' << voff[ev[j]]+s+1;
}
out << '\n';
}
}
// Print the elements.
out << NumOfElements << '\n';
for (i = 0; i < NumOfElements; i++)
{
nv = elements[i]->GetNVertices();
ind = elements[i]->GetVertices();
out << partitioning[i]+1 << ' '; // use subdomain number as attribute
out << nv << ' ';
for (j = 0; j < nv; j++)
{
out << ' ' << voff[ind[j]]+vcount[ind[j]]--;
vown[ind[j]][vcount[ind[j]]] = i;
}
out << '\n';
}
for (i = 0; i < NumOfVertices; i++)
{
vcount[i] = voff[i+1] - voff[i];
}
// Print the vertices.
out << voff[NumOfVertices] << '\n';
for (i = 0; i < NumOfVertices; i++)
for (k = 0; k < vcount[i]; k++)
{
for (j = 0; j < Dim; j++)
{
out << vertices[i](j) << ' ';
}
out << '\n';
}
}
// Dim is 3
else if (meshgen == 1)
{
out << "NETGEN_Neutral_Format\n";
// print the vertices
out << voff[NumOfVertices] << '\n';
for (i = 0; i < NumOfVertices; i++)
for (k = 0; k < vcount[i]; k++)
{
for (j = 0; j < Dim; j++)
{
out << ' ' << vertices[i](j);
}
out << '\n';
}
// print the elements
out << NumOfElements << '\n';
for (i = 0; i < NumOfElements; i++)
{
nv = elements[i]->GetNVertices();
ind = elements[i]->GetVertices();
out << partitioning[i]+1; // use subdomain number as attribute
for (j = 0; j < nv; j++)
{
out << ' ' << voff[ind[j]]+vcount[ind[j]]--;
vown[ind[j]][vcount[ind[j]]] = i;
}
out << '\n';
}
for (i = 0; i < NumOfVertices; i++)
{
vcount[i] = voff[i+1] - voff[i];
}
// print the boundary information.
int k, l, nbe;
nbe = 0;
for (i = 0; i < NumOfFaces; i++)
if ((l = faces_info[i].Elem2No) >= 0)
{
k = partitioning[faces_info[i].Elem1No];
l = partitioning[l];
if (interior_faces || k != l)
{
nbe += 2;
}
}
else
{
nbe++;
}
out << nbe << '\n';
for (i = 0; i < NumOfFaces; i++)
if ((l = faces_info[i].Elem2No) >= 0)
{
k = partitioning[faces_info[i].Elem1No];
l = partitioning[l];
if (interior_faces || k != l)
{
nv = faces[i]->GetNVertices();
ind = faces[i]->GetVertices();
out << k+1; // attribute
for (j = 0; j < nv; j++)
for (s = 0; s < vcount[ind[j]]; s++)
if (vown[ind[j]][s] == faces_info[i].Elem1No)
{
out << ' ' << voff[ind[j]]+s+1;
}
out << '\n';
out << l+1; // attribute
for (j = nv-1; j >= 0; j--)
for (s = 0; s < vcount[ind[j]]; s++)
if (vown[ind[j]][s] == faces_info[i].Elem2No)
{
out << ' ' << voff[ind[j]]+s+1;
}
out << '\n';
}
}
else
{
k = partitioning[faces_info[i].Elem1No];
nv = faces[i]->GetNVertices();
ind = faces[i]->GetVertices();
out << k+1; // attribute
for (j = 0; j < nv; j++)
for (s = 0; s < vcount[ind[j]]; s++)
if (vown[ind[j]][s] == faces_info[i].Elem1No)
{
out << ' ' << voff[ind[j]]+s+1;
}
out << '\n';
}
}
// Dim is 3
else if (meshgen == 2) // TrueGrid
{
// count the number of the boundary elements.
int k, l, nbe;
nbe = 0;
for (i = 0; i < NumOfFaces; i++)
if ((l = faces_info[i].Elem2No) >= 0)
{
k = partitioning[faces_info[i].Elem1No];
l = partitioning[l];
if (interior_faces || k != l)
{
nbe += 2;
}
}
else
{
nbe++;
}
out << "TrueGrid\n"
<< "1 " << voff[NumOfVertices] << " " << NumOfElements
<< " 0 0 0 0 0 0 0\n"
<< "0 0 0 1 0 0 0 0 0 0 0\n"
<< "0 0 " << nbe << " 0 0 0 0 0 0 0 0 0 0 0 0 0\n"
<< "0.0 0.0 0.0 0 0 0.0 0.0 0 0.0\n"
<< "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n";
for (i = 0; i < NumOfVertices; i++)
for (k = 0; k < vcount[i]; k++)
out << voff[i]+k << " 0.0 " << vertices[i](0) << ' '
<< vertices[i](1) << ' ' << vertices[i](2) << " 0.0\n";
for (i = 0; i < NumOfElements; i++)
{
nv = elements[i]->GetNVertices();
ind = elements[i]->GetVertices();
out << i+1 << ' ' << partitioning[i]+1; // partitioning as attribute
for (j = 0; j < nv; j++)
{
out << ' ' << voff[ind[j]]+vcount[ind[j]]--;
vown[ind[j]][vcount[ind[j]]] = i;
}
out << '\n';
}
for (i = 0; i < NumOfVertices; i++)
{
vcount[i] = voff[i+1] - voff[i];
}
// boundary elements
for (i = 0; i < NumOfFaces; i++)
if ((l = faces_info[i].Elem2No) >= 0)
{
k = partitioning[faces_info[i].Elem1No];
l = partitioning[l];
if (interior_faces || k != l)
{
nv = faces[i]->GetNVertices();
ind = faces[i]->GetVertices();
out << k+1; // attribute
for (j = 0; j < nv; j++)
for (s = 0; s < vcount[ind[j]]; s++)
if (vown[ind[j]][s] == faces_info[i].Elem1No)
{
out << ' ' << voff[ind[j]]+s+1;
}
out << " 1.0 1.0 1.0 1.0\n";
out << l+1; // attribute
for (j = nv-1; j >= 0; j--)
for (s = 0; s < vcount[ind[j]]; s++)
if (vown[ind[j]][s] == faces_info[i].Elem2No)
{
out << ' ' << voff[ind[j]]+s+1;
}
out << " 1.0 1.0 1.0 1.0\n";
}
}
else
{
k = partitioning[faces_info[i].Elem1No];
nv = faces[i]->GetNVertices();
ind = faces[i]->GetVertices();
out << k+1; // attribute
for (j = 0; j < nv; j++)
for (s = 0; s < vcount[ind[j]]; s++)
if (vown[ind[j]][s] == faces_info[i].Elem1No)
{
out << ' ' << voff[ind[j]]+s+1;
}
out << " 1.0 1.0 1.0 1.0\n";
}
}
out << flush;
for (i = 0; i < NumOfVertices; i++)
{
delete [] vown[i];
}
delete [] vcount;
delete [] voff;
delete [] vown;
}
void Mesh::PrintSurfaces(const Table & Aface_face, std::ostream &out) const
{
int i, j;
if (NURBSext)
{
mfem_error("Mesh::PrintSurfaces"
" NURBS mesh is not supported!");
return;
}
out << "MFEM mesh v1.0\n";
// optional
out <<
"\n#\n# MFEM Geometry Types (see mesh/geom.hpp):\n#\n"
"# POINT = 0\n"
"# SEGMENT = 1\n"
"# TRIANGLE = 2\n"
"# SQUARE = 3\n"
"# TETRAHEDRON = 4\n"
"# CUBE = 5\n"
"# PRISM = 6\n"
"#\n";
out << "\ndimension\n" << Dim
<< "\n\nelements\n" << NumOfElements << '\n';
for (i = 0; i < NumOfElements; i++)
{
PrintElement(elements[i], out);
}
out << "\nboundary\n" << Aface_face.Size_of_connections() << '\n';
const int * const i_AF_f = Aface_face.GetI();
const int * const j_AF_f = Aface_face.GetJ();
for (int iAF=0; iAF < Aface_face.Size(); ++iAF)
for (const int * iface = j_AF_f + i_AF_f[iAF];
iface < j_AF_f + i_AF_f[iAF+1];
++iface)
{
out << iAF+1 << ' ';
PrintElementWithoutAttr(faces[*iface],out);
}
out << "\nvertices\n" << NumOfVertices << '\n';
if (Nodes == NULL)
{
out << spaceDim << '\n';
for (i = 0; i < NumOfVertices; i++)
{
out << vertices[i](0);
for (j = 1; j < spaceDim; j++)
{
out << ' ' << vertices[i](j);
}
out << '\n';
}
out.flush();
}
else
{
out << "\nnodes\n";
Nodes->Save(out);
}
}
void Mesh::ScaleSubdomains(double sf)
{
int i,j,k;
Array<int> vert;
DenseMatrix pointmat;
int na = attributes.Size();
double *cg = new double[na*spaceDim];
int *nbea = new int[na];
int *vn = new int[NumOfVertices];
for (i = 0; i < NumOfVertices; i++)
{
vn[i] = 0;
}
for (i = 0; i < na; i++)
{
for (j = 0; j < spaceDim; j++)
{
cg[i*spaceDim+j] = 0.0;
}
nbea[i] = 0;
}
for (i = 0; i < NumOfElements; i++)
{
GetElementVertices(i, vert);
for (k = 0; k < vert.Size(); k++)
{
vn[vert[k]] = 1;
}
}
for (i = 0; i < NumOfElements; i++)
{
int bea = GetAttribute(i)-1;
GetPointMatrix(i, pointmat);
GetElementVertices(i, vert);
for (k = 0; k < vert.Size(); k++)
if (vn[vert[k]] == 1)
{
nbea[bea]++;
for (j = 0; j < spaceDim; j++)
{
cg[bea*spaceDim+j] += pointmat(j,k);
}
vn[vert[k]] = 2;
}
}
for (i = 0; i < NumOfElements; i++)
{
int bea = GetAttribute(i)-1;
GetElementVertices (i, vert);
for (k = 0; k < vert.Size(); k++)
if (vn[vert[k]])
{
for (j = 0; j < spaceDim; j++)
vertices[vert[k]](j) = sf*vertices[vert[k]](j) +
(1-sf)*cg[bea*spaceDim+j]/nbea[bea];
vn[vert[k]] = 0;
}
}
delete [] cg;
delete [] nbea;
delete [] vn;
}
void Mesh::ScaleElements(double sf)
{
int i,j,k;
Array<int> vert;
DenseMatrix pointmat;
int na = NumOfElements;
double *cg = new double[na*spaceDim];
int *nbea = new int[na];
int *vn = new int[NumOfVertices];
for (i = 0; i < NumOfVertices; i++)
{
vn[i] = 0;
}
for (i = 0; i < na; i++)
{
for (j = 0; j < spaceDim; j++)
{
cg[i*spaceDim+j] = 0.0;
}
nbea[i] = 0;
}
for (i = 0; i < NumOfElements; i++)
{
GetElementVertices(i, vert);
for (k = 0; k < vert.Size(); k++)
{
vn[vert[k]] = 1;
}
}
for (i = 0; i < NumOfElements; i++)
{
int bea = i;
GetPointMatrix(i, pointmat);
GetElementVertices(i, vert);
for (k = 0; k < vert.Size(); k++)
if (vn[vert[k]] == 1)
{
nbea[bea]++;
for (j = 0; j < spaceDim; j++)
{
cg[bea*spaceDim+j] += pointmat(j,k);
}
vn[vert[k]] = 2;
}
}
for (i = 0; i < NumOfElements; i++)
{
int bea = i;
GetElementVertices(i, vert);
for (k = 0; k < vert.Size(); k++)
if (vn[vert[k]])
{
for (j = 0; j < spaceDim; j++)
vertices[vert[k]](j) = sf*vertices[vert[k]](j) +
(1-sf)*cg[bea*spaceDim+j]/nbea[bea];
vn[vert[k]] = 0;
}
}
delete [] cg;
delete [] nbea;
delete [] vn;
}
void Mesh::Transform(void (*f)(const Vector&, Vector&))
{
// TODO: support for different new spaceDim.
if (Nodes == NULL)
{
Vector vold(spaceDim), vnew(NULL, spaceDim);
for (int i = 0; i < vertices.Size(); i++)
{
for (int j = 0; j < spaceDim; j++)
{
vold(j) = vertices[i](j);
}
vnew.SetData(vertices[i]());
(*f)(vold, vnew);
}
}
else
{
GridFunction xnew(Nodes->FESpace());
VectorFunctionCoefficient f_pert(spaceDim, f);
xnew.ProjectCoefficient(f_pert);
*Nodes = xnew;
}
}
void Mesh::Transform(VectorCoefficient &deformation)
{
MFEM_VERIFY(spaceDim == deformation.GetVDim(),
"incompatible vector dimensions");
if (Nodes == NULL)
{
LinearFECollection fec;
FiniteElementSpace fes(this, &fec, spaceDim, Ordering::byVDIM);
GridFunction xnew(&fes);
xnew.ProjectCoefficient(deformation);
for (int i = 0; i < NumOfVertices; i++)
for (int d = 0; d < spaceDim; d++)
{
vertices[i](d) = xnew(d + spaceDim*i);
}
}
else
{
GridFunction xnew(Nodes->FESpace());
xnew.ProjectCoefficient(deformation);
*Nodes = xnew;
}
}
void Mesh::RemoveUnusedVertices()
{
if (NURBSext || ncmesh) { return; }
Array<int> v2v(GetNV());
v2v = -1;
for (int i = 0; i < GetNE(); i++)
{
Element *el = GetElement(i);
int nv = el->GetNVertices();
int *v = el->GetVertices();
for (int j = 0; j < nv; j++)
{
v2v[v[j]] = 0;
}
}
for (int i = 0; i < GetNBE(); i++)
{
Element *el = GetBdrElement(i);
int *v = el->GetVertices();
int nv = el->GetNVertices();
for (int j = 0; j < nv; j++)
{
v2v[v[j]] = 0;
}
}
int num_vert = 0;
for (int i = 0; i < v2v.Size(); i++)
{
if (v2v[i] == 0)
{
vertices[num_vert] = vertices[i];
v2v[i] = num_vert++;
}
}
if (num_vert == v2v.Size()) { return; }
Vector nodes_by_element;
Array<int> vdofs;
if (Nodes)
{
int s = 0;
for (int i = 0; i < GetNE(); i++)
{
Nodes->FESpace()->GetElementVDofs(i, vdofs);
s += vdofs.Size();
}
nodes_by_element.SetSize(s);
s = 0;
for (int i = 0; i < GetNE(); i++)
{
Nodes->FESpace()->GetElementVDofs(i, vdofs);
Nodes->GetSubVector(vdofs, &nodes_by_element(s));
s += vdofs.Size();
}
}
vertices.SetSize(num_vert);
NumOfVertices = num_vert;
for (int i = 0; i < GetNE(); i++)
{
Element *el = GetElement(i);
int *v = el->GetVertices();
int nv = el->GetNVertices();
for (int j = 0; j < nv; j++)
{
v[j] = v2v[v[j]];
}
}
for (int i = 0; i < GetNBE(); i++)
{
Element *el = GetBdrElement(i);
int *v = el->GetVertices();
int nv = el->GetNVertices();
for (int j = 0; j < nv; j++)
{
v[j] = v2v[v[j]];
}
}
DeleteTables();
if (Dim > 1)
{
// generate el_to_edge, be_to_edge (2D), bel_to_edge (3D)
el_to_edge = new Table;
NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
}
if (Dim > 2)
{
// generate el_to_face, be_to_face
GetElementToFaceTable();
}
// Update faces and faces_info
GenerateFaces();
if (Nodes)
{
Nodes->FESpace()->Update();
Nodes->Update();
int s = 0;
for (int i = 0; i < GetNE(); i++)
{
Nodes->FESpace()->GetElementVDofs(i, vdofs);
Nodes->SetSubVector(vdofs, &nodes_by_element(s));
s += vdofs.Size();
}
}
}
void Mesh::RemoveInternalBoundaries()
{
if (NURBSext || ncmesh) { return; }
int num_bdr_elem = 0;
int new_bel_to_edge_nnz = 0;
for (int i = 0; i < GetNBE(); i++)
{
if (FaceIsInterior(GetBdrElementEdgeIndex(i)))
{
FreeElement(boundary[i]);
}
else
{
num_bdr_elem++;
if (Dim == 3)
{
new_bel_to_edge_nnz += bel_to_edge->RowSize(i);
}
}
}
if (num_bdr_elem == GetNBE()) { return; }
Array<Element *> new_boundary(num_bdr_elem);
Array<int> new_be_to_edge, new_be_to_face;
Table *new_bel_to_edge = NULL;
new_boundary.SetSize(0);
if (Dim == 2)
{
new_be_to_edge.Reserve(num_bdr_elem);
}
else if (Dim == 3)
{
new_be_to_face.Reserve(num_bdr_elem);
new_bel_to_edge = new Table;
new_bel_to_edge->SetDims(num_bdr_elem, new_bel_to_edge_nnz);
}
for (int i = 0; i < GetNBE(); i++)
{
if (!FaceIsInterior(GetBdrElementEdgeIndex(i)))
{
new_boundary.Append(boundary[i]);
if (Dim == 2)
{
new_be_to_edge.Append(be_to_edge[i]);
}
else if (Dim == 3)
{
int row = new_be_to_face.Size();
new_be_to_face.Append(be_to_face[i]);
int *e = bel_to_edge->GetRow(i);
int ne = bel_to_edge->RowSize(i);
int *new_e = new_bel_to_edge->GetRow(row);
for (int j = 0; j < ne; j++)
{
new_e[j] = e[j];
}
new_bel_to_edge->GetI()[row+1] = new_bel_to_edge->GetI()[row] + ne;
}
}
}
NumOfBdrElements = new_boundary.Size();
mfem::Swap(boundary, new_boundary);
if (Dim == 2)
{
mfem::Swap(be_to_edge, new_be_to_edge);
}
else if (Dim == 3)
{
mfem::Swap(be_to_face, new_be_to_face);
delete bel_to_edge;
bel_to_edge = new_bel_to_edge;
}
Array<int> attribs(num_bdr_elem);
for (int i = 0; i < attribs.Size(); i++)
{
attribs[i] = GetBdrAttribute(i);
}
attribs.Sort();
attribs.Unique();
bdr_attributes.DeleteAll();
attribs.Copy(bdr_attributes);
}
void Mesh::FreeElement(Element *E)
{
#ifdef MFEM_USE_MEMALLOC
if (E)
{
if (E->GetType() == Element::TETRAHEDRON)
{
TetMemory.Free((Tetrahedron*) E);
}
else
{
delete E;
}
}
#else
delete E;
#endif
}
std::ostream &operator<<(std::ostream &out, const Mesh &mesh)
{
mesh.Print(out);
return out;
}
int Mesh::FindPoints(DenseMatrix &point_mat, Array<int>& elem_ids,
Array<IntegrationPoint>& ips, bool warn,
InverseElementTransformation *inv_trans)
{
const int npts = point_mat.Width();
if (!npts) { return 0; }
MFEM_VERIFY(point_mat.Height() == spaceDim,"Invalid points matrix");
elem_ids.SetSize(npts);
ips.SetSize(npts);
elem_ids = -1;
if (!GetNE()) { return 0; }
double *data = point_mat.GetData();
InverseElementTransformation *inv_tr = inv_trans;
inv_tr = inv_tr ? inv_tr : new InverseElementTransformation;
// For each point in 'point_mat', find the element whose center is closest.
Vector min_dist(npts);
Array<int> e_idx(npts);
min_dist = std::numeric_limits<double>::max();
e_idx = -1;
Vector pt(spaceDim);
for (int i = 0; i < GetNE(); i++)
{
GetElementTransformation(i)->Transform(
Geometries.GetCenter(GetElementBaseGeometry(i)), pt);
for (int k = 0; k < npts; k++)
{
double dist = pt.DistanceTo(data+k*spaceDim);
if (dist < min_dist(k))
{
min_dist(k) = dist;
e_idx[k] = i;
}
}
}
// Checks if the points lie in the closest element
int pts_found = 0;
pt.NewDataAndSize(NULL, spaceDim);
for (int k = 0; k < npts; k++)
{
pt.SetData(data+k*spaceDim);
inv_tr->SetTransformation(*GetElementTransformation(e_idx[k]));
int res = inv_tr->Transform(pt, ips[k]);
if (res == InverseElementTransformation::Inside)
{
elem_ids[k] = e_idx[k];
pts_found++;
}
}
if (pts_found != npts)
{
Array<int> vertices;
Table *vtoel = GetVertexToElementTable();
for (int k = 0; k < npts; k++)
{
if (elem_ids[k] != -1) { continue; }
// Try all vertex-neighbors of element e_idx[k]
pt.SetData(data+k*spaceDim);
GetElementVertices(e_idx[k], vertices);
for (int v = 0; v < vertices.Size(); v++)
{
int vv = vertices[v];
int ne = vtoel->RowSize(vv);
const int* els = vtoel->GetRow(vv);
for (int e = 0; e < ne; e++)
{
if (els[e] == e_idx[k]) { continue; }
inv_tr->SetTransformation(*GetElementTransformation(els[e]));
int res = inv_tr->Transform(pt, ips[k]);
if (res == InverseElementTransformation::Inside)
{
elem_ids[k] = els[e];
pts_found++;
goto next_point;
}
}
}
// Try neighbors for non-conforming meshes
if (ncmesh)
{
Array<int> neigh;
int le = ncmesh->leaf_elements[e_idx[k]];
ncmesh->FindNeighbors(le,neigh);
for (int e = 0; e < neigh.Size(); e++)
{
int nn = neigh[e];
if (ncmesh->IsGhost(ncmesh->elements[nn])) { continue; }
int el = ncmesh->elements[nn].index;
inv_tr->SetTransformation(*GetElementTransformation(el));
int res = inv_tr->Transform(pt, ips[k]);
if (res == InverseElementTransformation::Inside)
{
elem_ids[k] = el;
pts_found++;
goto next_point;
}
}
}
next_point: ;
}
delete vtoel;
}
if (inv_trans == NULL) { delete inv_tr; }
if (warn && pts_found != npts)
{
MFEM_WARNING((npts-pts_found) << " points were not found");
}
return pts_found;
}
GeometricFactors::GeometricFactors(const Mesh *mesh, const IntegrationRule &ir,
int flags)
{
this->mesh = mesh;
IntRule = &ir;
computed_factors = flags;
const GridFunction *nodes = mesh->GetNodes();
const FiniteElementSpace *fespace = nodes->FESpace();
const FiniteElement *fe = fespace->GetFE(0);
const int dim = fe->GetDim();
const int vdim = fespace->GetVDim();
const int NE = fespace->GetNE();
const int ND = fe->GetDof();
const int NQ = ir.GetNPoints();
// For now, we are not using tensor product evaluation
const Operator *elem_restr = fespace->GetElementRestriction(
ElementDofOrdering::NATIVE);
unsigned eval_flags = 0;
if (flags & GeometricFactors::COORDINATES)
{
X.SetSize(vdim*NQ*NE);
eval_flags |= QuadratureInterpolator::VALUES;
}
if (flags & GeometricFactors::JACOBIANS)
{
J.SetSize(dim*vdim*NQ*NE);
eval_flags |= QuadratureInterpolator::DERIVATIVES;
}
if (flags & GeometricFactors::DETERMINANTS)
{
detJ.SetSize(NQ*NE);
eval_flags |= QuadratureInterpolator::DETERMINANTS;
}
const QuadratureInterpolator *qi = fespace->GetQuadratureInterpolator(ir);
// For now, we are not using tensor product evaluation (not implemented)
qi->DisableTensorProducts();
qi->SetOutputLayout(QVectorLayout::byNODES);
if (elem_restr)
{
Vector Enodes(vdim*ND*NE);
elem_restr->Mult(*nodes, Enodes);
qi->Mult(Enodes, eval_flags, X, J, detJ);
}
else
{
qi->Mult(*nodes, eval_flags, X, J, detJ);
}
}
FaceGeometricFactors::FaceGeometricFactors(const Mesh *mesh,
const IntegrationRule &ir,
int flags, FaceType type)
: type(type)
{
this->mesh = mesh;
IntRule = &ir;
computed_factors = flags;
const GridFunction *nodes = mesh->GetNodes();
const FiniteElementSpace *fespace = nodes->FESpace();
const int vdim = fespace->GetVDim();
const int NF = fespace->GetNFbyType(type);
const int NQ = ir.GetNPoints();
const Operator *face_restr = fespace->GetFaceRestriction(
ElementDofOrdering::LEXICOGRAPHIC,
type,
L2FaceValues::SingleValued );
Vector Fnodes(face_restr->Height());
face_restr->Mult(*nodes, Fnodes);
unsigned eval_flags = 0;
if (flags & FaceGeometricFactors::COORDINATES)
{
X.SetSize(vdim*NQ*NF);
eval_flags |= FaceQuadratureInterpolator::VALUES;
}
if (flags & FaceGeometricFactors::JACOBIANS)
{
J.SetSize(vdim*vdim*NQ*NF);
eval_flags |= FaceQuadratureInterpolator::DERIVATIVES;
}
if (flags & FaceGeometricFactors::DETERMINANTS)
{
detJ.SetSize(NQ*NF);
eval_flags |= FaceQuadratureInterpolator::DETERMINANTS;
}
if (flags & FaceGeometricFactors::NORMALS)
{
normal.SetSize(vdim*NQ*NF);
eval_flags |= FaceQuadratureInterpolator::NORMALS;
}
const FaceQuadratureInterpolator *qi = fespace->GetFaceQuadratureInterpolator(
ir, type);
qi->Mult(Fnodes, eval_flags, X, J, detJ, normal);
}
NodeExtrudeCoefficient::NodeExtrudeCoefficient(const int dim, const int _n,
const double _s)
: VectorCoefficient(dim), n(_n), s(_s), tip(p, dim-1)
{
}
void NodeExtrudeCoefficient::Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip)
{
V.SetSize(vdim);
T.Transform(ip, tip);
V(0) = p[0];
if (vdim == 2)
{
V(1) = s * ((ip.y + layer) / n);
}
else
{
V(1) = p[1];
V(2) = s * ((ip.z + layer) / n);
}
}
Mesh *Extrude1D(Mesh *mesh, const int ny, const double sy, const bool closed)
{
if (mesh->Dimension() != 1)
{
mfem::err << "Extrude1D : Not a 1D mesh!" << endl;
mfem_error();
}
int nvy = (closed) ? (ny) : (ny + 1);
int nvt = mesh->GetNV() * nvy;
Mesh *mesh2d;
if (closed)
{
mesh2d = new Mesh(2, nvt, mesh->GetNE()*ny, mesh->GetNBE()*ny);
}
else
mesh2d = new Mesh(2, nvt, mesh->GetNE()*ny,
mesh->GetNBE()*ny+2*mesh->GetNE());
// vertices
double vc[2];
for (int i = 0; i < mesh->GetNV(); i++)
{
vc[0] = mesh->GetVertex(i)[0];
for (int j = 0; j < nvy; j++)
{
vc[1] = sy * (double(j) / ny);
mesh2d->AddVertex(vc);
}
}
// elements
Array<int> vert;
for (int i = 0; i < mesh->GetNE(); i++)
{
const Element *elem = mesh->GetElement(i);
elem->GetVertices(vert);
const int attr = elem->GetAttribute();
for (int j = 0; j < ny; j++)
{
int qv[4];
qv[0] = vert[0] * nvy + j;
qv[1] = vert[1] * nvy + j;
qv[2] = vert[1] * nvy + (j + 1) % nvy;
qv[3] = vert[0] * nvy + (j + 1) % nvy;
mesh2d->AddQuad(qv, attr);
}
}
// 2D boundary from the 1D boundary
for (int i = 0; i < mesh->GetNBE(); i++)
{
const Element *elem = mesh->GetBdrElement(i);
elem->GetVertices(vert);
const int attr = elem->GetAttribute();
for (int j = 0; j < ny; j++)
{
int sv[2];
sv[0] = vert[0] * nvy + j;
sv[1] = vert[0] * nvy + (j + 1) % nvy;
if (attr%2)
{
Swap<int>(sv[0], sv[1]);
}
mesh2d->AddBdrSegment(sv, attr);
}
}
if (!closed)
{
// 2D boundary from the 1D elements (bottom + top)
int nba = (mesh->bdr_attributes.Size() > 0 ?
mesh->bdr_attributes.Max() : 0);
for (int i = 0; i < mesh->GetNE(); i++)
{
const Element *elem = mesh->GetElement(i);
elem->GetVertices(vert);
const int attr = nba + elem->GetAttribute();
int sv[2];
sv[0] = vert[0] * nvy;
sv[1] = vert[1] * nvy;
mesh2d->AddBdrSegment(sv, attr);
sv[0] = vert[1] * nvy + ny;
sv[1] = vert[0] * nvy + ny;
mesh2d->AddBdrSegment(sv, attr);
}
}
mesh2d->FinalizeQuadMesh(1, 0, false);
GridFunction *nodes = mesh->GetNodes();
if (nodes)
{
// duplicate the fec of the 1D mesh so that it can be deleted safely
// along with its nodes, fes and fec
FiniteElementCollection *fec2d = NULL;
FiniteElementSpace *fes2d;
const char *name = nodes->FESpace()->FEColl()->Name();
string cname = name;
if (cname == "Linear")
{
fec2d = new LinearFECollection;
}
else if (cname == "Quadratic")
{
fec2d = new QuadraticFECollection;
}
else if (cname == "Cubic")
{
fec2d = new CubicFECollection;
}
else if (!strncmp(name, "H1_", 3))
{
fec2d = new H1_FECollection(atoi(name + 7), 2);
}
else if (!strncmp(name, "L2_T", 4))
{
fec2d = new L2_FECollection(atoi(name + 10), 2, atoi(name + 4));
}
else if (!strncmp(name, "L2_", 3))
{
fec2d = new L2_FECollection(atoi(name + 7), 2);
}
else
{
delete mesh2d;
mfem::err << "Extrude1D : The mesh uses unknown FE collection : "
<< cname << endl;
mfem_error();
}
fes2d = new FiniteElementSpace(mesh2d, fec2d, 2);
mesh2d->SetNodalFESpace(fes2d);
GridFunction *nodes2d = mesh2d->GetNodes();
nodes2d->MakeOwner(fec2d);
NodeExtrudeCoefficient ecoeff(2, ny, sy);
Vector lnodes;
Array<int> vdofs2d;
for (int i = 0; i < mesh->GetNE(); i++)
{
ElementTransformation &T = *mesh->GetElementTransformation(i);
for (int j = ny-1; j >= 0; j--)
{
fes2d->GetElementVDofs(i*ny+j, vdofs2d);
lnodes.SetSize(vdofs2d.Size());
ecoeff.SetLayer(j);
fes2d->GetFE(i*ny+j)->Project(ecoeff, T, lnodes);
nodes2d->SetSubVector(vdofs2d, lnodes);
}
}
}
return mesh2d;
}
Mesh *Extrude2D(Mesh *mesh, const int nz, const double sz)
{
if (mesh->Dimension() != 2)
{
mfem::err << "Extrude2D : Not a 2D mesh!" << endl;
mfem_error();
}
int nvz = nz + 1;
int nvt = mesh->GetNV() * nvz;
Mesh *mesh3d = new Mesh(3, nvt, mesh->GetNE()*nz,
mesh->GetNBE()*nz+2*mesh->GetNE());
bool wdgMesh = false;
bool hexMesh = false;
// vertices
double vc[3];
for (int i = 0; i < mesh->GetNV(); i++)
{
vc[0] = mesh->GetVertex(i)[0];
vc[1] = mesh->GetVertex(i)[1];
for (int j = 0; j < nvz; j++)
{
vc[2] = sz * (double(j) / nz);
mesh3d->AddVertex(vc);
}
}
// elements
Array<int> vert;
for (int i = 0; i < mesh->GetNE(); i++)
{
const Element *elem = mesh->GetElement(i);
elem->GetVertices(vert);
const int attr = elem->GetAttribute();
Geometry::Type geom = elem->GetGeometryType();
switch (geom)
{
case Geometry::TRIANGLE:
wdgMesh = true;
for (int j = 0; j < nz; j++)
{
int pv[6];
pv[0] = vert[0] * nvz + j;
pv[1] = vert[1] * nvz + j;
pv[2] = vert[2] * nvz + j;
pv[3] = vert[0] * nvz + (j + 1) % nvz;
pv[4] = vert[1] * nvz + (j + 1) % nvz;
pv[5] = vert[2] * nvz + (j + 1) % nvz;
mesh3d->AddWedge(pv, attr);
}
break;
case Geometry::SQUARE:
hexMesh = true;
for (int j = 0; j < nz; j++)
{
int hv[8];
hv[0] = vert[0] * nvz + j;
hv[1] = vert[1] * nvz + j;
hv[2] = vert[2] * nvz + j;
hv[3] = vert[3] * nvz + j;
hv[4] = vert[0] * nvz + (j + 1) % nvz;
hv[5] = vert[1] * nvz + (j + 1) % nvz;
hv[6] = vert[2] * nvz + (j + 1) % nvz;
hv[7] = vert[3] * nvz + (j + 1) % nvz;
mesh3d->AddHex(hv, attr);
}
break;
default:
mfem::err << "Extrude2D : Invalid 2D element type \'"
<< geom << "\'" << endl;
mfem_error();
break;
}
}
// 3D boundary from the 2D boundary
for (int i = 0; i < mesh->GetNBE(); i++)
{
const Element *elem = mesh->GetBdrElement(i);
elem->GetVertices(vert);
const int attr = elem->GetAttribute();
for (int j = 0; j < nz; j++)
{
int qv[4];
qv[0] = vert[0] * nvz + j;
qv[1] = vert[1] * nvz + j;
qv[2] = vert[1] * nvz + (j + 1) % nvz;
qv[3] = vert[0] * nvz + (j + 1) % nvz;
mesh3d->AddBdrQuad(qv, attr);
}
}
// 3D boundary from the 2D elements (bottom + top)
int nba = (mesh->bdr_attributes.Size() > 0 ?
mesh->bdr_attributes.Max() : 0);
for (int i = 0; i < mesh->GetNE(); i++)
{
const Element *elem = mesh->GetElement(i);
elem->GetVertices(vert);
const int attr = nba + elem->GetAttribute();
Geometry::Type geom = elem->GetGeometryType();
switch (geom)
{
case Geometry::TRIANGLE:
{
int tv[3];
tv[0] = vert[0] * nvz;
tv[1] = vert[2] * nvz;
tv[2] = vert[1] * nvz;
mesh3d->AddBdrTriangle(tv, attr);
tv[0] = vert[0] * nvz + nz;
tv[1] = vert[1] * nvz + nz;
tv[2] = vert[2] * nvz + nz;
mesh3d->AddBdrTriangle(tv, attr);
}
break;
case Geometry::SQUARE:
{
int qv[4];
qv[0] = vert[0] * nvz;
qv[1] = vert[3] * nvz;
qv[2] = vert[2] * nvz;
qv[3] = vert[1] * nvz;
mesh3d->AddBdrQuad(qv, attr);
qv[0] = vert[0] * nvz + nz;
qv[1] = vert[1] * nvz + nz;
qv[2] = vert[2] * nvz + nz;
qv[3] = vert[3] * nvz + nz;
mesh3d->AddBdrQuad(qv, attr);
}
break;
default:
mfem::err << "Extrude2D : Invalid 2D element type \'"
<< geom << "\'" << endl;
mfem_error();
break;
}
}
if ( hexMesh && wdgMesh )
{
mesh3d->FinalizeMesh(0, false);
}
else if ( hexMesh )
{
mesh3d->FinalizeHexMesh(1, 0, false);
}
else if ( wdgMesh )
{
mesh3d->FinalizeWedgeMesh(1, 0, false);
}
GridFunction *nodes = mesh->GetNodes();
if (nodes)
{
// duplicate the fec of the 2D mesh so that it can be deleted safely
// along with its nodes, fes and fec
FiniteElementCollection *fec3d = NULL;
FiniteElementSpace *fes3d;
const char *name = nodes->FESpace()->FEColl()->Name();
string cname = name;
if (cname == "Linear")
{
fec3d = new LinearFECollection;
}
else if (cname == "Quadratic")
{
fec3d = new QuadraticFECollection;
}
else if (cname == "Cubic")
{
fec3d = new CubicFECollection;
}
else if (!strncmp(name, "H1_", 3))
{
fec3d = new H1_FECollection(atoi(name + 7), 3);
}
else if (!strncmp(name, "L2_T", 4))
{
fec3d = new L2_FECollection(atoi(name + 10), 3, atoi(name + 4));
}
else if (!strncmp(name, "L2_", 3))
{
fec3d = new L2_FECollection(atoi(name + 7), 3);
}
else
{
delete mesh3d;
mfem::err << "Extrude3D : The mesh uses unknown FE collection : "
<< cname << endl;
mfem_error();
}
fes3d = new FiniteElementSpace(mesh3d, fec3d, 3);
mesh3d->SetNodalFESpace(fes3d);
GridFunction *nodes3d = mesh3d->GetNodes();
nodes3d->MakeOwner(fec3d);
NodeExtrudeCoefficient ecoeff(3, nz, sz);
Vector lnodes;
Array<int> vdofs3d;
for (int i = 0; i < mesh->GetNE(); i++)
{
ElementTransformation &T = *mesh->GetElementTransformation(i);
for (int j = nz-1; j >= 0; j--)
{
fes3d->GetElementVDofs(i*nz+j, vdofs3d);
lnodes.SetSize(vdofs3d.Size());
ecoeff.SetLayer(j);
fes3d->GetFE(i*nz+j)->Project(ecoeff, T, lnodes);
nodes3d->SetSubVector(vdofs3d, lnodes);
}
}
}
return mesh3d;
}
#ifdef MFEM_DEBUG
void Mesh::DebugDump(std::ostream &out) const
{
// dump vertices and edges (NCMesh "nodes")
out << NumOfVertices + NumOfEdges << "\n";
for (int i = 0; i < NumOfVertices; i++)
{
const double *v = GetVertex(i);
out << i << " " << v[0] << " " << v[1] << " " << v[2]
<< " 0 0 " << i << " -1 0\n";
}
Array<int> ev;
for (int i = 0; i < NumOfEdges; i++)
{
GetEdgeVertices(i, ev);
double mid[3] = {0, 0, 0};
for (int j = 0; j < 2; j++)
{
for (int k = 0; k < spaceDim; k++)
{
mid[k] += GetVertex(ev[j])[k];
}
}
out << NumOfVertices+i << " "
<< mid[0]/2 << " " << mid[1]/2 << " " << mid[2]/2 << " "
<< ev[0] << " " << ev[1] << " -1 " << i << " 0\n";
}
// dump elements
out << NumOfElements << "\n";
for (int i = 0; i < NumOfElements; i++)
{
const Element* e = elements[i];
out << e->GetNVertices() << " ";
for (int j = 0; j < e->GetNVertices(); j++)
{
out << e->GetVertices()[j] << " ";
}
out << e->GetAttribute() << " 0 " << i << "\n";
}
// dump faces
out << "0\n";
}
#endif
}