Files
mfem/tests/unit/mesh/test_submesh.cpp
T

428 lines
11 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 <iomanip>
#include <memory>
#include "unit_tests.hpp"
using namespace mfem;
enum FECType
{
H1,
L2
};
enum FieldType
{
SCALAR,
VECTOR
};
enum TransferType
{
ParentToSub,
SubToParent
};
FiniteElementCollection *create_fec(FECType fec_type, int p, int dim)
{
switch (fec_type)
{
case H1:
return new H1_FECollection(p, dim);
break;
case L2:
return new L2_FECollection(p, dim, BasisType::GaussLobatto);
break;
}
return nullptr;
}
void test_2d(Element::Type element_type,
FECType fec_type,
FieldType field_type,
int polynomial_order,
int mesh_polynomial_order,
TransferType transfer_type,
SubMesh::From from)
{
constexpr int dim = 2;
const int vdim = (field_type == FieldType::SCALAR) ? 1 : dim;
double Hy = 1.0;
Mesh mesh = Mesh::MakeCartesian2D(5, 5, element_type, true, 1.0, Hy, false);
if (from == SubMesh::From::Boundary)
{
for (int i = 0; i < mesh.GetNBE(); i++)
{
Element *el = mesh.GetBdrElement(i);
el->SetAttribute(1);
Array<int> vertices;
el->GetVertices(vertices);
bool all_vtx_inside = true;
for (int j = 0; j < vertices.Size(); j++)
{
if (mesh.GetVertex(vertices[j])[1] < 1.0)
{
all_vtx_inside = false;
}
}
if (all_vtx_inside)
{
el->SetAttribute(2);
}
}
}
else if (from == SubMesh::From::Domain)
{
for (int i = 0; i < mesh.GetNE(); i++)
{
Element *el = mesh.GetElement(i);
el->SetAttribute(1);
Array<int> vertices;
el->GetVertices(vertices);
for (int j = 0; j < vertices.Size(); j++)
{
double *coords = mesh.GetVertex(vertices[j]);
if (coords[0] >= 0.25 &&
coords[0] <= 0.75 &&
coords[1] >= 0.25 &&
coords[1] <= 0.75)
{
el->SetAttribute(2);
}
}
}
}
mesh.SetAttributes();
// Deform original mesh
mesh.EnsureNodes();
mesh.SetCurvature(mesh_polynomial_order);
auto node_movement_coeff = VectorFunctionCoefficient(mesh.Dimension(),
[](const Vector &coords, Vector &u)
{
double x = coords(0);
double y = coords(1);
u(0) = x;
u(1) = y + 0.05 * sin(x * 2.0 * M_PI);
});
mesh.Transform(node_movement_coeff);
FiniteElementCollection *fec = create_fec(fec_type, polynomial_order, dim);
FiniteElementSpace parent_fes(&mesh, fec, vdim);
GridFunction parent_gf(&parent_fes);
parent_gf = 0.0;
auto coeff = FunctionCoefficient([](const Vector &coords)
{
double x = coords(0);
double y = coords(1);
return y + 0.05 * sin(x * 2.0 * M_PI);
});
auto vcoeff = VectorFunctionCoefficient(dim, [](const Vector &coords,
Vector &V)
{
V.SetSize(2);
double x = coords(0);
double y = coords(1);
V(0) = y + 0.05 * sin(x * 2.0 * M_PI);
V(1) = x + 0.05 * sin(y * 2.0 * M_PI);
});
Array<int> subdomain_attributes(1);
subdomain_attributes[0] = 2;
SubMesh* submesh = nullptr;
if (from == SubMesh::From::Domain)
{
submesh = new SubMesh(SubMesh::CreateFromDomain(mesh, subdomain_attributes));
}
else
{
submesh = new SubMesh(SubMesh::CreateFromBoundary(mesh, subdomain_attributes));
}
REQUIRE(submesh->GetNE() != 0);
FiniteElementCollection *sub_fec = create_fec(fec_type, polynomial_order,
submesh->Dimension());
FiniteElementSpace sub_fes(submesh, sub_fec, vdim);
GridFunction sub_gf(&sub_fes);
sub_gf = 0.0;
if (transfer_type == ParentToSub)
{
GridFunction sub_ex_gf(&sub_fes);
if (vdim == 1)
{
parent_gf.ProjectCoefficient(coeff);
sub_ex_gf.ProjectCoefficient(coeff);
}
else
{
parent_gf.ProjectCoefficient(vcoeff);
sub_ex_gf.ProjectCoefficient(vcoeff);
}
SubMesh::Transfer(parent_gf, sub_gf);
sub_gf -= sub_ex_gf;
REQUIRE(sub_gf.Norml2() < 1e-10);
}
else if (transfer_type == SubToParent)
{
GridFunction parent_ex_gf(&parent_fes);
if (vdim == 1)
{
parent_gf.ProjectCoefficient(coeff);
sub_gf.ProjectCoefficient(coeff);
parent_ex_gf.ProjectCoefficient(coeff);
}
else
{
parent_gf.ProjectCoefficient(vcoeff);
sub_gf.ProjectCoefficient(vcoeff);
parent_ex_gf.ProjectCoefficient(vcoeff);
}
SubMesh::Transfer(sub_gf, parent_gf);
parent_gf -= parent_ex_gf;
REQUIRE(parent_gf.Norml2() < 1e-10);
}
}
void test_3d(Element::Type element_type,
FECType fec_type,
FieldType field_type,
int polynomial_order,
int mesh_polynomial_order,
TransferType transfer_type,
SubMesh::From from)
{
constexpr int dim = 3;
const int vdim = (field_type == FieldType::SCALAR) ? 1 : dim;
double Hy = 1.0;
Mesh mesh = Mesh::MakeCartesian3D(5, 5, 5, element_type, 1.0, Hy, 1.0, false);
if (from == SubMesh::From::Boundary)
{
for (int i = 0; i < mesh.GetNBE(); i++)
{
Element *el = mesh.GetBdrElement(i);
el->SetAttribute(1);
Array<int> vertices;
el->GetVertices(vertices);
bool all_vtx_inside = true;
for (int j = 0; j < vertices.Size(); j++)
{
if (mesh.GetVertex(vertices[j])[1] < Hy)
{
all_vtx_inside = false;
}
}
if (all_vtx_inside)
{
el->SetAttribute(2);
}
}
}
else if (from == SubMesh::From::Domain)
{
for (int i = 0; i < mesh.GetNE(); i++)
{
Element *el = mesh.GetElement(i);
el->SetAttribute(1);
Array<int> vertices;
el->GetVertices(vertices);
bool all_vtx_inside = true;
for (int j = 0; j < vertices.Size(); j++)
{
if (mesh.GetVertex(vertices[j])[1] > 0.5 * Hy)
{
all_vtx_inside = false;
}
}
if (all_vtx_inside)
{
el->SetAttribute(2);
}
}
}
mesh.SetAttributes();
// Deform original mesh
mesh.EnsureNodes();
mesh.SetCurvature(mesh_polynomial_order);
auto node_movement_coeff = VectorFunctionCoefficient(mesh.Dimension(),
[](const Vector &coords, Vector &u)
{
double x = coords(0);
double y = coords(1);
double z = coords(2);
u(0) = x;
u(1) = y + 0.05 * sin(x * 2.0 * M_PI);
u(2) = z;
});
mesh.Transform(node_movement_coeff);
FiniteElementCollection *fec = create_fec(fec_type, polynomial_order, dim);
FiniteElementSpace parent_fes(&mesh, fec, vdim);
GridFunction parent_gf(&parent_fes);
auto coeff = FunctionCoefficient([](const Vector &coords)
{
double x = coords(0);
double y = coords(1);
double z = coords(2);
return y + 0.05 * sin(x * 2.0 * M_PI) + z;
});
auto vcoeff = VectorFunctionCoefficient(dim, [](const Vector &coords,
Vector &V)
{
V.SetSize(3);
double x = coords(0);
double y = coords(1);
double z = coords(2);
V(0) = y + 0.05 * sin(x * 2.0 * M_PI) + z;
V(1) = z + 0.05 * sin(y * 2.0 * M_PI) + x;
V(2) = x + 0.05 * sin(z * 2.0 * M_PI) + y;
});
Array<int> subdomain_attributes(1);
subdomain_attributes[0] = 2;
SubMesh* submesh = nullptr;
if (from == SubMesh::From::Domain)
{
submesh = new SubMesh(SubMesh::CreateFromDomain(mesh, subdomain_attributes));
}
else
{
submesh = new SubMesh(SubMesh::CreateFromBoundary(mesh, subdomain_attributes));
}
REQUIRE(submesh->GetNE() != 0);
FiniteElementCollection *sub_fec = create_fec(fec_type, polynomial_order,
submesh->Dimension());
FiniteElementSpace sub_fes(submesh, sub_fec, vdim);
GridFunction sub_gf(&sub_fes);
sub_gf = 0.0;
if (transfer_type == ParentToSub)
{
GridFunction sub_ex_gf(&sub_fes);
if (vdim == 1)
{
parent_gf.ProjectCoefficient(coeff);
sub_ex_gf.ProjectCoefficient(coeff);
}
else
{
parent_gf.ProjectCoefficient(vcoeff);
sub_ex_gf.ProjectCoefficient(vcoeff);
}
SubMesh::Transfer(parent_gf, sub_gf);
REQUIRE(sub_gf.Norml2() != 0.0);
sub_gf -= sub_ex_gf;
REQUIRE(sub_gf.Norml2() < 1e-10);
}
else if (transfer_type == SubToParent)
{
GridFunction parent_ex_gf(&parent_fes);
if (vdim == 1)
{
parent_gf.ProjectCoefficient(coeff);
sub_gf.ProjectCoefficient(coeff);
parent_ex_gf.ProjectCoefficient(coeff);
}
else
{
parent_gf.ProjectCoefficient(vcoeff);
sub_gf.ProjectCoefficient(vcoeff);
parent_ex_gf.ProjectCoefficient(vcoeff);
}
SubMesh::Transfer(sub_gf, parent_gf);
REQUIRE(parent_gf.Norml2() != 0.0);
parent_gf -= parent_ex_gf;
REQUIRE(parent_gf.Norml2() < 1e-10);
}
}
TEST_CASE("SubMesh", "[SubMesh]")
{
int polynomial_order = 4;
int mesh_polynomial_order = 2;
auto fec_type = GENERATE(FECType::H1, FECType::L2);
auto field_type = GENERATE(FieldType::SCALAR, FieldType::VECTOR);
auto transfer_type = GENERATE(TransferType::ParentToSub,
TransferType::SubToParent);
auto from = GENERATE(SubMesh::From::Domain,
SubMesh::From::Boundary);
SECTION("2D")
{
auto element = GENERATE(Element::QUADRILATERAL, Element::TRIANGLE);
if (fec_type == FECType::L2 && from == SubMesh::From::Boundary && false)
{
return;
}
test_2d(element, fec_type, field_type, polynomial_order,
mesh_polynomial_order, transfer_type, from);
}
SECTION("3D")
{
auto element = GENERATE(Element::HEXAHEDRON, Element::TETRAHEDRON);
if (fec_type == FECType::L2 &&
from == SubMesh::From::Boundary && false)
{
return;
}
test_3d(element, fec_type, field_type, polynomial_order,
mesh_polynomial_order, transfer_type, from);
}
}