168 lines
4.3 KiB
C++
168 lines
4.3 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 "catch.hpp"
|
|
|
|
using namespace mfem;
|
|
|
|
namespace blocknonlinearform
|
|
{
|
|
real_t rf0(const Vector &coords)
|
|
{
|
|
real_t x = coords(0);
|
|
real_t y = coords(1);
|
|
real_t z = coords(2);
|
|
|
|
real_t p = std::sqrt(x * x + y * y + z * z);
|
|
real_t rez = 0.0;
|
|
if (p < 1.0)
|
|
{
|
|
rez = 1.0;
|
|
}
|
|
return rez;
|
|
}
|
|
|
|
real_t uf0(const Vector &coords)
|
|
{
|
|
real_t x = coords(0);
|
|
real_t y = coords(1);
|
|
real_t z = coords(2);
|
|
|
|
real_t p = std::sqrt(x * x + y * y + z * z);
|
|
real_t rez = 0.0;
|
|
if (p < 1.5)
|
|
{
|
|
rez = 1;
|
|
}
|
|
return rez;
|
|
}
|
|
|
|
class CExample : public BlockNonlinearFormIntegrator
|
|
{
|
|
private:
|
|
public:
|
|
CExample() {}
|
|
~CExample() override {}
|
|
|
|
real_t GetElementEnergy(const Array<const FiniteElement *> &el,
|
|
ElementTransformation &trans,
|
|
const Array<const Vector *> &elfun) override
|
|
{
|
|
real_t energy = 0;
|
|
int dof_u = el[0]->GetDof();
|
|
int dof_r = el[1]->GetDof();
|
|
|
|
const IntegrationRule *ir = NULL;
|
|
int order = el[0]->GetOrder() + el[1]->GetOrder() + 1;
|
|
ir = &IntRules.Get(el[0]->GetGeomType(), order);
|
|
Vector shaperr(dof_r); // densities
|
|
Vector shaperu(dof_u); // prime field
|
|
|
|
real_t w;
|
|
real_t c1, c2;
|
|
for (int i = 0; i < ir->GetNPoints(); i++)
|
|
{
|
|
const IntegrationPoint &ip = ir->IntPoint(i);
|
|
trans.SetIntPoint(&ip);
|
|
w = trans.Weight();
|
|
w = ip.weight * w;
|
|
el[0]->CalcPhysShape(trans, shaperu);
|
|
el[1]->CalcPhysShape(trans, shaperr);
|
|
|
|
c1 = shaperr * (*elfun[1]);
|
|
c2 = shaperu * (*elfun[0]);
|
|
|
|
energy = energy + w * c1 * c2;
|
|
}
|
|
|
|
return energy;
|
|
}
|
|
};
|
|
|
|
#ifdef MFEM_USE_MPI
|
|
TEST_CASE("ParBlockNonlinearForm",
|
|
"[Parallel], [ParBlockNonlinearForm], [GetEnergy]")
|
|
{
|
|
int num_procs;
|
|
MPI_Comm_size(MPI_COMM_WORLD, &num_procs);
|
|
|
|
int my_rank;
|
|
MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);
|
|
|
|
FunctionCoefficient u0_coef(uf0);
|
|
FunctionCoefficient r0_coef(rf0);
|
|
|
|
for (int type = (int) Element::TETRAHEDRON;
|
|
type <= (int) Element::WEDGE;
|
|
type++)
|
|
{
|
|
int n = 4;
|
|
Mesh mesh = Mesh::MakeCartesian3D(
|
|
n, n, n, (Element::Type) type, 2.0, 2.0, 2.0);
|
|
int dim = mesh.Dimension();
|
|
ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, mesh);
|
|
mesh.Clear();
|
|
pmesh->UniformRefinement();
|
|
int uorder = 3;
|
|
int rorder = 2;
|
|
H1_FECollection ufec(uorder, dim);
|
|
H1_FECollection rfec(rorder, dim);
|
|
|
|
ParFiniteElementSpace ufes(pmesh, &ufec);
|
|
ParFiniteElementSpace rfes(pmesh, &rfec);
|
|
|
|
Array<ParFiniteElementSpace *> fes(2);
|
|
fes[0] = &ufes;
|
|
fes[1] = &rfes;
|
|
|
|
Array<int> block_trueOffsets(3);
|
|
block_trueOffsets[0] = 0;
|
|
block_trueOffsets[1] = ufes.TrueVSize();
|
|
block_trueOffsets[2] = rfes.TrueVSize();
|
|
block_trueOffsets.PartialSum();
|
|
|
|
ParGridFunction u_gf(&ufes);
|
|
ParGridFunction r_gf(&rfes);
|
|
|
|
FunctionCoefficient u0_coeff(uf0);
|
|
FunctionCoefficient r0_coeff(rf0);
|
|
|
|
u_gf.ProjectCoefficient(u0_coeff);
|
|
r_gf.ProjectCoefficient(r0_coeff);
|
|
|
|
BlockVector x(block_trueOffsets);
|
|
|
|
u_gf.GetTrueDofs(x.GetBlock(0));
|
|
r_gf.GetTrueDofs(x.GetBlock(1));
|
|
|
|
ParBlockNonlinearForm *nf = new ParBlockNonlinearForm(fes);
|
|
nf->AddDomainIntegrator(new CExample());
|
|
|
|
// Compute the energy: integral over 1/8 sphere = Pi*1*1*1/6
|
|
real_t A4 = nf->GetEnergy(x);
|
|
|
|
mfem::out << "Rank " << my_rank
|
|
<< ": ParBlockNonlinearForm::GetEnergy = " << A4
|
|
<< ", expected = " << M_PI / 6.0
|
|
<< ", diff = " << (A4 - M_PI / 6.0)
|
|
<< std::endl;
|
|
|
|
REQUIRE(fabs(A4 - M_PI / 6.0) < 1e-2);
|
|
|
|
delete nf;
|
|
delete pmesh;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
} // namespace blocknonlinearform
|