Files
mfem/tests/unit/fem/test_doftrans.cpp
T
Will Pazner 11b878612b Use three-argument version of GetElementDofs…
…and related functions that return a pointer to an internal DofTransformation
object that may be invalidated unexpectedly.
2025-06-02 21:04:31 -07:00

334 lines
8.5 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"
using namespace mfem;
namespace doftrans
{
TEST_CASE("DoF Transformation Classes",
"[DofTransformation]"
"[ND_TetDofTransformation]")
{
int p = 4;
int vdim = 3;
int seed = 123;
double tol = 1e-13;
SECTION("Nedelec Tetrahedral Transformations")
{
ND_TetDofTransformation Tnd(p);
Array<int> ori(4);
ori[0] = 1;
ori[1] = 3;
ori[2] = 5;
ori[3] = 1;
SECTION("VDim == 1")
{
DofTransformation T(Tnd);
T.SetFaceOrientations(ori);
Vector u(T.Width());
Vector v(T.Width());
Vector f(T.Width());
Vector ut;
Vector vt;
Vector ft;
u.Randomize(seed);
v.Randomize(seed+1);
f.Randomize(seed+2);
SECTION("Inverse DoF transformation")
{
Vector w;
ut = u; T.TransformPrimal(ut);
w = ut; T.InvTransformPrimal(w);
w -= u;
REQUIRE(w.Norml2() < tol * u.Norml2());
}
SECTION("Inverse Dual DoF transformation")
{
Vector w;
ut = u; T.TransformDual(ut);
w = ut; T.InvTransformDual(w);
w -= u;
REQUIRE(w.Norml2() < tol * u.Norml2());
}
SECTION("Inner product with linear form f(v)")
{
vt = v; T.TransformPrimal(vt);
ft = f; T.TransformDual(ft);
double fv = f * v;
REQUIRE(fabs(fv - ft * vt) < tol * fabs(fv));
}
DenseMatrix A(T.Width());
{
Vector Ac;
for (int i=0; i<A.Width(); i++)
{
A.GetColumnReference(i, Ac);
Ac.Randomize(seed+i);
}
}
SECTION("Inner product of two primal vectors")
{
// The matrix A in this case should be regarded as a BilinearForm.
DenseMatrix tA;
DenseMatrix At;
DenseMatrix tAt;
ut = u; T.TransformPrimal(ut);
vt = v; T.TransformPrimal(vt);
At = A; T.TransformDualRows(At);
tA = A; T.TransformDualCols(tA);
tAt = A; T.TransformDual(tAt);
double uAv = A.InnerProduct(v, u);
REQUIRE(fabs(uAv - At.InnerProduct(vt, u )) < tol * fabs(uAv));
REQUIRE(fabs(uAv - tA.InnerProduct(v, ut)) < tol * fabs(uAv));
REQUIRE(fabs(uAv - tAt.InnerProduct(vt, ut)) < tol * fabs(uAv));
}
SECTION("Inner product of a primal vector and a dual vector")
{
// The matrix A in this case should be regarded as a
// DiscreteLinearOperator.
DenseMatrix tA;
DenseMatrix At;
DenseMatrix tAt;
ft = f; T.TransformDual(ft);
vt = v; T.TransformPrimal(vt);
At = A; T.TransformDualRows(At);
tA = A; T.TransformPrimalCols(tA);
tAt = At; T.TransformPrimalCols(tAt);
double fAv = A.InnerProduct(v, f);
REQUIRE(fabs(fAv - At.InnerProduct(vt, f )) < tol * fabs(fAv));
REQUIRE(fabs(fAv - tA.InnerProduct(v, ft)) < tol * fabs(fAv));
REQUIRE(fabs(fAv - tAt.InnerProduct(vt, ft)) < tol * fabs(fAv));
}
}
SECTION("VDim > 1")
{
Vector v(vdim * Tnd.Width());
Vector f(vdim * Tnd.Width());
Vector vt;
Vector ft;
v.Randomize(seed);
f.Randomize(seed+1);
SECTION("Ordering == byNODES")
{
DofTransformation T(Tnd, vdim, Ordering::byNODES);
T.SetFaceOrientations(ori);
SECTION("Inverse DoF transformation")
{
Vector w;
vt = v; T.TransformPrimal(vt);
w = vt; T.InvTransformPrimal(w);
w -= v;
REQUIRE(w.Norml2() < tol * v.Norml2());
}
SECTION("Inverse Dual DoF transformation")
{
Vector w;
vt = v; T.TransformDual(vt);
w = vt; T.InvTransformDual(w);
w -= v;
REQUIRE(w.Norml2() < tol * v.Norml2());
}
SECTION("Inner product with linear form f(v)")
{
vt = v; T.TransformPrimal(vt);
ft = f; T.TransformDual(ft);
double fv = f * v;
REQUIRE(fabs(fv - ft * vt) < tol * fabs(fv));
}
}
SECTION("Ordering == byVDIM")
{
DofTransformation T(Tnd, vdim, Ordering::byVDIM);
T.SetFaceOrientations(ori);
SECTION("Inverse DoF transformation")
{
Vector w;
vt = v; T.TransformPrimal(vt);
w = vt; T.InvTransformPrimal(w);
w -= v;
REQUIRE(w.Norml2() < tol * v.Norml2());
}
SECTION("Inverse Dual DoF transformation")
{
Vector w;
vt = v; T.TransformDual(vt);
w = vt; T.InvTransformDual(w);
w -= v;
REQUIRE(w.Norml2() < tol * v.Norml2());
}
SECTION("Inner product with linear form f(v)")
{
vt = v; T.TransformPrimal(vt);
ft = f; T.TransformDual(ft);
double fv = f * v;
REQUIRE(fabs(fv - ft * vt) < tol * fabs(fv));
}
}
}
}
}
TEST_CASE("DoF Transformation Functions",
"[DofTransformation]"
"[TransformPrimal]"
"[TransformDual]")
{
int p = 3, q = 4;
int seed = 123;
double tol = 1e-13;
ND_TetDofTransformation Tndp(p);
ND_TetDofTransformation Tndq(q);
Array<int> ori(4);
ori[0] = 1;
ori[1] = 3;
ori[2] = 5;
ori[3] = 1;
DofTransformation Tp(Tndp), Tq(Tndq);
Tp.SetFaceOrientations(ori);
Tq.SetFaceOrientations(ori);
DenseMatrix A(Tp.Width(), Tq.Width());
{
Vector Ac;
for (int i=0; i<A.Width(); i++)
{
A.GetColumnReference(i, Ac);
Ac.Randomize(seed+i);
}
}
SECTION("TransformPrimal")
{
// The matrix A in this case should be regarded as a
// DiscreteLinearOperator.
Vector v(Tq.Width());
Vector f(Tp.Width());
Vector vt;
Vector ft;
v.Randomize(seed);
f.Randomize(seed+1);
vt = v; Tq.TransformPrimal(vt);
ft = f; Tp.TransformDual(ft);
DenseMatrix nAn;
DenseMatrix tA;
DenseMatrix At;
DenseMatrix tAt;
DofTransformation id;
nAn = A; TransformPrimal(id, id, nAn);
At = A; TransformPrimal(id, Tq, At);
tA = A; TransformPrimal( Tp, id, tA);
tAt = A; TransformPrimal( Tp, Tq, tAt);
double fAv = A.InnerProduct(v, f);
REQUIRE(fabs(fAv - nAn.InnerProduct(v, f )) < tol * fabs(fAv));
REQUIRE(fabs(fAv - At.InnerProduct(vt, f )) < tol * fabs(fAv));
REQUIRE(fabs(fAv - tA.InnerProduct(v, ft)) < tol * fabs(fAv));
REQUIRE(fabs(fAv - tAt.InnerProduct(vt, ft)) < tol * fabs(fAv));
}
SECTION("TransformDual")
{
// The matrix A in this case should be regarded as a BilinearForm.
Vector u(Tp.Width());
Vector v(Tq.Width());
Vector ut;
Vector vt;
u.Randomize(seed);
v.Randomize(seed+1);
ut = u; Tp.TransformPrimal(ut);
vt = v; Tq.TransformPrimal(vt);
DenseMatrix nAn;
DenseMatrix tA;
DenseMatrix At;
DenseMatrix tAt;
DofTransformation id;
nAn = A; TransformDual(id, id, nAn);
At = A; TransformDual(id, Tq, At);
tA = A; TransformDual(Tp, id, tA);
tAt = A; TransformDual(Tp, Tq, tAt);
double uAv = A.InnerProduct(v, u);
REQUIRE(fabs(uAv - nAn.InnerProduct(v, u )) < tol * fabs(uAv));
REQUIRE(fabs(uAv - At.InnerProduct(vt, u )) < tol * fabs(uAv));
REQUIRE(fabs(uAv - tA.InnerProduct(v, ut)) < tol * fabs(uAv));
REQUIRE(fabs(uAv - tAt.InnerProduct(vt, ut)) < tol * fabs(uAv));
}
}
} // namespace doftrans