Files
mfem/tests/unit/fem/test_calcvshape.cpp
T

388 lines
11 KiB
C++

// Copyright (c) 2010-2025, 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 "mfem.hpp"
#include "catch.hpp"
#include <iostream>
#include <cmath>
using namespace mfem;
/**
* Utility function to generate IntegerationPoints, based on param ip
* that are outside the unit interval. Results are placed in output
* parameter arr.
*
* Note: this is defined in test_calcshape.cpp
*/
void GetRelatedIntegrationPoints(const IntegrationPoint& ip, int dim,
Array<IntegrationPoint>& arr);
/**
* Utility function to setup IsoparametricTransformations for reference
* elements of various types.
*/
void GetReferenceTransformation(const Element::Type ElemType,
IsoparametricTransformation & T)
{
T.Attribute = 1;
T.ElementNo = 0;
switch (ElemType)
{
case Element::POINT :
T.GetPointMat().SetSize(1, 1);
T.GetPointMat()(0, 0) = 0.0;
T.SetFE(&PointFE);
break;
case Element::SEGMENT :
T.GetPointMat().SetSize(1, 2);
T.GetPointMat()(0, 0) = 0.0;
T.GetPointMat()(0, 1) = 1.0;
T.SetFE(&SegmentFE);
break;
case Element::TRIANGLE :
T.GetPointMat().SetSize(2, 3);
T.GetPointMat()(0, 0) = 0.0;
T.GetPointMat()(1, 0) = 0.0;
T.GetPointMat()(0, 1) = 1.0;
T.GetPointMat()(1, 1) = 0.0;
T.GetPointMat()(0, 2) = 0.0;
T.GetPointMat()(1, 2) = 1.0;
T.SetFE(&TriangleFE);
break;
case Element::QUADRILATERAL :
T.GetPointMat().SetSize(2, 4);
T.GetPointMat()(0, 0) = 0.0;
T.GetPointMat()(1, 0) = 0.0;
T.GetPointMat()(0, 1) = 1.0;
T.GetPointMat()(1, 1) = 0.0;
T.GetPointMat()(0, 2) = 1.0;
T.GetPointMat()(1, 2) = 1.0;
T.GetPointMat()(0, 3) = 0.0;
T.GetPointMat()(1, 3) = 1.0;
T.SetFE(&QuadrilateralFE);
break;
case Element::TETRAHEDRON :
T.GetPointMat().SetSize(3, 4);
T.GetPointMat()(0, 0) = 0.0;
T.GetPointMat()(1, 0) = 0.0;
T.GetPointMat()(2, 0) = 0.0;
T.GetPointMat()(0, 1) = 1.0;
T.GetPointMat()(1, 1) = 0.0;
T.GetPointMat()(2, 1) = 0.0;
T.GetPointMat()(0, 2) = 0.0;
T.GetPointMat()(1, 2) = 1.0;
T.GetPointMat()(2, 2) = 0.0;
T.GetPointMat()(0, 3) = 0.0;
T.GetPointMat()(1, 3) = 0.0;
T.GetPointMat()(2, 3) = 1.0;
T.SetFE(&TetrahedronFE);
break;
case Element::HEXAHEDRON :
T.GetPointMat().SetSize(3, 8);
T.GetPointMat()(0, 0) = 0.0;
T.GetPointMat()(1, 0) = 0.0;
T.GetPointMat()(2, 0) = 0.0;
T.GetPointMat()(0, 1) = 1.0;
T.GetPointMat()(1, 1) = 0.0;
T.GetPointMat()(2, 1) = 0.0;
T.GetPointMat()(0, 2) = 1.0;
T.GetPointMat()(1, 2) = 1.0;
T.GetPointMat()(2, 2) = 0.0;
T.GetPointMat()(0, 3) = 0.0;
T.GetPointMat()(1, 3) = 1.0;
T.GetPointMat()(2, 3) = 0.0;
T.GetPointMat()(0, 4) = 0.0;
T.GetPointMat()(1, 4) = 0.0;
T.GetPointMat()(2, 4) = 1.0;
T.GetPointMat()(0, 5) = 1.0;
T.GetPointMat()(1, 5) = 0.0;
T.GetPointMat()(2, 5) = 1.0;
T.GetPointMat()(0, 6) = 1.0;
T.GetPointMat()(1, 6) = 1.0;
T.GetPointMat()(2, 6) = 1.0;
T.GetPointMat()(0, 7) = 0.0;
T.GetPointMat()(1, 7) = 1.0;
T.GetPointMat()(2, 7) = 1.0;
T.SetFE(&HexahedronFE);
break;
case Element::WEDGE :
T.GetPointMat().SetSize(3, 6);
T.GetPointMat()(0, 0) = 0.0;
T.GetPointMat()(1, 0) = 0.0;
T.GetPointMat()(2, 0) = 0.0;
T.GetPointMat()(0, 1) = 1.0;
T.GetPointMat()(1, 1) = 0.0;
T.GetPointMat()(2, 1) = 0.0;
T.GetPointMat()(0, 2) = 0.0;
T.GetPointMat()(1, 2) = 1.0;
T.GetPointMat()(2, 2) = 0.0;
T.GetPointMat()(0, 3) = 0.0;
T.GetPointMat()(1, 3) = 0.0;
T.GetPointMat()(2, 3) = 1.0;
T.GetPointMat()(0, 4) = 1.0;
T.GetPointMat()(1, 4) = 0.0;
T.GetPointMat()(2, 4) = 1.0;
T.GetPointMat()(0, 5) = 0.0;
T.GetPointMat()(1, 5) = 1.0;
T.GetPointMat()(2, 5) = 1.0;
T.SetFE(&WedgeFE);
break;
case Element::PYRAMID :
T.GetPointMat().SetSize(3, 5);
T.GetPointMat()(0, 0) = 0.0;
T.GetPointMat()(1, 0) = 0.0;
T.GetPointMat()(2, 0) = 0.0;
T.GetPointMat()(0, 1) = 1.0;
T.GetPointMat()(1, 1) = 0.0;
T.GetPointMat()(2, 1) = 0.0;
T.GetPointMat()(0, 2) = 1.0;
T.GetPointMat()(1, 2) = 1.0;
T.GetPointMat()(2, 2) = 0.0;
T.GetPointMat()(0, 3) = 0.0;
T.GetPointMat()(1, 3) = 1.0;
T.GetPointMat()(2, 3) = 0.0;
T.GetPointMat()(0, 4) = 0.0;
T.GetPointMat()(1, 4) = 0.0;
T.GetPointMat()(2, 4) = 1.0;
T.SetFE(&PyramidFE);
break;
default:
MFEM_ABORT("Unknown element type \"" << ElemType << "\"");
break;
}
}
/**
* Tests fe->CalcVShape() over a grid of IntegrationPoints
* of resolution res. Also tests at integration points
* that are outside the element.
*/
void TestCalcVShape(FiniteElement* fe, ElementTransformation * T, int res)
{
int dim = fe->GetDim();
int dof = fe->GetDof();
Vector dofsx(dof);
Vector dofsy(dof);
Vector dofsz(dof);
Vector v(dim);
Vector vx(dim); vx = 0.0; vx[0] = 1.0;
Vector vy(dim); vy = 0.0;
if (dim > 1) { vy[1] = 1.0; }
Vector vz(dim); vz = 0.0;
if (dim > 2) { vz[2] = 1.0; }
DenseMatrix weights( dof, dim );
VectorConstantCoefficient vxCoef(vx);
VectorConstantCoefficient vyCoef(vy);
VectorConstantCoefficient vzCoef(vz);
fe->Project(vxCoef, *T, dofsx);
if (dim> 1) { fe->Project(vyCoef, *T, dofsy); }
if (dim> 2) { fe->Project(vzCoef, *T, dofsz); }
// Get a uniform grid or integration points
RefinedGeometry* ref = GlobGeometryRefiner.Refine( fe->GetGeomType(), res);
const IntegrationRule& intRule = ref->RefPts;
int npoints = intRule.GetNPoints();
for (int i=0; i < npoints; ++i)
{
// Get the current integration point from intRule
IntegrationPoint pt = intRule.IntPoint(i);
// Get several variants of this integration point
// some of which are inside the element and some are outside
Array<IntegrationPoint> ipArr;
GetRelatedIntegrationPoints( pt, dim, ipArr );
// For each such integration point check that the weights
// from CalcShape() sum to one
for (int j=0; j < ipArr.Size(); ++j)
{
IntegrationPoint& ip = ipArr[j];
// Pyramid basis functions are poorly behaved outside the
// reference pyramid
if (fe->GetGeomType() == Geometry::PYRAMID &&
(ip.z >= 1.0 || ip.y > 1.0 - ip.z || ip.x > 1.0 - ip.z)) { continue; }
CAPTURE(ip.x, ip.y, ip.z);
fe->CalcVShape(ip, weights);
weights.MultTranspose(dofsx, v);
REQUIRE( v[0] == Approx(1.) );
if (dim > 1)
{
weights.MultTranspose(dofsy, v);
REQUIRE( v[1] == Approx(1.) );
}
if (dim > 2)
{
weights.MultTranspose(dofsz, v);
REQUIRE( v[2] == Approx(1.) );
}
}
}
}
TEST_CASE("CalcVShape ND",
"[ND_SegmentElement]"
"[ND_TriangleElement]"
"[ND_QuadrilateralElement]"
"[ND_TetrahedronElement]"
"[ND_WedgeElement]"
"[ND_FuentesPyramidElement]"
"[ND_HexahedronElement]")
{
const int maxOrder = 5;
const int resolution = 10;
auto order = GENERATE_COPY(range(1, maxOrder + 1));
CAPTURE(order);
SECTION("ND_SegmentElement")
{
IsoparametricTransformation T;
GetReferenceTransformation(Element::SEGMENT, T);
ND_SegmentElement fe(order);
TestCalcVShape(&fe, &T, resolution);
}
SECTION("ND_TriangleElement")
{
IsoparametricTransformation T;
GetReferenceTransformation(Element::TRIANGLE, T);
ND_TriangleElement fe(order);
TestCalcVShape(&fe, &T, resolution);
}
SECTION("ND_QuadrilateralElement")
{
IsoparametricTransformation T;
GetReferenceTransformation(Element::QUADRILATERAL, T);
ND_QuadrilateralElement fe(order);
TestCalcVShape(&fe, &T, resolution);
}
SECTION("ND_TetrahedronElement")
{
IsoparametricTransformation T;
GetReferenceTransformation(Element::TETRAHEDRON, T);
ND_TetrahedronElement fe(order);
TestCalcVShape(&fe, &T, resolution);
}
SECTION("ND_WedgeElement")
{
IsoparametricTransformation T;
GetReferenceTransformation(Element::WEDGE, T);
ND_WedgeElement fe(order);
TestCalcVShape(&fe, &T, resolution);
}
SECTION("ND_FuentesPyramidElement")
{
IsoparametricTransformation T;
GetReferenceTransformation(Element::PYRAMID, T);
ND_FuentesPyramidElement fe(order);
TestCalcVShape(&fe, &T, resolution);
}
SECTION("ND_HexahedronElement")
{
IsoparametricTransformation T;
GetReferenceTransformation(Element::HEXAHEDRON, T);
ND_HexahedronElement fe(order);
TestCalcVShape(&fe, &T, resolution);
}
}
TEST_CASE("CalcVShape RT",
"[RT_TriangleElement]"
"[RT_QuadrilateralElement]"
"[RT_TetrahedronElement]"
"[RT_WedgeElement]"
"[RT_FuentesPyramidElement]"
"[RT_HexahedronElement]")
{
const int maxOrder = 5;
const int resolution = 10;
auto order = GENERATE_COPY(range(1, maxOrder + 1));
CAPTURE(order);
SECTION("RT_TriangleElement")
{
IsoparametricTransformation T;
GetReferenceTransformation(Element::TRIANGLE, T);
RT_TriangleElement fe(order);
TestCalcVShape(&fe, &T, resolution);
}
SECTION("RT_QuadrilateralElement")
{
IsoparametricTransformation T;
GetReferenceTransformation(Element::QUADRILATERAL, T);
RT_QuadrilateralElement fe(order);
TestCalcVShape(&fe, &T, resolution);
}
SECTION("RT_TetrahedronElement")
{
IsoparametricTransformation T;
GetReferenceTransformation(Element::TETRAHEDRON, T);
RT_TetrahedronElement fe(order);
TestCalcVShape(&fe, &T, resolution);
}
SECTION("RT_WedgeElement")
{
IsoparametricTransformation T;
GetReferenceTransformation(Element::WEDGE, T);
RT_WedgeElement fe(order);
TestCalcVShape(&fe, &T, resolution);
}
SECTION("RT_FuentesElement")
{
IsoparametricTransformation T;
GetReferenceTransformation(Element::PYRAMID, T);
RT_FuentesPyramidElement fe(order);
TestCalcVShape(&fe, &T, resolution);
}
SECTION("RT_HexahedronElement")
{
IsoparametricTransformation T;
GetReferenceTransformation(Element::HEXAHEDRON, T);
RT_HexahedronElement fe(order);
TestCalcVShape(&fe, &T, resolution);
}
}