MFEM v4.9.0
Finite element discretization library
Loading...
Searching...
No Matches
mortarassembler.cpp
Go to the documentation of this file.
1// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
2// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
3// LICENSE and NOTICE for details. LLNL-CODE-806117.
4//
5// This file is part of the MFEM library. For more information and source code
6// availability visit https://mfem.org.
7//
8// MFEM is free software; you can redistribute it and/or modify it under the
9// terms of the BSD-3 license. We welcome feedback and contributions, see file
10// CONTRIBUTING.md for details.
11
13
14#ifdef MFEM_USE_MOONOLITH
15
16#include "mortarassembler.hpp"
18
19#include "cut.hpp"
20#include "transferutils.hpp"
21
22#include <cassert>
23
24// Moonolith includes
25#include "moonolith_aabb.hpp"
26#include "moonolith_serial_hash_grid.hpp"
27#include "moonolith_stream_utils.hpp"
28#include "par_moonolith_config.hpp"
29
30using namespace mfem::internal;
31
32namespace mfem
33{
34
35struct MortarAssembler::Impl
36{
37public:
38 std::shared_ptr<FiniteElementSpace> source;
39 std::shared_ptr<FiniteElementSpace> destination;
40 std::vector<std::shared_ptr<MortarIntegrator>> integrators;
41 std::shared_ptr<SparseMatrix> coupling_matrix;
42 std::shared_ptr<SparseMatrix> mass_matrix;
43 bool verbose{false};
44 bool assemble_mass_and_coupling_together{true};
45 int max_solver_iterations{400};
46
47 BilinearFormIntegrator * newBFormIntegrator() const
48 {
49 assert(!integrators.empty());
50 return integrators[0]->newBFormIntegrator();
51 }
52};
53
55
57{
58 impl_->assemble_mass_and_coupling_together = value;
59}
60
61void MortarAssembler::SetMaxSolverIterations(const int max_solver_iterations)
62{
63 impl_->max_solver_iterations = max_solver_iterations;
64}
65
67 const std::shared_ptr<MortarIntegrator> &integrator)
68{
69 impl_->integrators.push_back(integrator);
70}
71
72void MortarAssembler::SetVerbose(const bool verbose)
73{
74 impl_->verbose = verbose;
75}
76
77template <int Dim>
78void BuildBoxes(const Mesh &mesh,
79 std::vector<::moonolith::AABB<Dim, double>> &element_boxes)
80{
81#ifndef NDEBUG
82 const int dim = mesh.Dimension();
83 assert(dim == Dim);
84#endif
85 element_boxes.resize(mesh.GetNE());
86
88 for (int i = 0; i < mesh.GetNE(); ++i)
89 {
90 mesh.GetPointMatrix(i, pts);
91 MinCol(pts, &element_boxes[i].min_[0], false);
92 MaxCol(pts, &element_boxes[i].max_[0], false);
93 }
94}
95
96bool HashGridDetectIntersections(const Mesh &src, const Mesh &dest,
97 std::vector<moonolith::Integer> &pairs)
98{
99 const int dim = dest.Dimension();
100
101 switch (dim)
102 {
103 case 1:
104 {
105 std::vector<::moonolith::AABB<1, double>> src_boxes, dest_boxes;
106 BuildBoxes(src, src_boxes);
107 BuildBoxes(dest, dest_boxes);
108
109 ::moonolith::SerialHashGrid<1, double> grid;
110 return grid.detect(src_boxes, dest_boxes, pairs);
111 }
112 case 2:
113 {
114 std::vector<::moonolith::AABB<2, double>> src_boxes, dest_boxes;
115 BuildBoxes(src, src_boxes);
116 BuildBoxes(dest, dest_boxes);
117
118 ::moonolith::SerialHashGrid<2, double> grid;
119 return grid.detect(src_boxes, dest_boxes, pairs);
120 }
121 case 3:
122 {
123 std::vector<::moonolith::AABB<3, double>> src_boxes, dest_boxes;
124 BuildBoxes(src, src_boxes);
125 BuildBoxes(dest, dest_boxes);
126
127 ::moonolith::SerialHashGrid<3, double> grid;
128 return grid.detect(src_boxes, dest_boxes, pairs);
129 }
130 default:
131 {
132 assert(false);
133 return false;
134 }
135 }
136}
137
139 const std::shared_ptr<FiniteElementSpace> &source,
140 const std::shared_ptr<FiniteElementSpace> &destination)
141 : impl_(new Impl())
142{
143 impl_->source = source;
144 impl_->destination = destination;
145}
146
147int order_multiplier(const Geometry::Type type, const int dim)
148{
149 return
150 (type == Geometry::TRIANGLE || type == Geometry::TETRAHEDRON ||
151 type == Geometry::SEGMENT)? 1 : dim;
152}
153
154bool MortarAssembler::Assemble(std::shared_ptr<SparseMatrix> &B)
155{
156 using namespace std;
157 const bool verbose = impl_->verbose;
158
159 const auto &source_mesh = *impl_->source->GetMesh();
160 const auto &destination_mesh = *impl_->destination->GetMesh();
161
162 int dim = source_mesh.Dimension();
163
164 std::vector<::moonolith::Integer> pairs;
165 if (!HashGridDetectIntersections(source_mesh, destination_mesh, pairs))
166 {
167 return false;
168 }
169
170 std::shared_ptr<Cut> cut = NewCut(dim);
171 if (!cut)
172 {
173 assert(false && "NOT Supported!");
174 return false;
175 }
176
177
178 IntegrationRule source_ir;
179 IntegrationRule destination_ir;
180
181 int skip_zeros = 1;
182 B = make_shared<SparseMatrix>(impl_->destination->GetNDofs(),
183 impl_->source->GetNDofs());
184
185 std::unique_ptr<BilinearFormIntegrator> mass_integr(
186 impl_->newBFormIntegrator());
187
188 if (impl_->assemble_mass_and_coupling_together)
189 {
190 impl_->mass_matrix = make_shared<SparseMatrix>(impl_->destination->GetNDofs(),
191 impl_->destination->GetNDofs());
192 }
193
194 Array<int> source_vdofs, destination_vdofs;
195 DenseMatrix elemmat;
196 DenseMatrix cumulative_elemmat;
197 double local_element_matrices_sum = 0.0;
198
199 long n_intersections = 0;
200 long n_candidates = 0;
201
202 int max_q_order = 0;
203
204 for (auto i_ptr : impl_->integrators)
205 {
206 max_q_order = std::max(i_ptr->GetQuadratureOrder(), max_q_order);
207 }
208
209 bool intersected = false;
210 for (auto it = begin(pairs); it != end(pairs); /* inside */)
211 {
212 const int source_index = *it++;
213 const int destination_index = *it++;
214
215 auto &source_fe = *impl_->source->GetFE(source_index);
216 auto &destination_fe = *impl_->destination->GetFE(destination_index);
217
218 ElementTransformation &destination_Trans =
219 *impl_->destination->GetElementTransformation(destination_index);
220
221 // Quadrature order mangling
222 int src_order_mult = order_multiplier(source_fe.GetGeomType(), dim);
223 int dest_order_mult = order_multiplier(destination_fe.GetGeomType(), dim);
224
225 const int src_order = src_order_mult * source_fe.GetOrder();
226 const int dest_order = dest_order_mult * destination_fe.GetOrder();
227
228 int contraction_order = src_order + dest_order;
229
230 if (impl_->assemble_mass_and_coupling_together)
231 {
232 contraction_order = std::max(contraction_order, 2 * dest_order);
233 }
234
235 const int order = contraction_order + dest_order_mult *
236 destination_Trans.OrderW() + max_q_order;
237
238 // Update the quadrature rule in case it changed the order
239 cut->SetIntegrationOrder(order);
240
241 n_candidates++;
242
243 if (cut->BuildQuadrature(*impl_->source, source_index, *impl_->destination,
244 destination_index, source_ir, destination_ir))
245 {
246 impl_->source->GetElementVDofs(source_index, source_vdofs);
247 impl_->destination->GetElementVDofs(destination_index, destination_vdofs);
248
249 ElementTransformation &source_Trans =
250 *impl_->source->GetElementTransformation(source_index);
251
252 bool first = true;
253 for (auto i_ptr : impl_->integrators)
254 {
255 if (first)
256 {
257 i_ptr->AssembleElementMatrix(source_fe, source_ir, source_Trans,
258 destination_fe, destination_ir,
259 destination_Trans, cumulative_elemmat);
260 first = false;
261 }
262 else
263 {
264 i_ptr->AssembleElementMatrix(source_fe, source_ir, source_Trans,
265 destination_fe, destination_ir,
266 destination_Trans, elemmat);
267 cumulative_elemmat += elemmat;
268 }
269 }
270
271 local_element_matrices_sum += Sum(cumulative_elemmat);
272
273 B->AddSubMatrix(destination_vdofs, source_vdofs, cumulative_elemmat,
274 skip_zeros);
275
276 if (impl_->assemble_mass_and_coupling_together)
277 {
278 mass_integr->SetIntRule(&destination_ir);
279 mass_integr->AssembleElementMatrix(destination_fe, destination_Trans, elemmat);
280 impl_->mass_matrix->AddSubMatrix(destination_vdofs, destination_vdofs, elemmat,
281 skip_zeros);
282 }
283
284 intersected = true;
285 ++n_intersections;
286 }
287 }
288
289 if (!intersected)
290 {
291 return false;
292 }
293
294 B->Finalize();
295
296 if (impl_->assemble_mass_and_coupling_together)
297 {
298 impl_->mass_matrix->Finalize();
299 }
300
301 if (verbose)
302 {
303 mfem::out << "local_element_matrices_sum: " << local_element_matrices_sum
304 << std::endl;
305 mfem::out << "B in R^(" << B->Height() << " x " << B->Width() << ")"
306 << std::endl;
307
308 mfem::out << "n_intersections: " << n_intersections
309 << ", n_candidates: " << n_candidates << '\n';
310
311 cut->Describe();
312 }
313
314 return true;
315}
316
318 GridFunction &dest_fun)
319{
320 return Update() && Apply(src_fun, dest_fun);
321}
322
324 GridFunction &dest_fun)
325{
326 if (!impl_->coupling_matrix)
327 {
328 if (!Update())
329 {
330 return false;
331 }
332 }
333
334 Vector temp(impl_->coupling_matrix->Height());
335 impl_->coupling_matrix->Mult(src_fun, temp);
336
337 CGSolver Dinv;
338 Dinv.SetMaxIter(impl_->max_solver_iterations);
339
340 if (impl_->verbose)
341 {
342 Dinv.SetPrintLevel(3);
343 }
344
345 Dinv.SetOperator(*impl_->mass_matrix);
346 Dinv.SetRelTol(1e-6);
347 Dinv.SetMaxIter(80);
348 Dinv.Mult(temp, dest_fun);
349 return true;
350}
351
353{
354 using namespace std;
355 const bool verbose = impl_->verbose;
356
357 StopWatch chrono;
358
359 if (verbose)
360 {
361 mfem::out << "\nAssembling coupling operator..." << endl;
362 }
363
364 chrono.Start();
365
366 if (!Assemble(impl_->coupling_matrix))
367 {
368 return false;
369 }
370
371 chrono.Stop();
372 if (verbose)
373 {
374 mfem::out << "Done. time: ";
375 mfem::out << chrono.RealTime() << " seconds" << endl;
376 }
377
378 if (!impl_->assemble_mass_and_coupling_together)
379 {
380 BilinearForm b_form(impl_->destination.get());
381
382 b_form.AddDomainIntegrator(impl_->newBFormIntegrator());
383
384 b_form.Assemble();
385 b_form.Finalize();
386
387 impl_->mass_matrix = std::shared_ptr<SparseMatrix>(b_form.LoseMat());
388 }
389
390 if (verbose)
391 {
392 Vector brs(impl_->coupling_matrix->Height());
393 impl_->coupling_matrix->GetRowSums(brs);
394
395 Vector drs(impl_->mass_matrix->Height());
396 impl_->mass_matrix->GetRowSums(drs);
397
398 mfem::out << "sum(B): " << brs.Sum() << std::endl;
399 mfem::out << "sum(D): " << drs.Sum() << std::endl;
400 }
401
402 return true;
403}
404
405} // namespace mfem
406
407#endif // MFEM_USE_MOONOLITH
Abstract base class BilinearFormIntegrator.
A "square matrix" operator for the associated FE space and BLFIntegrators The sum of all the BLFInteg...
void AddDomainIntegrator(BilinearFormIntegrator *bfi)
Adds new Domain Integrator. Assumes ownership of bfi.
void Finalize(int skip_zeros=1) override
Finalizes the matrix initialization if the AssemblyLevel is AssemblyLevel::LEGACY....
void Assemble(int skip_zeros=1)
Assembles the form i.e. sums over all domain/bdr integrators.
SparseMatrix * LoseMat()
Nullifies the internal matrix and returns a pointer to it. Used for transferring ownership.
Conjugate gradient method.
Definition solvers.hpp:627
void Mult(const Vector &b, Vector &x) const override
Iterative solution of the linear system using the Conjugate Gradient method.
Definition solvers.cpp:869
void SetOperator(const Operator &op) override
Set/update the solver for the given operator.
Definition solvers.hpp:640
Data type dense matrix using column-major storage.
Definition densemat.hpp:24
virtual int OrderW() const =0
Return the order of the determinant of the Jacobian (weight) of the transformation.
Class for grid function - Vector with associated FE space.
Definition gridfunc.hpp:32
Class for an integration rule - an Array of IntegrationPoint.
Definition intrules.hpp:100
void SetRelTol(real_t rtol)
Definition solvers.hpp:238
virtual void SetPrintLevel(int print_lvl)
Legacy method to set the level of verbosity of the solver output.
Definition solvers.cpp:76
void SetMaxIter(int max_it)
Definition solvers.hpp:240
Mesh data type.
Definition mesh.hpp:65
int GetNE() const
Returns number of elements.
Definition mesh.hpp:1377
int Dimension() const
Dimension of the reference space used within the elements.
Definition mesh.hpp:1306
void GetPointMatrix(int i, DenseMatrix &pointmat) const
Definition mesh.cpp:7972
void SetAssembleMassAndCouplingTogether(const bool value)
Control if the Mass matrix is computed together with the coupling operator every time.
void SetMaxSolverIterations(const int max_solver_iterations)
Control the maximum numbers of conjugate gradients steps for mass matrix inversion.
void SetVerbose(const bool verbose)
Expose process details with verbose output.
void AddMortarIntegrator(const std::shared_ptr< MortarIntegrator > &integrator)
This method must be called before Assemble or Transfer. It will assemble the operator in all intersec...
MortarAssembler(const std::shared_ptr< FiniteElementSpace > &source, const std::shared_ptr< FiniteElementSpace > &destination)
constructs the object with source and destination spaces
bool Apply(const GridFunction &src_fun, GridFunction &dest_fun)
transfer a function from source to destination. It requires that the Update function is called before
bool Transfer(const GridFunction &src_fun, GridFunction &dest_fun)
transfer a function from source to destination. if the transfer is to be performed multiple times use...
bool Assemble(std::shared_ptr< SparseMatrix > &B)
assembles the coupling matrix B. B : source -> destination If u is a coefficient associated with sour...
bool Update()
assembles the various components necessary for the transfer. To be called before calling the Apply fu...
Timing object.
Definition tic_toc.hpp:36
double RealTime()
Return the number of real seconds elapsed since the stopwatch was started.
Definition tic_toc.cpp:432
void Start()
Start the stopwatch. The elapsed time is not cleared.
Definition tic_toc.cpp:411
void Stop()
Stop the stopwatch.
Definition tic_toc.cpp:422
Vector data type.
Definition vector.hpp:82
real_t Sum() const
Return the sum of the vector entries.
Definition vector.cpp:1246
int dim
Definition ex24.cpp:53
void source(const Vector &x, Vector &f)
Definition ex25.cpp:620
std::shared_ptr< Cut > NewCut(const int dim)
Definition cut.cpp:455
OutStream out(std::cout)
Global stream used by the library for standard output. Initially it uses the same std::streambuf as s...
Definition globals.hpp:66
void BuildBoxes(const Mesh &mesh, std::vector<::moonolith::AABB< Dim, double > > &element_boxes)
bool HashGridDetectIntersections(const Mesh &src, const Mesh &dest, std::vector< moonolith::Integer > &pairs)
int order_multiplier(const Geometry::Type type, const int dim)
void pts(int iphi, int t, real_t x[])