Files
mfem/tests/unit/mesh/test_ncmesh.cpp
T
2022-12-02 14:54:17 -08:00

303 lines
9.4 KiB
C++

// Copyright (c) 2010-2022, 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"
namespace mfem
{
constexpr double EPS = 1e-10;
// Test case: Verify that a conforming mesh yields the same norm for the
// assembled diagonal with PA when using the standard (conforming)
// Mesh vs. the corresponding (non-conforming) NCMesh.
// (note: permutations of the values in the diagonal are expected)
TEST_CASE("NCMesh PA diagonal", "[NCMesh]")
{
SECTION("Quad mesh")
{
int ne = 2;
Mesh mesh = Mesh::MakeCartesian2D(
ne, ne, Element::QUADRILATERAL, 1, 1.0, 1.0);
Mesh nc_mesh = Mesh::MakeCartesian2D(
ne, ne, Element::QUADRILATERAL, 1, 1.0, 1.0);
nc_mesh.EnsureNCMesh();
mesh.UniformRefinement();
nc_mesh.UniformRefinement();
int dim = 2;
for (int order = 1; order <= 3; ++order)
{
ND_FECollection fec(order, dim);
FiniteElementSpace fes(&mesh, &fec);
FiniteElementSpace nc_fes(&nc_mesh, &fec);
BilinearForm a(&fes);
BilinearForm nc_a(&nc_fes);
a.SetAssemblyLevel(AssemblyLevel::PARTIAL);
nc_a.SetAssemblyLevel(AssemblyLevel::PARTIAL);
ConstantCoefficient coef(1.0);
a.AddDomainIntegrator(new CurlCurlIntegrator(coef));
nc_a.AddDomainIntegrator(new CurlCurlIntegrator(coef));
a.Assemble();
nc_a.Assemble();
Vector diag(fes.GetTrueVSize());
Vector nc_diag(nc_fes.GetTrueVSize());
a.AssembleDiagonal(diag);
nc_a.AssembleDiagonal(nc_diag);
double error = fabs(diag.Norml2() - nc_diag.Norml2());
CAPTURE(order, error);
REQUIRE(error == MFEM_Approx(0.0, EPS));
}
}
SECTION("Hexa mesh")
{
int ne = 2;
Mesh mesh = Mesh::MakeCartesian3D(
ne, ne, ne, Element::HEXAHEDRON, 1.0, 1.0, 1.0);
Mesh nc_mesh = Mesh::MakeCartesian3D(
ne, ne, ne, Element::HEXAHEDRON, 1.0, 1.0, 1.0);
nc_mesh.EnsureNCMesh();
mesh.UniformRefinement();
nc_mesh.UniformRefinement();
int dim = 3;
for (int order = 1; order <= 3; ++order)
{
ND_FECollection fec(order, dim);
FiniteElementSpace fes(&mesh, &fec);
FiniteElementSpace nc_fes(&nc_mesh, &fec);
BilinearForm a(&fes);
BilinearForm nc_a(&nc_fes);
a.SetAssemblyLevel(AssemblyLevel::PARTIAL);
nc_a.SetAssemblyLevel(AssemblyLevel::PARTIAL);
ConstantCoefficient coef(1.0);
a.AddDomainIntegrator(new CurlCurlIntegrator(coef));
nc_a.AddDomainIntegrator(new CurlCurlIntegrator(coef));
a.Assemble();
nc_a.Assemble();
Vector diag(fes.GetTrueVSize());
Vector nc_diag(nc_fes.GetTrueVSize());
a.AssembleDiagonal(diag);
nc_a.AssembleDiagonal(nc_diag);
double error = fabs(diag.Sum() - nc_diag.Sum());
CAPTURE(order, error);
REQUIRE(error == MFEM_Approx(0.0, EPS));
}
}
} // test case
TEST_CASE("NCMesh 3D Refined Volume", "[NCMesh]")
{
auto mesh_fname = GENERATE("../../data/ref-tetrahedron.mesh",
"../../data/ref-cube.mesh",
"../../data/ref-prism.mesh",
"../../data/ref-pyramid.mesh"
);
auto ref_type = GENERATE(Refinement::X,
Refinement::Y,
Refinement::Z,
Refinement::XY,
Refinement::XZ,
Refinement::YZ,
Refinement::XYZ);
Mesh mesh(mesh_fname, 1, 1);
mesh.EnsureNCMesh(true);
double original_volume = mesh.GetElementVolume(0);
Array<Refinement> ref(1);
ref[0].ref_type = ref_type; ref[0].index = 0;
mesh.GeneralRefinement(ref, 1);
double summed_volume = 0.0;
for (int i = 0; i < mesh.GetNE(); ++i)
{
summed_volume += mesh.GetElementVolume(i);
}
REQUIRE(summed_volume == MFEM_Approx(original_volume));
} // test case
TEST_CASE("NCMesh 3D Derefined Volume", "[NCMesh]")
{
auto mesh_fname = GENERATE("../../data/ref-tetrahedron.mesh",
"../../data/ref-cube.mesh",
"../../data/ref-prism.mesh",
"../../data/ref-pyramid.mesh"
);
auto ref_type = GENERATE(Refinement::XYZ);
Mesh mesh(mesh_fname, 1, 1);
mesh.EnsureNCMesh(true);
double original_volume = mesh.GetElementVolume(0);
Array<Refinement> ref(1);
ref[0].ref_type = ref_type; ref[0].index = 0;
mesh.GeneralRefinement(ref, 1);
Array<double> elem_error(mesh.GetNE());
for (int i = 0; i < mesh.GetNE(); ++i)
{
elem_error[i] = 0.0;
}
mesh.DerefineByError(elem_error, 1.0);
double derefined_volume = mesh.GetElementVolume(0);
REQUIRE(derefined_volume == MFEM_Approx(original_volume));
} // test case
#ifdef MFEM_USE_MPI
// Test case: Verify that a conforming mesh yields the same norm for the
// assembled diagonal with PA when using the standard (conforming)
// Mesh vs. the corresponding (non-conforming) NCMesh.
// (note: permutations of the values in the diagonal are expected)
TEST_CASE("pNCMesh PA diagonal", "[Parallel], [NCMesh]")
{
int rank;
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
SECTION("Quad pmesh")
{
int ne = 2;
Mesh mesh = Mesh::MakeCartesian2D(
ne, ne, Element::QUADRILATERAL, 1, 1.0, 1.0);
Mesh nc_mesh = Mesh::MakeCartesian2D(
ne, ne, Element::QUADRILATERAL, 1, 1.0, 1.0);
nc_mesh.EnsureNCMesh();
mesh.UniformRefinement();
nc_mesh.UniformRefinement();
ParMesh pmesh(MPI_COMM_WORLD, mesh);
ParMesh nc_pmesh(MPI_COMM_WORLD, nc_mesh);
int dim = 2;
for (int order = 1; order <= 3; ++order)
{
ND_FECollection fec(order, dim);
ParFiniteElementSpace pfes(&pmesh, &fec);
ParFiniteElementSpace nc_pfes(&nc_pmesh, &fec);
ParBilinearForm a(&pfes);
ParBilinearForm nc_a(&nc_pfes);
a.SetAssemblyLevel(AssemblyLevel::PARTIAL);
nc_a.SetAssemblyLevel(AssemblyLevel::PARTIAL);
ConstantCoefficient coef(1.0);
a.AddDomainIntegrator(new CurlCurlIntegrator(coef));
nc_a.AddDomainIntegrator(new CurlCurlIntegrator(coef));
a.Assemble();
nc_a.Assemble();
Vector diag(pfes.GetTrueVSize());
Vector nc_diag(nc_pfes.GetTrueVSize());
a.AssembleDiagonal(diag);
nc_a.AssembleDiagonal(nc_diag);
double diag_lsum = diag.Sum(), nc_diag_lsum = nc_diag.Sum();
double diag_gsum = 0.0, nc_diag_gsum = 0.0;
MPI_Allreduce(&diag_lsum, &diag_gsum, 1, MPI_DOUBLE, MPI_SUM,
MPI_COMM_WORLD);
MPI_Allreduce(&nc_diag_lsum, &nc_diag_gsum, 1, MPI_DOUBLE, MPI_SUM,
MPI_COMM_WORLD);
double error = fabs(diag_gsum - nc_diag_gsum);
CAPTURE(order, error);
REQUIRE(error == MFEM_Approx(0.0, EPS));
MPI_Barrier(MPI_COMM_WORLD);
}
}
SECTION("Hexa pmesh")
{
int ne = 2;
Mesh mesh = Mesh::MakeCartesian3D(
ne, ne, ne, Element::HEXAHEDRON, 1.0, 1.0, 1.0);
Mesh nc_mesh = Mesh::MakeCartesian3D(
ne, ne, ne, Element::HEXAHEDRON, 1.0, 1.0, 1.0);
nc_mesh.EnsureNCMesh();
mesh.UniformRefinement();
nc_mesh.UniformRefinement();
ParMesh pmesh(MPI_COMM_WORLD, mesh);
ParMesh nc_pmesh(MPI_COMM_WORLD, nc_mesh);
int dim = 3;
for (int order = 1; order <= 3; ++order)
{
ND_FECollection fec(order, dim);
ParFiniteElementSpace pfes(&pmesh, &fec);
ParFiniteElementSpace nc_pfes(&nc_pmesh, &fec);
ParBilinearForm a(&pfes);
ParBilinearForm nc_a(&nc_pfes);
a.SetAssemblyLevel(AssemblyLevel::PARTIAL);
nc_a.SetAssemblyLevel(AssemblyLevel::PARTIAL);
ConstantCoefficient coef(1.0);
a.AddDomainIntegrator(new CurlCurlIntegrator(coef));
nc_a.AddDomainIntegrator(new CurlCurlIntegrator(coef));
a.Assemble();
nc_a.Assemble();
Vector diag(pfes.GetTrueVSize());
Vector nc_diag(nc_pfes.GetTrueVSize());
a.AssembleDiagonal(diag);
nc_a.AssembleDiagonal(nc_diag);
double diag_lsum = diag.Sum(), nc_diag_lsum = nc_diag.Sum();
double diag_gsum = 0.0, nc_diag_gsum = 0.0;
MPI_Allreduce(&diag_lsum, &diag_gsum, 1, MPI_DOUBLE, MPI_SUM,
MPI_COMM_WORLD);
MPI_Allreduce(&nc_diag_lsum, &nc_diag_gsum, 1, MPI_DOUBLE, MPI_SUM,
MPI_COMM_WORLD);
double error = fabs(diag_gsum - nc_diag_gsum);
CAPTURE(order, error);
REQUIRE(error == MFEM_Approx(0.0, EPS));
MPI_Barrier(MPI_COMM_WORLD);
}
}
} // test case
#endif // MFEM_USE_MPI
} // namespace mfem