Files
mfem/tests/unit/fem/test_sparse_matrix.cpp
T
2020-10-29 13:03:10 -07:00

218 lines
6.0 KiB
C++

// Copyright (c) 2010-2020, 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 "catch.hpp"
#include "mfem.hpp"
#include <fstream>
#include <iostream>
using namespace mfem;
#ifdef MFEM_USE_MPI
namespace sparse_matrix_test
{
double coeff_function(const Vector &x)
{
return 1.0 + x[0]*x[0];
}
void velocity_function(const Vector &x, Vector &v)
{
int dim = x.Size();
switch (dim)
{
case 1: v(0) = 1.0; break;
case 2: v(0) = x(1); v(1) = -x(0); break;
case 3: v(0) = x(1); v(1) = -x(0); v(2) = x(0); break;
}
}
static std::string getString(AssemblyLevel assembly)
{
switch (assembly)
{
case AssemblyLevel::NONE:
return "NONE";
break;
case AssemblyLevel::PARTIAL:
return "PARTIAL";
break;
case AssemblyLevel::ELEMENT:
return "ELEMENT";
break;
case AssemblyLevel::FULL:
return "FULL";
break;
case AssemblyLevel::LEGACYFULL:
return "LEGACYFULL";
break;
}
mfem_error("Unknown AssemblyLevel.");
return "";
}
enum class Coeff {Const, Grid, Quad};
static std::string getString(Coeff coeff_type)
{
switch (coeff_type)
{
case Coeff::Const:
return "Const";
break;
case Coeff::Grid:
return "Grid";
break;
case Coeff::Quad:
return "Quad";
break;
}
mfem_error("Unknown CeedCoeff.");
return "";
}
enum class Problem {Mass, Convection, Diffusion};
static std::string getString(Problem pb)
{
switch (pb)
{
case Problem::Mass:
return "Mass";
break;
case Problem::Convection:
return "Convection";
break;
case Problem::Diffusion:
return "Diffusion";
break;
}
mfem_error("Unknown Problem.");
return "";
}
void test_sparse_matrix(const char* input, int order, const Coeff coeff_type,
const Problem pb, const AssemblyLevel assembly)
{
std::string section = "assembly: " + getString(assembly) + "\n" +
"coeff_type: " + getString(coeff_type) + "\n" +
"pb: " + getString(pb) + "\n" +
"order: " + std::to_string(order) + "\n" +
"mesh: " + input;
INFO(section);
Mesh mesh(input, 1, 1);
mesh.EnsureNodes();
mesh.UniformRefinement();
ParMesh pmesh(MPI_COMM_WORLD, mesh);
int dim = mesh.Dimension();
FiniteElementCollection *fec;
if (pb == Problem::Convection)
{
fec = new L2_FECollection(order, dim, BasisType::GaussLobatto);
}
else
{
fec = new H1_FECollection(order, dim);
}
ParFiniteElementSpace fes(&pmesh, fec);
ParBilinearForm k_test(&fes);
ParBilinearForm k_ref(&fes);
ParFiniteElementSpace coeff_fes(&pmesh, fec);
ParGridFunction gf(&coeff_fes);
Coefficient *coeff = nullptr;
ConstantCoefficient rho(1.0);
VectorFunctionCoefficient velocity(dim, velocity_function);
switch (coeff_type)
{
case Coeff::Const:
coeff = new ConstantCoefficient(1.0);
break;
case Coeff::Grid:
{
FunctionCoefficient f_coeff(coeff_function);
gf.ProjectCoefficient(f_coeff);
coeff = new GridFunctionCoefficient(&gf);
break;
}
case Coeff::Quad:
coeff = new FunctionCoefficient(coeff_function);
break;
}
switch (pb)
{
case Problem::Mass:
k_ref.AddDomainIntegrator(new MassIntegrator(*coeff));
k_test.AddDomainIntegrator(new MassIntegrator(*coeff));
break;
case Problem::Convection:
k_ref.AddDomainIntegrator(new ConvectionIntegrator(velocity, -1.0));
k_ref.AddInteriorFaceIntegrator(
new TransposeIntegrator(new DGTraceIntegrator(rho, velocity, 1.0, -0.5)));
k_ref.AddBdrFaceIntegrator(
new TransposeIntegrator(new DGTraceIntegrator(rho, velocity, 1.0, -0.5)));
k_test.AddDomainIntegrator(new ConvectionIntegrator(velocity, -1.0));
k_test.AddInteriorFaceIntegrator(
new TransposeIntegrator(new DGTraceIntegrator(rho, velocity, 1.0, -0.5)));
k_test.AddBdrFaceIntegrator(
new TransposeIntegrator(new DGTraceIntegrator(rho, velocity, 1.0, -0.5)));
break;
case Problem::Diffusion:
k_ref.AddDomainIntegrator(new DiffusionIntegrator(*coeff));
k_test.AddDomainIntegrator(new DiffusionIntegrator(*coeff));
break;
}
k_ref.Assemble();
k_ref.Finalize();
k_ref.KeepNbrBlock();
k_test.SetAssemblyLevel(assembly);
k_test.Assemble();
const int sizeIn = pb == Problem::Convection ?
fes.GetVSize() + fes.GetFaceNbrVSize() :
fes.GetVSize();
const int sizeOut = fes.GetVSize();
Vector x(sizeIn), y_test(sizeOut), y_ref(sizeOut);
x.Randomize(1);
k_test.SpMat().Mult(x,y_test);
k_ref.Mult(x,y_ref);
y_test -= y_ref;
REQUIRE(y_test.Norml2() < 1.e-12);
delete coeff;
delete fec;
}
TEST_CASE("Sparse Matrix", "[Parallel]")
{
auto assembly = GENERATE(AssemblyLevel::PARTIAL,AssemblyLevel::ELEMENT);
auto coeff_type = GENERATE(Coeff::Const,Coeff::Grid,Coeff::Quad);
auto pb = GENERATE(Problem::Mass,Problem::Convection,Problem::Diffusion);
auto order = GENERATE(1,2,3);
auto mesh = GENERATE("../../data/inline-quad.mesh","../../data/inline-hex.mesh",
"../../data/star-q2.mesh");
test_sparse_matrix(mesh, order, coeff_type, pb, assembly);
} // test case
} // namespace sparse_matrix_test
#endif // MFEM_USE_MPI