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