Files
mfem/tests/unit/fem/test_quadf_coef.cpp
T
Will Pazner 6602df4fd2 In QuadratureInterpolator::SupportsFESpace, return false for mixed meshes or variable orders
In QuadratureFunction::ProjectGridFunction unit test, test (element) QuadratureSpace also.
2025-10-17 14:58:46 -07:00

364 lines
10 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;
TEST_CASE("Quadrature Function Coefficients",
"[Coefficient][QuadratureFunction][QuadratureFunctionCoefficient]")
{
int order_h1 = 2, n = 4, dim = 3;
double tol = 1e-14;
Mesh mesh = Mesh::MakeCartesian3D(
n, n, n, Element::HEXAHEDRON, 1.0, 1.0, 1.0);
mesh.SetCurvature(order_h1);
int intOrder = 2 * order_h1 + 1;
QuadratureSpace qspace(&mesh, intOrder);
QuadratureFunction quadf_coeff(&qspace, 1);
QuadratureFunction quadf_vcoeff(&qspace, dim);
REQUIRE(quadf_coeff.UseDevice());
const IntegrationRule &ir = qspace.GetElementIntRule(0);
const GeometricFactors *geom_facts =
mesh.GetGeometricFactors(ir, GeometricFactors::COORDINATES);
{
int nelems = quadf_coeff.Size() / quadf_coeff.GetVDim() / ir.GetNPoints();
int vdim = ir.GetNPoints();
geom_facts->X.HostRead();
for (int i = 0; i < nelems; i++)
{
for (int j = 0; j < vdim; j++)
{
//X has dims nqpts x sdim x ne
quadf_coeff((i * vdim) + j) =
geom_facts->X((i * vdim * dim) + (vdim * 2) + j );
}
}
}
{
int nqpts = ir.GetNPoints();
int nelems = quadf_vcoeff.Size() / quadf_vcoeff.GetVDim() / nqpts;
int vdim = quadf_vcoeff.GetVDim();
for (int i = 0; i < nelems; i++)
{
for (int j = 0; j < vdim; j++)
{
for (int k = 0; k < nqpts; k++)
{
//X has dims nqpts x sdim x ne
quadf_vcoeff((i * nqpts * vdim) + (k * vdim ) + j) =
geom_facts->X((i * nqpts * vdim) + (j * nqpts) + k);
}
}
}
}
QuadratureFunctionCoefficient qfc(quadf_coeff);
VectorQuadratureFunctionCoefficient qfvc(quadf_vcoeff);
SECTION("Operators on VecQuadFuncCoeff")
{
#ifdef MFEM_USE_EXCEPTIONS
REQUIRE_THROWS(qfvc.SetComponent(3, 1));
REQUIRE_THROWS(qfvc.SetComponent(-1, 1));
REQUIRE_NOTHROW(qfvc.SetComponent(1, 2));
REQUIRE_THROWS(qfvc.SetComponent(0, 4));
REQUIRE_THROWS(qfvc.SetComponent(1, 3));
REQUIRE_NOTHROW(qfvc.SetComponent(0, 2));
REQUIRE_THROWS(qfvc.SetComponent(0, 0));
#endif
qfvc.SetComponent(0, 3);
}
SECTION("Operators on VectorQuadratureLFIntegrator")
{
H1_FECollection fec_h1(order_h1, dim);
FiniteElementSpace fespace_h1(&mesh, &fec_h1, dim);
GridFunction nodes(&fespace_h1);
mesh.GetNodes(nodes);
Vector output(nodes.Size());
output = 0.0;
LinearForm lf(&fespace_h1);
lf.AddDomainIntegrator(new VectorQuadratureLFIntegrator(qfvc, NULL));
lf.Assemble();
BilinearForm L2(&fespace_h1);
L2.AddDomainIntegrator(new VectorMassIntegrator());
L2.Assemble();
SparseMatrix mat = L2.SpMat();
mat.Mult(nodes, output);
output -= lf;
REQUIRE(output.Norml2() < tol);
}
SECTION("Operators on QuadratureLFIntegrator")
{
H1_FECollection fec_h1(order_h1, dim);
FiniteElementSpace fespace_h1(&mesh, &fec_h1, 1);
FiniteElementSpace fespace_h3(&mesh, &fec_h1, 3);
GridFunction nodes(&fespace_h3);
mesh.GetNodes(nodes);
Vector output(nodes.Size() / dim);
Vector nz(nodes.Size() / dim);
output = 0.0;
nz.MakeRef(nodes, nz.Size() * 2);
LinearForm lf(&fespace_h1);
lf.AddDomainIntegrator(new QuadratureLFIntegrator(qfc, NULL));
lf.Assemble();
BilinearForm L2(&fespace_h1);
L2.AddDomainIntegrator(new MassIntegrator(&ir));
L2.Assemble();
SparseMatrix mat = L2.SpMat();
mat.Mult(nz, output);
output -= lf;
REQUIRE(output.Norml2() < tol);
}
}
TEST_CASE("Quadrature Function Integration", "[QuadratureFunction][GPU]")
{
auto fname = GENERATE(
"../../data/star.mesh",
"../../data/star-q3.mesh",
"../../data/fichera.mesh",
"../../data/fichera-q3.mesh"
);
const int order = GENERATE(1, 2, 3);
CAPTURE(fname, order);
Mesh mesh = Mesh::LoadFromFile(fname);
H1_FECollection fec(1, mesh.Dimension());
FiniteElementSpace fes(&mesh, &fec);
int int_order = 2*order + 1;
SECTION("QuadratureSpace")
{
QuadratureSpace qs(&mesh, int_order);
// Make sure invalidating the cached weights works properly
qs.GetWeights();
mesh.Transform([](const Vector &xold, Vector &xnew)
{
xnew = xold;
xnew *= 1.1;
});
const IntegrationRule &ir = qs.GetIntRule(0);
QuadratureFunction qf(qs);
qf.Randomize(1);
QuadratureFunctionCoefficient qf_coeff(qf);
LinearForm lf(&fes);
lf.AddDomainIntegrator(new DomainLFIntegrator(qf_coeff, &ir));
lf.Assemble();
const double integ_1 = lf.Sum();
const double integ_2 = qf.Integrate();
const double integ_3 = qs.Integrate(qf_coeff);
REQUIRE(integ_1 == MFEM_Approx(integ_2));
REQUIRE(integ_1 == MFEM_Approx(integ_3));
}
SECTION("Vector-valued")
{
const int vdim = 3;
const int ordering = Ordering::byNODES;
FiniteElementSpace fes_vec(&mesh, &fec, vdim, ordering);
QuadratureSpace qs(&mesh, int_order);
const IntegrationRule &ir = qs.GetIntRule(0);
QuadratureFunction qf(qs, vdim);
qf.Randomize(1);
VectorQuadratureFunctionCoefficient qf_coeff(qf);
LinearForm lf(&fes_vec);
auto *integrator = new VectorDomainLFIntegrator(qf_coeff);
integrator->SetIntRule(&ir);
lf.AddDomainIntegrator(integrator);
lf.Assemble();
Vector integrals_1(vdim);
Vector integrals_2(vdim);
qf.Integrate(integrals_1);
qs.Integrate(qf_coeff, integrals_2);
const int ndof = fes.GetNDofs();
for (int vd = 0; vd < vdim; ++vd)
{
double integ = 0.0;
for (int i = 0; i < ndof; ++i)
{
integ += lf[i + vd*ndof];
}
REQUIRE(integ == MFEM_Approx(integrals_1[vd]));
REQUIRE(integ == MFEM_Approx(integrals_2[vd]));
}
}
SECTION("FaceQuadratureSpace")
{
FaceQuadratureSpace qs(mesh, int_order, FaceType::Boundary);
const IntegrationRule &ir = qs.GetIntRule(0);
QuadratureFunction qf(qs);
qf.Randomize(1);
QuadratureFunctionCoefficient qf_coeff(qf);
LinearForm lf(&fes);
auto *integ = new BoundaryLFIntegrator(qf_coeff);
integ->SetIntRule(&ir);
lf.AddBoundaryIntegrator(integ);
lf.Assemble();
const double integ_1 = lf.Sum();
const double integ_2 = qf.Integrate();
REQUIRE(integ_1 == MFEM_Approx(integ_2));
}
}
namespace lin_interp
{
double f3(const Vector &x);
void F3(const Vector &x, Vector &v);
}
TEST_CASE("Face Quadrature Function Coefficients", "[Coefficient]")
{
auto ftype = GENERATE(FaceType::Interior, FaceType::Boundary);
auto int_order = GENERATE(3, 5);
int n = 4, dim = 3;
Mesh mesh = Mesh::MakeCartesian3D(
n, n, n, Element::HEXAHEDRON, 1.0, 1.0, 1.0);
FunctionCoefficient f_coeff(lin_interp::f3);
VectorFunctionCoefficient vf_coeff(dim, lin_interp::F3);
FaceQuadratureSpace qspace(mesh, int_order, ftype);
QuadratureFunction qf(qspace);
QuadratureFunction vqf(&qspace, dim);
f_coeff.Project(qf);
vf_coeff.Project(vqf);
QuadratureFunctionCoefficient qf_coeff(qf);
VectorQuadratureFunctionCoefficient vqf_coeff(vqf);
for (int i = 0; i < qspace.GetNE(); ++i)
{
const IntegrationRule &ir = qspace.GetIntRule(i);
ElementTransformation &T = *qspace.GetTransformation(i);
for (int iq = 0; iq < ir.Size(); ++iq)
{
const IntegrationPoint &ip = ir[iq];
REQUIRE(f_coeff.Eval(T, ip) == qf_coeff.Eval(T, ip));
}
}
}
TEST_CASE("QuadratureFunction::ProjectGridFunction",
"[Coefficient][QuadratureFunction]")
{
const int order = GENERATE(1, 2);
const auto mesh_fname = GENERATE(
"../../data/star.mesh",
"../../data/star-mixed.mesh",
"../../data/fichera.mesh",
"../../data/fichera-mixed.mesh",
"../../data/inline-tri.mesh",
"../../data/inline-tet.mesh",
"../../data/inline-wedge.mesh",
"../../data/inline-pyramid.mesh"
);
CAPTURE(order, mesh_fname);
Mesh mesh(mesh_fname);
H1_FECollection fec(order, mesh.Dimension());
FiniteElementSpace fes(&mesh, &fec);
GridFunction gf(&fes);
gf.Randomize(1);
GridFunctionCoefficient coeff(&gf);
auto compare_qf_to_coeff = [](QuadratureFunction &qf, Coefficient &coeff)
{
auto &qs = *qf.GetSpace();
for (int i = 0; i < qs.GetNE(); ++i)
{
const IntegrationRule &ir = qs.GetIntRule(i);
ElementTransformation &T = *qs.GetTransformation(i);
Vector values;
qf.GetValues(i, values);
for (int iq = 0; iq < ir.Size(); ++iq)
{
const int iq_p = qs.GetPermutedIndex(i, iq);
const IntegrationPoint &ip = ir[iq];
REQUIRE(coeff.Eval(T, ip) == MFEM_Approx(values[iq_p]));
}
}
};
SECTION("QuadratureSpace")
{
QuadratureSpace qs(&mesh, order + 1);
QuadratureFunction qf(qs);
coeff.Project(qf);
compare_qf_to_coeff(qf, coeff);
}
SECTION("FaceQuadratureSpace")
{
const auto ftype = GENERATE(FaceType::Interior, FaceType::Boundary);
CAPTURE(ftype);
FaceQuadratureSpace qs(mesh, order + 1, ftype);
QuadratureFunction qf(qs);
coeff.Project(qf);
compare_qf_to_coeff(qf, coeff);
}
}