120 lines
3.9 KiB
C++
120 lines
3.9 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 "mfem.hpp"
|
|
#include "unit_tests.hpp"
|
|
|
|
using namespace mfem;
|
|
|
|
TEST_CASE("BlockOperators", "[BlockOperators], [GPU]")
|
|
{
|
|
const int dim = 2, nx = 3, ny = 3, order = 2;
|
|
Element::Type e_type = Element::QUADRILATERAL;
|
|
Mesh mesh = Mesh::MakeCartesian2D(nx, ny, e_type);
|
|
|
|
RT_FECollection rt_fe(order, dim);
|
|
L2_FECollection l2_fe(order, dim);
|
|
|
|
FiniteElementSpace R_fes(&mesh, &rt_fe), W_fes(&mesh, &l2_fe);
|
|
|
|
Array<int> block_offsets(3);
|
|
block_offsets[0] = 0;
|
|
block_offsets[1] = R_fes.GetVSize();
|
|
block_offsets[2] = W_fes.GetVSize();
|
|
block_offsets.PartialSum();
|
|
|
|
VectorFunctionCoefficient fcoeff(dim, [](const Vector &, Vector &f) { f = M_PI; }),
|
|
ucoeff(dim, [](const Vector &x, Vector &u)
|
|
{
|
|
const real_t xi(x(0)), yi(x(1)), zi(0.0);
|
|
u(0) = -std::exp(xi) * std::sin(yi) * std::cos(zi);
|
|
u(1) = -std::exp(xi) * std::cos(yi) * std::cos(zi);
|
|
});
|
|
auto pFun_ex = [](const Vector &x)
|
|
{
|
|
real_t xi(x(0)), yi(x(1)), zi(0.0);
|
|
return std::exp(xi) * std::sin(yi) * std::cos(zi);
|
|
};
|
|
|
|
FunctionCoefficient fnatcoeff([&](const Vector &x) { return (-pFun_ex(x)); }),
|
|
gcoeff([](const Vector &) {return M_PI_2;}), pcoeff(pFun_ex);
|
|
|
|
const MemoryType mt = Device::GetMemoryType();
|
|
BlockVector x(block_offsets, mt), y(block_offsets, mt), rhs(block_offsets, mt);
|
|
|
|
LinearForm lf, lg;
|
|
lf.Update(&R_fes, rhs.GetBlock(0), 0);
|
|
lf.AddDomainIntegrator(new VectorFEDomainLFIntegrator(fcoeff));
|
|
lf.AddBoundaryIntegrator(new VectorFEBoundaryFluxLFIntegrator(fnatcoeff));
|
|
lf.Assemble();
|
|
lf.SyncAliasMemory(rhs);
|
|
|
|
lg.Update(&W_fes, rhs.GetBlock(1), 0);
|
|
lg.AddDomainIntegrator(new DomainLFIntegrator(gcoeff));
|
|
lg.Assemble();
|
|
lg.SyncAliasMemory(rhs);
|
|
|
|
BilinearForm vmass(&R_fes);
|
|
ConstantCoefficient k(1.0);
|
|
vmass.AddDomainIntegrator(new VectorFEMassIntegrator(k));
|
|
vmass.SetAssemblyLevel(AssemblyLevel::PARTIAL);
|
|
vmass.Assemble();
|
|
|
|
MixedBilinearForm vdiv(&R_fes, &W_fes);
|
|
vdiv.AddDomainIntegrator(new VectorFEDivergenceIntegrator);
|
|
vdiv.SetAssemblyLevel(AssemblyLevel::PARTIAL);
|
|
vdiv.Assemble();
|
|
|
|
BlockOperator blockOp(block_offsets);
|
|
TransposeOperator Bt(vdiv);
|
|
|
|
blockOp.SetBlock(0, 0, &vmass);
|
|
blockOp.SetBlock(0, 1, &Bt, -1.0);
|
|
blockOp.SetBlock(1, 0, &vdiv, -1.0);
|
|
|
|
Vector Md(vmass.Height());
|
|
vmass.AssembleDiagonal(Md);
|
|
BlockDiagonalPreconditioner darcyDiagonalPrec(block_offsets);
|
|
BlockLowerTriangularPreconditioner darcyLowerTriangularPrec(block_offsets);
|
|
|
|
Vector invMd(Md);
|
|
invMd.Reciprocal();
|
|
|
|
Vector BMBt_diag(vdiv.Height());
|
|
vdiv.AssembleDiagonal_ADAt(invMd, BMBt_diag);
|
|
OperatorJacobiSmoother invM(Md, {}), invS(BMBt_diag, {});
|
|
|
|
darcyDiagonalPrec.SetDiagonalBlock(0, &invM);
|
|
darcyDiagonalPrec.SetDiagonalBlock(1, &invS);
|
|
darcyLowerTriangularPrec.SetDiagonalBlock(0, &invM);
|
|
darcyLowerTriangularPrec.SetDiagonalBlock(1, &invS);
|
|
|
|
MINRESSolver solver;
|
|
const auto rtol = 1e-6, atol = 1e-8;
|
|
const auto print_lvl = 3, max_it = 100;
|
|
solver.SetAbsTol(atol);
|
|
solver.SetRelTol(rtol);
|
|
solver.SetPrintLevel(print_lvl);
|
|
solver.SetMaxIter(max_it);
|
|
solver.SetOperator(blockOp);
|
|
|
|
solver.SetPreconditioner(darcyDiagonalPrec);
|
|
x = 0.0, solver.Mult(rhs, x);
|
|
REQUIRE(solver.GetConverged());
|
|
|
|
solver.SetPreconditioner(darcyLowerTriangularPrec);
|
|
y = 0.0, solver.Mult(rhs, y);
|
|
REQUIRE(solver.GetConverged());
|
|
|
|
x -= y;
|
|
REQUIRE(x.Normlinf() == MFEM_Approx(0.0));
|
|
}
|