180 lines
6.0 KiB
C++
180 lines
6.0 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"
|
|
|
|
#include "fem/qinterp/grad.hpp"
|
|
|
|
using namespace std;
|
|
using namespace mfem;
|
|
|
|
static IntegrationRule PermuteIR(const IntegrationRule *irule,
|
|
const Array<int> &perm)
|
|
{
|
|
const int np = irule->GetNPoints();
|
|
MFEM_VERIFY(np == perm.Size(), "Invalid permutation size");
|
|
IntegrationRule ir(np);
|
|
ir.SetOrder(irule->GetOrder());
|
|
|
|
for (int i = 0; i < np; i++)
|
|
{
|
|
IntegrationPoint &ip_new = ir.IntPoint(i);
|
|
const IntegrationPoint &ip_old = irule->IntPoint(perm[i]);
|
|
ip_new.Set(ip_old.x, ip_old.y, ip_old.z, ip_old.weight);
|
|
}
|
|
|
|
return ir;
|
|
}
|
|
|
|
TEST_CASE("Collocated Derivative Kernels", "[QuadratureInterpolator]")
|
|
{
|
|
// Add some specializations for the kernels
|
|
// DIM, LAYOUT, PHYS, VDIM, D1D, Q1D
|
|
QuadratureInterpolator::GradKernels::Specialization
|
|
<1, QVectorLayout::byNODES, false, 1, 2, 2>::Add();
|
|
QuadratureInterpolator::GradKernels::Specialization
|
|
<1, QVectorLayout::byNODES, true, 1, 2, 2>::Add();
|
|
|
|
QuadratureInterpolator::CollocatedGradKernels::Specialization
|
|
<1, QVectorLayout::byNODES, false, 1, 2>::Add();
|
|
QuadratureInterpolator::CollocatedGradKernels::Specialization
|
|
<1, QVectorLayout::byNODES, true, 1, 2>::Add();
|
|
|
|
const auto mesh_fname = GENERATE(
|
|
"../../data/inline-segment.mesh",
|
|
"../../data/inline-quad.mesh",
|
|
"../../data/inline-hex.mesh",
|
|
"../../data/star.mesh",
|
|
"../../data/star-q3.mesh",
|
|
"../../data/fichera.mesh",
|
|
"../../data/fichera-q3.mesh",
|
|
"../../data/diag-segment-2d.mesh", // 1D mesh in 2D
|
|
"../../data/diag-segment-3d.mesh", // 1D mesh in 3D
|
|
"../../data/star-surf.mesh" // surface mesh
|
|
);
|
|
int p = GENERATE(range(1,7)); // element order, 1 <= p < 7
|
|
int vdim = GENERATE(1,2,3); // vector dimension for grid-function
|
|
|
|
const int seed = 0x100001b3;
|
|
Mesh mesh = Mesh::LoadFromFile(mesh_fname);
|
|
const int dim = mesh.Dimension();
|
|
const int sdim = mesh.SpaceDimension();
|
|
|
|
CAPTURE(mesh_fname, dim, sdim, p, vdim);
|
|
|
|
int nelem = mesh.GetNE();
|
|
|
|
const H1_FECollection fec(p, dim);
|
|
FiniteElementSpace fes(&mesh, &fec, vdim);
|
|
FiniteElementSpace nfes(&mesh, &fec, sdim);
|
|
|
|
GridFunction x(&fes);
|
|
VectorFunctionCoefficient gfc(vdim, [](const Vector &x, Vector &p)
|
|
{
|
|
for (int i = 0; i < p.Size(); i++)
|
|
{
|
|
p(i) = 0.0;
|
|
for (int j = 0; j < x.Size(); j++)
|
|
{
|
|
p(i) += std::pow(x(j), i+1.0);
|
|
}
|
|
}
|
|
});
|
|
x.ProjectCoefficient(gfc);
|
|
|
|
GridFunction nodes(&nfes);
|
|
mesh.SetNodalGridFunction(&nodes);
|
|
{
|
|
Array<int> dofs, vdofs;
|
|
GridFunction rdm(&nfes);
|
|
Vector h0(nfes.GetNDofs());
|
|
rdm.Randomize(seed);
|
|
rdm -= 0.5;
|
|
h0 = infinity();
|
|
for (int i = 0; i < mesh.GetNE(); i++)
|
|
{
|
|
nfes.GetElementDofs(i, dofs);
|
|
const real_t hi = mesh.GetElementSize(i);
|
|
for (int j = 0; j < dofs.Size(); j++)
|
|
{
|
|
h0(dofs[j]) = std::min(h0(dofs[j]), hi);
|
|
}
|
|
}
|
|
rdm.HostReadWrite();
|
|
for (int i = 0; i < nfes.GetNDofs(); i++)
|
|
{
|
|
for (int d = 0; d < sdim; d++)
|
|
{
|
|
rdm(nfes.DofToVDof(i,d)) *= (0.25/p)*h0(i);
|
|
}
|
|
}
|
|
for (int i = 0; i < nfes.GetNBE(); i++)
|
|
{
|
|
nfes.GetBdrElementVDofs(i, vdofs);
|
|
for (int j = 0; j < vdofs.Size(); j++) { rdm(vdofs[j]) = 0.0; }
|
|
}
|
|
nodes -= rdm;
|
|
}
|
|
|
|
const FiniteElement &fe = *(fes.GetFE(0));
|
|
const IntegrationRule irnodes = fe.GetNodes();
|
|
|
|
const NodalFiniteElement *nfe = dynamic_cast<const NodalFiniteElement*>
|
|
(&fe);
|
|
const Array<int> &irordering = nfe->GetLexicographicOrdering();
|
|
IntegrationRule ir = PermuteIR(&irnodes, irordering);
|
|
|
|
int nqp = ir.GetNPoints();
|
|
const DofToQuad maps = fe.GetDofToQuad(ir, DofToQuad::TENSOR);
|
|
auto geom = mesh.GetGeometricFactors(ir, GeometricFactors::JACOBIANS);
|
|
|
|
Vector evec_values;
|
|
const ElementDofOrdering ordering = ElementDofOrdering::LEXICOGRAPHIC;
|
|
const Operator *n0_R = fes.GetElementRestriction(ordering);
|
|
evec_values.SetSize(n0_R->Height());
|
|
n0_R->Mult(x, evec_values);
|
|
|
|
using GK = QuadratureInterpolator::GradKernels;
|
|
using CGK = QuadratureInterpolator::CollocatedGradKernels;
|
|
|
|
SECTION("Compare collocated kernels")
|
|
{
|
|
auto L = GENERATE(QVectorLayout::byNODES, QVectorLayout::byVDIM);
|
|
auto P = GENERATE(true, false);
|
|
|
|
CAPTURE(L, P);
|
|
|
|
const int nd = maps.ndof;
|
|
const int nq = maps.nqpt;
|
|
|
|
Vector qp_der(nelem*vdim*nqp*(P ? sdim : dim));
|
|
GK::Run(dim, L, P, vdim, nd, nq, nelem, maps.B.Read(),
|
|
maps.G.Read(), geom->J.Read(), evec_values.Read(),
|
|
qp_der.Write(), sdim, vdim, nd, nq);
|
|
|
|
Vector col_der(nelem*vdim*nqp*(P ? sdim : dim));
|
|
CGK::Run(dim, L, P, vdim, nd, nelem, maps.G.Read(), geom->J.Read(),
|
|
evec_values.Read(), col_der.Write(), sdim, vdim, nd);
|
|
|
|
const real_t max_norm = qp_der.Normlinf();
|
|
|
|
qp_der -= col_der;
|
|
|
|
const real_t abs_err = qp_der.Normlinf();
|
|
const real_t rel_err = max_norm > 0_r ?
|
|
abs_err/max_norm :
|
|
abs_err > 0_r ? mfem::infinity() : 0_r;
|
|
CAPTURE(rel_err, max_norm);
|
|
CHECK(rel_err <= 1e-13);
|
|
}
|
|
}
|