MFEM v4.8.0
Finite element discretization library
Loading...
Searching...
No Matches
pmortarassembler.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
12
14
15#ifdef MFEM_USE_MOONOLITH
16#ifdef MFEM_USE_MPI
17
18#include "pmortarassembler.hpp"
19#include "transferutils.hpp"
20
21#include "cut.hpp"
22
23#include "moonolith_bounding_volume_with_span.hpp"
24#include "moonolith_n_tree_mutator_factory.hpp"
25#include "moonolith_n_tree_with_span_mutator_factory.hpp"
26#include "moonolith_n_tree_with_tags_mutator_factory.hpp"
27#include "moonolith_profiler.hpp"
28#include "moonolith_redistribute.hpp"
29#include "moonolith_sparse_matrix.hpp"
30#include "moonolith_tree.hpp"
31#include "par_moonolith.hpp"
32
33#include <memory>
34
35using namespace mfem::internal;
36
37namespace mfem
38{
39
40struct ParMortarAssembler::Impl
41{
42public:
43 MPI_Comm comm;
44 std::shared_ptr<ParFiniteElementSpace> source;
45 std::shared_ptr<ParFiniteElementSpace> destination;
46 std::vector<std::shared_ptr<MortarIntegrator>> integrators;
47
48 std::shared_ptr<HypreParMatrix> coupling_matrix;
49 std::shared_ptr<HypreParMatrix> mass_matrix;
50
51 std::shared_ptr<IterativeSolver> solver;
52
53 bool verbose{false};
54
55 bool assemble_mass_and_coupling_together{true};
56
57 void ensure_solver()
58 {
59 if (!solver)
60 {
61 solver = std::make_shared<BiCGSTABSolver>(comm);
62 solver->SetMaxIter(20000);
63 solver->SetRelTol(1e-6);
64 }
65 }
66
67 bool is_vector_fe() const
68 {
69 bool is_vector_fe = false;
70 for (auto i_ptr : integrators)
71 {
72 if (i_ptr->is_vector_fe())
73 {
74 is_vector_fe = true;
75 break;
76 }
77 }
78
79 return is_vector_fe;
80 }
81
82 BilinearFormIntegrator * new_mass_integrator() const
83 {
84 if (is_vector_fe())
85 {
86 return new VectorFEMassIntegrator();
87 }
88 else
89 {
90 return new MassIntegrator();
91 }
92 }
93};
94
96
97void ParMortarAssembler::SetSolver(const std::shared_ptr<IterativeSolver>
98 &solver)
99{
100 impl_->solver = solver;
101}
102
104{
105 impl_->assemble_mass_and_coupling_together = value;
106}
107
108void ParMortarAssembler::SetMaxSolverIterations(const int max_solver_iterations)
109{
110 impl_->ensure_solver();
111
112 impl_->solver->SetMaxIter(max_solver_iterations);
113}
114
116 const std::shared_ptr<MortarIntegrator> &integrator)
117{
118 impl_->integrators.push_back(integrator);
119}
120
121void ParMortarAssembler::SetVerbose(const bool verbose)
122{
123 impl_->verbose = verbose;
124}
125
126template <int Dimension> class ElementAdapter : public moonolith::Serializable
127{
128public:
129 using Bound = moonolith::AABBWithKDOPSpan<Dimension, double>;
130 using Point = moonolith::Vector<double, Dimension>;
131
132 inline int tag() const { return tag_; }
133
134 const Bound &bound() const { return bound_; }
135
136 Bound &bound() { return bound_; }
137
138 void applyRW(moonolith::Stream &stream)
139 {
140 stream &bound_;
141 stream &element_;
142 stream &element_handle_;
143 }
144
145 ElementAdapter(FiniteElementSpace &fe, const long element,
146 const long element_handle, const int tag)
147 : fe_(&fe), element_(element), element_handle_(element_handle), tag_(tag),
148 dof_map_(nullptr)
149 {
150 assert(element < fe.GetNE());
151
153 fe_->GetMesh()->GetPointMatrix(element, pts);
154
155 Point p;
156 for (int j = 0; j < pts.Width(); ++j)
157 {
158 for (int i = 0; i < pts.Height(); ++i)
159 {
160 p[i] = pts.Elem(i, j);
161 }
162
163 bound_.static_bound() += p;
164 bound_.dynamic_bound() += p;
165 }
166 }
167
168 ElementAdapter()
169 : fe_(nullptr), element_(-1), element_handle_(-1), tag_(-1),
170 dof_map_(nullptr) {}
171
172 inline long handle() const { return element_handle_; }
173
174 inline long element() const { return element_; }
175
176 inline const FiniteElement &get() const
177 {
178 assert(fe_);
179 assert(element_ < fe_->GetNE());
180 return *fe_->GetFE(element_);
181 }
182
183 inline const FiniteElementSpace &space() const
184 {
185 assert(fe_);
186 return *fe_;
187 }
188
189 void set_dof_map(std::vector<long> *ptr) { dof_map_ = ptr; }
190
191 void get_elements_vdofs(Array<int> &vdofs) const
192 {
193 fe_->GetElementVDofs(element_, vdofs);
194
195 if (dof_map_)
196 {
197 assert(dof_map_->size() == vdofs.Size());
198
199 for (int i = 0; i < vdofs.Size(); ++i)
200 {
201 vdofs[i] = dof_map_->at(i);
202 }
203
204 }
205 else
206 {
207 assert(false);
208 }
209 }
210
211private:
213 long element_;
214 long element_handle_;
215 int tag_;
216 Bound bound_;
217 std::vector<long> *dof_map_;
218};
219
220template <int _Dimension> class TreeTraits
221{
222public:
223 enum { Dimension = _Dimension };
224
225 using Bound = moonolith::AABBWithKDOPSpan<Dimension, double>;
226 using DataType = mfem::ElementAdapter<Dimension>;
227};
228
229template <int Dimension>
230class MFEMTree : public moonolith::Tree<TreeTraits<Dimension>>
231{
232public:
233 using Traits = mfem::TreeTraits<Dimension>;
234
235 MFEMTree() {};
236
237 static std::shared_ptr<MFEMTree>
238 New(const int maxElementsXNode = moonolith::DEFAULT_REFINE_MAX_ELEMENTS,
239 const int maxDepth = moonolith::DEFAULT_REFINE_DEPTH)
240 {
241 using namespace moonolith;
242
243 std::shared_ptr<MFEMTree> tree = std::make_shared<MFEMTree>();
244 std::shared_ptr<NTreeWithSpanMutatorFactory<MFEMTree>> factory =
245 std::make_shared<NTreeWithSpanMutatorFactory<MFEMTree>>();
246 factory->set_refine_params(maxElementsXNode, maxDepth);
247 tree->set_mutator_factory(factory);
248 return tree;
249 }
250
251 static std::shared_ptr<MFEMTree>
252 New(const std::shared_ptr<moonolith::Predicate> &predicate,
253 const int maxElementsXNode = moonolith::DEFAULT_REFINE_MAX_ELEMENTS,
254 const int maxDepth = moonolith::DEFAULT_REFINE_DEPTH)
255 {
256 using namespace moonolith;
257
258 if (!predicate)
259 {
260 return New(maxElementsXNode, maxDepth);
261 }
262
263 std::shared_ptr<MFEMTree> tree = std::make_shared<MFEMTree>();
264 std::shared_ptr<NTreeWithTagsMutatorFactory<MFEMTree>> factory =
265 std::make_shared<NTreeWithTagsMutatorFactory<MFEMTree>>(predicate);
266 factory->set_refine_params(maxElementsXNode, maxDepth);
267 tree->set_mutator_factory(factory);
268 return tree;
269 }
270};
271
272class ElementDofMap : public moonolith::Serializable
273{
274public:
275 void read(moonolith::InputStream &is) override
276 {
277 int n;
278 is >> n;
279 global.resize(n);
280 is.read(&global[0], n);
281 }
282
283 void write(moonolith::OutputStream &os) const override
284 {
285 int n = global.size();
286 os << n;
287 os.write(&global[0], n);
288 }
289
290 std::vector<long> global;
291};
292
293class Spaces
294{
295public:
296 explicit Spaces(const moonolith::Communicator &comm) : comm(comm)
297 {
298 must_destroy_attached[0] = false;
299 must_destroy_attached[1] = false;
300 }
301
302 Spaces(const std::shared_ptr<ParFiniteElementSpace> &source,
303 const std::shared_ptr<ParFiniteElementSpace> &destination)
304 {
305 spaces_.reserve(2);
306 spaces_.push_back(source);
307 spaces_.push_back(destination);
308
309 must_destroy_attached[0] = false;
310 must_destroy_attached[1] = false;
311
312 copy_global_dofs(*source, dof_maps_[0]);
313 copy_global_dofs(*destination, dof_maps_[1]);
314 }
315
316 ~Spaces()
317 {
318 Mesh *m = nullptr;
319 FiniteElementCollection *fec = nullptr;
320
321 for (int i = 0; i < spaces_.size(); ++i)
322 {
323 if (spaces_[i] && must_destroy_attached[0])
324 {
325 m = spaces_[i]->GetMesh();
326 fec = const_cast<FiniteElementCollection *>(spaces_[i]->FEColl());
327
328 // make it null
329 spaces_[i] = std::shared_ptr<FiniteElementSpace>();
330
331 delete m;
332 delete fec;
333 }
334 }
335 }
336
337 inline long n_elements() const
338 {
339 long ret = 0;
340 for (auto s : spaces_)
341 {
342 if (s)
343 {
344 ret += s->GetNE();
345 }
346 }
347
348 return ret;
349 }
350
351 inline std::vector<std::shared_ptr<FiniteElementSpace>> &spaces()
352 {
353 return spaces_;
354 }
355
356 inline const std::vector<std::shared_ptr<FiniteElementSpace>> &
357 spaces() const
358 {
359 return spaces_;
360 }
361
362 inline std::vector<ElementDofMap> &dof_map(const int i)
363 {
364 assert(i < 2);
365 assert(i >= 0);
366 return dof_maps_[i];
367 }
368
369 inline const std::vector<ElementDofMap> &dof_map(const int i) const
370 {
371 assert(i < 2);
372 assert(i >= 0);
373 return dof_maps_[i];
374 }
375
376 inline void set_must_destroy_attached(const int index, const bool value)
377 {
378 assert(index < 2);
379 assert(index >= 0);
380 must_destroy_attached[index] = value;
381 }
382
383private:
384 std::vector<std::shared_ptr<FiniteElementSpace>> spaces_;
385 moonolith::Communicator comm;
386 std::vector<ElementDofMap> dof_maps_[2];
387 bool must_destroy_attached[2];
388
389 inline static void copy_global_dofs(ParFiniteElementSpace &fe,
390 std::vector<ElementDofMap> &dof_map)
391 {
392 dof_map.resize(fe.GetNE());
393 Array<int> vdofs;
394 for (int i = 0; i < fe.GetNE(); ++i)
395 {
396 fe.GetElementVDofs(i, vdofs);
397 for (int k = 0; k < vdofs.Size(); ++k)
398 {
399 long g_dof = 0;
400 if (vdofs[k] >= 0)
401 {
402 g_dof = fe.GetGlobalTDofNumber(vdofs[k]);
403 }
404 else
405 {
406 g_dof = -1 - fe.GetGlobalTDofNumber(-1 - vdofs[k]);
407 }
408
409 dof_map[i].global.push_back(g_dof);
410 }
411 }
412 }
413};
414
415template <class Iterator>
416static void write_space(const Iterator &begin, const Iterator &end,
418 const std::vector<ElementDofMap> &dof_map,
419 const int role, moonolith::OutputStream &os)
420{
421 const int dim = space.GetMesh()->Dimension();
422 const long n_elements = std::distance(begin, end);
423
424 std::set<long> nodeIds;
425 std::map<long, long> mapping;
426
427 Array<int> verts;
428 for (Iterator it = begin; it != end; ++it)
429 {
430 const int i = *it;
431 space.GetElementVertices(i, verts);
432
433 for (int j = 0; j < verts.Size(); ++j)
434 {
435 nodeIds.insert(verts[j]);
436 }
437 }
438
439 long n_nodes = nodeIds.size();
440
441 // Estimate for allocation
442 os.request_space((n_elements * 8 + n_nodes * dim) *
443 (sizeof(double) + sizeof(long)));
444
445 auto fe_coll = space.FEColl();
446 const char *name = fe_coll->Name();
447 const int name_lenght = strlen(name);
448 // WRITE 1
449 os << dim << role;
450 os << name_lenght;
451 os.write(name, name_lenght);
452
453 long index = 0;
454 for (auto nodeId : nodeIds)
455 {
456 mapping[nodeId] = index++;
457 }
458
459 // WRITE 2
460 os << n_nodes;
461 // WRITE 6
462 os << n_elements;
463
464 Array<int> vdofs;
465 for (auto node_id : nodeIds)
466 {
467 double *v = space.GetMesh()->GetVertex(node_id);
468 for (int i = 0; i < dim; ++i)
469 {
470 // WRITE 3
471 os << v[i];
472 }
473 }
474
475 for (Iterator it = begin; it != end; ++it)
476 {
477 const int k = *it;
478 space.GetElementVertices(k, verts);
479
480 const int attribute = space.GetAttribute(k);
481 const int e_n_nodes = verts.Size();
482 const int type = space.GetElementType(k);
483 const int order = space.GetOrder(k);
484
485 // WRITE 7
486 os << type << attribute << order << e_n_nodes;
487
488 for (int i = 0; i < e_n_nodes; ++i)
489 {
490 auto it = mapping.find(verts[i]);
491 assert(it != mapping.end());
492
493 int index = it->second;
494
495 // WRITE 8
496 os << index;
497 }
498
499 // WRITE 9
500 os << dof_map.at(k);
501 }
502}
503
504template <class Iterator>
505static void write_element_selection(const Iterator &begin, const Iterator &end,
506 const Spaces &spaces,
507 moonolith::OutputStream &os)
508{
509 if (spaces.spaces().empty())
510 {
511 assert(false);
512 return;
513 }
514
515 auto m = spaces.spaces()[0];
516 std::shared_ptr<FiniteElementSpace> s = nullptr;
517
518 if (spaces.spaces().size() > 1)
519 {
520 s = spaces.spaces()[1];
521 }
522
523 std::vector<long> source_selection;
524 std::vector<long> destination_selection;
525
526 bool met_destination_selection = false;
527
528 for (Iterator it = begin; it != end; ++it)
529 {
530 long index = *it;
531
532 if (m && index >= m->GetNE())
533 {
534 index -= m->GetNE();
535 destination_selection.push_back(index);
536 }
537 else if (!m)
538 {
539 met_destination_selection = true;
540 destination_selection.push_back(index);
541 }
542 else
543 {
544 assert(!met_destination_selection);
545 assert(index < m->GetNE());
546 source_selection.push_back(index);
547 }
548 }
549
550 const bool has_source = !source_selection.empty();
551 const bool has_destination = !destination_selection.empty();
552
553 os << has_source << has_destination;
554
555 if (has_source)
556 {
557 write_space(source_selection.begin(), source_selection.end(), *m,
558 spaces.dof_map(0), 0, os);
559 }
560
561 if (has_destination)
562 {
563 write_space(destination_selection.begin(), destination_selection.end(), *s,
564 spaces.dof_map(1), 1, os);
565 }
566}
567
568static FiniteElementCollection *FECollFromName(const std::string &comp_name)
569{
570 return FiniteElementCollection::New(comp_name.c_str());
571}
572
573static void read_space(moonolith::InputStream &is,
574 std::shared_ptr<FiniteElementSpace> &space,
575 std::vector<ElementDofMap> &dof_map)
576{
577 using namespace std;
578
579 // READ 1
580 int dim, role, name_lenght;
581 is >> dim >> role;
582 is >> name_lenght;
583
584 std::string name(name_lenght, 0);
585 is.read(&name[0], name_lenght);
586
587 // READ 2
588 long n_nodes;
589 is >> n_nodes;
590
591 // READ 6
592 long n_elements;
593 is >> n_elements;
594
595 auto fe_coll = FECollFromName(name);
596 auto mesh_ptr = new Mesh(dim, n_nodes, n_elements);
597
598 for (long i = 0; i < n_nodes; ++i)
599 {
600 double v[3];
601 for (int i = 0; i < dim; ++i)
602 {
603 // READ 3
604 is >> v[i];
605 }
606
607 mesh_ptr->AddVertex(v);
608 }
609
610 dof_map.resize(n_elements);
611 std::vector<int> e2v;
612 for (long i = 0; i < n_elements; ++i)
613 {
614 // READ 7
615 int type, attribute, order, e_n_nodes;
616 is >> type >> attribute >> order >> e_n_nodes;
617 e2v.resize(e_n_nodes);
618 int index;
619 for (int i = 0; i < e_n_nodes; ++i)
620 {
621 // READ 8
622 is >> index;
623 e2v[i] = index;
624 }
625
626 mesh_ptr->AddElement(NewElem(type, &e2v[0], attribute));
627 // READ 9
628 is >> dof_map.at(i);
629 }
630
631 // if(mesh_ptr->Dimension() == 3) {
632 Finalize(*mesh_ptr, true);
633 // }
634
635 space = make_shared<FiniteElementSpace>(mesh_ptr, fe_coll);
636}
637
638static void read_spaces(moonolith::InputStream &is, Spaces &spaces)
639{
640 bool has_source, has_destination;
641 is >> has_source >> has_destination;
642
643 spaces.spaces().resize(2);
644
645 if (has_source)
646 {
647 read_space(is, spaces.spaces()[0], spaces.dof_map(0));
648 spaces.set_must_destroy_attached(0, true);
649 }
650 else
651 {
652 spaces.spaces()[0] = nullptr;
653 }
654
655 if (has_destination)
656 {
657 read_space(is, spaces.spaces()[1], spaces.dof_map(1));
658 spaces.set_must_destroy_attached(1, true);
659 }
660 else
661 {
662 spaces.spaces()[1] = nullptr;
663 }
664}
665
666template <int Dimensions, class Fun>
667static bool Assemble(moonolith::Communicator &comm,
668 std::shared_ptr<ParFiniteElementSpace> &source,
669 std::shared_ptr<ParFiniteElementSpace> &destination,
670 Fun process_fun, const moonolith::SearchSettings &settings,
671 const bool verbose)
672{
673 using namespace moonolith;
674
675 typedef mfem::MFEMTree<Dimensions> NTreeT;
676 typedef typename NTreeT::DataContainer DataContainer;
677 typedef typename NTreeT::DataType Adapter;
678
679 long maxNElements = settings.max_elements;
680 long maxDepth = settings.max_depth;
681
682 const int n_elements_source = source->GetNE();
683 const int n_elements_destination = destination->GetNE();
684 const int n_elements = n_elements_source + n_elements_destination;
685
686 auto predicate = std::make_shared<MasterAndSlave>();
687 predicate->add(0, 1);
688
689 MOONOLITH_EVENT_BEGIN("create_adapters");
690
691 std::shared_ptr<NTreeT> tree = NTreeT::New(predicate, maxNElements, maxDepth);
692 tree->reserve(n_elements);
693
694 std::shared_ptr<Spaces> local_spaces =
695 std::make_shared<Spaces>(source, destination);
696
697 int offset = 0;
698 int space_num = 0;
699
700 for (auto s : local_spaces->spaces())
701 {
702 if (s)
703 {
704 for (int i = 0; i < s->GetNE(); ++i)
705 {
706 Adapter a(*s, i, offset + i, space_num);
707 a.set_dof_map(&local_spaces->dof_map(space_num)[i].global);
708 tree->insert(a);
709 }
710
711 offset += s->GetNE();
712 }
713
714 ++space_num;
715 }
716
717 tree->root()->bound().static_bound().enlarge(1e-6);
718
719 MOONOLITH_EVENT_END("create_adapters");
720
721 // Just to have an indexed-storage
722 std::map<long, std::shared_ptr<Spaces>> spaces;
723 std::map<long, std::vector<std::shared_ptr<Spaces>>> migrated_spaces;
724
725 auto read = [&spaces, &migrated_spaces,
726 comm](const long ownerrank, const long /*senderrank*/,
727 bool is_forwarding, DataContainer &data, InputStream &in)
728 {
729 CHECK_STREAM_READ_BEGIN("vol_proj", in);
730
731 std::shared_ptr<Spaces> proc_space = std::make_shared<Spaces>(comm);
732
733 read_spaces(in, *proc_space);
734
735 if (!is_forwarding)
736 {
737 assert(!spaces[ownerrank]);
738 spaces[ownerrank] = proc_space;
739 }
740 else
741 {
742 migrated_spaces[ownerrank].push_back(proc_space);
743 }
744
745 data.reserve(data.size() + proc_space->n_elements());
746
747 int space_num = 0;
748 long offset = 0;
749 for (auto s : proc_space->spaces())
750 {
751 if (s)
752 {
753 for (int i = 0; i < s->GetNE(); ++i)
754 {
755 data.push_back(Adapter(*s, i, offset + i, space_num));
756 data.back().set_dof_map(&proc_space->dof_map(space_num)[i].global);
757 }
758
759 offset += s->GetNE();
760 }
761
762 ++space_num;
763 }
764
765 CHECK_STREAM_READ_END("vol_proj", in);
766 };
767
768 auto write = [&local_spaces, &spaces,
769 &comm](const long ownerrank, const long /*recvrank*/,
770 const std::vector<long>::const_iterator &begin,
771 const std::vector<long>::const_iterator &end,
772 const DataContainer &/*data*/, OutputStream &out)
773 {
774 CHECK_STREAM_WRITE_BEGIN("vol_proj", out);
775
776 if (ownerrank == comm.rank())
777 {
778 write_element_selection(begin, end, *local_spaces, out);
779 }
780 else
781 {
782 auto it = spaces.find(ownerrank);
783 assert(it != spaces.end());
784 std::shared_ptr<Spaces> spaceptr = it->second;
785 assert(std::distance(begin, end) > 0);
786 write_element_selection(begin, end, *spaceptr, out);
787 }
788
789 CHECK_STREAM_WRITE_END("vol_proj", out);
790 };
791
792 long n_false_positives = 0, n_intersections = 0;
793 auto fun = [&n_false_positives, &n_intersections,
794 &process_fun](Adapter &source, Adapter &destination) -> bool
795 {
796 bool ok = process_fun(source, destination);
797
798 if (ok)
799 {
800 n_intersections++;
801 return true;
802 }
803 else
804 {
805 n_false_positives++;
806 return false;
807 }
808
809 return true;
810 };
811
812 moonolith::search_and_compute(comm, tree, predicate, read, write, fun,
813 settings);
814
815 if (verbose)
816 {
817 long n_total_candidates = n_intersections + n_false_positives;
818
819 long n_collection[3] = {n_intersections, n_total_candidates,
820 n_false_positives
821 };
822 comm.all_reduce(n_collection, 3, moonolith::MPISum());
823
824 if (comm.is_root())
825 {
826 mfem::out << "n_intersections: " << n_collection[0]
827 << ", n_total_candidates: " << n_collection[1]
828 << ", n_false_positives: " << n_collection[2] << std::endl;
829 }
830 }
831
832 return true;
833}
834
835static void add_matrix(const Array<int> &destination_vdofs,
836 const Array<int> &source_vdofs,
837 const DenseMatrix &elem_mat, moonolith::SparseMatrix<double> &mat_buffer)
838{
839 for (int i = 0; i < destination_vdofs.Size(); ++i)
840 {
841 long dof_I = destination_vdofs[i];
842
843 double sign_I = 1.0;
844
845 if (dof_I < 0)
846 {
847 sign_I = -1.0;
848 dof_I = -dof_I - 1;
849 }
850
851 for (int j = 0; j < source_vdofs.Size(); ++j)
852 {
853 long dof_J = source_vdofs[j];
854
855 double sign_J = sign_I;
856
857 if (dof_J < 0)
858 {
859 sign_J = -sign_I;
860 dof_J = -dof_J - 1;
861 }
862
863 mat_buffer.add(dof_I, dof_J, sign_J * elem_mat.Elem(i, j));
864 }
865 }
866}
867
868std::shared_ptr<HypreParMatrix> convert_to_hypre_matrix(
869 const std::vector<moonolith::Integer> &destination_ranges,
870 HYPRE_BigInt *s_offsets,
871 HYPRE_BigInt *m_offsets,
872 moonolith::SparseMatrix<double> &mat_buffer)
873{
874
875 auto && comm = mat_buffer.comm();
876
877 moonolith::Redistribute<moonolith::SparseMatrix<double>> redist(comm.get());
878 redist.apply(destination_ranges, mat_buffer, moonolith::AddAssign<double>());
879
880 std::vector<int> I(s_offsets[1] - s_offsets[0] + 1);
881 I[0] = 0;
882
883 std::vector<HYPRE_Int> J;
884 std::vector<double> data;
885 J.reserve(mat_buffer.n_local_entries());
886 data.reserve(J.size());
887
888 for (auto it = mat_buffer.iter(); it; ++it)
889 {
890 I[it.row() - s_offsets[0] + 1]++;
891 J.push_back(it.col());
892 data.push_back(*it);
893 }
894
895 for (int i = 1; i < I.size(); ++i)
896 {
897 I[i] += I[i - 1];
898 }
899
900 return std::make_shared<HypreParMatrix>(
901 comm.get(), s_offsets[1] - s_offsets[0], mat_buffer.rows(),
902 mat_buffer.cols(), &I[0], &J[0], &data[0], s_offsets, m_offsets);
903}
904
905
906int order_multiplier(const Geometry::Type type, const int dim)
907{
908 return
909 (type == Geometry::TRIANGLE || type == Geometry::TETRAHEDRON ||
910 type == Geometry::SEGMENT)? 1 : dim;
911}
912
913template <int Dimensions>
914static bool
915Assemble(moonolith::Communicator &comm,
916 ParMortarAssembler::Impl &impl,
917 const moonolith::SearchSettings &settings, const bool verbose)
918{
919 auto &source = impl.source;
920 auto &destination = impl.destination;
921
922 auto &integrators = impl.integrators;
923 auto &pmat = impl.coupling_matrix;
924
925
926 bool lump_mass = false;
927 int max_q_order = 0;
928
929 for (auto i_ptr : integrators)
930 {
931 max_q_order = std::max(i_ptr->GetQuadratureOrder(), max_q_order);
932 }
933
934 const int dim = source->GetMesh()->Dimension();
935 std::shared_ptr<Cut> cut = NewCut(dim);
936 if (!cut)
937 {
938 assert(false && "NOT Supported!");
939 return false;
940 }
941
942 Array<int> source_vdofs, destination_vdofs;
943 DenseMatrix elemmat;
944 DenseMatrix cumulative_elemmat;
945 IntegrationRule src_ir;
946 IntegrationRule dest_ir;
947
948 double local_element_matrices_sum = 0.0;
949
950 const auto m_global_n_dofs = source->GlobalVSize();
951 auto *m_offsets = source->GetDofOffsets();
952
953 const auto s_global_n_dofs = destination->GlobalVSize();
954 auto *s_offsets = destination->GetDofOffsets();
955
956 moonolith::SparseMatrix<double> mat_buffer(comm);
957 mat_buffer.set_size(s_global_n_dofs, m_global_n_dofs);
958
959 moonolith::SparseMatrix<double> mass_mat_buffer(comm);
960 mass_mat_buffer.set_size(s_global_n_dofs, s_global_n_dofs);
961
962 std::unique_ptr<BilinearFormIntegrator> mass_integr(impl.new_mass_integrator());
963
964 auto fun = [&](const ElementAdapter<Dimensions> &source,
965 const ElementAdapter<Dimensions> &destination) -> bool
966 {
967 const auto &src = source.space();
968 const auto &dest = destination.space();
969
970 const int src_index = source.element();
971 const int dest_index = destination.element();
972
973 auto &src_fe = *src.GetFE(src_index);
974 auto &dest_fe = *dest.GetFE(dest_index);
975
976 ElementTransformation &dest_Trans =
977 *dest.GetElementTransformation(dest_index);
978
979 // Quadrature order mangling
980
981 int src_order_mult = order_multiplier(src_fe.GetGeomType(), Dimensions);
982 int dest_order_mult = order_multiplier(dest_fe.GetGeomType(), Dimensions);
983
984 const int src_order = src_order_mult * src_fe.GetOrder();
985 const int dest_order = dest_order_mult * dest_fe.GetOrder();
986
987 int contraction_order = src_order + dest_order;
988 if (impl.assemble_mass_and_coupling_together)
989 {
990 contraction_order = std::max(contraction_order, 2 * dest_order);
991 }
992
993 const int order = contraction_order + dest_order_mult * dest_Trans.OrderW() + max_q_order;
994
995 cut->SetIntegrationOrder(order);
996
997 if (cut->BuildQuadrature(src, src_index, dest, dest_index, src_ir,
998 dest_ir))
999 {
1000 // make reference quadratures
1001 ElementTransformation &src_Trans =
1002 *src.GetElementTransformation(src_index);
1003
1004 source.get_elements_vdofs(source_vdofs);
1005 destination.get_elements_vdofs(destination_vdofs);
1006
1007 bool first = true;
1008 for (auto i_ptr : integrators)
1009 {
1010 if (first)
1011 {
1012 i_ptr->AssembleElementMatrix(src_fe, src_ir, src_Trans, dest_fe,
1013 dest_ir, dest_Trans, cumulative_elemmat);
1014 first = false;
1015 }
1016 else
1017 {
1018 i_ptr->AssembleElementMatrix(src_fe, src_ir, src_Trans, dest_fe,
1019 dest_ir, dest_Trans, elemmat);
1020 cumulative_elemmat += elemmat;
1021 }
1022 }
1023
1024 local_element_matrices_sum += Sum(cumulative_elemmat);
1025 add_matrix(destination_vdofs, source_vdofs, cumulative_elemmat, mat_buffer);
1026
1027 if (impl.assemble_mass_and_coupling_together)
1028 {
1029 mass_integr->SetIntRule(&dest_ir);
1030 mass_integr->AssembleElementMatrix(dest_fe, dest_Trans, elemmat);
1031
1032 if (lump_mass)
1033 {
1034 int n = destination_vdofs.Size();
1035
1036 for (int i = 0; i < n; ++i)
1037 {
1038 double row_sum = 0.;
1039 for (int j = 0; j < n; ++j)
1040 {
1041 row_sum += elemmat(i, j);
1042 elemmat(i, j) = 0.;
1043 }
1044
1045 elemmat(i, i) = row_sum;
1046 }
1047 }
1048
1049 add_matrix(destination_vdofs, destination_vdofs, elemmat, mass_mat_buffer);
1050 }
1051
1052 return true;
1053 }
1054 else
1055 {
1056 return false;
1057 }
1058 };
1059
1060 if (!Assemble<Dimensions>(comm, source, destination, fun, settings,
1061 verbose))
1062 {
1063 return false;
1064 }
1065
1066 if (verbose)
1067 {
1068 double volumes[2] = {local_element_matrices_sum};
1069 comm.all_reduce(volumes, 2, moonolith::MPISum());
1070
1071 cut->Describe();
1072
1073 if (comm.is_root())
1074 {
1075 mfem::out << "sum(B): " << volumes[0] << std::endl;
1076 }
1077 }
1078
1079 std::vector<moonolith::Integer> destination_ranges(comm.size() + 1, 0);
1080
1081 std::copy(s_offsets, s_offsets + 2, destination_ranges.begin() + comm.rank());
1082 comm.all_reduce(&destination_ranges[0], destination_ranges.size(),
1083 moonolith::MPIMax());
1084
1086 destination_ranges,
1087 s_offsets,
1088 m_offsets,
1089 mat_buffer);
1090
1091 if (impl.assemble_mass_and_coupling_together)
1092 {
1093 impl.mass_matrix = convert_to_hypre_matrix(destination_ranges, s_offsets,
1094 s_offsets, mass_mat_buffer);
1095 }
1096
1097 return true;
1098}
1099
1101 const std::shared_ptr<ParFiniteElementSpace> &source,
1102 const std::shared_ptr<ParFiniteElementSpace> &destination)
1103 : impl_(new Impl())
1104{
1105 impl_->comm = source->GetComm();
1106 impl_->source = source;
1107 impl_->destination = destination;
1108}
1109
1110bool ParMortarAssembler::Assemble(std::shared_ptr<HypreParMatrix> &pmat)
1111{
1112 assert(!impl_->integrators.empty() &&
1113 "it must have at least on integrator see class MortarIntegrator");
1114
1115 moonolith::SearchSettings settings;
1116 // settings.set("disable_redistribution", moonolith::Boolean(true));
1117 // settings.set("disable_asynch", moonolith::Boolean(true));
1118
1119 bool ok = false;
1120
1121 moonolith::Communicator comm(impl_->comm);
1122 if (impl_->source->GetMesh()->Dimension() == 2)
1123 {
1124 ok = mfem::Assemble<2>(comm, *impl_, settings,
1125 impl_->verbose);
1126 }
1127
1128 if (impl_->source->GetMesh()->Dimension() == 3)
1129 {
1130 ok = mfem::Assemble<3>(comm, *impl_, settings,
1131 impl_->verbose);
1132 }
1133
1134 pmat = impl_->coupling_matrix;
1135 return ok;
1136}
1137
1139 ParGridFunction &dest_fun)
1140{
1141
1142 return Update() && Apply(src_fun, dest_fun);
1143}
1144
1146 ParGridFunction &dest_fun)
1147{
1148 const bool verbose = impl_->verbose;
1149
1150 if (!impl_->coupling_matrix)
1151 {
1152 if (!Update())
1153 {
1154 return false;
1155 }
1156 }
1157
1158 auto &B = *impl_->coupling_matrix;
1159 auto &D = *impl_->mass_matrix;
1160
1161 auto &P_source = *impl_->source->Dof_TrueDof_Matrix();
1162 auto &P_destination = *impl_->destination->Dof_TrueDof_Matrix();
1163
1164 impl_->ensure_solver();
1165
1166 // BlockILU prec(D);
1167 // impl_->solver->SetPreconditioner(prec);
1168
1169 impl_->solver->SetOperator(D);
1170
1171 if (verbose)
1172 {
1173 impl_->solver->SetPrintLevel(3);
1174 }
1175
1176 Vector P_x_src_fun(B.Width());
1177 P_source.MultTranspose(src_fun, P_x_src_fun);
1178
1179 Vector B_x_src_fun(B.Height());
1180 B_x_src_fun = 0.0;
1181
1182 B.Mult(P_x_src_fun, B_x_src_fun);
1183
1184 Vector R_x_dest_fun(D.Height());
1185 R_x_dest_fun = 0.0;
1186
1187 impl_->solver->Mult(B_x_src_fun, R_x_dest_fun);
1188
1189 P_destination.Mult(R_x_dest_fun, dest_fun);
1190
1191 dest_fun.Update();
1192
1193 // Sanity check!
1194 int converged = impl_->solver->GetFinalNorm() < 1e-5;
1195 MPI_Allreduce(MPI_IN_PLACE, &converged, 1, MPI_INT, MPI_MIN, impl_->comm);
1196 return converged;
1197}
1198
1200{
1201 using namespace std;
1202 const bool verbose = impl_->verbose;
1203 static const bool dof_transformation = true;
1204
1205 moonolith::Communicator comm(impl_->comm);
1206
1207 if (verbose)
1208 {
1209
1210 moonolith::root_describe(
1211 "--------------------------------------------------------"
1212 "\nAssembly begin: ",
1213 comm, mfem::out);
1214 }
1215
1216 if (!Assemble(impl_->coupling_matrix))
1217 {
1218 return false;
1219 }
1220
1221 if (verbose)
1222 {
1223 moonolith::root_describe(
1224 "\nAssembly end: "
1225 "--------------------------------------------------------",
1226 comm, mfem::out);
1227 }
1228
1229 if (dof_transformation)
1230 {
1231 impl_->coupling_matrix.reset(RAP(impl_->destination->Dof_TrueDof_Matrix(),
1232 impl_->coupling_matrix.get(),
1233 impl_->source->Dof_TrueDof_Matrix()));
1234 }
1235
1236 auto &B = *impl_->coupling_matrix;
1237
1238 if (!impl_->assemble_mass_and_coupling_together)
1239 {
1240 ParBilinearForm b_form(impl_->destination.get());
1241 b_form.AddDomainIntegrator(impl_->new_mass_integrator());
1242 b_form.Assemble();
1243 b_form.Finalize();
1244 impl_->mass_matrix = shared_ptr<HypreParMatrix>(b_form.ParallelAssemble());
1245 }
1246
1247
1248 comm.barrier();
1249
1250 if (verbose && comm.is_root())
1251 {
1252 mfem::out << "P in R^(" << impl_->source->Dof_TrueDof_Matrix()->Height();
1253 mfem::out << " x " << impl_->source->Dof_TrueDof_Matrix()->Width() << ")\n";
1254 mfem::out << "Q in R^("
1255 << impl_->destination->Dof_TrueDof_Matrix()->Height();
1256 mfem::out << " x " << impl_->destination->Dof_TrueDof_Matrix()->Width()
1257 << ")\n";
1258 }
1259
1260 if (dof_transformation && impl_->assemble_mass_and_coupling_together)
1261 {
1262 impl_->mass_matrix.reset(RAP(impl_->destination->Dof_TrueDof_Matrix(),
1263 impl_->mass_matrix.get(),
1264 impl_->destination->Dof_TrueDof_Matrix()));
1265 }
1266
1267 auto &D = *impl_->mass_matrix;
1268
1269 comm.barrier();
1270
1271 if (verbose && comm.is_root())
1272 {
1273 mfem::out << "--------------------------------------------------------"
1274 << std::endl;
1275 mfem::out << "B in R^(" << B.GetGlobalNumRows() << " x "
1276 << B.GetGlobalNumCols() << ")" << std::endl;
1277 mfem::out << "D in R^(" << D.GetGlobalNumRows() << " x "
1278 << D.GetGlobalNumCols() << ")" << std::endl;
1279 mfem::out << "--------------------------------------------------------"
1280 << std::endl;
1281 }
1282
1283 comm.barrier();
1284
1285 if (verbose)
1286 {
1287 Vector v(D.Width());
1288 v = 1.0;
1289
1290 Vector Dv(D.Height());
1291 D.Mult(v, Dv);
1292
1293 double sum_Dv = Dv.Sum();
1294 comm.all_reduce(&sum_Dv, 1, MPI_SUM);
1295
1296 if (comm.is_root())
1297 {
1298 mfem::out << "sum(D): " << sum_Dv << std::endl;
1299 }
1300 }
1301
1302 return true;
1303}
1304
1305} // namespace mfem
1306
1307#endif // MFEM_USE_MPI
1308#endif // MFEM_USE_MOONOLITH
int Size() const
Return the logical size of the array.
Definition array.hpp:147
Abstract base class BilinearFormIntegrator.
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....
Data type dense matrix using column-major storage.
Definition densemat.hpp:24
real_t & Elem(int i, int j) override
Returns reference to a_{ij}.
Definition densemat.cpp:115
virtual int OrderW() const =0
Return the order of the determinant of the Jacobian (weight) of the transformation.
Collection of finite elements from the same family in multiple dimensions. This class is used to matc...
Definition fe_coll.hpp:27
static FiniteElementCollection * New(const char *name)
Factory method: return a newly allocated FiniteElementCollection according to the given name.
Definition fe_coll.cpp:124
Class FiniteElementSpace - responsible for providing FEM view of the mesh, mainly managing the set of...
Definition fespace.hpp:244
DofTransformation * GetElementVDofs(int i, Array< int > &vdofs) const
Returns indices of degrees of freedom for the i'th element. The returned indices are offsets into an ...
Definition fespace.cpp:332
virtual const FiniteElement * GetFE(int i) const
Returns pointer to the FiniteElement in the FiniteElementCollection associated with i'th element in t...
Definition fespace.cpp:3835
int GetNE() const
Returns number of elements in the mesh.
Definition fespace.hpp:891
Mesh * GetMesh() const
Returns the mesh.
Definition fespace.hpp:679
Abstract class for all finite elements.
Definition fe_base.hpp:244
Class for an integration rule - an Array of IntegrationPoint.
Definition intrules.hpp:100
Mesh data type.
Definition mesh.hpp:64
int GetNE() const
Returns number of elements.
Definition mesh.hpp:1282
void GetPointMatrix(int i, DenseMatrix &pointmat) const
Definition mesh.cpp:7645
Class for parallel bilinear form.
HypreParMatrix * ParallelAssemble()
Returns the matrix assembled on the true dofs, i.e. P^t A P.
void Assemble(int skip_zeros=1)
Assemble the local matrix.
Abstract parallel finite element space.
Definition pfespace.hpp:29
HYPRE_BigInt GetGlobalTDofNumber(int ldof) const
Returns the global tdof number of the given local degree of freedom.
Class for parallel grid function.
Definition pgridfunc.hpp:50
void Update() override
Transform by the Space UpdateMatrix (e.g., on Mesh change).
Definition pgridfunc.cpp:91
bool Apply(const ParGridFunction &src_fun, ParGridFunction &dest_fun)
transfer a function from source to destination. It requires that the Update function is called before
void SetSolver(const std::shared_ptr< IterativeSolver > &solver)
Changes the solver to be used for solving the mass-matrix linear system.
void SetMaxSolverIterations(const int max_solver_iterations)
Control the maximum numbers of conjugate gradients steps for mass matrix inversion.
bool Assemble(std::shared_ptr< HypreParMatrix > &B)
assembles the coupling matrix B. B : source -> destination If u is a coefficient associated with sour...
ParMortarAssembler(const std::shared_ptr< ParFiniteElementSpace > &source, const std::shared_ptr< ParFiniteElementSpace > &destination)
constructs the object with source and destination spaces
void SetAssembleMassAndCouplingTogether(const bool value)
Control if the Mass matrix is computed together with the coupling operator every time.
bool Transfer(const ParGridFunction &src_fun, ParGridFunction &dest_fun)
transfer a function from source to destination. if the transfer is to be performed multiple times use...
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...
bool Update()
assembles the various components necessary for the transfer. To be called before calling the Apply fu...
Data type point element.
Definition point.hpp:23
Vector data type.
Definition vector.hpp:82
real_t Sum() const
Return the sum of the vector entries.
Definition vector.cpp:1204
int dim
Definition ex24.cpp:53
void source(const Vector &x, Vector &f)
Definition ex25.cpp:620
HYPRE_Int HYPRE_BigInt
int index(int i, int j, int nx, int ny)
Definition life.cpp:236
real_t a
Definition lissajous.cpp:41
string space
void write(std::ostream &os, T value)
Write 'value' to stream.
Definition binaryio.hpp:30
T read(std::istream &is)
Read a value from the stream and return it.
Definition binaryio.hpp:37
std::shared_ptr< Cut > NewCut(const int dim)
Definition cut.cpp:441
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 RAP(const DenseMatrix &A, const DenseMatrix &P, DenseMatrix &RAP)
int order_multiplier(const Geometry::Type type, const int dim)
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)
real_t p(const Vector &x, real_t t)
void pts(int iphi, int t, real_t x[])