294 lines
8.6 KiB
C++
294 lines
8.6 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"
|
|
|
|
namespace mfem
|
|
{
|
|
|
|
real_t f1(const Vector &x)
|
|
{
|
|
real_t r = pow(x(0),2);
|
|
if (x.Size() >= 2) { r += pow(x(1), 3); }
|
|
if (x.Size() >= 3) { r += pow(x(2), 4); }
|
|
return r;
|
|
}
|
|
|
|
void gradf1(const Vector &x, Vector &u)
|
|
{
|
|
u(0) = 2*x(0);
|
|
if (x.Size() >= 2) { u(1) = 3*pow(x(1), 2); }
|
|
if (x.Size() >= 3) { u(2) = 4*pow(x(2), 3); }
|
|
}
|
|
|
|
TEST_CASE("FormRectangular", "[FormRectangularSystemMatrix]")
|
|
{
|
|
enum FECType { H1, L2_VALUE, L2_INTEGRAL };
|
|
auto fec_type = GENERATE(FECType::H1, FECType::L2_VALUE,
|
|
FECType::L2_INTEGRAL);
|
|
|
|
SECTION("MixedBilinearForm::FormRectangularSystemMatrix")
|
|
{
|
|
Mesh mesh = Mesh::MakeCartesian2D(
|
|
10, 10, Element::QUADRILATERAL, 0, 1.0, 1.0);
|
|
int dim = mesh.Dimension();
|
|
int order = 4;
|
|
|
|
int nattr = mesh.bdr_attributes.Max();
|
|
Array<int> ess_trial_tdof_list, ess_test_tdof_list;
|
|
|
|
Array<int> ess_bdr(nattr);
|
|
ess_bdr = 0;
|
|
ess_bdr[0] = 1;
|
|
|
|
// Scalar
|
|
H1_FECollection fec1(order, dim);
|
|
FiniteElementSpace fes1(&mesh, &fec1);
|
|
fes1.GetEssentialTrueDofs(ess_bdr, ess_trial_tdof_list);
|
|
GridFunction field(&fes1);
|
|
|
|
// Vector valued
|
|
std::unique_ptr<FiniteElementCollection> fec2;
|
|
switch (fec_type)
|
|
{
|
|
case FECType::H1:
|
|
fec2.reset(new H1_FECollection(order, dim));
|
|
break;
|
|
case FECType::L2_VALUE:
|
|
fec2.reset(new L2_FECollection(order, dim, BasisType::GaussLegendre,
|
|
FiniteElement::VALUE));
|
|
break;
|
|
case FECType::L2_INTEGRAL:
|
|
fec2.reset(new L2_FECollection(order, dim, BasisType::GaussLegendre,
|
|
FiniteElement::INTEGRAL));
|
|
break;
|
|
}
|
|
FiniteElementSpace fes2(&mesh, fec2.get(), dim);
|
|
fes2.GetEssentialTrueDofs(ess_bdr, ess_test_tdof_list);
|
|
GridFunction field2(&fes2);
|
|
|
|
MixedBilinearForm gform(&fes1, &fes2);
|
|
gform.AddDomainIntegrator(new GradientIntegrator);
|
|
gform.Assemble();
|
|
|
|
// Project u = f1
|
|
FunctionCoefficient fcoeff1(f1);
|
|
field.ProjectCoefficient(fcoeff1);
|
|
|
|
VectorFunctionCoefficient fcoeff2(dim, gradf1);
|
|
LinearForm lf(&fes2);
|
|
lf.AddDomainIntegrator(new VectorDomainLFIntegrator(fcoeff2));
|
|
lf.Assemble();
|
|
|
|
OperatorHandle G;
|
|
Vector X, B;
|
|
gform.FormRectangularLinearSystem(ess_trial_tdof_list,
|
|
ess_test_tdof_list,
|
|
field,
|
|
lf,
|
|
G,
|
|
X,
|
|
B);
|
|
|
|
G->Mult(field, field2);
|
|
|
|
subtract(B, field2, field2);
|
|
REQUIRE(field2.Norml2() == MFEM_Approx(0.0));
|
|
}
|
|
}
|
|
|
|
#ifdef MFEM_USE_MPI
|
|
|
|
TEST_CASE("ParallelFormRectangular",
|
|
"[Parallel], [FormRectangularSystemMatrix]")
|
|
{
|
|
SECTION("ParMixedBilinearForm::FormRectangularSystemMatrix")
|
|
{
|
|
Mesh mesh = Mesh::MakeCartesian2D(
|
|
10, 10, Element::QUADRILATERAL, 0, 1.0, 1.0);
|
|
int dim = mesh.Dimension();
|
|
int order = 4;
|
|
|
|
int nattr = mesh.bdr_attributes.Max();
|
|
Array<int> ess_trial_tdof_list, ess_test_tdof_list;
|
|
|
|
Array<int> ess_bdr(nattr);
|
|
ess_bdr = 0;
|
|
ess_bdr[0] = 1;
|
|
|
|
ParMesh pmesh(MPI_COMM_WORLD, mesh);
|
|
|
|
// Scalar
|
|
H1_FECollection fec1(order, dim);
|
|
ParFiniteElementSpace fes1(&pmesh, &fec1);
|
|
|
|
// Vector valued
|
|
H1_FECollection fec2(order, dim);
|
|
ParFiniteElementSpace fes2(&pmesh, &fec2, dim);
|
|
|
|
fes1.GetEssentialTrueDofs(ess_bdr, ess_trial_tdof_list);
|
|
fes2.GetEssentialTrueDofs(ess_bdr, ess_test_tdof_list);
|
|
|
|
ParGridFunction field(&fes1), field2(&fes2);
|
|
|
|
ParMixedBilinearForm gform(&fes1, &fes2);
|
|
gform.AddDomainIntegrator(new GradientIntegrator);
|
|
gform.Assemble();
|
|
|
|
// Project u = f1
|
|
FunctionCoefficient fcoeff1(f1);
|
|
field.ProjectCoefficient(fcoeff1);
|
|
|
|
VectorFunctionCoefficient fcoeff2(dim, gradf1);
|
|
ParLinearForm lf(&fes2);
|
|
lf.AddDomainIntegrator(new VectorDomainLFIntegrator(fcoeff2));
|
|
lf.Assemble();
|
|
|
|
OperatorHandle G;
|
|
Vector X, B;
|
|
gform.FormRectangularLinearSystem(ess_trial_tdof_list,
|
|
ess_test_tdof_list,
|
|
field,
|
|
lf,
|
|
G,
|
|
X,
|
|
B);
|
|
|
|
Vector *field_tdof = field.ParallelProject();
|
|
Vector *field2_tdof = field2.ParallelProject();
|
|
|
|
G->Mult(*field_tdof, *field2_tdof);
|
|
|
|
subtract(B, *field2_tdof, *field2_tdof);
|
|
REQUIRE(field2_tdof->Norml2() == MFEM_Approx(0.0));
|
|
|
|
delete field2_tdof;
|
|
delete field_tdof;
|
|
}
|
|
}
|
|
|
|
TEST_CASE("HypreParMatrixBlocksRectangular",
|
|
"[Parallel], [BlockMatrix]")
|
|
{
|
|
SECTION("HypreParMatrixFromBlocks")
|
|
{
|
|
int rank;
|
|
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
|
|
|
|
Mesh mesh = Mesh::MakeCartesian2D(
|
|
10, 10, Element::QUADRILATERAL, 0, 1.0, 1.0);
|
|
int dim = mesh.Dimension();
|
|
int order = 2;
|
|
|
|
int nattr = mesh.bdr_attributes.Max();
|
|
Array<int> ess_trial_tdof_list, ess_test_tdof_list;
|
|
|
|
Array<int> ess_bdr(nattr);
|
|
ess_bdr = 0;
|
|
ess_bdr[0] = 1;
|
|
|
|
ParMesh pmesh(MPI_COMM_WORLD, mesh);
|
|
|
|
FiniteElementCollection *hdiv_coll(new RT_FECollection(order, dim));
|
|
FiniteElementCollection *l2_coll(new L2_FECollection(order, dim));
|
|
|
|
ParFiniteElementSpace R_space(&pmesh, hdiv_coll);
|
|
ParFiniteElementSpace W_space(&pmesh, l2_coll);
|
|
|
|
ParBilinearForm RmVarf(&R_space);
|
|
ParBilinearForm WmVarf(&W_space);
|
|
ParMixedBilinearForm bVarf(&R_space, &W_space);
|
|
|
|
HypreParMatrix *MR, *MW, *B;
|
|
|
|
RmVarf.AddDomainIntegrator(new VectorFEMassIntegrator());
|
|
RmVarf.Assemble();
|
|
RmVarf.Finalize();
|
|
MR = RmVarf.ParallelAssemble();
|
|
|
|
WmVarf.AddDomainIntegrator(new MassIntegrator());
|
|
WmVarf.Assemble();
|
|
WmVarf.Finalize();
|
|
MW = WmVarf.ParallelAssemble();
|
|
|
|
bVarf.AddDomainIntegrator(new VectorFEDivergenceIntegrator);
|
|
bVarf.Assemble();
|
|
bVarf.Finalize();
|
|
B = bVarf.ParallelAssemble();
|
|
(*B) *= -1;
|
|
|
|
HypreParMatrix *BT = B->Transpose();
|
|
|
|
Array<int> blockRow_trueOffsets(3); // number of variables + 1
|
|
blockRow_trueOffsets[0] = 0;
|
|
blockRow_trueOffsets[1] = R_space.TrueVSize();
|
|
blockRow_trueOffsets[2] = W_space.TrueVSize();
|
|
blockRow_trueOffsets.PartialSum();
|
|
|
|
Array<int> blockCol_trueOffsets(4); // number of variables + 1
|
|
blockCol_trueOffsets[0] = 0;
|
|
blockCol_trueOffsets[1] = R_space.TrueVSize();
|
|
blockCol_trueOffsets[2] = W_space.TrueVSize();
|
|
blockCol_trueOffsets[3] = W_space.TrueVSize();
|
|
blockCol_trueOffsets.PartialSum();
|
|
|
|
BlockOperator blockOper(blockRow_trueOffsets, blockCol_trueOffsets);
|
|
blockOper.SetBlock(0, 0, MR);
|
|
blockOper.SetBlock(0, 1, BT);
|
|
blockOper.SetBlock(1, 0, B);
|
|
blockOper.SetBlock(0, 2, BT, 3.14);
|
|
blockOper.SetBlock(1, 2, MW);
|
|
|
|
Array2D<const HypreParMatrix*> hBlocks(2,3);
|
|
hBlocks = NULL;
|
|
hBlocks(0, 0) = MR;
|
|
hBlocks(0, 1) = BT;
|
|
hBlocks(1, 0) = B;
|
|
hBlocks(0, 2) = BT;
|
|
hBlocks(1, 2) = MW;
|
|
|
|
Array2D<real_t> blockCoeff(2,3);
|
|
blockCoeff = 1.0;
|
|
blockCoeff(0, 2) = 3.14;
|
|
HypreParMatrix *H = HypreParMatrixFromBlocks(hBlocks, &blockCoeff);
|
|
|
|
Vector x(blockCol_trueOffsets[3]);
|
|
Vector yB(blockRow_trueOffsets[2]);
|
|
Vector yH(blockRow_trueOffsets[2]);
|
|
|
|
x.Randomize();
|
|
yB = 0.0;
|
|
yH = 0.0;
|
|
|
|
blockOper.Mult(x, yB);
|
|
H->Mult(x, yH);
|
|
|
|
yH -= yB;
|
|
real_t error = yH.Norml2();
|
|
mfem::out << " order: " << order
|
|
<< ", block matrix error norm on rank " << rank << ": " << error << std::endl;
|
|
REQUIRE(error < 1.e-12);
|
|
|
|
delete H;
|
|
delete BT;
|
|
delete B;
|
|
delete MW;
|
|
delete MR;
|
|
delete l2_coll;
|
|
delete hdiv_coll;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
} // namespace mfem
|