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

154 lines
4.4 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 test_GetGradient_shared_faces
{
int dim;
double func(const Vector &coord)
{
double x = coord(0),
y = (dim > 1) ? coord(1) : 0.0,
z = (dim > 2) ? coord(2) : 0.0;
return x * x + y * y + z * z;
}
void func_grad(const Vector &coord, Vector &grad)
{
grad.SetSize(coord.Size());
grad(0) = 2.0 * coord(0);
if (dim > 1) { grad(1) = 2.0 * coord(1); }
if (dim > 2) { grad(2) = 2.0 * coord(2); }
}
void vec_func(const Vector &coord, Vector &res)
{
double x = coord(0),
y = (dim > 1) ? coord(1) : 0.0,
z = (dim > 2) ? coord(2) : 0.0;
res(0) = (x + 1.0) * (y + 1.0) * (z + 1.0);
res(1) = x * x + y * y + z * z;
}
void vec_func_grad(const Vector &coord, DenseMatrix &grad)
{
grad.SetSize(2, dim);
double x = coord(0),
y = (dim > 1) ? coord(1) : 0.0,
z = (dim> 2) ? coord(2) : 0.0;
grad(0, 0) = (y + 1.0) * (z + 1.0);
grad(1, 0) = 2.0 * x;
if (dim > 1)
{
grad(0, 1) = (x + 1.0) * (z + 1.0);
grad(1, 1) = 2.0 * y;
}
if (dim > 2)
{
grad(0, 2) = (x + 1.0) * (y + 1.0);
grad(1, 2) = 2.0 * z;
}
}
#ifdef MFEM_USE_MPI
TEST_CASE("GetGradient Shared Faces", "[ParGridFunction][Parallel]")
{
const int fe_order = 3;
for (dim = 1; dim <= 3; dim++)
{
int num_procs;
MPI_Comm_size(MPI_COMM_WORLD, &num_procs);
int myid;
MPI_Comm_rank(MPI_COMM_WORLD, &myid);
Mesh mesh;
if (dim == 1)
{
mesh = Mesh::MakeCartesian1D(100, 1.0);
}
else if (dim == 2)
{
mesh = Mesh::LoadFromFile("../../data/star-mixed.mesh");
}
else
{
mesh = Mesh::LoadFromFile("../../data/fichera-mixed.mesh");
}
for (int i = 0; i < 2; i++) { mesh.UniformRefinement(); }
ParMesh pmesh(MPI_COMM_WORLD, mesh);
FunctionCoefficient func_coeff(func);
VectorFunctionCoefficient vec_func_coeff(2, vec_func);
H1_FECollection fec(fe_order, dim);
ParFiniteElementSpace pfes(&pmesh, &fec), pfes_vec(&pmesh, &fec, 2);
ParGridFunction pgf(&pfes), pgf_vec(&pfes_vec);
pgf.ProjectCoefficient(func_coeff);
pgf_vec.ProjectCoefficient(vec_func_coeff);
pgf.ExchangeFaceNbrData();
pgf_vec.ExchangeFaceNbrData();
double max_error_grad = 0.0, max_error_grads = 0.0,
max_error_vec_grad = 0.0;
for (int f = 0; f < pmesh.GetNSharedFaces(); f++)
{
auto tr_f = pmesh.GetSharedFaceTransformations(f);
ElementTransformation &tr_e2 = tr_f->GetElement2Transformation();
const IntegrationRule &ir_f = IntRules.Get(tr_f->GetGeometryType(),
2*fe_order + 2);
IntegrationRule ir_e2(ir_f.GetNPoints());
tr_f->Loc2.Transform(ir_f, ir_e2);
Vector grad_e2, grad_exact, coord(dim);
DenseMatrix grads_e2, vec_grad_e2, vec_grad_exact;
pgf.GetGradients(tr_e2, ir_e2, grads_e2);
for (int q = 0; q < ir_f.GetNPoints(); q++)
{
const IntegrationPoint &ip_f = ir_f.IntPoint(q);
tr_f->SetIntPoint(&ip_f);
tr_f->Transform(ip_f, coord);
func_grad(coord, grad_exact);
pgf.GetGradient(tr_e2, grad_e2);
grad_e2 -= grad_exact;
max_error_grad = fmax(max_error_grad, grad_e2.Norml2());
grads_e2.GetColumn(q, grad_e2);
grad_e2 -= grad_exact;
max_error_grads = fmax(max_error_grads, grad_e2.Norml2());
pgf_vec.GetVectorGradient(tr_e2, vec_grad_e2);
vec_func_grad(coord, vec_grad_exact);
vec_grad_e2 -= vec_grad_exact;
max_error_vec_grad = fmax(max_error_vec_grad, vec_grad_e2.FNorm2());
}
}
REQUIRE(max_error_grad == MFEM_Approx(0.0));
REQUIRE(max_error_grads == MFEM_Approx(0.0));
REQUIRE(max_error_vec_grad == MFEM_Approx(0.0));
}
}
#endif
}