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

212 lines
6.2 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 "unit_tests.hpp"
using namespace mfem;
FiniteElement * GetH1PosFiniteElement(Geometry::Type type, int order)
{
FiniteElement *fe = NULL;
switch (type)
{
case Geometry::SEGMENT:
fe = new H1Pos_SegmentElement(order);
break;
case Geometry::TRIANGLE:
fe = new H1Pos_TriangleElement(order);
break;
case Geometry::SQUARE:
fe = new H1Pos_QuadrilateralElement(order);
break;
case Geometry::TETRAHEDRON:
fe = new H1Pos_TetrahedronElement(order);
break;
case Geometry::CUBE:
fe = new H1Pos_HexahedronElement(order);
break;
case Geometry::PRISM:
fe = new H1Pos_WedgeElement(order);
break;
case Geometry::PYRAMID:
fe = new H1Pos_PyramidElement(order);
break;
default:
break;
}
return fe;
}
FiniteElement * GetL2PosFiniteElement(Geometry::Type type, int order)
{
FiniteElement *fe = NULL;
switch (type)
{
case Geometry::SEGMENT:
fe = new L2Pos_SegmentElement(order);
break;
case Geometry::TRIANGLE:
fe = new L2Pos_TriangleElement(order);
break;
case Geometry::SQUARE:
fe = new L2Pos_QuadrilateralElement(order);
break;
case Geometry::TETRAHEDRON:
fe = new L2Pos_TetrahedronElement(order);
break;
case Geometry::CUBE:
fe = new L2Pos_HexahedronElement(order);
break;
case Geometry::PRISM:
fe = new L2Pos_WedgeElement(order);
break;
case Geometry::PYRAMID:
fe = new L2Pos_PyramidElement(order);
break;
default:
break;
}
return fe;
}
TEST_CASE("Positive H1 Bases",
"[H1Pos_SegmentElement]"
"[H1Pos_TriangleElement]"
"[H1Pos_QuadrilateralElement]"
"[H1Pos_TetrahedronElement]"
"[H1Pos_HexahedronElement]"
"[H1Pos_WedgeElement]"
"[H1Pos_PyramidElement]")
{
const int maxOrder = 5;
const int resolution = 10;
auto geom = GENERATE(Geometry::SEGMENT,
Geometry::TRIANGLE, Geometry::SQUARE,
Geometry::TETRAHEDRON, Geometry::CUBE,
Geometry::PRISM, Geometry::PYRAMID);
auto p = GENERATE_COPY(range(1, maxOrder + 1));
CAPTURE(geom);
CAPTURE(p);
SECTION("H1 Basis Summation")
{
FiniteElement *fe = GetH1PosFiniteElement(geom, p);
int dim = fe->GetDim();
int ndof = fe->GetDof();
Vector ones(ndof); ones = 1.0;
Vector zeros(dim);
Vector shape(ndof);
DenseMatrix dshape(ndof, dim);
// Get a uniform grid of integration points
RefinedGeometry* ref = GlobGeometryRefiner.Refine( fe->GetGeomType(),
resolution);
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);
fe->CalcShape(pt, shape);
// Verify that the basis functions are non-negative
REQUIRE(shape.Min() >= -2*std::numeric_limits<real_t>::epsilon());
// Verify that the basis functions sum to one
REQUIRE(shape * ones == MFEM_Approx(1.0));
// Verify that the basis functions are non-negative
REQUIRE(shape.Norml1() == MFEM_Approx(1.0));
fe->CalcDShape(pt, dshape);
dshape.MultTranspose(ones, zeros);
// Verify that the gradients sum to zero
REQUIRE(zeros.Norml2() == MFEM_Approx(0.0));
}
delete fe;
}
}
TEST_CASE("Positive L2 Bases",
"[L2Pos_SegmentElement]"
"[L2Pos_TriangleElement]"
"[L2Pos_QuadrilateralElement]"
"[L2Pos_TetrahedronElement]"
"[L2Pos_HexahedronElement]"
"[L2Pos_WedgeElement]"
"[L2Pos_PyramidElement]")
{
const int maxOrder = 5;
const int resolution = 10;
auto geom = GENERATE(Geometry::SEGMENT,
Geometry::TRIANGLE, Geometry::SQUARE,
Geometry::TETRAHEDRON, Geometry::CUBE,
Geometry::PRISM, Geometry::PYRAMID);
auto p = GENERATE_COPY(range(0, maxOrder + 1));
CAPTURE(geom);
CAPTURE(p);
SECTION("L2 Basis Summation")
{
FiniteElement *fe = GetL2PosFiniteElement(geom, p);
int dim = fe->GetDim();
int ndof = fe->GetDof();
Vector ones(ndof); ones = 1.0;
Vector zeros(dim);
Vector shape(ndof);
DenseMatrix dshape(ndof, dim);
// Get a uniform grid of integration points
RefinedGeometry* ref = GlobGeometryRefiner.Refine( fe->GetGeomType(),
resolution);
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);
fe->CalcShape(pt, shape);
// Verify that the basis functions are non-negative
REQUIRE(shape.Min() >= -2*std::numeric_limits<real_t>::epsilon());
// Verify that the basis functions sum to one
REQUIRE(shape * ones == MFEM_Approx(1.0));
// Verify that the basis functions are non-negative
REQUIRE(shape.Norml1() == MFEM_Approx(1.0));
fe->CalcDShape(pt, dshape);
dshape.MultTranspose(ones, zeros);
// Verify that the gradients sum to zero
REQUIRE(zeros.Norml2() == MFEM_Approx(0.0));
}
delete fe;
}
}