Files
mfem/examples/dfem_test.cpp
T

350 lines
10 KiB
C++

#include <functional>
#include <iostream>
#include <variant>
#include <vector>
#include "dfem.hpp"
using namespace mfem;
using mfem::internal::dual;
using mfem::internal::tensor;
using namespace std;
int test_integrate_boundary()
{
int polynomial_order = 1;
Mesh mesh = Mesh::MakeCartesian2D(10, 2, Element::QUADRILATERAL, false, 0.0,
2.0 * M_PI);
mesh.EnsureNodes();
ParMesh pmesh(MPI_COMM_WORLD, mesh);
H1_FECollection h1_fec(polynomial_order);
ParFiniteElementSpace h1_fes(&pmesh, &h1_fec);
auto ir_face = const_cast<IntegrationRule *>(
&IntRules.Get(mesh.GetBdrElementGeometry(0),
3 * mesh.GetNodes()->FESpace()->GetElementOrder(0) + 1));
auto h1_prolongation = h1_fes.GetProlongationMatrix();
ParGridFunction boundary_load(&h1_fes);
boundary_load = 0.0;
VectorFunctionCoefficient boundary_load_coeff(2, [](const Vector &x, Vector &u)
{
u(0) = 0.0;
u(1) = 1.0;
});
{
Array<int> boundary_load_attr(pmesh.bdr_attributes.Max());
boundary_load_attr = 0;
boundary_load_attr[2] = 1;
boundary_load.ProjectBdrCoefficient(boundary_load_coeff, boundary_load_attr);
}
Vector boundary_load_qp;
interpolate_boundary(boundary_load, *ir_face, boundary_load_qp);
auto foo = Reshape(boundary_load_qp.Read(), h1_fes.GetVDim(),
ir_face->GetNPoints(), pmesh.GetNBE());
Vector vec(h1_fes.GetVDim());
for (int e = 0; e < pmesh.GetNBE(); e++)
{
auto Tr = pmesh.GetBdrElementTransformation(e);
for (int qp = 0; qp < ir_face->GetNPoints(); qp++)
{
const IntegrationPoint &ip = ir_face->IntPoint(qp);
Tr->SetIntPoint(&ip);
boundary_load_coeff.Eval(vec, *Tr, ip);
out << "(" << ip.x << "," << "y)" << " = " << vec(0) << " " << vec(1) << "\n";
}
}
return 0;
}
void compute_element_jacobian_inverse(Mesh &mesh, IntegrationRule *ir,
Vector &element_jacobian_inverse)
{
const int dim = mesh.Dimension();
const int num_el = mesh.GetNE();
const int num_qp = ir->GetNPoints();
element_jacobian_inverse.SetSize(num_qp * dim * dim * num_el);
// Cache inverse Jacobian on each quadrature point
const GeometricFactors *geom = mesh.GetGeometricFactors(
*ir, GeometricFactors::JACOBIANS);
auto J = Reshape(geom->J.Read(), num_qp, dim, dim, num_el);
auto Jinv = Reshape(element_jacobian_inverse.Write(), num_qp, dim, dim, num_el);
DenseMatrix Jqp(dim, dim), JqpInv(dim, dim);
for (int e = 0; e < num_el; e++)
{
for (int qp = 0; qp < num_qp; qp++)
{
for (int i = 0; i < dim; i++)
{
for (int j = 0; j < dim; j++)
{
Jqp(i, j) = J(qp, i, j, e);
}
}
CalcInverse(Jqp, JqpInv);
for (int i = 0; i < dim; i++)
{
for (int j = 0; j < dim; j++)
{
Jinv(qp, i, j, e) = JqpInv(i, j);
}
}
}
}
}
inline
std::string check_result(double norm, double rtol = 1e-12)
{
if (norm < rtol)
{
return "✅";
}
return "❌";
}
int main(int argc, char *argv[])
{
using namespace std;
Mpi::Init();
int num_procs = Mpi::WorldSize();
int myid = Mpi::WorldRank();
Hypre::Init();
int dimension = 2;
int polynomial_order = 1;
OptionsParser args(argc, argv);
args.AddOption(&polynomial_order, "-o", "--order",
"Finite element order (polynomial degree)");
args.ParseCheck();
std::cout << "Polynomial order = " << polynomial_order << "\n";
FunctionCoefficient linear_scalar_coeff([&](const Vector &c)
{
double x = c(0), y = c(1);
return 2.0 * x + x * y;
});
VectorFunctionCoefficient dlinear_scalardx_coeff(dimension, [&](const Vector &c,
Vector &u)
{
double x = c(0), y = c(1);
u(0) = 2.0 + c(1);
u(1) = c(0);
});
FunctionCoefficient quadratic_coeff([&](const Vector &c)
{
double x = c(0), y = c(1);
return 2.0*x*x + x*y*y;
});
VectorFunctionCoefficient dquadraticdx_coeff(dimension, [&](const Vector &c,
Vector &u)
{
double x = c(0), y = c(1);
u(0) = 4.0*x+y*y,
u(1) = 2.0*x*y;
});
{
Mesh mesh = Mesh::MakeCartesian2D(1, 1, Element::QUADRILATERAL, false, 1.0,
1.0);
mesh.EnsureNodes();
ParMesh pmesh(MPI_COMM_WORLD, mesh);
H1_FECollection h1_fec(polynomial_order);
ParFiniteElementSpace h1_fes(&pmesh, &h1_fec);
ParFiniteElementSpace h1_vfes(&pmesh, &h1_fec, dimension, Ordering::byVDIM);
cout << "#dofs: " << h1_fes.GetVSize() << "\n\n";
auto ir = const_cast<IntegrationRule *>(
&IntRules.Get(mesh.GetElementGeometry(0),
2 * mesh.GetNodes()->FESpace()->GetElementOrder(0)));
Vector element_jacobian_inverse;
compute_element_jacobian_inverse(mesh, ir, element_jacobian_inverse);
auto h1v_prolongation = h1_vfes.GetProlongationMatrix();
ParGridFunction u(&h1_fes), du(&h1_vfes), uv(&h1_vfes);
u = 0.0, du = 0.0, uv = 0.0;
{
cout << "scalar interpolation\n";
Vector u_qp;
u.ProjectCoefficient(linear_scalar_coeff);
interpolate(u, *ir, u_qp);
integrate_basis(u_qp, h1_fes, *ir, u);
double integral = 0.0;
for (int dof = 0; dof < h1_fes.GetVSize(); dof++)
{
integral += u(dof);
}
cout << "|I[u]dx - I[u_ex]dx| = " << abs(integral - 5.0/4.0) << "\n";
cout << endl;
}
{
cout << "weak gradient of scalar\n";
Vector dudx_qp;
u.ProjectCoefficient(linear_scalar_coeff);
gradient_wrt_x(u, *ir, dudx_qp);
integrate_basis(dudx_qp, h1_vfes, *ir, du);
Vector integral(2);
for (int d = 0; d < du.FESpace()->GetVDim(); d++)
{
integral(d) = 0.0;
for (int i = 0; i < du.FESpace()->GetNDofs(); i++)
{
int idx = Ordering::Map<Ordering::byVDIM>(
du.FESpace()->GetNDofs(),
du.FESpace()->GetVDim(),
i,
d);
integral(d) += du(idx);
}
}
cout << "|I[du]dx - I[du_ex]dx| = " << abs(integral(0) - 5.0/2.0) << "\n"
<< "|I[du]dy - I[du_ex]dy| = " << abs(integral(1) - 1.0/2.0) << "\n";
ParLinearForm l(&h1_vfes);
auto integrator = new VectorDomainLFIntegrator(dlinear_scalardx_coeff);
integrator->SetIntRule(ir);
l.AddDomainIntegrator(integrator);
l.Assemble();
du -= *l.ParallelAssemble();
cout << "|du - du_form|_l2 = " << du.Norml2()
<< check_result(du.Norml2()) << "\n";
cout << endl;
}
{
cout << "scalar diffusion, linear u\n";
Vector dudx_qp, ru(h1_fes.GetVSize());
u.ProjectCoefficient(linear_scalar_coeff);
gradient_wrt_x(u, *ir, dudx_qp);
integrate_basis_gradient(dudx_qp, h1_fes, *ir, ru,
element_jacobian_inverse);
ParBilinearForm b(&h1_fes);
auto integrator = new DiffusionIntegrator;
integrator->SetIntRule(ir);
b.AddDomainIntegrator(integrator);
b.Assemble();
b.Finalize();
ParGridFunction y(&h1_fes);
b.Mult(u, y);
y -= ru;
cout << "|r(u) - r(u)_form|_l2 = " << y.Norml2()
<< check_result(y.Norml2()) << "\n";
cout << endl;
}
{
cout << "scalar diffusion, quadratic u\n";
Vector dudx_qp, ru(h1_fes.GetVSize());
u.ProjectCoefficient(quadratic_coeff);
gradient_wrt_x(u, *ir, dudx_qp);
integrate_basis_gradient(dudx_qp, h1_fes, *ir, ru,
element_jacobian_inverse);
ParBilinearForm b(&h1_fes);
auto integrator = new DiffusionIntegrator;
integrator->SetIntRule(ir);
b.AddDomainIntegrator(integrator);
b.Assemble();
b.Finalize();
ParGridFunction y(&h1_fes);
b.Mult(u, y);
y -= ru;
cout << "|r(u) - r(u)_form|_l2 = " << y.Norml2()
<< check_result(y.Norml2()) << "\n";
cout << endl;
}
{
cout << "vector diffusion, linear u\n";
Vector duvdx_qp, ru(h1_vfes.GetVSize());
uv.ProjectCoefficient(dlinear_scalardx_coeff);
gradient_wrt_x(uv, *ir, duvdx_qp);
integrate_basis_gradient(duvdx_qp, h1_vfes, *ir, ru,
element_jacobian_inverse);
ParBilinearForm b(&h1_vfes);
auto integrator = new VectorDiffusionIntegrator;
integrator->SetIntRule(ir);
b.AddDomainIntegrator(integrator);
b.Assemble();
b.Finalize();
ParGridFunction y(&h1_vfes);
b.Mult(uv, y);
y -= ru;
cout << "|r(u) - r(u)_form|_l2 = " << y.Norml2()
<< check_result(y.Norml2()) << "\n";
cout << endl;
}
{
cout << "vector diffusion, quadratic u\n";
Vector duvdx_qp, ru(h1_vfes.GetVSize());
uv.ProjectCoefficient(dquadraticdx_coeff);
gradient_wrt_x(uv, *ir, duvdx_qp);
integrate_basis_gradient(duvdx_qp, h1_vfes, *ir, ru,
element_jacobian_inverse);
ParBilinearForm b(&h1_vfes);
auto integrator = new VectorDiffusionIntegrator;
integrator->SetIntRule(ir);
b.AddDomainIntegrator(integrator);
b.Assemble();
b.Finalize();
ParGridFunction y(&h1_vfes);
b.Mult(uv, y);
y -= ru;
cout << "|r(u) - r(u)_form|_l2 = " << y.Norml2()
<< check_result(y.Norml2()) << "\n";
cout << endl;
}
}
return 0;
}