475 lines
12 KiB
C++
475 lines
12 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 "../../config/config.hpp"
|
|
|
|
#ifdef MFEM_USE_MOONOLITH
|
|
|
|
#include "cut.hpp"
|
|
|
|
#include "transferutils.hpp"
|
|
|
|
#include "moonolith_build_quadrature.hpp"
|
|
|
|
using namespace mfem::internal;
|
|
|
|
// #define MFEM_DEBUG_MOONOLITH
|
|
|
|
namespace mfem
|
|
{
|
|
|
|
template <class Polytope> class CutGeneric : public Cut
|
|
{
|
|
public:
|
|
using Point = typename Polytope::Point;
|
|
static const int Dim = Point::n_dims;
|
|
using Quadrature_t = moonolith::Quadrature<double, Dim>;
|
|
using BuildQuadrature_t = moonolith::BuildQuadrature<Polytope>;
|
|
|
|
bool BuildQuadrature(const FiniteElementSpace &from_space,
|
|
const int from_elem_idx,
|
|
const FiniteElementSpace &to_space,
|
|
const int to_elem_idx, IntegrationRule &from_quadrature,
|
|
IntegrationRule &to_quadrature) override;
|
|
|
|
void Describe() const override;
|
|
|
|
protected:
|
|
inline Quadrature_t &GetQRule() { return q_rule_; }
|
|
inline int GetOrder() const { return order_; }
|
|
|
|
inline void SetOrder(const int order) { order_ = order; }
|
|
|
|
virtual void MakePolytope(Mesh &mesh, const int elem_idx,
|
|
Polytope &polygon) = 0;
|
|
|
|
bool IsValidPhysicalPoint(const Vector &p_mfem) const
|
|
{
|
|
Point p;
|
|
|
|
for (int d = 0; d < Dim; ++d)
|
|
{
|
|
p[d] = p_mfem[d];
|
|
}
|
|
|
|
moonolith::HPolytope<double, Dim> poly;
|
|
poly.make(from());
|
|
|
|
if (!poly.contains(p, 1e-8)) { return false; }
|
|
|
|
poly.make(to());
|
|
|
|
return poly.contains(p, 1e-8);
|
|
}
|
|
|
|
bool IsValidQPoint(const IntegrationPoint &p) const
|
|
{
|
|
assert(p.x == p.x);
|
|
assert(p.y == p.y);
|
|
assert(p.z == p.z);
|
|
|
|
assert(p.x >= -1e-8);
|
|
assert(p.y >= -1e-8);
|
|
assert(p.z >= -1e-8);
|
|
|
|
assert(p.x <= 1 + 1e-8);
|
|
assert(p.y <= 1 + 1e-8);
|
|
assert(p.z <= 1 + 1e-8);
|
|
|
|
bool ok = true;
|
|
|
|
ok = ok && (p.x == p.x);
|
|
ok = ok && (p.y == p.y);
|
|
ok = ok && (p.z == p.z);
|
|
|
|
ok = ok && (p.x >= -1e-8);
|
|
ok = ok && (p.y >= -1e-8);
|
|
ok = ok && (p.z >= -1e-8);
|
|
|
|
ok = ok && (p.x <= 1 + 1e-8);
|
|
ok = ok && (p.y <= 1 + 1e-8);
|
|
ok = ok && (p.z <= 1 + 1e-8);
|
|
return ok;
|
|
}
|
|
|
|
static void ConvertQRule(const IntegrationRule &ir, Quadrature_t &result)
|
|
{
|
|
const int size = ir.Size();
|
|
|
|
result.resize(size);
|
|
|
|
for (int k = 0; k < size; ++k)
|
|
{
|
|
auto &qp = ir[k];
|
|
result.points[k][0] = qp.x;
|
|
result.points[k][1] = qp.y;
|
|
if constexpr (Dim == 3)
|
|
{
|
|
result.points[k][12] = qp.z;
|
|
}
|
|
|
|
result.weights[k] = qp.weight;
|
|
}
|
|
}
|
|
|
|
inline const Polytope & from() const { return from_; }
|
|
inline const Polytope & to() const { return to_; }
|
|
|
|
private:
|
|
Polytope from_, to_;
|
|
Quadrature_t q_rule_;
|
|
Quadrature_t physical_quadrature_;
|
|
BuildQuadrature_t builder_;
|
|
int order_{-1};
|
|
double intersection_measure_{0};
|
|
};
|
|
|
|
class Cut2D : public CutGeneric<moonolith::Polygon<double, 2>>
|
|
{
|
|
public:
|
|
using Polygon_t = moonolith::Polygon<double, 2>;
|
|
using Quadrature_t = moonolith::Quadrature<double, 2>;
|
|
using BuildQuadrature_t = moonolith::BuildQuadrature<Polygon_t>;
|
|
|
|
void SetIntegrationOrder(const int order) override;
|
|
|
|
protected:
|
|
void SetQuadratureRule(const IntegrationRule &ir) override;
|
|
void MakePolytope(Mesh &mesh, const int elem_idx,
|
|
Polygon_t &polygon) override;
|
|
|
|
private:
|
|
DenseMatrix buffer_pts;
|
|
};
|
|
|
|
class Cut3D : public CutGeneric<moonolith::Polyhedron<double>>
|
|
{
|
|
public:
|
|
using Polyhedron_t = moonolith::Polyhedron<double>;
|
|
using Quadrature_t = moonolith::Quadrature<double, 3>;
|
|
using BuildQuadrature_t = moonolith::BuildQuadrature<Polyhedron_t>;
|
|
|
|
void SetIntegrationOrder(const int order) override;
|
|
|
|
protected:
|
|
void SetQuadratureRule(const IntegrationRule &ir) override;
|
|
void MakePolytope(Mesh &mesh, const int elem_idx,
|
|
Polyhedron_t &polyhedron) override;
|
|
|
|
private:
|
|
DenseMatrix buffer_pts;
|
|
Array<int> buffer_vertices;
|
|
Array<int> buffer_faces, buffer_cor;
|
|
};
|
|
|
|
void TransformToReference(ElementTransformation &Trans, int type,
|
|
const Vector &physical_p, const double &w,
|
|
IntegrationPoint &ref_p)
|
|
{
|
|
|
|
int dim = physical_p.Size();
|
|
Trans.TransformBack(physical_p, ref_p);
|
|
|
|
assert(ref_p.x >= -1e-8);
|
|
assert(ref_p.y >= -1e-8);
|
|
assert(ref_p.z >= -1e-8);
|
|
|
|
assert(ref_p.x <= 1 + 1e-8);
|
|
assert(ref_p.y <= 1 + 1e-8);
|
|
assert(ref_p.z <= 1 + 1e-8);
|
|
|
|
#ifdef MFEM_DEBUG_MOONOLITH
|
|
{
|
|
Vector physical_test_p;
|
|
Trans.Transform(ref_p, physical_test_p);
|
|
|
|
physical_test_p -= physical_p;
|
|
|
|
assert(physical_test_p.Norml2() < 1e-10);
|
|
}
|
|
#endif
|
|
|
|
ref_p.weight = w;
|
|
|
|
if (type == Geometry::TRIANGLE && dim == 2)
|
|
{
|
|
ref_p.weight *= 0.5;
|
|
}
|
|
else if (type == Geometry::TETRAHEDRON && dim == 3)
|
|
{
|
|
ref_p.weight *= 1. / 6;
|
|
}
|
|
else
|
|
{
|
|
assert(dim == 2 || dim == 3);
|
|
}
|
|
}
|
|
|
|
template <class Polytope>
|
|
bool CutGeneric<Polytope>::BuildQuadrature(const FiniteElementSpace &from_space,
|
|
const int from_elem_idx,
|
|
const FiniteElementSpace &to_space,
|
|
const int to_elem_idx,
|
|
IntegrationRule &from_quadrature,
|
|
IntegrationRule &to_quadrature)
|
|
{
|
|
|
|
MakePolytope(*from_space.GetMesh(), from_elem_idx, from_);
|
|
MakePolytope(*to_space.GetMesh(), to_elem_idx, to_);
|
|
|
|
if (!builder_.apply(q_rule_, from_, to_, physical_quadrature_))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
|
|
#ifdef MFEM_DEBUG_MOONOLITH
|
|
|
|
{
|
|
static int counter = 0;
|
|
|
|
moonolith::MatlabScripter script;
|
|
script.hold_on();
|
|
script.plot(from_, "\'g-\'");
|
|
script.plot(from_, "\'g.\'");
|
|
script.plot(to_, "\'r-\'");
|
|
script.plot(to_, "\'r.\'");
|
|
script.plot(physical_quadrature_.points, "\'*b\'");
|
|
|
|
mfem::out << "measure(" << counter << "): " << moonolith::measure(
|
|
physical_quadrature_) << "\n";
|
|
|
|
script.save("out_" + std::to_string(counter++) + ".m");
|
|
}
|
|
|
|
#endif // MFEM_DEBUG_MOONOLITH
|
|
|
|
int from_type = from_space.GetFE(from_elem_idx)->GetGeomType();
|
|
int to_type = to_space.GetFE(to_elem_idx)->GetGeomType();
|
|
|
|
const int n_qp = physical_quadrature_.n_points();
|
|
|
|
from_quadrature.SetSize(n_qp);
|
|
to_quadrature.SetSize(n_qp);
|
|
|
|
ElementTransformation &from_trans =
|
|
*from_space.GetElementTransformation(from_elem_idx);
|
|
ElementTransformation &to_trans =
|
|
*to_space.GetElementTransformation(to_elem_idx);
|
|
|
|
|
|
const bool from_is_linear = (from_type == Geometry::CUBE &&
|
|
from_trans.OrderW() <= 2) || from_trans.OrderW() <= 1;
|
|
const bool to_is_linear = (to_type == Geometry::CUBE &&
|
|
to_trans.OrderW() <= 2) || to_trans.OrderW() <= 1;
|
|
|
|
if (!from_is_linear || !to_is_linear)
|
|
{
|
|
MFEM_ABORT("CutGeneric::BuildQuadrature() detected high-order element geometries."
|
|
"Moonolith only supports elements with affine faces.\n");
|
|
}
|
|
|
|
double from_measure = moonolith::measure(from_);
|
|
double to_measure = moonolith::measure(to_);
|
|
|
|
Vector p(Dim);
|
|
for (int qp = 0; qp < n_qp; ++qp)
|
|
{
|
|
|
|
for (int d = 0; d < Dim; ++d)
|
|
{
|
|
p(d) = physical_quadrature_.points[qp][d];
|
|
}
|
|
|
|
double w = physical_quadrature_.weights[qp];
|
|
intersection_measure_ += w;
|
|
|
|
assert(IsValidPhysicalPoint(p));
|
|
|
|
TransformToReference(from_trans, from_type, p, w / from_measure,
|
|
from_quadrature[qp]);
|
|
|
|
TransformToReference(to_trans, to_type, p, w / to_measure,
|
|
to_quadrature[qp]);
|
|
|
|
|
|
assert(IsValidQPoint(from_quadrature[qp]));
|
|
assert(IsValidQPoint(to_quadrature[qp]));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
template <class Polytope> void CutGeneric<Polytope>::Describe() const
|
|
{
|
|
mfem::out << "Cut measure " << intersection_measure_ << '\n';
|
|
}
|
|
|
|
template class CutGeneric<::moonolith::Polygon<double, 2>>;
|
|
template class CutGeneric<::moonolith::Polyhedron<double>>;
|
|
|
|
void Cut2D::MakePolytope(Mesh &mesh, const int elem_idx, Polygon_t &polygon)
|
|
{
|
|
|
|
mesh.GetPointMatrix(elem_idx, buffer_pts);
|
|
|
|
const int n_points = buffer_pts.Width();
|
|
polygon.resize(n_points);
|
|
|
|
for (int k = 0; k < n_points; ++k)
|
|
{
|
|
for (int d = 0; d < 2; ++d)
|
|
{
|
|
polygon.points[k][d] = buffer_pts(d, k);
|
|
}
|
|
}
|
|
|
|
assert(polygon.check_convexity());
|
|
assert(::moonolith::measure(polygon) > 0.0);
|
|
}
|
|
|
|
|
|
|
|
void Cut2D::SetQuadratureRule(const IntegrationRule &ir)
|
|
{
|
|
const int size = ir.Size();
|
|
auto &q_rule = this->GetQRule();
|
|
|
|
q_rule.resize(size);
|
|
|
|
double rule_w = 0.0;
|
|
for (int k = 0; k < size; ++k)
|
|
{
|
|
auto &qp = ir[k];
|
|
q_rule.points[k][0] = qp.x;
|
|
q_rule.points[k][1] = qp.y;
|
|
q_rule.weights[k] = qp.weight;
|
|
rule_w += qp.weight;
|
|
}
|
|
|
|
this->SetOrder(ir.GetOrder());
|
|
q_rule.normalize();
|
|
}
|
|
|
|
void Cut2D::SetIntegrationOrder(const int order)
|
|
{
|
|
if (this->GetOrder() != order)
|
|
{
|
|
const IntegrationRule &ir = IntRules.Get(Geometry::TRIANGLE, order);
|
|
assert(ir.GetOrder() >= order);
|
|
SetQuadratureRule(ir);
|
|
}
|
|
}
|
|
|
|
void Cut3D::SetIntegrationOrder(const int order)
|
|
{
|
|
if (this->GetOrder() != order)
|
|
{
|
|
const IntegrationRule &ir = IntRules.Get(Geometry::TETRAHEDRON, order);
|
|
assert(ir.GetOrder() >= order);
|
|
SetQuadratureRule(ir);
|
|
}
|
|
}
|
|
|
|
void Cut3D::MakePolytope(Mesh &mesh, const int elem_idx,
|
|
Polyhedron_t &polyhedron)
|
|
{
|
|
using namespace std;
|
|
const int dim = mesh.Dimension();
|
|
|
|
assert(mesh.GetElement(elem_idx));
|
|
|
|
const Element &e = *mesh.GetElement(elem_idx);
|
|
const int e_type = e.GetType();
|
|
|
|
mesh.GetElementFaces(elem_idx, buffer_faces, buffer_cor);
|
|
mesh.GetPointMatrix(elem_idx, buffer_pts);
|
|
mesh.GetElementVertices(elem_idx, buffer_vertices);
|
|
|
|
const int n_faces = buffer_faces.Size();
|
|
polyhedron.clear();
|
|
polyhedron.el_ptr.resize(n_faces + 1);
|
|
polyhedron.points.resize(buffer_vertices.Size());
|
|
polyhedron.el_index.resize(MaxVertsXFace(e_type) * n_faces);
|
|
polyhedron.el_ptr[0] = 0;
|
|
|
|
for (int i = 0; i < buffer_vertices.Size(); ++i)
|
|
{
|
|
for (int j = 0; j < dim; ++j)
|
|
{
|
|
polyhedron.points[i][j] = buffer_pts(j, i);
|
|
}
|
|
}
|
|
|
|
Array<int> f2v;
|
|
for (int i = 0; i < buffer_faces.Size(); ++i)
|
|
{
|
|
mesh.GetFaceVertices(buffer_faces[i], f2v);
|
|
const int eptr = polyhedron.el_ptr[i];
|
|
|
|
for (int j = 0; j < f2v.Size(); ++j)
|
|
{
|
|
const int v_offset = buffer_vertices.Find(f2v[j]);
|
|
polyhedron.el_index[eptr + j] = v_offset;
|
|
}
|
|
|
|
polyhedron.el_ptr[i + 1] = polyhedron.el_ptr[i] + f2v.Size();
|
|
}
|
|
|
|
polyhedron.fix_ordering();
|
|
}
|
|
|
|
|
|
void Cut3D::SetQuadratureRule(const IntegrationRule &ir)
|
|
{
|
|
const int size = ir.Size();
|
|
|
|
auto &q_rule = this->GetQRule();
|
|
|
|
q_rule.resize(size);
|
|
|
|
double rule_w = 0.0;
|
|
|
|
for (int k = 0; k < size; ++k)
|
|
{
|
|
auto &qp = ir[k];
|
|
q_rule.points[k][0] = qp.x;
|
|
q_rule.points[k][1] = qp.y;
|
|
q_rule.points[k][2] = qp.z;
|
|
q_rule.weights[k] = qp.weight;
|
|
rule_w += qp.weight;
|
|
}
|
|
|
|
this->SetOrder(ir.GetOrder());
|
|
q_rule.normalize();
|
|
}
|
|
|
|
std::shared_ptr<Cut> NewCut(const int dim)
|
|
{
|
|
if (dim == 2)
|
|
{
|
|
return std::make_shared<Cut2D>();
|
|
}
|
|
else if (dim == 3)
|
|
{
|
|
return std::make_shared<Cut3D>();
|
|
}
|
|
else
|
|
{
|
|
assert(false);
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
} // namespace mfem
|
|
|
|
#endif // MFEM_USE_MOONOLITH
|