Files
mfem/fem/moonolith/pmortarassembler.cpp
T

1314 lines
34 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
#ifdef MFEM_USE_MPI
#include "pmortarassembler.hpp"
#include "transferutils.hpp"
#include "cut.hpp"
#include "moonolith_bounding_volume_with_span.hpp"
#include "moonolith_n_tree_mutator_factory.hpp"
#include "moonolith_n_tree_with_span_mutator_factory.hpp"
#include "moonolith_n_tree_with_tags_mutator_factory.hpp"
#include "moonolith_profiler.hpp"
#include "moonolith_redistribute.hpp"
#include "moonolith_sparse_matrix.hpp"
#include "moonolith_tree.hpp"
#include "par_moonolith.hpp"
#include <memory>
using namespace mfem::internal;
static const bool dof_transformation = false;
namespace mfem
{
struct ParMortarAssembler::Impl
{
public:
MPI_Comm comm;
std::shared_ptr<ParFiniteElementSpace> source;
std::shared_ptr<ParFiniteElementSpace> destination;
std::vector<std::shared_ptr<MortarIntegrator>> integrators;
std::shared_ptr<HypreParMatrix> coupling_matrix;
std::shared_ptr<HypreParMatrix> mass_matrix;
std::shared_ptr<IterativeSolver> solver;
bool verbose{false};
bool assemble_mass_and_coupling_together{true};
void ensure_solver()
{
if (!solver)
{
solver = std::make_shared<BiCGSTABSolver>(comm);
solver->SetMaxIter(20000);
solver->SetRelTol(1e-6);
}
}
BilinearFormIntegrator *newBFormIntegrator() const
{
assert(!integrators.empty());
return integrators[0]->newBFormIntegrator();
}
};
ParMortarAssembler::~ParMortarAssembler() = default;
void ParMortarAssembler::SetSolver(
const std::shared_ptr<IterativeSolver> &solver)
{
impl_->solver = solver;
}
void ParMortarAssembler::SetAssembleMassAndCouplingTogether(const bool value)
{
impl_->assemble_mass_and_coupling_together = value;
}
void ParMortarAssembler::SetMaxSolverIterations(
const int max_solver_iterations)
{
impl_->ensure_solver();
impl_->solver->SetMaxIter(max_solver_iterations);
}
void ParMortarAssembler::AddMortarIntegrator(
const std::shared_ptr<MortarIntegrator> &integrator)
{
impl_->integrators.push_back(integrator);
}
void ParMortarAssembler::SetVerbose(const bool verbose)
{
impl_->verbose = verbose;
}
template <int Dimension> class ElementAdapter : public moonolith::Serializable
{
public:
using Bound = moonolith::AABBWithKDOPSpan<Dimension, double>;
using Point = moonolith::Vector<double, Dimension>;
inline int tag() const { return tag_; }
const Bound &bound() const { return bound_; }
Bound &bound() { return bound_; }
void applyRW(moonolith::Stream &stream)
{
stream &bound_;
stream &element_;
stream &element_handle_;
}
ElementAdapter(FiniteElementSpace &fe, const long element,
const long element_handle, const int tag)
: fe_(&fe), element_(element), element_handle_(element_handle), tag_(tag),
dof_map_(nullptr)
{
assert(element < fe.GetNE());
DenseMatrix pts;
fe_->GetMesh()->GetPointMatrix(element, pts);
Point p;
for (int j = 0; j < pts.Width(); ++j)
{
for (int i = 0; i < pts.Height(); ++i)
{
p[i] = pts.Elem(i, j);
}
bound_.static_bound() += p;
bound_.dynamic_bound() += p;
}
}
ElementAdapter()
: fe_(nullptr), element_(-1), element_handle_(-1), tag_(-1),
dof_map_(nullptr) {}
inline long handle() const { return element_handle_; }
inline long element() const { return element_; }
inline const FiniteElement &get() const
{
assert(fe_);
assert(element_ < fe_->GetNE());
return *fe_->GetFE(element_);
}
inline const FiniteElementSpace &space() const
{
assert(fe_);
return *fe_;
}
void set_dof_map(std::vector<long> *ptr) { dof_map_ = ptr; }
void get_elements_vdofs(Array<int> &vdofs) const
{
fe_->GetElementVDofs(element_, vdofs);
if (dof_map_)
{
assert(dof_map_->size() == vdofs.Size());
for (int i = 0; i < vdofs.Size(); ++i)
{
vdofs[i] = dof_map_->at(i);
}
}
else
{
assert(false);
}
}
private:
FiniteElementSpace *fe_;
long element_;
long element_handle_;
int tag_;
Bound bound_;
std::vector<long> *dof_map_;
};
template <int _Dimension> class TreeTraits
{
public:
enum { Dimension = _Dimension };
using Bound = moonolith::AABBWithKDOPSpan<Dimension, double>;
using DataType = mfem::ElementAdapter<Dimension>;
};
template <int Dimension>
class MFEMTree : public moonolith::Tree<TreeTraits<Dimension>>
{
public:
using Traits = mfem::TreeTraits<Dimension>;
MFEMTree() {};
static std::shared_ptr<MFEMTree>
New(const int maxElementsXNode = moonolith::DEFAULT_REFINE_MAX_ELEMENTS,
const int maxDepth = moonolith::DEFAULT_REFINE_DEPTH)
{
using namespace moonolith;
std::shared_ptr<MFEMTree> tree = std::make_shared<MFEMTree>();
std::shared_ptr<NTreeWithSpanMutatorFactory<MFEMTree>> factory =
std::make_shared<NTreeWithSpanMutatorFactory<MFEMTree>>();
factory->set_refine_params(maxElementsXNode, maxDepth);
tree->set_mutator_factory(factory);
return tree;
}
static std::shared_ptr<MFEMTree>
New(const std::shared_ptr<moonolith::Predicate> &predicate,
const int maxElementsXNode = moonolith::DEFAULT_REFINE_MAX_ELEMENTS,
const int maxDepth = moonolith::DEFAULT_REFINE_DEPTH)
{
using namespace moonolith;
if (!predicate)
{
return New(maxElementsXNode, maxDepth);
}
std::shared_ptr<MFEMTree> tree = std::make_shared<MFEMTree>();
std::shared_ptr<NTreeWithTagsMutatorFactory<MFEMTree>> factory =
std::make_shared<NTreeWithTagsMutatorFactory<MFEMTree>>(predicate);
factory->set_refine_params(maxElementsXNode, maxDepth);
tree->set_mutator_factory(factory);
return tree;
}
};
class ElementDofMap : public moonolith::Serializable
{
public:
void read(moonolith::InputStream &is) override
{
int n;
is >> n;
global.resize(n);
is.read(&global[0], n);
}
void write(moonolith::OutputStream &os) const override
{
int n = global.size();
os << n;
os.write(&global[0], n);
}
std::vector<long> global;
};
class Spaces
{
public:
explicit Spaces(const moonolith::Communicator &comm) : comm(comm)
{
must_destroy_attached[0] = false;
must_destroy_attached[1] = false;
}
Spaces(const std::shared_ptr<ParFiniteElementSpace> &source,
const std::shared_ptr<ParFiniteElementSpace> &destination)
{
spaces_.reserve(2);
spaces_.push_back(source);
spaces_.push_back(destination);
must_destroy_attached[0] = false;
must_destroy_attached[1] = false;
copy_global_dofs(*source, dof_maps_[0]);
copy_global_dofs(*destination, dof_maps_[1]);
}
~Spaces()
{
Mesh *m = nullptr;
FiniteElementCollection *fec = nullptr;
for (int i = 0; i < spaces_.size(); ++i)
{
if (spaces_[i] && must_destroy_attached[0])
{
m = spaces_[i]->GetMesh();
fec = const_cast<FiniteElementCollection *>(spaces_[i]->FEColl());
// make it null
spaces_[i] = std::shared_ptr<FiniteElementSpace>();
delete m;
delete fec;
}
}
}
inline long n_elements() const
{
long ret = 0;
for (auto s : spaces_)
{
if (s)
{
ret += s->GetNE();
}
}
return ret;
}
inline std::vector<std::shared_ptr<FiniteElementSpace>> &spaces()
{
return spaces_;
}
inline const std::vector<std::shared_ptr<FiniteElementSpace>> &
spaces() const
{
return spaces_;
}
inline std::vector<ElementDofMap> &dof_map(const int i)
{
assert(i < 2);
assert(i >= 0);
return dof_maps_[i];
}
inline const std::vector<ElementDofMap> &dof_map(const int i) const
{
assert(i < 2);
assert(i >= 0);
return dof_maps_[i];
}
inline void set_must_destroy_attached(const int index, const bool value)
{
assert(index < 2);
assert(index >= 0);
must_destroy_attached[index] = value;
}
private:
std::vector<std::shared_ptr<FiniteElementSpace>> spaces_;
moonolith::Communicator comm;
std::vector<ElementDofMap> dof_maps_[2];
bool must_destroy_attached[2];
inline static void copy_global_dofs(ParFiniteElementSpace &fe,
std::vector<ElementDofMap> &dof_map)
{
dof_map.resize(fe.GetNE());
Array<int> vdofs;
for (int i = 0; i < fe.GetNE(); ++i)
{
fe.GetElementVDofs(i, vdofs);
for (int k = 0; k < vdofs.Size(); ++k)
{
long g_dof = 0;
if (vdofs[k] >= 0)
{
g_dof = fe.GetGlobalTDofNumber(vdofs[k]);
}
else
{
g_dof = -1 - fe.GetGlobalTDofNumber(-1 - vdofs[k]);
}
dof_map[i].global.push_back(g_dof);
}
}
}
};
template <class Iterator>
static void write_space(const Iterator &begin, const Iterator &end,
FiniteElementSpace &space,
const std::vector<ElementDofMap> &dof_map,
const int role, moonolith::OutputStream &os)
{
const int dim = space.GetMesh()->Dimension();
const long n_elements = std::distance(begin, end);
std::set<long> nodeIds;
std::map<long, long> mapping;
Array<int> verts;
for (Iterator it = begin; it != end; ++it)
{
const int i = *it;
space.GetElementVertices(i, verts);
for (int j = 0; j < verts.Size(); ++j)
{
nodeIds.insert(verts[j]);
}
}
long n_nodes = nodeIds.size();
// Estimate for allocation
os.request_space((n_elements * 8 + n_nodes * dim) *
(sizeof(double) + sizeof(long)));
auto fe_coll = space.FEColl();
const char *name = fe_coll->Name();
const int name_lenght = strlen(name);
// WRITE 1
os << dim << role;
os << name_lenght;
os.write(name, name_lenght);
long index = 0;
for (auto nodeId : nodeIds)
{
mapping[nodeId] = index++;
}
// WRITE 2
os << n_nodes;
// WRITE 6
os << n_elements;
Array<int> vdofs;
for (auto node_id : nodeIds)
{
double *v = space.GetMesh()->GetVertex(node_id);
for (int i = 0; i < dim; ++i)
{
// WRITE 3
os << v[i];
}
}
for (Iterator it = begin; it != end; ++it)
{
const int k = *it;
space.GetElementVertices(k, verts);
const int attribute = space.GetAttribute(k);
const int e_n_nodes = verts.Size();
const int type = space.GetElementType(k);
const int order = space.GetOrder(k);
// WRITE 7
os << type << attribute << order << e_n_nodes;
for (int i = 0; i < e_n_nodes; ++i)
{
auto it = mapping.find(verts[i]);
assert(it != mapping.end());
int index = it->second;
// WRITE 8
os << index;
}
// WRITE 9
os << dof_map.at(k);
}
}
template <class Iterator>
static void write_element_selection(const Iterator &begin, const Iterator &end,
const Spaces &spaces,
moonolith::OutputStream &os)
{
if (spaces.spaces().empty())
{
assert(false);
return;
}
auto m = spaces.spaces()[0];
std::shared_ptr<FiniteElementSpace> s = nullptr;
if (spaces.spaces().size() > 1)
{
s = spaces.spaces()[1];
}
std::vector<long> source_selection;
std::vector<long> destination_selection;
bool met_destination_selection = false;
for (Iterator it = begin; it != end; ++it)
{
long index = *it;
if (m && index >= m->GetNE())
{
index -= m->GetNE();
destination_selection.push_back(index);
}
else if (!m)
{
met_destination_selection = true;
destination_selection.push_back(index);
}
else
{
assert(!met_destination_selection);
assert(index < m->GetNE());
source_selection.push_back(index);
}
}
const bool has_source = !source_selection.empty();
const bool has_destination = !destination_selection.empty();
os << has_source << has_destination;
if (has_source)
{
write_space(source_selection.begin(), source_selection.end(), *m,
spaces.dof_map(0), 0, os);
}
if (has_destination)
{
write_space(destination_selection.begin(), destination_selection.end(), *s,
spaces.dof_map(1), 1, os);
}
}
static FiniteElementCollection *FECollFromName(const std::string &comp_name)
{
return FiniteElementCollection::New(comp_name.c_str());
}
static void read_space(moonolith::InputStream &is,
const int vdim,
std::shared_ptr<FiniteElementSpace> &space,
std::vector<ElementDofMap> &dof_map)
{
using namespace std;
// READ 1
int dim, role, name_lenght;
is >> dim >> role;
is >> name_lenght;
std::string name(name_lenght, 0);
is.read(&name[0], name_lenght);
// READ 2
long n_nodes;
is >> n_nodes;
// READ 6
long n_elements;
is >> n_elements;
auto fe_coll = FECollFromName(name);
auto mesh_ptr = new Mesh(dim, n_nodes, n_elements);
for (long i = 0; i < n_nodes; ++i)
{
double v[3];
for (int i = 0; i < dim; ++i)
{
// READ 3
is >> v[i];
}
mesh_ptr->AddVertex(v);
}
dof_map.resize(n_elements);
std::vector<int> e2v;
for (long i = 0; i < n_elements; ++i)
{
// READ 7
int type, attribute, order, e_n_nodes;
is >> type >> attribute >> order >> e_n_nodes;
e2v.resize(e_n_nodes);
int index;
for (int i = 0; i < e_n_nodes; ++i)
{
// READ 8
is >> index;
e2v[i] = index;
}
mesh_ptr->AddElement(NewElem(type, &e2v[0], attribute));
// READ 9
is >> dof_map.at(i);
}
// if(mesh_ptr->Dimension() == 3) {
Finalize(*mesh_ptr, true);
// }
space = make_shared<FiniteElementSpace>(mesh_ptr, fe_coll, vdim);
}
static void read_spaces(moonolith::InputStream &is, const int vdim,
Spaces &spaces)
{
bool has_source, has_destination;
is >> has_source >> has_destination;
spaces.spaces().resize(2);
if (has_source)
{
read_space(is, vdim, spaces.spaces()[0], spaces.dof_map(0));
spaces.set_must_destroy_attached(0, true);
}
else
{
spaces.spaces()[0] = nullptr;
}
if (has_destination)
{
read_space(is, vdim, spaces.spaces()[1], spaces.dof_map(1));
spaces.set_must_destroy_attached(1, true);
}
else
{
spaces.spaces()[1] = nullptr;
}
}
template <int Dimensions, class Fun>
static bool Assemble(moonolith::Communicator &comm,
std::shared_ptr<ParFiniteElementSpace> &source,
std::shared_ptr<ParFiniteElementSpace> &destination,
Fun process_fun, const moonolith::SearchSettings &settings,
const bool verbose)
{
using namespace moonolith;
typedef mfem::MFEMTree<Dimensions> NTreeT;
typedef typename NTreeT::DataContainer DataContainer;
typedef typename NTreeT::DataType Adapter;
long maxNElements = settings.max_elements;
long maxDepth = settings.max_depth;
const int n_elements_source = source->GetNE();
const int n_elements_destination = destination->GetNE();
const int n_elements = n_elements_source + n_elements_destination;
auto predicate = std::make_shared<MasterAndSlave>();
predicate->add(0, 1);
MOONOLITH_EVENT_BEGIN("create_adapters");
std::shared_ptr<NTreeT> tree = NTreeT::New(predicate, maxNElements, maxDepth);
tree->reserve(n_elements);
std::shared_ptr<Spaces> local_spaces =
std::make_shared<Spaces>(source, destination);
int offset = 0;
int space_num = 0;
for (auto s : local_spaces->spaces())
{
if (s)
{
for (int i = 0; i < s->GetNE(); ++i)
{
Adapter a(*s, i, offset + i, space_num);
a.set_dof_map(&local_spaces->dof_map(space_num)[i].global);
tree->insert(a);
}
offset += s->GetNE();
}
++space_num;
}
tree->root()->bound().static_bound().enlarge(1e-6);
MOONOLITH_EVENT_END("create_adapters");
// Just to have an indexed-storage
std::map<long, std::shared_ptr<Spaces>> spaces;
std::map<long, std::vector<std::shared_ptr<Spaces>>> migrated_spaces;
auto read = [&spaces, &migrated_spaces, &source,
comm](const long ownerrank, const long /*senderrank*/,
bool is_forwarding, DataContainer &data, InputStream &in)
{
CHECK_STREAM_READ_BEGIN("vol_proj", in);
std::shared_ptr<Spaces> proc_space = std::make_shared<Spaces>(comm);
read_spaces(in, source->GetVDim(), *proc_space);
if (!is_forwarding)
{
assert(!spaces[ownerrank]);
spaces[ownerrank] = proc_space;
}
else
{
migrated_spaces[ownerrank].push_back(proc_space);
}
data.reserve(data.size() + proc_space->n_elements());
int space_num = 0;
long offset = 0;
for (auto s : proc_space->spaces())
{
if (s)
{
for (int i = 0; i < s->GetNE(); ++i)
{
data.push_back(Adapter(*s, i, offset + i, space_num));
data.back().set_dof_map(&proc_space->dof_map(space_num)[i].global);
}
offset += s->GetNE();
}
++space_num;
}
CHECK_STREAM_READ_END("vol_proj", in);
};
auto write = [&local_spaces, &spaces,
&comm](const long ownerrank, const long /*recvrank*/,
const std::vector<long>::const_iterator &begin,
const std::vector<long>::const_iterator &end,
const DataContainer & /*data*/, OutputStream &out)
{
CHECK_STREAM_WRITE_BEGIN("vol_proj", out);
if (ownerrank == comm.rank())
{
write_element_selection(begin, end, *local_spaces, out);
}
else
{
auto it = spaces.find(ownerrank);
assert(it != spaces.end());
std::shared_ptr<Spaces> spaceptr = it->second;
assert(std::distance(begin, end) > 0);
write_element_selection(begin, end, *spaceptr, out);
}
CHECK_STREAM_WRITE_END("vol_proj", out);
};
long n_false_positives = 0, n_intersections = 0;
auto fun = [&n_false_positives, &n_intersections,
&process_fun](Adapter &source, Adapter &destination) -> bool
{
bool ok = process_fun(source, destination);
if (ok)
{
n_intersections++;
return true;
}
else
{
n_false_positives++;
return false;
}
return true;
};
moonolith::search_and_compute(comm, tree, predicate, read, write, fun,
settings);
if (verbose)
{
long n_total_candidates = n_intersections + n_false_positives;
long n_collection[3] = {n_intersections, n_total_candidates,
n_false_positives
};
comm.all_reduce(n_collection, 3, moonolith::MPISum());
if (comm.is_root())
{
mfem::out << "n_intersections: " << n_collection[0]
<< ", n_total_candidates: " << n_collection[1]
<< ", n_false_positives: " << n_collection[2] << std::endl;
}
}
return true;
}
static void add_matrix(const Array<int> &destination_vdofs,
const Array<int> &source_vdofs,
const DenseMatrix &elem_mat,
moonolith::SparseMatrix<double> &mat_buffer)
{
for (int i = 0; i < destination_vdofs.Size(); ++i)
{
long dof_I = destination_vdofs[i];
double sign_I = 1.0;
if (dof_I < 0)
{
sign_I = -1.0;
dof_I = -dof_I - 1;
}
for (int j = 0; j < source_vdofs.Size(); ++j)
{
long dof_J = source_vdofs[j];
double sign_J = sign_I;
if (dof_J < 0)
{
sign_J = -sign_I;
dof_J = -dof_J - 1;
}
mat_buffer.add(dof_I, dof_J, sign_J * elem_mat.Elem(i, j));
}
}
}
std::shared_ptr<HypreParMatrix> convert_to_hypre_matrix(
const std::vector<moonolith::Integer> &destination_ranges,
HYPRE_BigInt *s_offsets,
HYPRE_BigInt *m_offsets,
moonolith::SparseMatrix<double> &mat_buffer)
{
auto &&comm = mat_buffer.comm();
moonolith::Redistribute<moonolith::SparseMatrix<double>> redist(comm.get());
redist.apply(destination_ranges, mat_buffer, moonolith::AddAssign<double>());
std::vector<int> I(s_offsets[1] - s_offsets[0] + 1);
I[0] = 0;
std::vector<HYPRE_Int> J;
std::vector<double> data;
J.reserve(mat_buffer.n_local_entries());
data.reserve(J.capacity());
for (auto it = mat_buffer.iter(); it; ++it)
{
I[it.row() - s_offsets[0] + 1]++;
J.push_back(it.col());
data.push_back(*it);
}
for (int i = 1; i < I.size(); ++i)
{
I[i] += I[i - 1];
}
auto ret = std::make_shared<HypreParMatrix>(
comm.get(), s_offsets[1] - s_offsets[0], mat_buffer.rows(),
mat_buffer.cols(), &I[0], &J[0], &data[0], s_offsets, m_offsets);
return ret;
}
int order_multiplier(const Geometry::Type type, const int dim)
{
return
(type == Geometry::TRIANGLE || type == Geometry::TETRAHEDRON ||
type == Geometry::SEGMENT)? 1 : dim;
}
template <int Dimensions>
static bool
Assemble(moonolith::Communicator &comm, ParMortarAssembler::Impl &impl,
const moonolith::SearchSettings &settings, const bool verbose)
{
auto &source = impl.source;
auto &destination = impl.destination;
auto &integrators = impl.integrators;
auto &pmat = impl.coupling_matrix;
bool lump_mass = false;
int max_q_order = 0;
for (auto i_ptr : integrators)
{
max_q_order = std::max(i_ptr->GetQuadratureOrder(), max_q_order);
}
const int dim = source->GetMesh()->Dimension();
std::shared_ptr<Cut> cut = NewCut(dim);
if (!cut)
{
assert(false && "NOT Supported!");
return false;
}
Array<int> source_vdofs, destination_vdofs;
DenseMatrix elemmat;
DenseMatrix cumulative_elemmat;
IntegrationRule src_ir;
IntegrationRule dest_ir;
const HYPRE_BigInt m_global_n_dofs = source->GlobalTrueVSize();
HYPRE_BigInt *m_offsets = source->GetTrueDofOffsets();
const HYPRE_BigInt s_global_n_dofs = destination->GlobalTrueVSize();
HYPRE_BigInt *s_offsets = destination->GetTrueDofOffsets();
double local_element_matrices_sum = 0.0;
moonolith::SparseMatrix<double> mat_buffer(comm);
mat_buffer.set_size(s_global_n_dofs, m_global_n_dofs);
moonolith::SparseMatrix<double> mass_mat_buffer(comm);
mass_mat_buffer.set_size(s_global_n_dofs, s_global_n_dofs);
std::unique_ptr<BilinearFormIntegrator> mass_integr(
impl.newBFormIntegrator());
auto fun = [&](const ElementAdapter<Dimensions> &source,
const ElementAdapter<Dimensions> &destination) -> bool
{
const auto &src = source.space();
const auto &dest = destination.space();
const int src_index = source.element();
const int dest_index = destination.element();
auto &src_fe = *src.GetFE(src_index);
auto &dest_fe = *dest.GetFE(dest_index);
ElementTransformation &dest_Trans =
*dest.GetElementTransformation(dest_index);
// Quadrature order mangling
int src_order_mult = order_multiplier(src_fe.GetGeomType(), Dimensions);
int dest_order_mult = order_multiplier(dest_fe.GetGeomType(), Dimensions);
const int src_order = src_order_mult * src_fe.GetOrder();
const int dest_order = dest_order_mult * dest_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 * dest_Trans.OrderW() + max_q_order;
cut->SetIntegrationOrder(order);
if (cut->BuildQuadrature(src, src_index, dest, dest_index, src_ir,
dest_ir))
{
// make reference quadratures
ElementTransformation &src_Trans =
*src.GetElementTransformation(src_index);
source.get_elements_vdofs(source_vdofs);
destination.get_elements_vdofs(destination_vdofs);
bool first = true;
for (auto i_ptr : integrators)
{
if (first)
{
i_ptr->AssembleElementMatrix(src_fe, src_ir, src_Trans, dest_fe,
dest_ir, dest_Trans, cumulative_elemmat);
first = false;
}
else
{
i_ptr->AssembleElementMatrix(src_fe, src_ir, src_Trans, dest_fe,
dest_ir, dest_Trans, elemmat);
cumulative_elemmat += elemmat;
}
}
local_element_matrices_sum += Sum(cumulative_elemmat);
add_matrix(destination_vdofs, source_vdofs, cumulative_elemmat,
mat_buffer);
if (impl.assemble_mass_and_coupling_together)
{
mass_integr->SetIntRule(&dest_ir);
mass_integr->AssembleElementMatrix(dest_fe, dest_Trans, elemmat);
if (lump_mass)
{
int n = destination_vdofs.Size();
for (int i = 0; i < n; ++i)
{
double row_sum = 0.;
for (int j = 0; j < n; ++j)
{
row_sum += elemmat(i, j);
elemmat(i, j) = 0.;
}
elemmat(i, i) = row_sum;
}
}
add_matrix(destination_vdofs, destination_vdofs, elemmat,
mass_mat_buffer);
}
return true;
}
else
{
return false;
}
};
if (!Assemble<Dimensions>(comm, source, destination, fun, settings,
verbose))
{
return false;
}
if (verbose)
{
double volumes[2] = {local_element_matrices_sum};
comm.all_reduce(volumes, 2, moonolith::MPISum());
cut->Describe();
if (comm.is_root())
{
mfem::out << "sum(B): " << volumes[0] << std::endl;
}
}
std::vector<moonolith::Integer> destination_ranges(comm.size() + 1, 0);
std::copy(s_offsets, s_offsets + 2, destination_ranges.begin() + comm.rank());
comm.all_reduce(&destination_ranges[0], destination_ranges.size(),
moonolith::MPIMax());
pmat = convert_to_hypre_matrix(destination_ranges, s_offsets, m_offsets,
mat_buffer);
if (impl.assemble_mass_and_coupling_together)
{
impl.mass_matrix = convert_to_hypre_matrix(destination_ranges, s_offsets,
s_offsets, mass_mat_buffer);
}
return true;
}
ParMortarAssembler::ParMortarAssembler(
const std::shared_ptr<ParFiniteElementSpace> &source,
const std::shared_ptr<ParFiniteElementSpace> &destination)
: impl_(new Impl())
{
impl_->comm = source->GetComm();
impl_->source = source;
impl_->destination = destination;
}
bool ParMortarAssembler::Assemble(std::shared_ptr<HypreParMatrix> &pmat)
{
assert(!impl_->integrators.empty() &&
"it must have at least on integrator see class MortarIntegrator");
moonolith::SearchSettings settings;
// settings.set("disable_redistribution", moonolith::Boolean(true));
// settings.set("disable_asynch", moonolith::Boolean(true));
bool ok = false;
moonolith::Communicator comm(impl_->comm);
if (impl_->source->GetMesh()->Dimension() == 2)
{
ok = mfem::Assemble<2>(comm, *impl_, settings, impl_->verbose);
}
if (impl_->source->GetMesh()->Dimension() == 3)
{
ok = mfem::Assemble<3>(comm, *impl_, settings, impl_->verbose);
}
pmat = impl_->coupling_matrix;
return ok;
}
bool ParMortarAssembler::Transfer(const ParGridFunction &src_fun,
ParGridFunction &dest_fun)
{
return Update() && Apply(src_fun, dest_fun);
}
bool ParMortarAssembler::Apply(const ParGridFunction &src_fun,
ParGridFunction &dest_fun)
{
const bool verbose = impl_->verbose;
if (!impl_->coupling_matrix)
{
if (!Update())
{
return false;
}
}
auto &B = *impl_->coupling_matrix;
auto &D = *impl_->mass_matrix;
auto &P_destination = *impl_->destination->GetProlongationMatrix();
impl_->ensure_solver();
// BlockILU prec(D);
// impl_->solver->SetPreconditioner(prec);
impl_->solver->SetOperator(D);
if (verbose)
{
impl_->solver->SetPrintLevel(3);
}
if (dof_transformation)
{
// Maybe delete in the future!?
Vector P_x_src_fun(B.Width());
auto &P_source = *impl_->source->GetProlongationMatrix();
P_source.MultTranspose(src_fun, P_x_src_fun);
Vector B_x_src_fun(B.Height());
B_x_src_fun = 0.0;
B.Mult(P_x_src_fun, B_x_src_fun);
Vector R_x_dest_fun(D.Height());
R_x_dest_fun = 0.0;
impl_->solver->Mult(B_x_src_fun, R_x_dest_fun);
P_destination.Mult(R_x_dest_fun, dest_fun);
dest_fun.Update();
}
else
{
std::unique_ptr<HypreParVector> td_src_fun =
std::unique_ptr<HypreParVector>(src_fun.ParallelProject());
Vector B_x_src_fun(B.Height());
B_x_src_fun = 0.0;
B.Mult(*td_src_fun, B_x_src_fun);
Vector R_x_dest_fun(D.Height());
R_x_dest_fun = 0.0;
impl_->solver->Mult(B_x_src_fun, R_x_dest_fun);
P_destination.Mult(R_x_dest_fun, dest_fun);
}
// Sanity check!
int converged = impl_->solver->GetFinalNorm() < 1e-5;
MPI_Allreduce(MPI_IN_PLACE, &converged, 1, MPI_INT, MPI_MIN, impl_->comm);
return converged;
}
bool ParMortarAssembler::Update()
{
using namespace std;
const bool verbose = impl_->verbose;
moonolith::Communicator comm(impl_->comm);
if (verbose)
{
moonolith::root_describe(
"--------------------------------------------------------"
"\nAssembly begin: ",
comm, mfem::out);
}
if (!Assemble(impl_->coupling_matrix))
{
return false;
}
if (verbose)
{
moonolith::root_describe(
"\nAssembly end: "
"--------------------------------------------------------",
comm, mfem::out);
}
if (dof_transformation)
{
impl_->coupling_matrix.reset(RAP(impl_->destination->Dof_TrueDof_Matrix(),
impl_->coupling_matrix.get(),
impl_->source->Dof_TrueDof_Matrix()));
}
auto &B = *impl_->coupling_matrix;
if (!impl_->assemble_mass_and_coupling_together)
{
ParBilinearForm b_form(impl_->destination.get());
b_form.AddDomainIntegrator(impl_->newBFormIntegrator());
b_form.Assemble();
b_form.Finalize();
impl_->mass_matrix = shared_ptr<HypreParMatrix>(b_form.ParallelAssemble());
}
comm.barrier();
if (verbose && comm.is_root())
{
mfem::out << "P in R^(" << impl_->source->Dof_TrueDof_Matrix()->Height();
mfem::out << " x " << impl_->source->Dof_TrueDof_Matrix()->Width() << ")\n";
mfem::out << "Q in R^("
<< impl_->destination->Dof_TrueDof_Matrix()->Height();
mfem::out << " x " << impl_->destination->Dof_TrueDof_Matrix()->Width()
<< ")\n";
}
if (dof_transformation && impl_->assemble_mass_and_coupling_together)
{
impl_->mass_matrix.reset(RAP(impl_->destination->Dof_TrueDof_Matrix(),
impl_->mass_matrix.get(),
impl_->destination->Dof_TrueDof_Matrix()));
}
auto &D = *impl_->mass_matrix;
comm.barrier();
if (verbose && comm.is_root())
{
mfem::out << "--------------------------------------------------------"
<< std::endl;
mfem::out << "B in R^(" << B.GetGlobalNumRows() << " x "
<< B.GetGlobalNumCols() << ")" << std::endl;
mfem::out << "D in R^(" << D.GetGlobalNumRows() << " x "
<< D.GetGlobalNumCols() << ")" << std::endl;
mfem::out << "--------------------------------------------------------"
<< std::endl;
}
comm.barrier();
if (verbose)
{
Vector v(D.Width());
v = 1.0;
Vector Dv(D.Height());
D.Mult(v, Dv);
double sum_Dv = Dv.Sum();
comm.all_reduce(&sum_Dv, 1, MPI_SUM);
if (comm.is_root())
{
mfem::out << "sum(D): " << sum_Dv << std::endl;
}
}
return true;
}
} // namespace mfem
#endif // MFEM_USE_MPI
#endif // MFEM_USE_MOONOLITH