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

299 lines
7.7 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 "unit_tests.hpp"
#include "mfem.hpp"
using namespace mfem;
namespace hptransfer_test
{
int order=1;
double u(const Vector & x)
{
return pow(x.Sum(),order);
}
void vecu(const Vector & x, Vector & U)
{
for (int i = 0; i<x.Size(); i++)
{
U[i] = pow(x[i], order);
}
}
void RandomPRefinement(FiniteElementSpace & fes)
{
Mesh *mesh = fes.GetMesh();
for (int i = 0; i < mesh->GetNE(); i++)
{
if ((double) rand() / RAND_MAX < 0.5)
{
const int eorder = fes.GetElementOrder(i);
fes.SetElementOrder(i,eorder+1);
}
}
fes.Update(false);
}
/* This function randomly selects elements to be de-refined and sets the
order of the elements that share the same parent to their minimum */
void PreprocessRandomDerefinement(FiniteElementSpace & fes, Array<int> &drefs,
double prob=0.5)
{
Mesh * mesh = fes.GetMesh();
const Table & dereftable = mesh->ncmesh->GetDerefinementTable();
int dref = dereftable.Size();
for (int i = 0; i < dref; i++)
{
if ((double) rand() / RAND_MAX < prob)
{
drefs.Append(i);
}
}
// Go through the possible derefinements and set the orders to minimum
Array<int> row;
for (int i = 0; i<drefs.Size(); i++)
{
dereftable.GetRow(drefs[i], row);
int minorder = 100;
for (int j = 0; j<row.Size(); j++)
{
minorder = std::min(minorder, fes.GetElementOrder(row[j]));
}
// set the min order
for (int j = 0; j<row.Size(); j++)
{
fes.SetElementOrder(row[j],minorder);
}
}
fes.Update(false);
}
void Derefine(Mesh &mesh, const Array<int> &drefs)
{
const Table & dereftable = mesh.ncmesh->GetDerefinementTable();
Array<int> row;
Vector errors(mesh.GetNE()); errors = infinity();
for (int i = 0; i<drefs.Size(); i++)
{
dereftable.GetRow(drefs[i], row);
for (int j = 0; j<row.Size(); j++)
{
errors[row[j]] = 0.0;
}
}
mesh.DerefineByError(errors,1.0);
}
enum class Space {H1, L2, VectorH1, VectorL2};
TEST_CASE("hpTransfer", "[hpTransfer]")
{
auto space = GENERATE(Space::H1, Space::L2, Space::VectorH1, Space::VectorL2);
int dim = GENERATE(2,3);
auto simplex = GENERATE(false, true);
order = GENERATE(1,2);
auto relax_conformity = GENERATE(false, true);
/* No need to distinguish between relaxed and full conformity in the DG case*/
if ((space == Space::L2 || space == Space::VectorL2) && relax_conformity) { return; }
constexpr int ne = 3;
CAPTURE(space, dim, simplex, order, relax_conformity);
Mesh mesh;
if (dim == 2)
{
Element::Type type = simplex ? Element::TRIANGLE : Element::QUADRILATERAL;
mesh = Mesh::MakeCartesian2D(ne, ne, type, 1, 1.0, 1.0);
}
else
{
Element::Type type = simplex ? Element::TETRAHEDRON : Element::HEXAHEDRON;
mesh = Mesh::MakeCartesian3D(ne, ne, ne, type, 1.0, 1.0, 1.0);
}
mesh.EnsureNCMesh(true);
// 1. Set up initial state by randomly h- and p- refinement
mesh.RandomRefinement(0.5);
FiniteElementCollection * fec = nullptr;
if (space == Space::H1 || space == Space::VectorH1)
{
fec = new H1_FECollection(order, dim);
}
else
{
fec = new L2_FECollection(order, dim);
}
int dimc = (space<=Space::L2) ? 1 : dim;
FiniteElementSpace fes(&mesh, fec, dimc);
fes.SetRelaxedHpConformity(relax_conformity);
RandomPRefinement(fes);
// 2. Set up a GridFunction on the initial hp-mesh
FunctionCoefficient f(u);
VectorFunctionCoefficient vf(dim,vecu);
GridFunction gf(&fes); gf = 0.0;
if (space<=Space::L2)
{
gf.ProjectCoefficient(f);
}
else
{
gf.ProjectCoefficient(vf);
}
// 3. Randomly h-refine the mesh and transfer the GridFunction
mesh.RandomRefinement(0.5);
fes.Update();
gf.Update();
GridFunction err_gf(&fes);
if (space<=Space::L2)
{
err_gf.ProjectCoefficient(f);
}
else
{
err_gf.ProjectCoefficient(vf);
}
err_gf-= gf;
if (fes.GetHpRestrictionMatrix())
{
Vector tmp0(fes.GetHpRestrictionMatrix()->Height());
fes.GetHpRestrictionMatrix()->Mult(err_gf,tmp0);
fes.GetProlongationMatrix()->Mult(tmp0,err_gf);
}
// 3a. Check if the prolonged GridFunction to the h-refined
// mesh exactly reproduces the polynomial GridFunction
REQUIRE(err_gf.Norml2() < 1e-11);
// 4. Randomly p-refine the mesh and transfer the GridFunction
Mesh cmesh(mesh);
FiniteElementSpace cfes(&cmesh, fec, dimc);
cfes.SetRelaxedHpConformity(relax_conformity);
for (int i = 0; i<cmesh.GetNE(); i++)
{
cfes.SetElementOrder(i,fes.GetElementOrder(i));
}
cfes.Update(false);
RandomPRefinement(fes);
PRefinementTransferOperator T(cfes, fes);
GridFunction hpgf(&fes);
T.Mult(gf,hpgf);
err_gf.SetSpace(&fes);
if (space<=Space::L2)
{
err_gf.ProjectCoefficient(f);
}
else
{
err_gf.ProjectCoefficient(vf);
}
err_gf-= hpgf;
if (fes.GetHpRestrictionMatrix())
{
Vector tmp(fes.GetHpRestrictionMatrix()->Height());
fes.GetHpRestrictionMatrix()->Mult(err_gf,tmp);
fes.GetProlongationMatrix()->Mult(tmp,err_gf);
}
// 4a. Check if the prolonged GridFunction to the p-refined
// mesh exactly reproduces the polynomial GridFunction
REQUIRE(err_gf.Norml2() < 1e-11);
// 5. Before randomly de-refining the mesh ensure that the elements
// (of the same parent) that are going to be de-refined
// have the same order
Mesh fmesh(mesh);
FiniteElementSpace ffes(&fmesh, fec, dimc);
ffes.SetRelaxedHpConformity(relax_conformity);
for (int i = 0; i<fmesh.GetNE(); i++)
{
ffes.SetElementOrder(i,fes.GetElementOrder(i));
}
ffes.Update(false);
Array<int> drefs;
// lower the order of the children to their minimum
PreprocessRandomDerefinement(fes, drefs);
PRefinementTransferOperator T2(ffes, fes);
gf.SetSpace(&fes);
T2.Mult(hpgf, gf);
err_gf.SetSpace(&fes);
if (space<=Space::L2)
{
err_gf.ProjectCoefficient(f);
}
else
{
err_gf.ProjectCoefficient(vf);
}
err_gf-= gf;
if (fes.GetHpRestrictionMatrix())
{
Vector temp(fes.GetHpRestrictionMatrix()->Height());
fes.GetHpRestrictionMatrix()->Mult(err_gf,temp);
fes.GetProlongationMatrix()->Mult(temp,err_gf);
}
// 5a. Check if the restricted GridFunction to the p-derefined
// mesh exactly reproduces the polynomial GridFunction
REQUIRE(err_gf.Norml2() < 1e-11);
// 6. De-refine the mesh and transfer the GridFunction
Derefine(mesh,drefs);
fes.Update();
gf.Update();
err_gf.SetSpace(&fes); err_gf = 0.0;
if (space<=Space::L2)
{
err_gf.ProjectCoefficient(f);
}
else
{
err_gf.ProjectCoefficient(vf);
}
err_gf-= gf;
if (fes.GetHpRestrictionMatrix())
{
Vector temp(fes.GetHpRestrictionMatrix()->Height());
fes.GetHpRestrictionMatrix()->Mult(err_gf,temp);
fes.GetProlongationMatrix()->Mult(temp,err_gf);
}
// 6a. Check if the restricted GridFunction to the de-refined
// mesh exactly reproduces the polynomial GridFunction
REQUIRE(err_gf.Norml2() < 1e-11);
delete fec;
}
}