Files
mfem/fem/moonolith/mortarassembler.cpp
T

405 lines
11 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 "mortarassembler.hpp"
#include "../../general/tic_toc.hpp"
#include "cut.hpp"
#include "transferutils.hpp"
#include <cassert>
// Moonolith includes
#include "moonolith_aabb.hpp"
#include "moonolith_serial_hash_grid.hpp"
#include "moonolith_stream_utils.hpp"
#include "par_moonolith_config.hpp"
using namespace mfem::internal;
namespace mfem
{
struct MortarAssembler::Impl
{
public:
std::shared_ptr<FiniteElementSpace> source;
std::shared_ptr<FiniteElementSpace> destination;
std::vector<std::shared_ptr<MortarIntegrator>> integrators;
std::shared_ptr<SparseMatrix> coupling_matrix;
std::shared_ptr<SparseMatrix> mass_matrix;
bool verbose{false};
bool assemble_mass_and_coupling_together{true};
int max_solver_iterations{400};
BilinearFormIntegrator * newBFormIntegrator() const
{
assert(!integrators.empty());
return integrators[0]->newBFormIntegrator();
}
};
MortarAssembler::~MortarAssembler() = default;
void MortarAssembler::SetAssembleMassAndCouplingTogether(const bool value)
{
impl_->assemble_mass_and_coupling_together = value;
}
void MortarAssembler::SetMaxSolverIterations(const int max_solver_iterations)
{
impl_->max_solver_iterations = max_solver_iterations;
}
void MortarAssembler::AddMortarIntegrator(
const std::shared_ptr<MortarIntegrator> &integrator)
{
impl_->integrators.push_back(integrator);
}
void MortarAssembler::SetVerbose(const bool verbose)
{
impl_->verbose = verbose;
}
template <int Dim>
void BuildBoxes(const Mesh &mesh,
std::vector<::moonolith::AABB<Dim, double>> &element_boxes)
{
MFEM_ASSERT(mesh.Dimension() == Dim, "Mesh and box dimensions mismatched");
element_boxes.resize(mesh.GetNE());
DenseMatrix pts;
for (int i = 0; i < mesh.GetNE(); ++i)
{
mesh.GetPointMatrix(i, pts);
MinCol(pts, &element_boxes[i].min_[0], false);
MaxCol(pts, &element_boxes[i].max_[0], false);
}
}
bool HashGridDetectIntersections(const Mesh &src, const Mesh &dest,
std::vector<moonolith::Integer> &pairs)
{
const int dim = dest.Dimension();
switch (dim)
{
case 1:
{
std::vector<::moonolith::AABB<1, double>> src_boxes, dest_boxes;
BuildBoxes(src, src_boxes);
BuildBoxes(dest, dest_boxes);
::moonolith::SerialHashGrid<1, double> grid;
return grid.detect(src_boxes, dest_boxes, pairs);
}
case 2:
{
std::vector<::moonolith::AABB<2, double>> src_boxes, dest_boxes;
BuildBoxes(src, src_boxes);
BuildBoxes(dest, dest_boxes);
::moonolith::SerialHashGrid<2, double> grid;
return grid.detect(src_boxes, dest_boxes, pairs);
}
case 3:
{
std::vector<::moonolith::AABB<3, double>> src_boxes, dest_boxes;
BuildBoxes(src, src_boxes);
BuildBoxes(dest, dest_boxes);
::moonolith::SerialHashGrid<3, double> grid;
return grid.detect(src_boxes, dest_boxes, pairs);
}
default:
{
assert(false);
return false;
}
}
}
MortarAssembler::MortarAssembler(
const std::shared_ptr<FiniteElementSpace> &source,
const std::shared_ptr<FiniteElementSpace> &destination)
: impl_(new Impl())
{
impl_->source = source;
impl_->destination = destination;
}
int order_multiplier(const Geometry::Type type, const int dim)
{
return
(type == Geometry::TRIANGLE || type == Geometry::TETRAHEDRON ||
type == Geometry::SEGMENT)? 1 : dim;
}
bool MortarAssembler::Assemble(std::shared_ptr<SparseMatrix> &B)
{
using namespace std;
const bool verbose = impl_->verbose;
const auto &source_mesh = *impl_->source->GetMesh();
const auto &destination_mesh = *impl_->destination->GetMesh();
int dim = source_mesh.Dimension();
std::vector<::moonolith::Integer> pairs;
if (!HashGridDetectIntersections(source_mesh, destination_mesh, pairs))
{
return false;
}
std::shared_ptr<Cut> cut = NewCut(dim);
if (!cut)
{
assert(false && "NOT Supported!");
return false;
}
IntegrationRule source_ir;
IntegrationRule destination_ir;
int skip_zeros = 1;
B = make_shared<SparseMatrix>(impl_->destination->GetNDofs(),
impl_->source->GetNDofs());
std::unique_ptr<BilinearFormIntegrator> mass_integr(
impl_->newBFormIntegrator());
if (impl_->assemble_mass_and_coupling_together)
{
impl_->mass_matrix = make_shared<SparseMatrix>(impl_->destination->GetNDofs(),
impl_->destination->GetNDofs());
}
Array<int> source_vdofs, destination_vdofs;
DenseMatrix elemmat;
DenseMatrix cumulative_elemmat;
double local_element_matrices_sum = 0.0;
long n_intersections = 0;
long n_candidates = 0;
int max_q_order = 0;
for (auto i_ptr : impl_->integrators)
{
max_q_order = std::max(i_ptr->GetQuadratureOrder(), max_q_order);
}
bool intersected = false;
for (auto it = begin(pairs); it != end(pairs); /* inside */)
{
const int source_index = *it++;
const int destination_index = *it++;
auto &source_fe = *impl_->source->GetFE(source_index);
auto &destination_fe = *impl_->destination->GetFE(destination_index);
ElementTransformation &destination_Trans =
*impl_->destination->GetElementTransformation(destination_index);
// Quadrature order mangling
int src_order_mult = order_multiplier(source_fe.GetGeomType(), dim);
int dest_order_mult = order_multiplier(destination_fe.GetGeomType(), dim);
const int src_order = src_order_mult * source_fe.GetOrder();
const int dest_order = dest_order_mult * destination_fe.GetOrder();
int contraction_order = src_order + dest_order;
if (impl_->assemble_mass_and_coupling_together)
{
contraction_order = std::max(contraction_order, 2 * dest_order);
}
const int order = contraction_order + dest_order_mult *
destination_Trans.OrderW() + max_q_order;
// Update the quadrature rule in case it changed the order
cut->SetIntegrationOrder(order);
n_candidates++;
if (cut->BuildQuadrature(*impl_->source, source_index, *impl_->destination,
destination_index, source_ir, destination_ir))
{
impl_->source->GetElementVDofs(source_index, source_vdofs);
impl_->destination->GetElementVDofs(destination_index, destination_vdofs);
ElementTransformation &source_Trans =
*impl_->source->GetElementTransformation(source_index);
bool first = true;
for (auto i_ptr : impl_->integrators)
{
if (first)
{
i_ptr->AssembleElementMatrix(source_fe, source_ir, source_Trans,
destination_fe, destination_ir,
destination_Trans, cumulative_elemmat);
first = false;
}
else
{
i_ptr->AssembleElementMatrix(source_fe, source_ir, source_Trans,
destination_fe, destination_ir,
destination_Trans, elemmat);
cumulative_elemmat += elemmat;
}
}
local_element_matrices_sum += Sum(cumulative_elemmat);
B->AddSubMatrix(destination_vdofs, source_vdofs, cumulative_elemmat,
skip_zeros);
if (impl_->assemble_mass_and_coupling_together)
{
mass_integr->SetIntRule(&destination_ir);
mass_integr->AssembleElementMatrix(destination_fe, destination_Trans, elemmat);
impl_->mass_matrix->AddSubMatrix(destination_vdofs, destination_vdofs, elemmat,
skip_zeros);
}
intersected = true;
++n_intersections;
}
}
if (!intersected)
{
return false;
}
B->Finalize();
if (impl_->assemble_mass_and_coupling_together)
{
impl_->mass_matrix->Finalize();
}
if (verbose)
{
mfem::out << "local_element_matrices_sum: " << local_element_matrices_sum
<< std::endl;
mfem::out << "B in R^(" << B->Height() << " x " << B->Width() << ")"
<< std::endl;
mfem::out << "n_intersections: " << n_intersections
<< ", n_candidates: " << n_candidates << '\n';
cut->Describe();
}
return true;
}
bool MortarAssembler::Transfer(const GridFunction &src_fun,
GridFunction &dest_fun)
{
return Update() && Apply(src_fun, dest_fun);
}
bool MortarAssembler::Apply(const GridFunction &src_fun,
GridFunction &dest_fun)
{
if (!impl_->coupling_matrix)
{
if (!Update())
{
return false;
}
}
Vector temp(impl_->coupling_matrix->Height());
impl_->coupling_matrix->Mult(src_fun, temp);
CGSolver Dinv;
Dinv.SetMaxIter(impl_->max_solver_iterations);
if (impl_->verbose)
{
Dinv.SetPrintLevel(3);
}
Dinv.SetOperator(*impl_->mass_matrix);
Dinv.SetRelTol(1e-6);
Dinv.SetMaxIter(80);
Dinv.Mult(temp, dest_fun);
return true;
}
bool MortarAssembler::Update()
{
using namespace std;
const bool verbose = impl_->verbose;
StopWatch chrono;
if (verbose)
{
mfem::out << "\nAssembling coupling operator..." << endl;
}
chrono.Start();
if (!Assemble(impl_->coupling_matrix))
{
return false;
}
chrono.Stop();
if (verbose)
{
mfem::out << "Done. time: ";
mfem::out << chrono.RealTime() << " seconds" << endl;
}
if (!impl_->assemble_mass_and_coupling_together)
{
BilinearForm b_form(impl_->destination.get());
b_form.AddDomainIntegrator(impl_->newBFormIntegrator());
b_form.Assemble();
b_form.Finalize();
impl_->mass_matrix = std::shared_ptr<SparseMatrix>(b_form.LoseMat());
}
if (verbose)
{
Vector brs(impl_->coupling_matrix->Height());
impl_->coupling_matrix->GetRowSums(brs);
Vector drs(impl_->mass_matrix->Height());
impl_->mass_matrix->GetRowSums(drs);
mfem::out << "sum(B): " << brs.Sum() << std::endl;
mfem::out << "sum(D): " << drs.Sum() << std::endl;
}
return true;
}
} // namespace mfem
#endif // MFEM_USE_MOONOLITH