205 lines
6.0 KiB
C++
205 lines
6.0 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 "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;
|
|
}
|
|
}
|
|
|
|
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 bool keep_nbr_block,
|
|
int basis)
|
|
{
|
|
std::string knb = keep_nbr_block ? "ON" : "OFF";
|
|
std::string section = "keep_nbr_block: " + knb + "\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();
|
|
if (mesh.GetNE() < 16) { mesh.UniformRefinement(); }
|
|
ParMesh pmesh(MPI_COMM_WORLD, mesh);
|
|
int dim = mesh.Dimension();
|
|
|
|
FiniteElementCollection *fec;
|
|
if (pb == Problem::Convection)
|
|
{
|
|
fec = new L2_FECollection(order, dim, basis);
|
|
}
|
|
else
|
|
{
|
|
fec = new H1_FECollection(order, dim, basis);
|
|
}
|
|
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;
|
|
}
|
|
|
|
if (keep_nbr_block) { k_ref.KeepNbrBlock(); }
|
|
k_ref.Assemble();
|
|
k_ref.Finalize();
|
|
|
|
k_test.SetAssemblyLevel(AssemblyLevel::FULL);
|
|
if (keep_nbr_block) { k_test.KeepNbrBlock(); }
|
|
k_test.Assemble();
|
|
|
|
const int sizeIn = pb == Problem::Convection ?
|
|
fes.GetVSize() + fes.GetFaceNbrVSize() :
|
|
fes.GetVSize();
|
|
const int sizeOut = (pb == Problem::Convection && keep_nbr_block)?
|
|
fes.GetVSize() + fes.GetFaceNbrVSize() :
|
|
fes.GetVSize();
|
|
const int sizeEnd = fes.GetVSize();
|
|
Vector x(sizeIn), y_test(sizeOut), y_ref(sizeOut);
|
|
x.Randomize(1);
|
|
|
|
k_test.SpMat().Mult(x,y_test);
|
|
k_ref.SpMat().Mult(x,y_ref);
|
|
|
|
y_test -= y_ref;
|
|
|
|
Vector result(y_test.HostReadWrite(), sizeEnd);
|
|
|
|
REQUIRE(result.Norml2() < 1.e-12);
|
|
delete coeff;
|
|
delete fec;
|
|
}
|
|
|
|
TEST_CASE("Sparse Matrix", "[Parallel]")
|
|
{
|
|
auto basis = GENERATE(BasisType::GaussLobatto,BasisType::Positive);
|
|
auto keep_nbr_block = GENERATE(false);
|
|
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",
|
|
"../../data/fichera-q2.mesh");
|
|
test_sparse_matrix(mesh, order, coeff_type, pb, keep_nbr_block, basis);
|
|
} // test case
|
|
|
|
} // namespace sparse_matrix_test
|
|
|
|
#endif // MFEM_USE_MPI
|