// Copyright (c) 2010-2023, 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. #include "fem.hpp" #include "../mesh/wedge.hpp" #include "../mesh/pyramid.hpp" namespace mfem { const char *Geometry::Name[NumGeom] = { "Point", "Segment", "Triangle", "Square", "Tetrahedron", "Cube", "Prism", "Pyramid" }; const double Geometry::Volume[NumGeom] = { 1.0, 1.0, 0.5, 1.0, 1./6, 1.0, 0.5, 1./3 }; Geometry::Geometry() { // Vertices for Geometry::POINT GeomVert[0] = new IntegrationRule(1); GeomVert[0]->IntPoint(0).x = 0.0; // Vertices for Geometry::SEGMENT GeomVert[1] = new IntegrationRule(2); GeomVert[1]->IntPoint(0).x = 0.0; GeomVert[1]->IntPoint(1).x = 1.0; // Vertices for Geometry::TRIANGLE GeomVert[2] = new IntegrationRule(3); GeomVert[2]->IntPoint(0).x = 0.0; GeomVert[2]->IntPoint(0).y = 0.0; GeomVert[2]->IntPoint(1).x = 1.0; GeomVert[2]->IntPoint(1).y = 0.0; GeomVert[2]->IntPoint(2).x = 0.0; GeomVert[2]->IntPoint(2).y = 1.0; // Vertices for Geometry::SQUARE GeomVert[3] = new IntegrationRule(4); GeomVert[3]->IntPoint(0).x = 0.0; GeomVert[3]->IntPoint(0).y = 0.0; GeomVert[3]->IntPoint(1).x = 1.0; GeomVert[3]->IntPoint(1).y = 0.0; GeomVert[3]->IntPoint(2).x = 1.0; GeomVert[3]->IntPoint(2).y = 1.0; GeomVert[3]->IntPoint(3).x = 0.0; GeomVert[3]->IntPoint(3).y = 1.0; // Vertices for Geometry::TETRAHEDRON GeomVert[4] = new IntegrationRule(4); GeomVert[4]->IntPoint(0).x = 0.0; GeomVert[4]->IntPoint(0).y = 0.0; GeomVert[4]->IntPoint(0).z = 0.0; GeomVert[4]->IntPoint(1).x = 1.0; GeomVert[4]->IntPoint(1).y = 0.0; GeomVert[4]->IntPoint(1).z = 0.0; GeomVert[4]->IntPoint(2).x = 0.0; GeomVert[4]->IntPoint(2).y = 1.0; GeomVert[4]->IntPoint(2).z = 0.0; GeomVert[4]->IntPoint(3).x = 0.0; GeomVert[4]->IntPoint(3).y = 0.0; GeomVert[4]->IntPoint(3).z = 1.0; // Vertices for Geometry::CUBE GeomVert[5] = new IntegrationRule(8); GeomVert[5]->IntPoint(0).x = 0.0; GeomVert[5]->IntPoint(0).y = 0.0; GeomVert[5]->IntPoint(0).z = 0.0; GeomVert[5]->IntPoint(1).x = 1.0; GeomVert[5]->IntPoint(1).y = 0.0; GeomVert[5]->IntPoint(1).z = 0.0; GeomVert[5]->IntPoint(2).x = 1.0; GeomVert[5]->IntPoint(2).y = 1.0; GeomVert[5]->IntPoint(2).z = 0.0; GeomVert[5]->IntPoint(3).x = 0.0; GeomVert[5]->IntPoint(3).y = 1.0; GeomVert[5]->IntPoint(3).z = 0.0; GeomVert[5]->IntPoint(4).x = 0.0; GeomVert[5]->IntPoint(4).y = 0.0; GeomVert[5]->IntPoint(4).z = 1.0; GeomVert[5]->IntPoint(5).x = 1.0; GeomVert[5]->IntPoint(5).y = 0.0; GeomVert[5]->IntPoint(5).z = 1.0; GeomVert[5]->IntPoint(6).x = 1.0; GeomVert[5]->IntPoint(6).y = 1.0; GeomVert[5]->IntPoint(6).z = 1.0; GeomVert[5]->IntPoint(7).x = 0.0; GeomVert[5]->IntPoint(7).y = 1.0; GeomVert[5]->IntPoint(7).z = 1.0; // Vertices for Geometry::PRISM GeomVert[6] = new IntegrationRule(6); GeomVert[6]->IntPoint(0).x = 0.0; GeomVert[6]->IntPoint(0).y = 0.0; GeomVert[6]->IntPoint(0).z = 0.0; GeomVert[6]->IntPoint(1).x = 1.0; GeomVert[6]->IntPoint(1).y = 0.0; GeomVert[6]->IntPoint(1).z = 0.0; GeomVert[6]->IntPoint(2).x = 0.0; GeomVert[6]->IntPoint(2).y = 1.0; GeomVert[6]->IntPoint(2).z = 0.0; GeomVert[6]->IntPoint(3).x = 0.0; GeomVert[6]->IntPoint(3).y = 0.0; GeomVert[6]->IntPoint(3).z = 1.0; GeomVert[6]->IntPoint(4).x = 1.0; GeomVert[6]->IntPoint(4).y = 0.0; GeomVert[6]->IntPoint(4).z = 1.0; GeomVert[6]->IntPoint(5).x = 0.0; GeomVert[6]->IntPoint(5).y = 1.0; GeomVert[6]->IntPoint(5).z = 1.0; // Vertices for Geometry::PYRAMID GeomVert[7] = new IntegrationRule(5); GeomVert[7]->IntPoint(0).x = 0.0; GeomVert[7]->IntPoint(0).y = 0.0; GeomVert[7]->IntPoint(0).z = 0.0; GeomVert[7]->IntPoint(1).x = 1.0; GeomVert[7]->IntPoint(1).y = 0.0; GeomVert[7]->IntPoint(1).z = 0.0; GeomVert[7]->IntPoint(2).x = 1.0; GeomVert[7]->IntPoint(2).y = 1.0; GeomVert[7]->IntPoint(2).z = 0.0; GeomVert[7]->IntPoint(3).x = 0.0; GeomVert[7]->IntPoint(3).y = 1.0; GeomVert[7]->IntPoint(3).z = 0.0; GeomVert[7]->IntPoint(4).x = 0.0; GeomVert[7]->IntPoint(4).y = 0.0; GeomVert[7]->IntPoint(4).z = 1.0; GeomCenter[POINT].x = 0.0; GeomCenter[POINT].y = 0.0; GeomCenter[POINT].z = 0.0; GeomCenter[SEGMENT].x = 0.5; GeomCenter[SEGMENT].y = 0.0; GeomCenter[SEGMENT].z = 0.0; GeomCenter[TRIANGLE].x = 1.0 / 3.0; GeomCenter[TRIANGLE].y = 1.0 / 3.0; GeomCenter[TRIANGLE].z = 0.0; GeomCenter[SQUARE].x = 0.5; GeomCenter[SQUARE].y = 0.5; GeomCenter[SQUARE].z = 0.0; GeomCenter[TETRAHEDRON].x = 0.25; GeomCenter[TETRAHEDRON].y = 0.25; GeomCenter[TETRAHEDRON].z = 0.25; GeomCenter[CUBE].x = 0.5; GeomCenter[CUBE].y = 0.5; GeomCenter[CUBE].z = 0.5; GeomCenter[PRISM].x = 1.0 / 3.0; GeomCenter[PRISM].y = 1.0 / 3.0; GeomCenter[PRISM].z = 0.5; GeomCenter[PYRAMID].x = 0.375; GeomCenter[PYRAMID].y = 0.375; GeomCenter[PYRAMID].z = 0.25; GeomToPerfGeomJac[POINT] = NULL; GeomToPerfGeomJac[SEGMENT] = new DenseMatrix(1); GeomToPerfGeomJac[TRIANGLE] = new DenseMatrix(2); GeomToPerfGeomJac[SQUARE] = new DenseMatrix(2); GeomToPerfGeomJac[TETRAHEDRON] = new DenseMatrix(3); GeomToPerfGeomJac[CUBE] = new DenseMatrix(3); GeomToPerfGeomJac[PRISM] = new DenseMatrix(3); GeomToPerfGeomJac[PYRAMID] = new DenseMatrix(3); PerfGeomToGeomJac[POINT] = NULL; PerfGeomToGeomJac[SEGMENT] = NULL; PerfGeomToGeomJac[TRIANGLE] = new DenseMatrix(2); PerfGeomToGeomJac[SQUARE] = NULL; PerfGeomToGeomJac[TETRAHEDRON] = new DenseMatrix(3); PerfGeomToGeomJac[CUBE] = NULL; PerfGeomToGeomJac[PRISM] = new DenseMatrix(3); PerfGeomToGeomJac[PYRAMID] = new DenseMatrix(3); GeomToPerfGeomJac[SEGMENT]->Diag(1.0, 1); { IsoparametricTransformation tri_T; tri_T.SetFE(&TriangleFE); GetPerfPointMat (TRIANGLE, tri_T.GetPointMat()); tri_T.SetIntPoint(&GeomCenter[TRIANGLE]); *GeomToPerfGeomJac[TRIANGLE] = tri_T.Jacobian(); CalcInverse(tri_T.Jacobian(), *PerfGeomToGeomJac[TRIANGLE]); } GeomToPerfGeomJac[SQUARE]->Diag(1.0, 2); { IsoparametricTransformation tet_T; tet_T.SetFE(&TetrahedronFE); GetPerfPointMat (TETRAHEDRON, tet_T.GetPointMat()); tet_T.SetIntPoint(&GeomCenter[TETRAHEDRON]); *GeomToPerfGeomJac[TETRAHEDRON] = tet_T.Jacobian(); CalcInverse(tet_T.Jacobian(), *PerfGeomToGeomJac[TETRAHEDRON]); } GeomToPerfGeomJac[CUBE]->Diag(1.0, 3); { IsoparametricTransformation pri_T; pri_T.SetFE(&WedgeFE); GetPerfPointMat (PRISM, pri_T.GetPointMat()); pri_T.SetIntPoint(&GeomCenter[PRISM]); *GeomToPerfGeomJac[PRISM] = pri_T.Jacobian(); CalcInverse(pri_T.Jacobian(), *PerfGeomToGeomJac[PRISM]); } { IsoparametricTransformation pyr_T; pyr_T.SetFE(&PyramidFE); GetPerfPointMat (PYRAMID, pyr_T.GetPointMat()); pyr_T.SetIntPoint(&GeomCenter[PYRAMID]); *GeomToPerfGeomJac[PYRAMID] = pyr_T.Jacobian(); CalcInverse(pyr_T.Jacobian(), *PerfGeomToGeomJac[PYRAMID]); } } Geometry::~Geometry() { for (int i = 0; i < NumGeom; i++) { delete PerfGeomToGeomJac[i]; delete GeomToPerfGeomJac[i]; delete GeomVert[i]; } } const IntegrationRule * Geometry::GetVertices(int GeomType) { switch (GeomType) { case Geometry::POINT: return GeomVert[0]; case Geometry::SEGMENT: return GeomVert[1]; case Geometry::TRIANGLE: return GeomVert[2]; case Geometry::SQUARE: return GeomVert[3]; case Geometry::TETRAHEDRON: return GeomVert[4]; case Geometry::CUBE: return GeomVert[5]; case Geometry::PRISM: return GeomVert[6]; case Geometry::PYRAMID: return GeomVert[7]; default: mfem_error ("Geometry::GetVertices(...)"); } // make some compilers happy. return GeomVert[0]; } // static method void Geometry::GetRandomPoint(int GeomType, IntegrationPoint &ip) { switch (GeomType) { case Geometry::POINT: ip.x = 0.0; break; case Geometry::SEGMENT: ip.x = double(rand()) / RAND_MAX; break; case Geometry::TRIANGLE: ip.x = double(rand()) / RAND_MAX; ip.y = double(rand()) / RAND_MAX; if (ip.x + ip.y > 1.0) { ip.x = 1.0 - ip.x; ip.y = 1.0 - ip.y; } break; case Geometry::SQUARE: ip.x = double(rand()) / RAND_MAX; ip.y = double(rand()) / RAND_MAX; break; case Geometry::TETRAHEDRON: ip.x = double(rand()) / RAND_MAX; ip.y = double(rand()) / RAND_MAX; ip.z = double(rand()) / RAND_MAX; // map to the triangular prism obtained by extruding the reference // triangle in z direction if (ip.x + ip.y > 1.0) { ip.x = 1.0 - ip.x; ip.y = 1.0 - ip.y; } // split the prism into 3 parts: 1 is the reference tet, and the // other two tets (as given below) are mapped to the reference tet if (ip.x + ip.z > 1.0) { // tet with vertices: (0,0,1),(1,0,1),(0,1,1),(1,0,0) ip.x = ip.x + ip.z - 1.0; // ip.y = ip.y; ip.z = 1.0 - ip.z; // mapped to: (0,0,0),(1,0,0),(0,1,0),(0,0,1) } else if (ip.x + ip.y + ip.z > 1.0) { // tet with vertices: (0,1,1),(0,1,0),(0,0,1),(1,0,0) double x = ip.x; ip.x = 1.0 - x - ip.z; ip.y = 1.0 - x - ip.y; ip.z = x; // mapped to: (0,0,0),(1,0,0),(0,1,0),(0,0,1) } break; case Geometry::CUBE: ip.x = double(rand()) / RAND_MAX; ip.y = double(rand()) / RAND_MAX; ip.z = double(rand()) / RAND_MAX; break; case Geometry::PRISM: ip.x = double(rand()) / RAND_MAX; ip.y = double(rand()) / RAND_MAX; ip.z = double(rand()) / RAND_MAX; if (ip.x + ip.y > 1.0) { ip.x = 1.0 - ip.x; ip.y = 1.0 - ip.y; } break; case Geometry::PYRAMID: ip.x = double(rand()) / RAND_MAX; ip.y = double(rand()) / RAND_MAX; ip.z = double(rand()) / RAND_MAX; if (ip.x + ip.z > 1.0 && ip.y < ip.x) { double x = ip.x; ip.x = ip.y; ip.y = 1.0 - ip.z; ip.z = 1.0 - x; } else if (ip.y + ip.z > 1.0) { double z = ip.z; ip.z = 1.0 - ip.y; ip.y = ip.x; ip.x = 1.0 - z; } break; default: MFEM_ABORT("Unknown type of reference element!"); } } namespace internal { // Fuzzy equality operator with absolute tolerance eps. inline bool NearlyEqual(double x, double y, double eps) { return std::abs(x-y) <= eps; } // Fuzzy greater than comparison operator with absolute tolerance eps. // Returns true when x is greater than y by at least eps. inline bool FuzzyGT(double x, double y, double eps) { return (x > y + eps); } // Fuzzy less than comparison operator with absolute tolerance eps. // Returns true when x is less than y by at least eps. inline bool FuzzyLT(double x, double y, double eps) { return (x < y - eps); } } // static method bool Geometry::CheckPoint(int GeomType, const IntegrationPoint &ip) { switch (GeomType) { case Geometry::POINT: if (ip.x != 0.0) { return false; } break; case Geometry::SEGMENT: if (ip.x < 0.0 || ip.x > 1.0) { return false; } break; case Geometry::TRIANGLE: if (ip.x < 0.0 || ip.y < 0.0 || ip.x+ip.y > 1.0) { return false; } break; case Geometry::SQUARE: if (ip.x < 0.0 || ip.x > 1.0 || ip.y < 0.0 || ip.y > 1.0) { return false; } break; case Geometry::TETRAHEDRON: if (ip.x < 0.0 || ip.y < 0.0 || ip.z < 0.0 || ip.x+ip.y+ip.z > 1.0) { return false; } break; case Geometry::CUBE: if (ip.x < 0.0 || ip.x > 1.0 || ip.y < 0.0 || ip.y > 1.0 || ip.z < 0.0 || ip.z > 1.0) { return false; } break; case Geometry::PRISM: if (ip.x < 0.0 || ip.y < 0.0 || ip.x+ip.y > 1.0 || ip.z < 0.0 || ip.z > 1.0) { return false; } break; case Geometry::PYRAMID: if (ip.x < 0.0 || ip.y < 0.0 || ip.x+ip.z > 1.0 || ip.y+ip.z > 1.0 || ip.z < 0.0 || ip.z > 1.0) { return false; } break; default: MFEM_ABORT("Unknown type of reference element!"); } return true; } bool Geometry::CheckPoint(int GeomType, const IntegrationPoint &ip, double eps) { switch (GeomType) { case Geometry::POINT: if (! internal::NearlyEqual(ip.x, 0.0, eps)) { return false; } break; case Geometry::SEGMENT: if ( internal::FuzzyLT(ip.x, 0.0, eps) || internal::FuzzyGT(ip.x, 1.0, eps) ) { return false; } break; case Geometry::TRIANGLE: if ( internal::FuzzyLT(ip.x, 0.0, eps) || internal::FuzzyLT(ip.y, 0.0, eps) || internal::FuzzyGT(ip.x+ip.y, 1.0, eps) ) { return false; } break; case Geometry::SQUARE: if ( internal::FuzzyLT(ip.x, 0.0, eps) || internal::FuzzyGT(ip.x, 1.0, eps) || internal::FuzzyLT(ip.y, 0.0, eps) || internal::FuzzyGT(ip.y, 1.0, eps) ) { return false; } break; case Geometry::TETRAHEDRON: if ( internal::FuzzyLT(ip.x, 0.0, eps) || internal::FuzzyLT(ip.y, 0.0, eps) || internal::FuzzyLT(ip.z, 0.0, eps) || internal::FuzzyGT(ip.x+ip.y+ip.z, 1.0, eps) ) { return false; } break; case Geometry::CUBE: if ( internal::FuzzyLT(ip.x, 0.0, eps) || internal::FuzzyGT(ip.x, 1.0, eps) || internal::FuzzyLT(ip.y, 0.0, eps) || internal::FuzzyGT(ip.y, 1.0, eps) || internal::FuzzyLT(ip.z, 0.0, eps) || internal::FuzzyGT(ip.z, 1.0, eps) ) { return false; } break; case Geometry::PRISM: if ( internal::FuzzyLT(ip.x, 0.0, eps) || internal::FuzzyLT(ip.y, 0.0, eps) || internal::FuzzyGT(ip.x+ip.y, 1.0, eps) || internal::FuzzyLT(ip.z, 0.0, eps) || internal::FuzzyGT(ip.z, 1.0, eps) ) { return false; } break; case Geometry::PYRAMID: if (internal::FuzzyLT(ip.x, 0.0, eps) || internal::FuzzyLT(ip.y, 0.0, eps) || internal::FuzzyGT(ip.x+ip.z, 1.0, eps) || internal::FuzzyGT(ip.y+ip.z, 1.0, eps) || internal::FuzzyLT(ip.z, 0.0, eps) || internal::FuzzyGT(ip.z, 1.0, eps) ) { return false; } break; default: MFEM_ABORT("Unknown type of reference element!"); } return true; } namespace internal { template inline bool IntersectSegment(double lbeg[N], double lend[N], IntegrationPoint &end) { double t = 1.0; bool out = false; for (int i = 0; i < N; i++) { lbeg[i] = std::max(lbeg[i], 0.0); // remove round-off if (lend[i] < 0.0) { out = true; t = std::min(t, lbeg[i]/(lbeg[i]-lend[i])); } } if (out) { if (dim >= 1) { end.x = t*lend[0] + (1.0-t)*lbeg[0]; } if (dim >= 2) { end.y = t*lend[1] + (1.0-t)*lbeg[1]; } if (dim >= 3) { end.z = t*lend[2] + (1.0-t)*lbeg[2]; } return false; } return true; } inline bool ProjectTriangle(double &x, double &y) { if (x < 0.0) { x = 0.0; if (y < 0.0) { y = 0.0; } else if (y > 1.0) { y = 1.0; } return false; } if (y < 0.0) { if (x > 1.0) { x = 1.0; } y = 0.0; return false; } const double l3 = 1.0-x-y; if (l3 < 0.0) { if (y - x > 1.0) { x = 0.0; y = 1.0; } else if (y - x < -1.0) { x = 1.0; y = 0.0; } else { x += l3/2; y += l3/2; } return false; } return true; } } // static method bool Geometry::ProjectPoint(int GeomType, const IntegrationPoint &beg, IntegrationPoint &end) { switch (GeomType) { case Geometry::POINT: { if (end.x != 0.0) { end.x = 0.0; return false; } break; } case Geometry::SEGMENT: { if (end.x < 0.0) { end.x = 0.0; return false; } if (end.x > 1.0) { end.x = 1.0; return false; } break; } case Geometry::TRIANGLE: { double lend[3] = { end.x, end.y, 1.0-end.x-end.y }; double lbeg[3] = { beg.x, beg.y, 1.0-beg.x-beg.y }; return internal::IntersectSegment<3,2>(lbeg, lend, end); } case Geometry::SQUARE: { double lend[4] = { end.x, end.y, 1.0-end.x, 1.0-end.y }; double lbeg[4] = { beg.x, beg.y, 1.0-beg.x, 1.0-beg.y }; return internal::IntersectSegment<4,2>(lbeg, lend, end); } case Geometry::TETRAHEDRON: { double lend[4] = { end.x, end.y, end.z, 1.0-end.x-end.y-end.z }; double lbeg[4] = { beg.x, beg.y, beg.z, 1.0-beg.x-beg.y-beg.z }; return internal::IntersectSegment<4,3>(lbeg, lend, end); } case Geometry::CUBE: { double lend[6] = { end.x, end.y, end.z, 1.0-end.x, 1.0-end.y, 1.0-end.z }; double lbeg[6] = { beg.x, beg.y, beg.z, 1.0-beg.x, 1.0-beg.y, 1.0-beg.z }; return internal::IntersectSegment<6,3>(lbeg, lend, end); } case Geometry::PRISM: { double lend[5] = { end.x, end.y, end.z, 1.0-end.x-end.y, 1.0-end.z }; double lbeg[5] = { beg.x, beg.y, beg.z, 1.0-beg.x-beg.y, 1.0-beg.z }; return internal::IntersectSegment<5,3>(lbeg, lend, end); } case Geometry::PYRAMID: { double lend[6] = { end.x, end.y, end.z, 1.0-end.x-end.z, 1.0-end.y-end.z, 1.0-end.z }; double lbeg[6] = { beg.x, beg.y, beg.z, 1.0-beg.x-beg.z, 1.0-beg.y-beg.z, 1.0-beg.z }; return internal::IntersectSegment<6,3>(lbeg, lend, end); } default: MFEM_ABORT("Unknown type of reference element!"); } return true; } // static method bool Geometry::ProjectPoint(int GeomType, IntegrationPoint &ip) { // If ip is outside the element, replace it with the point on the boundary // that is closest to the original ip and return false; otherwise, return // true without changing ip. switch (GeomType) { case SEGMENT: { if (ip.x < 0.0) { ip.x = 0.0; return false; } else if (ip.x > 1.0) { ip.x = 1.0; return false; } return true; } case TRIANGLE: { return internal::ProjectTriangle(ip.x, ip.y); } case SQUARE: { bool in_x, in_y; if (ip.x < 0.0) { in_x = false; ip.x = 0.0; } else if (ip.x > 1.0) { in_x = false; ip.x = 1.0; } else { in_x = true; } if (ip.y < 0.0) { in_y = false; ip.y = 0.0; } else if (ip.y > 1.0) { in_y = false; ip.y = 1.0; } else { in_y = true; } return in_x && in_y; } case TETRAHEDRON: { if (ip.z < 0.0) { ip.z = 0.0; internal::ProjectTriangle(ip.x, ip.y); return false; } if (ip.y < 0.0) { ip.y = 0.0; internal::ProjectTriangle(ip.x, ip.z); return false; } if (ip.x < 0.0) { ip.x = 0.0; internal::ProjectTriangle(ip.y, ip.z); return false; } const double l4 = 1.0-ip.x-ip.y-ip.z; if (l4 < 0.0) { const double l4_3 = l4/3; ip.x += l4_3; ip.y += l4_3; internal::ProjectTriangle(ip.x, ip.y); ip.z = 1.0-ip.x-ip.y; return false; } return true; } case CUBE: { bool in_x, in_y, in_z; if (ip.x < 0.0) { in_x = false; ip.x = 0.0; } else if (ip.x > 1.0) { in_x = false; ip.x = 1.0; } else { in_x = true; } if (ip.y < 0.0) { in_y = false; ip.y = 0.0; } else if (ip.y > 1.0) { in_y = false; ip.y = 1.0; } else { in_y = true; } if (ip.z < 0.0) { in_z = false; ip.z = 0.0; } else if (ip.z > 1.0) { in_z = false; ip.z = 1.0; } else { in_z = true; } return in_x && in_y && in_z; } case PRISM: { bool in_tri, in_z; in_tri = internal::ProjectTriangle(ip.x, ip.y); if (ip.z < 0.0) { in_z = false; ip.z = 0.0; } else if (ip.z > 1.0) { in_z = false; ip.z = 1.0; } else { in_z = true; } return in_tri && in_z; } case PYRAMID: { if (ip.x < 0.0) { ip.x = 0.0; internal::ProjectTriangle(ip.y, ip.z); return false; } if (ip.y < 0.0) { ip.y = 0.0; internal::ProjectTriangle(ip.x, ip.z); return false; } if (ip.z < 0.0) { ip.z = 0.0; if (ip.x > 1.0) { ip.x = 1.0; } if (ip.y > 1.0) { ip.y = 1.0; } return false; } if (ip.x >= ip.y) { bool in_y = true; bool in_tri = internal::ProjectTriangle(ip.x, ip.z); if (ip.y > ip.z) { in_y = false; ip.y = ip.z; } return in_tri && in_y; } else { bool in_x = true; bool in_tri = internal::ProjectTriangle(ip.y, ip.z); if (ip.x > ip.z) { in_x = false; ip.x = ip.z; } return in_tri && in_x; } } default: MFEM_ABORT("Reference element type is not supported!"); } return true; } void Geometry::GetPerfPointMat(int GeomType, DenseMatrix &pm) { switch (GeomType) { case Geometry::SEGMENT: { pm.SetSize (1, 2); pm(0,0) = 0.0; pm(0,1) = 1.0; } break; case Geometry::TRIANGLE: { pm.SetSize (2, 3); pm(0,0) = 0.0; pm(1,0) = 0.0; pm(0,1) = 1.0; pm(1,1) = 0.0; pm(0,2) = 0.5; pm(1,2) = 0.86602540378443864676; } break; case Geometry::SQUARE: { pm.SetSize (2, 4); pm(0,0) = 0.0; pm(1,0) = 0.0; pm(0,1) = 1.0; pm(1,1) = 0.0; pm(0,2) = 1.0; pm(1,2) = 1.0; pm(0,3) = 0.0; pm(1,3) = 1.0; } break; case Geometry::TETRAHEDRON: { pm.SetSize (3, 4); pm(0,0) = 0.0; pm(1,0) = 0.0; pm(2,0) = 0.0; pm(0,1) = 1.0; pm(1,1) = 0.0; pm(2,1) = 0.0; pm(0,2) = 0.5; pm(1,2) = 0.86602540378443864676; pm(2,2) = 0.0; pm(0,3) = 0.5; pm(1,3) = 0.28867513459481288225; pm(2,3) = 0.81649658092772603273; } break; case Geometry::CUBE: { pm.SetSize (3, 8); pm(0,0) = 0.0; pm(1,0) = 0.0; pm(2,0) = 0.0; pm(0,1) = 1.0; pm(1,1) = 0.0; pm(2,1) = 0.0; pm(0,2) = 1.0; pm(1,2) = 1.0; pm(2,2) = 0.0; pm(0,3) = 0.0; pm(1,3) = 1.0; pm(2,3) = 0.0; pm(0,4) = 0.0; pm(1,4) = 0.0; pm(2,4) = 1.0; pm(0,5) = 1.0; pm(1,5) = 0.0; pm(2,5) = 1.0; pm(0,6) = 1.0; pm(1,6) = 1.0; pm(2,6) = 1.0; pm(0,7) = 0.0; pm(1,7) = 1.0; pm(2,7) = 1.0; } break; case Geometry::PRISM: { pm.SetSize (3, 6); pm(0,0) = 0.0; pm(1,0) = 0.0; pm(2,0) = 0.0; pm(0,1) = 1.0; pm(1,1) = 0.0; pm(2,1) = 0.0; pm(0,2) = 0.5; pm(1,2) = 0.86602540378443864676; pm(2,2) = 0.0; pm(0,3) = 0.0; pm(1,3) = 0.0; pm(2,3) = 1.0; pm(0,4) = 1.0; pm(1,4) = 0.0; pm(2,4) = 1.0; pm(0,5) = 0.5; pm(1,5) = 0.86602540378443864676; pm(2,5) = 1.0; } break; case Geometry::PYRAMID: { pm.SetSize (3, 5); pm(0,0) = 0.0; pm(1,0) = 0.0; pm(2,0) = 0.0; pm(0,1) = 1.0; pm(1,1) = 0.0; pm(2,1) = 0.0; pm(0,2) = 1.0; pm(1,2) = 1.0; pm(2,2) = 0.0; pm(0,3) = 0.0; pm(1,3) = 1.0; pm(2,3) = 0.0; pm(0,4) = 0.5; pm(1,4) = 0.5; pm(2,4) = 0.7071067811865475; } break; default: mfem_error ("Geometry::GetPerfPointMat (...)"); } } void Geometry::JacToPerfJac(int GeomType, const DenseMatrix &J, DenseMatrix &PJ) const { if (PerfGeomToGeomJac[GeomType]) { Mult(J, *PerfGeomToGeomJac[GeomType], PJ); } else { PJ = J; } } const int Geometry::NumBdrArray[NumGeom] = { 0, 2, 3, 4, 4, 6, 5, 5 }; const int Geometry::Dimension[NumGeom] = { 0, 1, 2, 2, 3, 3, 3, 3 }; const int Geometry::DimStart[MaxDim+2] = { POINT, SEGMENT, TRIANGLE, TETRAHEDRON, NUM_GEOMETRIES }; const int Geometry::NumVerts[NumGeom] = { 1, 2, 3, 4, 4, 8, 6, 5 }; const int Geometry::NumEdges[NumGeom] = { 0, 1, 3, 4, 6, 12, 9, 8 }; const int Geometry::NumFaces[NumGeom] = { 0, 0, 1, 1, 4, 6, 5, 5 }; const int Geometry:: Constants::Orient[1][1] = {{0}}; const int Geometry:: Constants::InvOrient[1] = {0}; const int Geometry:: Constants::Edges[1][2] = { {0, 1} }; const int Geometry:: Constants::Orient[2][2] = { {0, 1}, {1, 0} }; const int Geometry:: Constants::InvOrient[2] = { 0, 1 }; const int Geometry:: Constants::Edges[3][2] = {{0, 1}, {1, 2}, {2, 0}}; const int Geometry:: Constants::VertToVert::I[3] = {0, 2, 3}; const int Geometry:: Constants::VertToVert::J[3][2] = {{1, 0}, {2, -3}, {2, 1}}; const int Geometry:: Constants::FaceVert[1][3] = {{0, 1, 2}}; const int Geometry:: Constants::Orient[6][3] = { {0, 1, 2}, {1, 0, 2}, {2, 0, 1}, {2, 1, 0}, {1, 2, 0}, {0, 2, 1} }; const int Geometry:: Constants::InvOrient[6] = { 0, 1, 4, 3, 2, 5 }; const int Geometry:: Constants::Edges[4][2] = {{0, 1}, {1, 2}, {2, 3}, {3, 0}}; const int Geometry:: Constants::VertToVert::I[4] = {0, 2, 3, 4}; const int Geometry:: Constants::VertToVert::J[4][2] = {{1, 0}, {3, -4}, {2, 1}, {3, 2}}; const int Geometry:: Constants::FaceVert[1][4] = {{0, 1, 2, 3}}; const int Geometry:: Constants::Orient[8][4] = { {0, 1, 2, 3}, {0, 3, 2, 1}, {1, 2, 3, 0}, {1, 0, 3, 2}, {2, 3, 0, 1}, {2, 1, 0, 3}, {3, 0, 1, 2}, {3, 2, 1, 0} }; const int Geometry:: Constants::InvOrient[8] = { 0, 1, 6, 3, 4, 5, 2, 7 }; const int Geometry:: Constants::Edges[6][2] = {{0, 1}, {0, 2}, {0, 3}, {1, 2}, {1, 3}, {2, 3}}; const int Geometry:: Constants::FaceTypes[4] = { Geometry::TRIANGLE, Geometry::TRIANGLE, Geometry::TRIANGLE, Geometry::TRIANGLE }; const int Geometry:: Constants::FaceVert[4][3] = {{1, 2, 3}, {0, 3, 2}, {0, 1, 3}, {0, 2, 1}}; const int Geometry:: Constants::VertToVert::I[4] = {0, 3, 5, 6}; const int Geometry:: Constants::VertToVert::J[6][2] = { {1, 0}, {2, 1}, {3, 2}, // 0,1:0 0,2:1 0,3:2 {2, 3}, {3, 4}, // 1,2:3 1,3:4 {3, 5} // 2,3:5 }; const int Geometry:: Constants::Orient[24][4] = { {0, 1, 2, 3}, {0, 1, 3, 2}, {0, 2, 3, 1}, {0, 2, 1, 3}, {0, 3, 1, 2}, {0, 3, 2, 1}, {1, 2, 0, 3}, {1, 2, 3, 0}, {1, 3, 2, 0}, {1, 3, 0, 2}, {1, 0, 3, 2}, {1, 0, 2, 3}, {2, 3, 0, 1}, {2, 3, 1, 0}, {2, 0, 1, 3}, {2, 0, 3, 1}, {2, 1, 3, 0}, {2, 1, 0, 3}, {3, 0, 2, 1}, {3, 0, 1, 2}, {3, 1, 0, 2}, {3, 1, 2, 0}, {3, 2, 1, 0}, {3, 2, 0, 1} }; const int Geometry:: Constants::InvOrient[24] = { 0, 1, 4, 3, 2, 5, 14, 19, 18, 15, 10, 11, 12, 23, 6, 9, 20, 17, 8, 7, 16, 21, 22, 13 }; const int Geometry:: Constants::Edges[12][2] = { {0, 1}, {1, 2}, {3, 2}, {0, 3}, {4, 5}, {5, 6}, {7, 6}, {4, 7}, {0, 4}, {1, 5}, {2, 6}, {3, 7} }; const int Geometry:: Constants::FaceTypes[6] = { Geometry::SQUARE, Geometry::SQUARE, Geometry::SQUARE, Geometry::SQUARE, Geometry::SQUARE, Geometry::SQUARE }; const int Geometry:: Constants::FaceVert[6][4] = { {3, 2, 1, 0}, {0, 1, 5, 4}, {1, 2, 6, 5}, {2, 3, 7, 6}, {3, 0, 4, 7}, {4, 5, 6, 7} }; const int Geometry:: Constants::VertToVert::I[8] = {0, 3, 5, 7, 8, 10, 11, 12}; const int Geometry:: Constants::VertToVert::J[12][2] = { {1, 0}, {3, 3}, {4, 8}, // 0,1:0 0,3:3 0,4:8 {2, 1}, {5, 9}, // 1,2:1 1,5:9 {3,-3}, {6,10}, // 2,3:-3 2,6:10 {7,11}, // 3,7:11 {5, 4}, {7, 7}, // 4,5:4 4,7:7 {6, 5}, // 5,6:5 {7,-7} // 6,7:-7 }; const int Geometry:: Constants::Edges[9][2] = {{0, 1}, {1, 2}, {2, 0}, {3, 4}, {4, 5}, {5, 3}, {0, 3}, {1, 4}, {2, 5}}; const int Geometry:: Constants::FaceTypes[5] = { Geometry::TRIANGLE, Geometry::TRIANGLE, Geometry::SQUARE, Geometry::SQUARE, Geometry::SQUARE }; const int Geometry:: Constants::FaceVert[5][4] = {{0, 2, 1, -1}, {3, 4, 5, -1}, {0, 1, 4, 3}, {1, 2, 5, 4}, {2, 0, 3, 5}}; const int Geometry:: Constants::VertToVert::I[6] = {0, 3, 5, 6, 8, 9}; const int Geometry:: Constants::VertToVert::J[9][2] = { {1, 0}, {2, -3}, {3, 6}, // 0,1:0 0,2:-3 0,3:6 {2, 1}, {4, 7}, // 1,2:1 1,4:7 {5, 8}, // 2,5:8 {4, 3}, {5, -6}, // 3,4:3 3,5:-6 {5, 4} // 4,5:4 }; const int Geometry:: Constants::Edges[8][2] = {{0, 1}, {1, 2}, {3, 2}, {0, 3}, {0, 4}, {1, 4}, {2, 4}, {3, 4}}; const int Geometry:: Constants::FaceTypes[5] = { Geometry::SQUARE, Geometry::TRIANGLE, Geometry::TRIANGLE, Geometry::TRIANGLE, Geometry::TRIANGLE }; const int Geometry:: Constants::FaceVert[5][4] = {{3, 2, 1, 0}, {0, 1, 4, -1}, {1, 2, 4, -1}, {2, 3, 4, -1}, {3, 0, 4, -1}}; const int Geometry:: Constants::VertToVert::I[5] = {0, 3, 5, 7, 8}; const int Geometry:: Constants::VertToVert::J[8][2] = { {1, 0}, {3, 3}, {4, 4}, // 0,1:0 0,3:3 0,4:4 {2, 1}, {4, 5}, // 1,2:1 1,4:5 {3,-3}, {4, 6}, // 2,3:-3 2,4:6 {4, 7} // 3,4:7 }; GeometryRefiner::GeometryRefiner() { type = Quadrature1D::ClosedUniform; } GeometryRefiner::~GeometryRefiner() { for (int i = 0; i < Geometry::NumGeom; i++) { for (int j = 0; j < RGeom[i].Size(); j++) { delete RGeom[i][j]; } for (int j = 0; j < IntPts[i].Size(); j++) { delete IntPts[i][j]; } } } RefinedGeometry *GeometryRefiner::FindInRGeom(Geometry::Type Geom, int Times, int ETimes, int Type) { Array &RGA = RGeom[Geom]; for (int i = 0; i < RGA.Size(); i++) { RefinedGeometry &RG = *RGA[i]; if (RG.Times == Times && RG.ETimes == ETimes && RG.Type == Type) { return &RG; } } return NULL; } IntegrationRule *GeometryRefiner::FindInIntPts(Geometry::Type Geom, int NPts) { Array &IPA = IntPts[Geom]; for (int i = 0; i < IPA.Size(); i++) { IntegrationRule &ir = *IPA[i]; if (ir.GetNPoints() == NPts) { return &ir; } } return NULL; } RefinedGeometry * GeometryRefiner::Refine(Geometry::Type Geom, int Times, int ETimes) { int i, j, k, l, m; Times = std::max(Times, 1); ETimes = Geometry::Dimension[Geom] <= 1 ? 0 : std::max(ETimes, 1); const double *cp = poly1d.GetPoints(Times, BasisType::GetNodalBasis(type)); RefinedGeometry *RG = FindInRGeom(Geom, Times, ETimes, type); if (RG) { return RG; } switch (Geom) { case Geometry::POINT: { RG = new RefinedGeometry(1, 1, 0); RG->Times = 1; RG->ETimes = 0; RG->Type = type; RG->RefPts.IntPoint(0).x = cp[0]; RG->RefGeoms[0] = 0; RGeom[Geometry::POINT].Append(RG); return RG; } case Geometry::SEGMENT: { RG = new RefinedGeometry(Times+1, 2*Times, 0); RG->Times = Times; RG->ETimes = 0; RG->Type = type; for (i = 0; i <= Times; i++) { IntegrationPoint &ip = RG->RefPts.IntPoint(i); ip.x = cp[i]; } Array &G = RG->RefGeoms; for (i = 0; i < Times; i++) { G[2*i+0] = i; G[2*i+1] = i+1; } RGeom[Geometry::SEGMENT].Append(RG); return RG; } case Geometry::TRIANGLE: { RG = new RefinedGeometry((Times+1)*(Times+2)/2, 3*Times*Times, 3*Times*(ETimes+1), 3*Times); RG->Times = Times; RG->ETimes = ETimes; RG->Type = type; for (k = j = 0; j <= Times; j++) for (i = 0; i <= Times-j; i++, k++) { IntegrationPoint &ip = RG->RefPts.IntPoint(k); ip.x = cp[i]/(cp[i] + cp[j] + cp[Times-i-j]); ip.y = cp[j]/(cp[i] + cp[j] + cp[Times-i-j]); } Array &G = RG->RefGeoms; for (l = k = j = 0; j < Times; j++, k++) for (i = 0; i < Times-j; i++, k++) { G[l++] = k; G[l++] = k+1; G[l++] = k+Times-j+1; if (i+j+1 < Times) { G[l++] = k+1; G[l++] = k+Times-j+2; G[l++] = k+Times-j+1; } } Array &E = RG->RefEdges; int lb = 0, li = 2*RG->NumBdrEdges; // horizontal edges for (k = 0; k < Times; k += Times/ETimes) { int < = (k == 0) ? lb : li; j = k*(Times+1)-((k-1)*k)/2; for (i = 0; i < Times-k; i++) { E[lt++] = j; j++; E[lt++] = j; } } // diagonal edges for (k = Times; k > 0; k -= Times/ETimes) { int < = (k == Times) ? lb : li; j = k; for (i = 0; i < k; i++) { E[lt++] = j; j += Times-i; E[lt++] = j; } } // vertical edges for (k = 0; k < Times; k += Times/ETimes) { int < = (k == 0) ? lb : li; j = k; for (i = 0; i < Times-k; i++) { E[lt++] = j; j += Times-i+1; E[lt++] = j; } } RGeom[Geometry::TRIANGLE].Append(RG); return RG; } case Geometry::SQUARE: { RG = new RefinedGeometry((Times+1)*(Times+1), 4*Times*Times, 4*(ETimes+1)*Times, 4*Times); RG->Times = Times; RG->ETimes = ETimes; RG->Type = type; for (k = j = 0; j <= Times; j++) for (i = 0; i <= Times; i++, k++) { IntegrationPoint &ip = RG->RefPts.IntPoint(k); ip.x = cp[i]; ip.y = cp[j]; } Array &G = RG->RefGeoms; for (l = k = j = 0; j < Times; j++, k++) for (i = 0; i < Times; i++, k++) { G[l++] = k; G[l++] = k+1; G[l++] = k+Times+2; G[l++] = k+Times+1; } Array &E = RG->RefEdges; int lb = 0, li = 2*RG->NumBdrEdges; // horizontal edges for (k = 0; k <= Times; k += Times/ETimes) { int < = (k == 0 || k == Times) ? lb : li; for (i = 0, j = k*(Times+1); i < Times; i++) { E[lt++] = j; j++; E[lt++] = j; } } // vertical edges (in right-to-left order) for (k = Times; k >= 0; k -= Times/ETimes) { int < = (k == Times || k == 0) ? lb : li; for (i = 0, j = k; i < Times; i++) { E[lt++] = j; j += Times+1; E[lt++] = j; } } RGeom[Geometry::SQUARE].Append(RG); return RG; } case Geometry::CUBE: { RG = new RefinedGeometry ((Times+1)*(Times+1)*(Times+1), 8*Times*Times*Times, 0); RG->Times = Times; RG->ETimes = ETimes; RG->Type = type; for (l = k = 0; k <= Times; k++) for (j = 0; j <= Times; j++) for (i = 0; i <= Times; i++, l++) { IntegrationPoint &ip = RG->RefPts.IntPoint(l); ip.x = cp[i]; ip.y = cp[j]; ip.z = cp[k]; } Array &G = RG->RefGeoms; for (l = k = 0; k < Times; k++) for (j = 0; j < Times; j++) for (i = 0; i < Times; i++) { G[l++] = i+0 + (j+0 + (k+0) * (Times+1)) * (Times+1); G[l++] = i+1 + (j+0 + (k+0) * (Times+1)) * (Times+1); G[l++] = i+1 + (j+1 + (k+0) * (Times+1)) * (Times+1); G[l++] = i+0 + (j+1 + (k+0) * (Times+1)) * (Times+1); G[l++] = i+0 + (j+0 + (k+1) * (Times+1)) * (Times+1); G[l++] = i+1 + (j+0 + (k+1) * (Times+1)) * (Times+1); G[l++] = i+1 + (j+1 + (k+1) * (Times+1)) * (Times+1); G[l++] = i+0 + (j+1 + (k+1) * (Times+1)) * (Times+1); } RGeom[Geometry::CUBE].Append(RG); return RG; } case Geometry::TETRAHEDRON: { // subdivide the tetrahedron with vertices // (0,0,0), (0,0,1), (1,1,1), (0,1,1) // vertices: 0 <= i <= j <= k <= Times // (3-combination with repetitions) // number of vertices: (n+3)*(n+2)*(n+1)/6, n = Times // elements: the vertices are: v1=(i,j,k), v2=v1+u1, v3=v2+u2, v4=v3+u3 // where 0 <= i <= j <= k <= n-1 and // u1,u2,u3 is a permutation of (1,0,0),(0,1,0),(0,0,1) // such that all v2,v3,v4 have non-decreasing components // number of elements: n^3 const int n = Times; RG = new RefinedGeometry((n+3)*(n+2)*(n+1)/6, 4*n*n*n, 0); RG->Times = Times; RG->ETimes = ETimes; RG->Type = type; // enumerate and define the vertices Array vi((n+1)*(n+1)*(n+1)); vi = -1; m = 0; // vertices are given in lexicographic ordering on the reference // element for (int kk = 0; kk <= n; kk++) for (int jj = 0; jj <= n-kk; jj++) for (int ii = 0; ii <= n-jj-kk; ii++) { IntegrationPoint &ip = RG->RefPts.IntPoint(m); double w = cp[ii] + cp[jj] + cp[kk] + cp[Times-ii-jj-kk]; ip.x = cp[ii]/w; ip.y = cp[jj]/w; ip.z = cp[kk]/w; // (ii,jj,kk) are coordinates in the reference tetrahedron, // transform to coordinates (i,j,k) in the auxiliary // tetrahedron defined by (0,0,0), (0,0,1), (1,1,1), (0,1,1) i = jj; j = jj+kk; k = ii+jj+kk; l = i + (j + k * (n+1)) * (n+1); // map from linear Cartesian hex index in the auxiliary tet // to lexicographic in the reference tet vi[l] = m; m++; } if (m != (n+3)*(n+2)*(n+1)/6) { mfem_error("GeometryRefiner::Refine() for TETRAHEDRON #1"); } // elements Array &G = RG->RefGeoms; m = 0; for (k = 0; k < n; k++) for (j = 0; j <= k; j++) for (i = 0; i <= j; i++) { // the ordering of the vertices is chosen to ensure: // 1) correct orientation // 2) the x,y,z edges are in the set of edges // {(0,1),(2,3), (0,2),(1,3)} // (goal is to ensure that subsequent refinement using // this procedure preserves the six tetrahedral shapes) // zyx: (i,j,k)-(i,j,k+1)-(i+1,j+1,k+1)-(i,j+1,k+1) G[m++] = vi[i+0 + (j+0 + (k+0) * (n+1)) * (n+1)]; G[m++] = vi[i+0 + (j+0 + (k+1) * (n+1)) * (n+1)]; G[m++] = vi[i+1 + (j+1 + (k+1) * (n+1)) * (n+1)]; G[m++] = vi[i+0 + (j+1 + (k+1) * (n+1)) * (n+1)]; if (j < k) { // yzx: (i,j,k)-(i+1,j+1,k+1)-(i,j+1,k)-(i,j+1,k+1) G[m++] = vi[i+0 + (j+0 + (k+0) * (n+1)) * (n+1)]; G[m++] = vi[i+1 + (j+1 + (k+1) * (n+1)) * (n+1)]; G[m++] = vi[i+0 + (j+1 + (k+0) * (n+1)) * (n+1)]; G[m++] = vi[i+0 + (j+1 + (k+1) * (n+1)) * (n+1)]; // yxz: (i,j,k)-(i,j+1,k)-(i+1,j+1,k+1)-(i+1,j+1,k) G[m++] = vi[i+0 + (j+0 + (k+0) * (n+1)) * (n+1)]; G[m++] = vi[i+0 + (j+1 + (k+0) * (n+1)) * (n+1)]; G[m++] = vi[i+1 + (j+1 + (k+1) * (n+1)) * (n+1)]; G[m++] = vi[i+1 + (j+1 + (k+0) * (n+1)) * (n+1)]; } if (i < j) { // xzy: (i,j,k)-(i+1,j,k)-(i+1,j+1,k+1)-(i+1,j,k+1) G[m++] = vi[i+0 + (j+0 + (k+0) * (n+1)) * (n+1)]; G[m++] = vi[i+1 + (j+0 + (k+0) * (n+1)) * (n+1)]; G[m++] = vi[i+1 + (j+1 + (k+1) * (n+1)) * (n+1)]; G[m++] = vi[i+1 + (j+0 + (k+1) * (n+1)) * (n+1)]; if (j < k) { // xyz: (i,j,k)-(i+1,j+1,k+1)-(i+1,j,k)-(i+1,j+1,k) G[m++] = vi[i+0 + (j+0 + (k+0) * (n+1)) * (n+1)]; G[m++] = vi[i+1 + (j+1 + (k+1) * (n+1)) * (n+1)]; G[m++] = vi[i+1 + (j+0 + (k+0) * (n+1)) * (n+1)]; G[m++] = vi[i+1 + (j+1 + (k+0) * (n+1)) * (n+1)]; } // zxy: (i,j,k)-(i+1,j+1,k+1)-(i,j,k+1)-(i+1,j,k+1) G[m++] = vi[i+0 + (j+0 + (k+0) * (n+1)) * (n+1)]; G[m++] = vi[i+1 + (j+1 + (k+1) * (n+1)) * (n+1)]; G[m++] = vi[i+0 + (j+0 + (k+1) * (n+1)) * (n+1)]; G[m++] = vi[i+1 + (j+0 + (k+1) * (n+1)) * (n+1)]; } } if (m != 4*n*n*n) { mfem_error("GeometryRefiner::Refine() for TETRAHEDRON #2"); } for (i = 0; i < m; i++) if (G[i] < 0) { mfem_error("GeometryRefiner::Refine() for TETRAHEDRON #3"); } RGeom[Geometry::TETRAHEDRON].Append(RG); return RG; } case Geometry::PYRAMID: { const int n = Times; RG = new RefinedGeometry ((n+1)*(n+2)*(2*n+3)/6, 5*n*(2*n-1)*(2*n+1)/3, 0); RG->Times = Times; RG->ETimes = ETimes; RG->Type = type; // enumerate and define the vertices m = 0; for (k = 0; k <= n; k++) { const double *cpij = poly1d.GetPoints(Times - k, BasisType::GetNodalBasis(type)); for (j = 0; j <= n - k; j++) for (i = 0; i <= n - k; i++) { IntegrationPoint &ip = RG->RefPts.IntPoint(m); if (type == 0) { ip.x = (n > k) ? (double(i) / (n - k)) : 0.0; ip.y = (n > k) ? (double(j) / (n - k)) : 0.0; ip.z = double(k) / n; } else { ip.x = cpij[i] * (1.0 - cp[k]); ip.y = cpij[j] * (1.0 - cp[k]); ip.z = cp[k]; } m++; } } if (m != (n+1)*(n+2)*(2*n+3)/6) { mfem_error("GeometryRefiner::Refine() for PYRAMID #1"); } // elements Array &G = RG->RefGeoms; m = 0; for (k = 0; k < n; k++) { int lk = k * (k * (2 * k - 6 * n - 9) + 6 * n * (n + 3) + 13) / 6; int lkp1 = (k + 1) * (k * (2 * k - 6 * n -5) + 6 * n * (n + 2) + 6) / 6; for (j = 0; j < n - k; j++) { for (i = 0; i < n - k; i++) { G[m++] = lk + j * (n - k + 1) + i; G[m++] = lk + j * (n - k + 1) + i + 1; G[m++] = lk + (j + 1) * (n - k + 1) + i + 1; G[m++] = lk + (j + 1) * (n - k + 1) + i; G[m++] = lkp1 + j * (n - k) + i; } } for (j = 0; j < n - k - 1; j++) { for (i = 0; i < n - k - 1; i++) { G[m++] = lkp1 + j * (n - k) + i; G[m++] = lkp1 + (j + 1) * (n - k) + i; G[m++] = lkp1 + (j + 1) * (n - k) + i + 1; G[m++] = lkp1 + j * (n - k) + i + 1; G[m++] = lk + (j + 1) * (n - k + 1) + i + 1; } } for (j = 0; j < n - k; j++) { for (i = 0; i < n - k - 1; i++) { G[m++] = lk + j * (n - k + 1) + i + 1; G[m++] = lk + (j + 1) * (n - k + 1) + i + 1; G[m++] = lkp1 + j * (n - k) + i; G[m++] = lkp1 + j * (n - k) + i + 1; G[m++] = -1; } } for (j = 0; j < n - k - 1; j++) { for (i = 0; i < n - k; i++) { G[m++] = lk + (j + 1) * (n - k + 1) + i; G[m++] = lk + (j + 1) * (n - k + 1) + i + 1; G[m++] = lkp1 + (j + 1) * (n - k) + i; G[m++] = lkp1 + j * (n - k) + i; G[m++] = -1; } } } if (m != 5*n*(2*n-1)*(2*n+1)/3) { mfem_error("GeometryRefiner::Refine() for PYRAMID #2"); } RGeom[Geometry::PYRAMID].Append(RG); return RG; } case Geometry::PRISM: { const int n = Times; RG = new RefinedGeometry ((n+1)*(n+1)*(n+2)/2, 6*n*n*n, 0); RG->Times = Times; RG->ETimes = ETimes; RG->Type = type; // enumerate and define the vertices m = 0; for (l = k = 0; k <= n; k++) for (j = 0; j <= n; j++) for (i = 0; i <= n-j; i++, l++) { IntegrationPoint &ip = RG->RefPts.IntPoint(l); ip.x = cp[i]/(cp[i] + cp[j] + cp[n-i-j]); ip.y = cp[j]/(cp[i] + cp[j] + cp[n-i-j]); ip.z = cp[k]; m++; } if (m != (n+1)*(n+1)*(n+2)/2) { mfem_error("GeometryRefiner::Refine() for PRISM #1"); } // elements Array &G = RG->RefGeoms; m = 0; for (m = k = 0; k < n; k++) for (l = j = 0; j < n; j++, l++) for (i = 0; i < n-j; i++, l++) { G[m++] = l + (k+0) * (n+1) * (n+2) / 2; G[m++] = l + 1 + (k+0) * (n+1) * (n+2) / 2; G[m++] = l - j + (2 + (k+0) * (n+2)) * (n+1) / 2; G[m++] = l + (k+1) * (n+1) * (n+2) / 2; G[m++] = l + 1 + (k+1) * (n+1) * (Times+2) / 2; G[m++] = l - j + (2 + (k+1) * (n+2)) * (n+1) / 2; if (i+j+1 < n) { G[m++] = l + 1 + (k+0) * (n+1) * (n+2)/2; G[m++] = l - j + (2 + (k+0) * (n+1)) * (n+2) / 2; G[m++] = l - j + (2 + (k+0) * (n+2)) * (n+1) / 2; G[m++] = l + 1 + (k+1) * (n+1) * (n+2) / 2; G[m++] = l - j + (2 + (k+1) * (n+1)) * (n+2) / 2; G[m++] = l - j + (2 + (k+1) * (n+2)) * (n+1) / 2; } } if (m != 6*n*n*n) { mfem_error("GeometryRefiner::Refine() for PRISM #2"); } for (i = 0; i < m; i++) if (G[i] < 0) { mfem_error("GeometryRefiner::Refine() for PRISM #3"); } RGeom[Geometry::PRISM].Append(RG); return RG; } default: return NULL; } } const IntegrationRule *GeometryRefiner::RefineInterior(Geometry::Type Geom, int Times) { IntegrationRule *ir = NULL; switch (Geom) { case Geometry::SEGMENT: { if (Times < 2) { return NULL; } ir = FindInIntPts(Geom, Times-1); if (ir) { return ir; } ir = new IntegrationRule(Times-1); for (int i = 1; i < Times; i++) { IntegrationPoint &ip = ir->IntPoint(i-1); ip.x = double(i) / Times; ip.y = ip.z = 0.0; } } break; case Geometry::TRIANGLE: { if (Times < 3) { return NULL; } ir = FindInIntPts(Geom, ((Times-1)*(Times-2))/2); if (ir) { return ir; } ir = new IntegrationRule(((Times-1)*(Times-2))/2); for (int k = 0, j = 1; j < Times-1; j++) for (int i = 1; i < Times-j; i++, k++) { IntegrationPoint &ip = ir->IntPoint(k); ip.x = double(i) / Times; ip.y = double(j) / Times; ip.z = 0.0; } } break; case Geometry::SQUARE: { if (Times < 2) { return NULL; } ir = FindInIntPts(Geom, (Times-1)*(Times-1)); if (ir) { return ir; } ir = new IntegrationRule((Times-1)*(Times-1)); for (int k = 0, j = 1; j < Times; j++) for (int i = 1; i < Times; i++, k++) { IntegrationPoint &ip = ir->IntPoint(k); ip.x = double(i) / Times; ip.y = double(j) / Times; ip.z = 0.0; } } break; default: mfem_error("GeometryRefiner::RefineInterior(...)"); } MFEM_ASSERT(ir != NULL, "Failed to construct the refined IntegrationRule."); IntPts[Geom].Append(ir); return ir; } int GeometryRefiner::GetRefinementLevelFromPoints(Geometry::Type geom, int Npts) { switch (geom) { case Geometry::POINT: { return -1; } case Geometry::SEGMENT: { return Npts -1; } case Geometry::TRIANGLE: { for (int n = 0, np = 0; (n < 15) && (np < Npts) ; n++) { np = (n+1)*(n+2)/2; if (np == Npts) { return n; } } return -1; } case Geometry::SQUARE: { for (int n = 0, np = 0; (n < 15) && (np < Npts) ; n++) { np = (n+1)*(n+1); if (np == Npts) { return n; } } return -1; } case Geometry::CUBE: { for (int n = 0, np = 0; (n < 15) && (np < Npts) ; n++) { np = (n+1)*(n+1)*(n+1); if (np == Npts) { return n; } } return -1; } case Geometry::TETRAHEDRON: { for (int n = 0, np = 0; (n < 15) && (np < Npts) ; n++) { np = (n+3)*(n+2)*(n+1)/6; if (np == Npts) { return n; } } return -1; } case Geometry::PRISM: { for (int n = 0, np = 0; (n < 15) && (np < Npts) ; n++) { np = (n+1)*(n+1)*(n+2)/2; if (np == Npts) { return n; } } return -1; } default: { mfem_error("Non existing Geometry."); } } return -1; } int GeometryRefiner::GetRefinementLevelFromElems(Geometry::Type geom, int Nels) { switch (geom) { case Geometry::POINT: { return -1; } case Geometry::SEGMENT: { return Nels; } case Geometry::TRIANGLE: case Geometry::SQUARE: { for (int n = 0; (n < 15) && (n*n < Nels+1) ; n++) { if (n*n == Nels) { return n-1; } } return -1; } case Geometry::CUBE: case Geometry::TETRAHEDRON: case Geometry::PRISM: { for (int n = 0; (n < 15) && (n*n*n < Nels+1) ; n++) { if (n*n*n == Nels) { return n-1; } } return -1; } default: { mfem_error("Non existing Geometry."); } } return -1; } GeometryRefiner GlobGeometryRefiner; }