303 lines
9.4 KiB
C++
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
|