FEAT 3
Finite Element Analysis Toolbox
Loading...
Searching...
No Matches
alg_dof_parti.hpp
1// FEAT3: Finite Element Analysis Toolbox, Version 3
2// Copyright (C) 2010 by Stefan Turek & the FEAT group
3// FEAT3 is released under the GNU General Public License version 3,
4// see the file 'copyright.txt' in the top level directory for details.
5
6#pragma once
7
8#include <kernel/adjacency/dynamic_graph.hpp>
9#include <kernel/global/gate.hpp>
10#include <kernel/global/vector.hpp>
11#include <kernel/global/filter.hpp>
12#include <kernel/global/matrix.hpp>
13#include <kernel/lafem/dense_vector.hpp>
14#include <kernel/lafem/sparse_matrix_csr.hpp>
15#include <kernel/lafem/unit_filter.hpp>
16#include <kernel/lafem/vector_mirror.hpp>
17#include <kernel/lafem/matrix_mirror.hpp>
18
19#include <algorithm>
20#include <vector>
21
22namespace FEAT
23{
24 namespace Global
25 {
31 template<typename LocalVector_, typename Mirror_>
33
83 template<typename DT_, typename IT_>
84 class AlgDofParti<LAFEM::DenseVector<DT_, IT_>, LAFEM::VectorMirror<DT_, IT_>>
85 {
86 public:
88 typedef DT_ DataType;
90 typedef IT_ IndexType;
91
100
101 protected:
109 std::vector<IndexType> _glob_dof_idx;
113 std::vector<std::pair<int, MirrorType>> _neighbors_owner;
115 std::vector<std::pair<int, MirrorType>> _neighbors_donee;
116
118 std::vector<int> _all_glob_dof_offset;
120 std::vector<int> _all_glob_dof_counts;
121
122 public:
123 AlgDofParti() :
124 _comm(nullptr),
125 _glob_dof_offset(0),
126 _glob_dof_count(0)
127 {
128 }
129
131 void clear()
132 {
133 _comm = nullptr;
134 _glob_dof_offset = IndexType(0);
135 _glob_dof_count = IndexType(0);
136 _glob_dof_idx.clear();
137 _owned_mirror.clear();
138 _neighbors_owner.clear();
139 _neighbors_donee.clear();
140 _all_glob_dof_offset.clear();
141 _all_glob_dof_counts.clear();
142 }
143
145 const Dist::Comm* get_comm() const
146 {
147 return _comm;
148 }
149
152 {
153 return IndexType(this->_owned_mirror.num_indices());
154 }
155
158 {
159 return IndexType(this->_owned_mirror.size());
160 }
161
164 {
165 return this->_glob_dof_count;
166 }
167
170 {
171 return this->_glob_dof_offset;
172 }
173
177 const std::vector<IndexType> get_global_dof_indices() const
178 {
179 return this->_glob_dof_idx;
180 }
181
192 {
193 ASSERT(local_dof_idx < this->_owned_mirror.size());
194 return this->_glob_dof_idx.at(local_dof_idx);
195 }
196
207 {
208 ASSERT(owned_dof_idx < this->_owned_mirror.num_indices());
209 return this->_owned_mirror.indices()[owned_dof_idx];
210 }
211
216 {
217 return this->_owned_mirror;
218 }
219
222 {
223 return IndexType(this->_neighbors_owner.size());
224 }
225
228 {
229 return IndexType(this->_neighbors_donee.size());
230 }
231
242 {
243 return this->_neighbors_owner.at(i).first;
244 }
245
256 {
257 return this->_neighbors_donee.at(i).first;
258 }
259
273 {
274 return this->_neighbors_owner.at(i).second;
275 }
276
290 {
291 return this->_neighbors_donee.at(i).second;
292 }
293
303 const std::vector<int>& get_all_global_dof_offsets() const
304 {
305 XASSERTM(!this->_all_glob_dof_offset.empty(), "You did not ask to assemble the global dof offsets");
306 return this->_all_glob_dof_offset;
307 }
308
318 const std::vector<int>& get_all_global_dof_counts() const
319 {
320 XASSERTM(!this->_all_glob_dof_counts.empty(), "You did not ask to assemble the global dof counts");
321 return this->_all_glob_dof_counts;
322 }
323
338 void assemble_allgather(bool yes_i_really_want_this = false)
339 {
340 XASSERTM(yes_i_really_want_this, "You probably don't want to do this!");
341
342 std::size_t num_ranks = std::size_t(this->_comm->size());
343
344 int my_nod = int(this->get_num_owned_dofs());
345 int my_off = int(this->get_global_dof_offset());
346
347 // resize vectors
348 this->_all_glob_dof_offset.resize(num_ranks, 0);
349 this->_all_glob_dof_counts.resize(num_ranks, 0);
350
351 // allgather offsets + counts
352 this->_comm->allgather(&my_off, 1, this->_all_glob_dof_offset.data(), 1);
353 this->_comm->allgather(&my_nod, 1, this->_all_glob_dof_counts.data(), 1);
354 }
355
361 void assemble_by_gate(const GateType& gate)
362 {
363 // set our communicator
364 this->_comm = gate.get_comm();
365 XASSERTM(this->_comm != nullptr, "need a communicator here!");
366
367 // get the communicator
368 const Dist::Comm& comm = *this->_comm;
369
370 // get my rank and neighbor ranks
371 const int my_rank = comm.rank();
372 const std::vector<int>& neighbor_ranks = gate._ranks;
373
374 // get number of local DOFs on our patch
375 const IndexType num_local_dofs = IndexType(gate._freqs.size());
376
377 // As a very first step, we have to decide which process will become the
378 // owner of each DOF. In this implementation, each DOF is owned by by the
379 // process with the lowest rank, which simplifies a lot of things.
380
381 // So, first create a vector, which stores the rank of the owner process
382 // of each of our local DOFs and format the vector to our rank, i.e. at
383 // startup assume that we own all local DOFs.
384 std::vector<int> dof_owners(num_local_dofs, my_rank);
385
386 // Now loop over all our neighbor processes
387 for(std::size_t i(0); i < neighbor_ranks.size(); ++i)
388 {
389 // Check whether the neighbors rank is less than our rank, as otherwise
390 // that particular neighbor can not own any of our local DOFs.
391 const int neighbor_rank = neighbor_ranks.at(i);
392 if(neighbor_rank < my_rank)
393 {
394 // loop over all DOFs in the mirror of that neighbor
395 const MirrorType& mirror = gate._mirrors.at(i);
396 const Index num_indices = mirror.num_indices();
397 const IndexType* idx = mirror.indices();
398 for(Index j(0); j < num_indices; ++j)
399 {
400 // get a reference to the current owner rank of that DOF and
401 // update the DOF owner if that neighbor has a lower rank
402 // than the previous "owner candidate"
403 int& dof_owner = dof_owners.at(idx[j]);
404 if(neighbor_rank < dof_owner)
405 dof_owner = neighbor_rank;
406 }
407 }
408 }
409
410 // Okay, at this point we (and all other processes) know the owners of
411 // each of our local DOFs. Now, we have to generate a vector of all
412 // local DOF indices of the DOFs that we own:
413 std::vector<IndexType> owned_dofs;
414 owned_dofs.reserve(num_local_dofs);
415
416 // Furthermore, we need the 'inverse' information, i.e. for each of our
417 // local DOFs, we have to store the "owned DOF index" if we own this
418 // particular DOF. We initialize the vector with ~0, which stands for
419 // "not my DOF":
420 std::vector<IndexType> own_dof_idx(num_local_dofs, ~IndexType(0));
421
422 // Loop over all local DOFs
423 for(IndexType i(0); i < num_local_dofs; ++i)
424 {
425 // Am I the owner of this DOF?
426 if(dof_owners[i] == my_rank)
427 {
428 // Yes, that's my DOF, so let's give it a new 'my owned DOF' index:
429 own_dof_idx.at(i) = IndexType(owned_dofs.size());
430 // And remember that I own this DOF:
431 owned_dofs.push_back(i);
432 }
433 }
434
436 XASSERTM(!owned_dofs.empty(), "this process has no DOFs!");
437
438 // get number of my owned DOFS
439 const IndexType num_owned_dofs = IndexType(owned_dofs.size());
440
441 // Next, we have to determine the global offset of our first owned DOF, which
442 // is easily obtained by an exclusive-scan over each processes owned DOF count:
443 comm.exscan(&num_owned_dofs, &this->_glob_dof_offset, 1, Dist::op_sum);
444
445 // Furthermore, we also perform an allreduce to obtain the total number of
446 // global DOFs, which is generally helpful and can be used for sanity checks.
447 comm.allreduce(&num_owned_dofs, &this->_glob_dof_count, 1, Dist::op_sum);
448
449 // Next, we have to set up the mirror for all of our owned DOFs, which we can
450 // easily generate from our "owned_dofs" vector:
451 this->_owned_mirror = _vidx2mirror(num_local_dofs, owned_dofs);
452
453 // Now we also have to create the mirrors for each of our neighbor processes:
454 for(std::size_t i(0); i < neighbor_ranks.size(); ++i)
455 {
456 // get the neighbor rank and its mirror
457 const int neighbor_rank = neighbor_ranks.at(i);
458 const MirrorType& mirror = gate._mirrors.at(i);
459
460 // There are two cases now:
461 // 1) The neighbor process has a lower rank, so it is a (potential) owner
462 // of at least one of our local DOFs.
463 // 2) The neighbor process has a greater rank, so we are an owner of at least
464 // one of the neighbor process's local DOFs.
465 if(neighbor_rank < my_rank)
466 {
467 // This is a potential "owner-neighbor", which may own one or several of
468 // our local DOFs. We call a helper function, which will create a vector
469 // of all *local* DOF indices, which are owned by that neighbor process.
470 // Note that it is perfectly legit if the vector is empty, as this simply
471 // means that all our local DOFs, which are shared with that particular
472 // neighbor, are owned by some *other* neighbor process(es) and not by
473 // the particular neighbor that we are currently considering.
474 std::vector<IndexType> v = _owner_vidx(mirror, dof_owners, neighbor_rank);
475 if(!v.empty())
476 {
477 // That neighbor owns at least one of our local DOFs, so create a mirror
478 // from the index-vector and push it into our list of "owner-neighbors".
479 // Note that the indices in the mirror are *local* DOF indices.
480 _neighbors_owner.emplace_back(std::make_pair(neighbor_rank, _vidx2mirror(num_local_dofs, v)));
481 }
482 }
483 else
484 {
485 // This is a potential "donee-neighbor", which shares one or several of
486 // our local DOFs, which we might own. We call a helper function, which
487 // will create a vector of all our *owned* DOF indices, which are shared
488 // with that particular neighbor process.
489 // Note that it is perfectly legit if the vector is empty, as this simply
490 // means that all our local DOFs, which are shared with that particular
491 // neighbor, are owned by some *other* neighbor process(es) and not by
492 // this process (i.e. us).
493 std::vector<IndexType> v = _donee_vidx(mirror, own_dof_idx);
494 if(!v.empty())
495 {
496 // We own at least of one of the neighbor's local DOFs, so create a mirror
497 // from the index-vector and push it into our list of "donee-neighbors".
498 // Note that the indices in the mirror are *owned* DOF indices.
499 _neighbors_donee.emplace_back(std::make_pair(neighbor_rank, _vidx2mirror(num_owned_dofs, v)));
500 }
501 }
502 }
503
504 // Finally, we have to determine the global DOF index for each of our local DOFs,
505 // so that we can perform a "local-to-global" DOF index lookup. This information
506 // is required for the assembly of matrices later on, so we have to store this in
507 // a member-variable vector.
508
509 // Let's create the vector of the required length, initialize all its indices to
510 // ~0, which will can be used for a sanity check later on to ensure that we know
511 // the global indices of all our local DOFs:
512 this->_glob_dof_idx.resize(num_local_dofs, ~IndexType(0));
513
514 // Next, let's loop over all DOFs that we own and assign their corresponding
515 // global DOF indices, starting with our global DOF offset, which we have already
516 // determined before:
517 for(std::size_t i(0); i < owned_dofs.size(); ++i)
518 this->_glob_dof_idx[owned_dofs[i]] = this->_glob_dof_offset + IndexType(i);
519
520 // Finally, we also need to query the global DOF indices of all our local DOFs,
521 // which are owned by some neighbor process (i.e. we are the donee for these DOFs).
522 // Of course, we also have to send the global DOF indices of all DOFs that we own
523 // to all of our "donee-neighbors".
524
525 // Allocate index buffers and post receives for all of our owner-neighbors:
526 const IndexType num_neigh_owner = IndexType(_neighbors_owner.size());
527 Dist::RequestVector recv_reqs(num_neigh_owner);
528 std::vector<std::vector<IndexType>> recv_bufs(num_neigh_owner);
529 for(IndexType i(0); i < num_neigh_owner; ++i)
530 {
531 const std::size_t num_idx = _neighbors_owner.at(i).second.num_indices();
532 recv_bufs.at(i).resize(num_idx);
533 recv_reqs[i] = comm.irecv(recv_bufs.at(i).data(), num_idx, _neighbors_owner.at(i).first);
534 }
535
536 // Allocate index buffers, fill them with the global DOF indices of our owned
537 // DOFs and send them to the donee-neighbors:
538 const IndexType num_neigh_donee = IndexType(_neighbors_donee.size());
539 Dist::RequestVector send_reqs(num_neigh_donee);
540 std::vector<std::vector<IndexType>> send_bufs(num_neigh_donee);
541 for(IndexType i(0); i < num_neigh_donee; ++i)
542 {
543 // Get the mirror for this donee-neighbor:
544 const MirrorType& mirror = _neighbors_donee.at(i).second;
545 const Index num_idx = mirror.num_indices();
546 const IndexType* mir_idx = mirror.indices();
547 // Allocate buffer of required size and translate the indices of our mirror,
548 // which are "owned DOF indices", to global DOF indices, where:
549 // global_dof_index := global_dof_offset + owned_dof_index
550 send_bufs.at(i).resize(num_idx);
551 IndexType* jdx = send_bufs.at(i).data();
552 for(IndexType j(0); j < num_idx; ++j)
553 jdx[j] = this->_glob_dof_offset + mir_idx[j];
554 send_reqs[i] = comm.isend(send_bufs.at(i).data(), num_idx, _neighbors_donee.at(i).first);
555 }
556
557 // Now process all receive requests from our owner-neighbors:
558 for(std::size_t i(0u); recv_reqs.wait_any(i); )
559 {
560 const MirrorType& mirror = _neighbors_owner.at(i).second;
561 const Index num_idx = mirror.num_indices();
562 const IndexType* mir_idx = mirror.indices();
563 const IndexType* jdx = recv_bufs.at(i).data();
564 // Scatter the global DOF indices, which our friendly owner-neighbor has
565 // provided us with, into our global DOF index vector:
566 for(IndexType j(0); j < num_idx; ++j)
567 this->_glob_dof_idx[mir_idx[j]] = jdx[j];
568 }
569
570 // wait for all sends to finish
571 send_reqs.wait_all();
572
573#ifdef DEBUG
574 // Last but not least: debug-mode sanity check
575 // We now should know the global DOF indices for all of our local DOFs:
576 for(IndexType gdi : this->_glob_dof_idx)
577 {
578 XASSERTM(gdi != ~IndexType(0), "invalid global DOF index");
579 }
580#endif // DEBUG
581 }
582
583 private:
584 // auxiliary function: create a mirror from a vector of indices
585 static MirrorType _vidx2mirror(const IndexType size, const std::vector<IndexType>& vidx)
586 {
587 const IndexType nidx = IndexType(vidx.size());
588 MirrorType mirror(size, nidx);
589 IndexType* idx = mirror.indices();
590 for(IndexType i(0); i < nidx; ++i)
591 idx[i] = vidx[i];
592 return mirror;
593 }
594
595 // auxiliary function: create an index vector of all *local DOFs*
596 // which are owned by the process with the given neighbor rank:
597 static std::vector<IndexType> _owner_vidx(const MirrorType& old_mir,
598 const std::vector<int>& dof_owners, const int neighbor_rank)
599 {
600 std::vector<IndexType> v;
601 v.reserve(old_mir.num_indices());
602
603 const IndexType* old_idx = old_mir.indices();
604 for(IndexType i(0); i < old_mir.num_indices(); ++i)
605 {
606 // is this neighbor the owner?
607 if(neighbor_rank == dof_owners[old_idx[i]])
608 v.push_back(old_idx[i]);
609 }
610 return v;
611 }
612
613 // auxiliary function: create an index vector all *owned DOFs*
614 // which are local DOFs of the process whose mirror is given:
615 static std::vector<IndexType> _donee_vidx(const MirrorType& old_mir,
616 const std::vector<IndexType>& own_dof_idx)
617 {
618 std::vector<IndexType> v;
619 v.reserve(old_mir.num_indices());
620
621 const IndexType* old_idx = old_mir.indices();
622 for(IndexType i(0); i < old_mir.num_indices(); ++i)
623 {
624 // do we own the DOF in this mirror?
625 if(own_dof_idx[old_idx[i]] != ~IndexType(0))
626 v.push_back(own_dof_idx[old_idx[i]]);
627 }
628 return v;
629 }
630 }; // class AlgDofParti
631
655 template<typename LocalVector_, typename Mirror_>
657 {
658 public:
660 typedef typename LocalVector_::DataType DataType;
662 typedef typename LocalVector_::IndexType IndexType;
663
665 typedef LocalVector_ LocalVectorType;
667 typedef Mirror_ MirrorType;
668
671
674
679
680 protected:
685
686 public:
689 _alg_dof_parti(nullptr),
690 _vector()
691 {
692 }
693
703 explicit AlgDofPartiVector(const AlgDofPartiType* adp_in) :
704 _alg_dof_parti(adp_in),
705 _vector(adp_in->get_num_owned_dofs())
706 {
707 }
708
710 void clear()
711 {
712 this->_alg_dof_parti = nullptr;
713 this->_vector.clear();
714 }
715
718 {
719 return this->_vector;
720 }
721
723 const OwnedVectorType& owned() const
724 {
725 return this->_vector;
726 }
727
729 const AlgDofPartiType* get_alg_dof_parti() const
730 {
731 return this->_alg_dof_parti;
732 }
733
735 const Dist::Comm* get_comm() const
736 {
737 XASSERT(this->_alg_dof_parti != nullptr);
738 return this->_alg_dof_parti->get_comm();
739 }
740
757 void allgather(BufferVectorType& vec_full) const
758 {
759 XASSERTM(!this->_alg_dof_parti->get_all_global_dof_counts().empty(),
760 "You did not ask to assemble the required allgather data");
761
762 XASSERT(vec_full.size() == this->_alg_dof_parti->get_num_global_dofs());
763 this->_alg_dof_parti->get_comm()->allgatherv(this->_vector.elements(),
764 this->_alg_dof_parti->get_num_owned_dofs(),
765 vec_full.elements(),
766 this->_alg_dof_parti->get_all_global_dof_counts().data(),
767 this->_alg_dof_parti->get_all_global_dof_offsets().data());
768 }
769
776 void upload(const GlobalVectorType& global_vector)
777 {
778 this->upload(global_vector.local());
779 }
780
787 void upload(const LocalVectorType& local_vector)
788 {
789 XASSERT(this->_alg_dof_parti != nullptr);
790 XASSERT(this->_vector.size() == this->_alg_dof_parti->get_num_owned_dofs());
791 XASSERT(local_vector.size() == this->_alg_dof_parti->get_num_local_dofs());
792
793 // Uploading is simple: we have a mirror that will extract all our owned DOFs
794 // from our local vector, so we just have to call this mirror's gather function:
795 this->_alg_dof_parti->get_owned_mirror().gather(this->_vector, local_vector);
796 }
797
805 void download(GlobalVectorType& global_vector) const
806 {
807 this->download(global_vector.local());
808 }
809
817 void download(LocalVectorType& local_vector) const
818 {
819 XASSERT(this->_alg_dof_parti != nullptr);
820 XASSERT(this->_vector.size() == this->_alg_dof_parti->get_num_owned_dofs());
821 XASSERT(local_vector.size() == this->_alg_dof_parti->get_num_local_dofs());
822
823 // get our algebraic dof partitioning object
824 const AlgDofPartiType& adp = *this->_alg_dof_parti;
825
826 // get the communicator
827 const Dist::Comm& comm = *this->get_comm();
828
829 // get the number of owner- and donee-neighbors
830 const IndexType num_neigh_owner = adp.get_num_owner_neighbors();
831 const IndexType num_neigh_donee = adp.get_num_donee_neighbors();
832
833 // The basic idea is simple:
834 // 1) Send the shared DOFs to all of our donee-neighbors
835 // 2) Receive the shared DOFs from all of our owner-neighbors
836 // 3) Scatter our own owned DOFs into our local vector
837
838 // Create receive buffers and post receives for all owner-neighbors
839 Dist::RequestVector recv_reqs(num_neigh_owner);
840 std::vector<BufferVectorType> recv_bufs(num_neigh_owner);
841 for(IndexType i(0); i < num_neigh_owner; ++i)
842 {
843 // create a vector buffer
844 // note: owner mirrors relate to local DOF indices
845 recv_bufs.at(i) = adp.get_owner_mirror(i).create_buffer(local_vector);
846 // post receive from owner neighbor
847 recv_reqs[i] = comm.irecv(recv_bufs.at(i).elements(), recv_bufs.at(i).size(), adp.get_owner_rank(i));
848 }
849
850 // Create send buffers, fill them with our owned DOFs and send this
851 // to our donee-neighbors
852 Dist::RequestVector send_reqs(num_neigh_donee);
853 std::vector<BufferVectorType> send_bufs(num_neigh_donee);
854 for(IndexType i(0); i < num_neigh_donee; ++i)
855 {
856 // get mirror, create buffer and gather the shared DOFs
857 // note: donee mirrors relate to owned DOF indices
858 const MirrorType& mir = adp.get_donee_mirror(i);
859 send_bufs.at(i) = mir.create_buffer(this->_vector);
860 mir.gather(send_bufs.at(i), this->_vector);
861 // post send to donee neighbor
862 send_reqs[i] = comm.isend(send_bufs.at(i).elements(), send_bufs.at(i).size(), adp.get_donee_rank(i));
863 }
864
865 // format the output vector
866 local_vector.format();
867
868 // scatter our own owned DOFs into our output vector
869 adp.get_owned_mirror().scatter_axpy(local_vector, this->_vector);
870
871 // process receives from our owner-neighbors
872 for(std::size_t idx(0u); recv_reqs.wait_any(idx); )
873 {
874 // scatter received DOFs into our output vector
875 adp.get_owner_mirror(IndexType(idx)).scatter_axpy(local_vector, recv_bufs.at(idx));
876 }
877
878 // at this point, all receives should have finished
879 XASSERT(recv_reqs.is_null());
880
881 // wait for all sends to finish
882 send_reqs.wait_all();
883 }
884 }; // class AlgDofPartiVector
885
912 template<typename LocalMatrix_, typename Mirror_>
914 {
915 public:
917 typedef typename LocalMatrix_::DataType DataType;
919 typedef typename LocalMatrix_::IndexType IndexType;
920
922 typedef LocalMatrix_ LocalMatrixType;
924 typedef Mirror_ MirrorType;
927
929 typedef typename LocalMatrixType::VectorTypeR LocalVectorType;
930
935
938
943
944 protected:
949
951 std::vector<BufferMatrixType> _donee_bufs, _owner_bufs;
953 std::vector<MirrorType> _data_donee_mirs, _data_owner_mirs;
955 std::vector<std::pair<IndexType,IndexType>> _my_data_mir;
956
957 public:
960 _alg_dof_parti(nullptr),
961 _matrix()
962 {
963 }
964
977 explicit AlgDofPartiMatrix(const AlgDofPartiType* adp_in) :
978 _alg_dof_parti(adp_in),
979 _matrix()
980 {
981 }
982
984 void clear()
985 {
986 _alg_dof_parti = nullptr;
987 _matrix.clear();
988 _donee_bufs.clear();
989 _owner_bufs.clear();
990 _data_donee_mirs.clear();
991 _data_owner_mirs.clear();
992 _my_data_mir.clear();
993 }
994
997 {
998 return this->_matrix;
999 }
1000
1002 const OwnedMatrixType& owned() const
1003 {
1004 return this->_matrix;
1005 }
1006
1008 const AlgDofPartiType* get_alg_dof_parti() const
1009 {
1010 return this->_alg_dof_parti;
1011 }
1012
1014 const Dist::Comm* get_comm() const
1015 {
1016 XASSERT(this->_alg_dof_parti != nullptr);
1017 return this->_alg_dof_parti->get_comm();
1018 }
1019
1040 void apply(AlgDofPartiVectorType& vec_r, const AlgDofPartiVectorType& vec_x) const
1041 {
1042 XASSERTM(!this->_alg_dof_parti->get_all_global_dof_counts().empty(),
1043 "You did not ask to assemble the required allgather data");
1044
1045 // create full vector
1046 LocalVectorType vec_full(this->_alg_dof_parti->get_num_global_dofs());
1047
1048 // gather full vector
1049 vec_x.allgather(vec_full);
1050
1051 // apply our matrix
1052 this->_matrix.apply(vec_r.owned(), vec_full);
1053 }
1054
1061 void upload(const GlobalMatrixType& mat_a)
1062 {
1063 upload(mat_a.local());
1064 }
1065
1072 void upload(const LocalMatrixType& mat_a)
1073 {
1074 upload_symbolic(mat_a);
1075 upload_numeric(mat_a);
1076 }
1077
1091 {
1092 upload_symbolic(mat_a.local());
1093 }
1094
1108 {
1109 XASSERT(this->_alg_dof_parti != nullptr);
1110
1111 this->_assemble_buffers(mat_a);
1112 this->_assemble_structure(mat_a);
1113 this->_assemble_data_mirrors(mat_a);
1114 }
1115
1126 {
1127 upload_numeric(mat_a.local());
1128 }
1129
1140 {
1141 XASSERT(this->_alg_dof_parti != nullptr);
1142
1143 // get our partitioning
1144 const AlgDofPartiType& adp = *this->_alg_dof_parti;
1145
1146 // get our communicator
1147 const Dist::Comm& comm = *this->get_comm();
1148
1149 // format our internal matrix
1150 this->_matrix.format();
1151
1152 const IndexType num_neigh_owner = adp.get_num_owner_neighbors();
1153 const IndexType num_neigh_donee = adp.get_num_donee_neighbors();
1154
1155 Dist::RequestVector recv_reqs(num_neigh_donee);
1156 Dist::RequestVector send_reqs(num_neigh_owner);
1157
1158 std::vector<BufferVectorType> donee_vbufs(num_neigh_donee);
1159 std::vector<BufferVectorType> owner_vbufs(num_neigh_owner);
1160
1161 // create receive buffers and post receives
1162 for(IndexType i(0); i < num_neigh_donee; ++i)
1163 {
1164 // create buffer
1165 BufferVectorType& buf = donee_vbufs.at(i);
1166 buf = BufferVectorType(this->_data_donee_mirs.at(i).num_indices());
1167
1168 // post receive
1169 recv_reqs[i] = comm.irecv(buf.elements(), buf.size(), adp.get_donee_rank(i));
1170 }
1171
1172 // post sends
1173 for(IndexType i(0); i < num_neigh_owner; ++i)
1174 {
1175 // create buffer
1176 BufferVectorType& buf = owner_vbufs.at(i);
1177 buf = BufferVectorType(this->_data_owner_mirs.at(i).num_indices());
1178
1179 // gather from mirror
1180 this->_gather_data(buf, mat_a, this->_data_owner_mirs.at(i));
1181
1182 // post send
1183 send_reqs[i] = comm.isend(buf.elements(), buf.size(), adp.get_owner_rank(i));
1184 }
1185
1186 // upload our own data
1187 this->_upload_own(mat_a);
1188
1189 // process all pending receives
1190 for(std::size_t idx(0u); recv_reqs.wait_any(idx); )
1191 {
1192 // scatter from buffer
1193 this->_scatter_data(this->_matrix, donee_vbufs.at(idx), this->_data_donee_mirs.at(idx));
1194 }
1195
1196 // wait for all sends to finish
1197 send_reqs.wait_all();
1198 }
1199
1212 template<typename LocalFilter_>
1214 {
1215 filter_matrix(filter.local());
1216 }
1217
1231 {
1232 // empty filter?
1233 if(filter.used_elements() <= IndexType(0))
1234 return;
1235
1236 // get our partitioning
1237 const AlgDofPartiType& adp = *this->_alg_dof_parti;
1238
1239 // get global owned DOF offset and count
1240 const IndexType off_glob = adp.get_global_dof_offset();
1241 const IndexType num_glob = adp.get_num_global_dofs();
1242
1243 // get local filter indices
1244 const Index num_idx = filter.used_elements();
1245 const IndexType* fil_idx = filter.get_indices();
1246
1247 // get owned matrix arrays
1248 const IndexType* row_ptr = this->_matrix.row_ptr();
1249 const IndexType* col_idx = this->_matrix.col_ind();
1250 DataType* val = this->_matrix.val();
1251
1252 // loop over all filter indices
1253 for(IndexType i(0); i < num_idx; ++i)
1254 {
1255 // translate local to global filter DOF index
1256 const IndexType gidx = adp.map_local_to_global_index(fil_idx[i]);
1257
1258 // determine whether we own this DOF
1259 if((gidx < off_glob) || (off_glob + num_glob <= gidx))
1260 continue; // not my DOF
1261
1262 // okay, that's my DOF, so filter the row
1263 const IndexType row = gidx - off_glob;
1264 for(IndexType j(row_ptr[row]); j < row_ptr[row+1]; ++j)
1265 {
1266 val[j] = DataType(col_idx[j] == gidx ? 1 : 0);
1267 }
1268 }
1269 }
1270
1271 protected:
1294 const LocalMatrixType& local_matrix, const MirrorType& mirror,
1295 const std::vector<IndexType>& gdi, const IndexType num_glob_dofs)
1296 {
1297 const Index num_idx = mirror.num_indices();
1298 const IndexType* mir_idx = mirror.indices();
1299
1300 const IndexType* row_ptr = local_matrix.row_ptr();
1301 const IndexType* col_idx = local_matrix.col_ind();
1302
1303 // count number of non-zeros in matrix buffer
1304 IndexType nnze = IndexType(0);
1305 for(IndexType i(0); i < num_idx; ++i)
1306 {
1307 nnze += (row_ptr[mir_idx[i]+1] - row_ptr[mir_idx[i]]);
1308 }
1309
1310 // allocate matrix buffer of appropriate dimensions
1311 BufferMatrixType buf(num_idx, num_glob_dofs, nnze, 1);
1312 IndexType* buf_ptr = buf.row_ptr();
1313 IndexType* buf_idx = buf.col_ind();
1314
1315 // set up buffer structure
1316 buf_ptr[0] = IndexType(0);
1317 for(IndexType i(0); i < num_idx; ++i)
1318 {
1319 const IndexType row = mir_idx[i];
1320 IndexType k = buf_ptr[i];
1321 // map local DOFs to global DOFs
1322 for(IndexType j(row_ptr[row]); j < row_ptr[row+1]; ++j, ++k)
1323 buf_idx[k] = gdi[col_idx[j]];
1324 buf_ptr[i+1] = k;
1325 }
1326
1327 // Note: we leave the buffer indices unsorted
1328
1329 // okay
1330 return buf;
1331 }
1332
1340 void _assemble_buffers(const LocalMatrixType& local_matrix)
1341 {
1342 // get our partitioning
1343 const AlgDofPartiType& adp = *this->_alg_dof_parti;
1344
1345 // get out communicator
1346 const Dist::Comm& comm = *this->get_comm();
1347
1348 // get number of neighbors and allocate buffer vectors
1349 const IndexType num_neigh_owner = adp.get_num_owner_neighbors();
1350 const IndexType num_neigh_donee = adp.get_num_donee_neighbors();
1351 _owner_bufs.resize(num_neigh_owner);
1352 _donee_bufs.resize(num_neigh_donee);
1353
1354 // create the owner buffers by using the auxiliary helper function.
1355 for(IndexType i(0); i < num_neigh_owner; ++i)
1356 {
1357 this->_owner_bufs.at(i) = _asm_mat_buf(local_matrix, adp.get_owner_mirror(i),
1358 adp.get_global_dof_indices(), adp.get_num_global_dofs());
1359 }
1360
1361 // We have assembled our owner-neighbor matrix buffers, however, we cannot
1362 // assemble the donee-neighbor matrix buffers on our own. Instead, each owner
1363 // neighbor has to receive its donee-buffers from its donee-neighbors, because
1364 // only the donee knows the matrix layout of those buffers.
1365 // Yes, this is brain-twisting, but believe me, it cannot be realized otherwise.
1366
1367 // Note:
1368 // The following code is effectively just copy-&-paste from the SynchMatrix::init()
1369 // function, as the same buffer problem also arises when converting global type-0
1370 // matrices to local type-1 matrices.
1371
1372 Dist::RequestVector send_reqs(num_neigh_owner);
1373 Dist::RequestVector recv_reqs(num_neigh_donee);
1374
1375 // receive buffer dimensions vector
1376 std::vector<std::array<IndexType,4>> send_dims(num_neigh_owner);
1377 std::vector<std::array<IndexType,4>> recv_dims(num_neigh_donee);
1378
1379 // post send-buffer dimension receives
1380 for(IndexType i(0); i < num_neigh_donee; ++i)
1381 {
1382 recv_reqs[i] = comm.irecv(recv_dims.at(i).data(), std::size_t(4), adp.get_donee_rank(i));
1383 }
1384
1385 // send owner-buffer dimensions
1386 for(IndexType i(0); i < num_neigh_owner; ++i)
1387 {
1388 const BufferMatrixType& sbuf = this->_owner_bufs.at(i);
1389 send_dims.at(i)[0] = IndexType(sbuf.rows());
1390 send_dims.at(i)[1] = IndexType(sbuf.columns());
1391 send_dims.at(i)[2] = IndexType(sbuf.entries_per_nonzero());
1392 send_dims.at(i)[3] = IndexType(sbuf.used_elements());
1393 send_reqs[i] = comm.isend(send_dims.at(i).data(), std::size_t(4), adp.get_owner_rank(i));
1394 }
1395
1396 // wait for all receives to finish
1397 recv_reqs.wait_all();
1398
1399 // allocate donee-buffers
1400 for(IndexType i(0); i < num_neigh_donee; ++i)
1401 {
1402 // get the receive buffer dimensions
1403 IndexType nrows = recv_dims.at(i)[0];
1404 IndexType ncols = recv_dims.at(i)[1];
1405 IndexType nepnz = recv_dims.at(i)[2];
1406 IndexType nnze = recv_dims.at(i)[3];
1407
1408 // allocate receive buffer
1409 this->_donee_bufs.at(i) = BufferMatrixType(nrows, ncols, nnze, nepnz);
1410 }
1411
1412 // post donee-buffer row-pointer array receives
1413 for(IndexType i(0); i < num_neigh_donee; ++i)
1414 {
1415 recv_reqs[i] = comm.irecv(this->_donee_bufs.at(i).row_ptr(),
1416 this->_donee_bufs.at(i).rows()+std::size_t(1), adp.get_donee_rank(i));
1417 }
1418
1419 // wait for all previous sends to finish
1420 send_reqs.wait_all();
1421
1422 // post owner-buffer row-pointer array sends
1423 for(IndexType i(0); i < num_neigh_owner; ++i)
1424 {
1425 send_reqs[i] = comm.isend(this->_owner_bufs.at(i).row_ptr(),
1426 this->_owner_bufs.at(i).rows()+std::size_t(1), adp.get_owner_rank(i));
1427 }
1428
1429 // wait for all previous receives to finish
1430 recv_reqs.wait_all();
1431
1432 // post donee-buffer column-index array receives
1433 for(IndexType i(0); i < num_neigh_donee; ++i)
1434 {
1435 recv_reqs[i] = comm.irecv(this->_donee_bufs.at(i).col_ind(),
1436 this->_donee_bufs.at(i).used_elements(), adp.get_donee_rank(i));
1437 }
1438
1439 // wait for all previous sends to finish
1440 send_reqs.wait_all();
1441
1442 // post owner-buffer column-index array sends
1443 for(IndexType i(0); i < num_neigh_owner; ++i)
1444 {
1445 send_reqs[i] = comm.isend(this->_owner_bufs.at(i).col_ind(),
1446 this->_owner_bufs.at(i).used_elements(), adp.get_owner_rank(i));
1447 }
1448
1449 // wait for all receives and sends to finish
1450 recv_reqs.wait_all();
1451 send_reqs.wait_all();
1452 }
1453
1461 void _assemble_structure(const LocalMatrixType& local_matrix)
1462 {
1463 // get our partitioning
1464 const AlgDofPartiType& adp = *this->_alg_dof_parti;
1465
1466 // get our matrix dimensions:
1467 // * each row of our matrix corresponds to one owned DOF
1468 // * each column corresponds to one global DOF
1469 const IndexType num_rows = adp.get_num_owned_dofs();
1470 const IndexType num_cols = adp.get_num_global_dofs();
1471
1472 // Unfortunately, assembling the matrix structure is not that easy,
1473 // because it is a union of our owned matrix structure and all of
1474 // our donee matrix buffers. We don't have a chance to determine how
1475 // many duplicate (i,j) pairs we will encounter here, so we use the
1476 // DynamicGraph class here to keep things simple.
1477
1478 // create a dynamic graph for our matrix
1479 Adjacency::DynamicGraph dynamic_graph(num_rows, num_cols);
1480
1481 // First, let us add all local matrix rows which correspond to the
1482 // owned DOFs of this process, which are given by the "owned mirror"
1483 const IndexType* loc_row_ptr = local_matrix.row_ptr();
1484 const IndexType* loc_col_idx = local_matrix.col_ind();
1485 const IndexType* own_mir_idx = adp.get_owned_mirror().indices();
1486 for(IndexType i(0); i < num_rows; ++i)
1487 {
1488 // get the local DOF index of our i-th owned DOF
1489 const IndexType lrow = own_mir_idx[i];
1490 for(IndexType j(loc_row_ptr[lrow]); j < loc_row_ptr[lrow + 1]; ++j)
1491 {
1492 // translate the local column indices into global DOF indices
1493 dynamic_graph.insert(i, adp.map_local_to_global_index(loc_col_idx[j]));
1494 }
1495 }
1496
1497 // process all donee-neighbor buffers
1498 const IndexType num_neigh_donee = adp.get_num_donee_neighbors();
1499 for(IndexType ineigh(0); ineigh < num_neigh_donee; ++ineigh)
1500 {
1501 // get the mirror and the matrix buffer of that donee neighbor
1502 // note: the donee mirror indices are owned DOF indices
1503 const MirrorType& mir = adp.get_donee_mirror(ineigh);
1504 const BufferMatrixType& buf = this->_donee_bufs.at(ineigh);
1505 XASSERT(mir.num_indices() == buf.rows());
1506 const Index num_idx = mir.num_indices();
1507 const IndexType* mir_idx = mir.indices();
1508 const IndexType* buf_ptr = buf.row_ptr();
1509 const IndexType* buf_idx = buf.col_ind();
1510
1511 // loop over all matrix buffer rows
1512 for(IndexType i(0); i < num_idx; ++i)
1513 {
1514 // the owned DOF index of our the buffer matrix row
1515 const IndexType row = mir_idx[i];
1516
1517 // loop over all buffer indices
1518 for(IndexType j(buf_ptr[i]); j < buf_ptr[i + 1]; ++j)
1519 {
1520 // insert entry into our local matrix
1521 dynamic_graph.insert(row, buf_idx[j]);
1522 }
1523 }
1524 }
1525
1526 // render into a standard graph
1527 Adjacency::Graph graph(Adjacency::RenderType::as_is, dynamic_graph);
1528
1529 // create our dof-partitoned matrix object
1530 this->_matrix = OwnedMatrixType(graph);
1531 }
1532
1547 const MirrorType& mirror, const BufferMatrixType& buffer)
1548 {
1549 // allocate mirror
1550 const Index buf_rows = buffer.rows();
1551 const Index num_idx = buffer.used_elements();
1552 MirrorType dat_mir(local_matrix.used_elements(), num_idx);
1553
1554 const IndexType* row_idx = mirror.indices();
1555 const IndexType* row_ptr = local_matrix.row_ptr();
1556 const IndexType* col_idx = local_matrix.col_ind();
1557 const IndexType* buf_ptr = buffer.row_ptr();
1558 const IndexType* buf_idx = buffer.col_ind();
1559 IndexType* dat_idx = dat_mir.indices();
1560
1561 // loop over all buffer matrix rows
1562 for(IndexType i(0); i < buf_rows; ++i)
1563 {
1564 // get local matrix row index
1565 const IndexType row = row_idx[i];
1566 for(IndexType j(buf_ptr[i]); j < buf_ptr[i + 1]; ++j)
1567 {
1568 // get global column index
1569 const IndexType col = buf_idx[j];
1570
1571 // loop over the corresponding row of our matrix
1572 for(IndexType k(row_ptr[row]); k < row_ptr[row + 1]; ++k)
1573 {
1574 // is this the column we are looking for?
1575 if(col_idx[k] == col)
1576 {
1577 // Yup, store this index in the mirror
1578 dat_idx[j] = k;
1579 break;
1580 }
1581 }
1582 }
1583 }
1584
1585 return dat_mir;
1586 }
1587
1605 const MirrorType& mirror, const BufferMatrixType& buffer,
1606 const std::vector<IndexType>& glob_dof_idx)
1607 {
1608 // allocate mirror
1609 const Index buf_rows = buffer.rows();
1610 const Index num_idx = buffer.used_elements();
1611 MirrorType dat_mir(local_matrix.used_elements(), num_idx);
1612
1613 const IndexType* row_idx = mirror.indices();
1614 const IndexType* row_ptr = local_matrix.row_ptr();
1615 const IndexType* col_idx = local_matrix.col_ind();
1616 const IndexType* buf_ptr = buffer.row_ptr();
1617 const IndexType* buf_idx = buffer.col_ind();
1618 IndexType* dat_idx = dat_mir.indices();
1619
1620 // loop over all buffer matrix rows
1621 for(IndexType i(0); i < buf_rows; ++i)
1622 {
1623 // get local matrix row index
1624 const IndexType row = row_idx[i];
1625 for(IndexType j(buf_ptr[i]); j < buf_ptr[i + 1]; ++j)
1626 {
1627 // get global column index
1628 const IndexType col = buf_idx[j];
1629
1630 // loop over the corresponding row of our matrix
1631 for(IndexType k(row_ptr[row]); k < row_ptr[row + 1]; ++k)
1632 {
1633 // is this the column we are looking for?
1634 if(glob_dof_idx[col_idx[k]] == col)
1635 {
1636 // Yup, store this index in the mirror
1637 dat_idx[j] = k;
1638 break;
1639 }
1640 }
1641 }
1642 }
1643
1644 return dat_mir;
1645 }
1646
1659 void _assemble_data_mirrors(const LocalMatrixType& local_matrix)
1660 {
1661 // get our partitioning
1662 const AlgDofPartiType& adp = *this->_alg_dof_parti;
1663
1664 // assemble donee data mirrors
1665 this->_data_donee_mirs.resize(this->_donee_bufs.size());
1666 for(IndexType i(0); i < this->_donee_bufs.size(); ++i)
1667 this->_data_donee_mirs.at(i) = _asm_donee_data_mir(this->_matrix,
1668 adp.get_donee_mirror(i), this->_donee_bufs.at(i));
1669
1670 // assemble owner data mirrors
1671 this->_data_owner_mirs.resize(this->_owner_bufs.size());
1672 for(IndexType i(0); i < this->_owner_bufs.size(); ++i)
1673 this->_data_owner_mirs.at(i) = _asm_owner_data_mir(local_matrix,
1674 adp.get_owner_mirror(i), this->_owner_bufs.at(i),
1675 adp.get_global_dof_indices());
1676
1677 // get matrix arrays
1678 const IndexType* row_ptr_x = this->_matrix.row_ptr();
1679 const IndexType* col_idx_x = this->_matrix.col_ind();
1680 const IndexType* row_ptr_a = local_matrix.row_ptr();
1681 const IndexType* col_idx_a = local_matrix.col_ind();
1682
1683 // reset my data mirror
1684 _my_data_mir.clear();
1685 _my_data_mir.reserve(this->_matrix.used_elements());
1686
1687 const IndexType num_owned_dofs = adp.get_num_owned_dofs();
1688 const IndexType* loc_dof_idx = adp.get_owned_mirror().indices();
1689 const std::vector<IndexType>& glob_dof_idx = adp.get_global_dof_indices();
1690
1691 XASSERT(num_owned_dofs == this->_matrix.rows());
1692
1693 // loop over all our owned DOFS
1694 for(IndexType own_dof(0); own_dof < num_owned_dofs; ++own_dof)
1695 {
1696 // get the local DOF index for this owned DOF
1697 const IndexType loc_dof = loc_dof_idx[own_dof];
1698
1699 // loop over all columns of our owned DOF matrix
1700 for(IndexType j(row_ptr_x[own_dof]); j < row_ptr_x[own_dof + 1]; ++j)
1701 {
1702 // get the column index, which is a global DOF index
1703 const IndexType col_x = col_idx_x[j];
1704
1705 // loop over the row of the local matrix
1706 for(IndexType k(row_ptr_a[loc_dof]); k < row_ptr_a[loc_dof + 1]; ++k)
1707 {
1708 // get the local column index and map it to a global one
1709 const IndexType col_a = glob_dof_idx[col_idx_a[k]];
1710
1711 // match?
1712 if(col_a == col_x)
1713 {
1714 // Okay, add this matrix entry to our mirror
1715 _my_data_mir.push_back(std::make_pair(j, k));
1716 break;
1717 }
1718 }
1719 }
1720 }
1721 }
1722
1729 void _upload_own(const LocalMatrixType& local_matrix)
1730 {
1731 // get our data arrays
1732 const DataType* val_a = local_matrix.val();
1733 DataType* val_x = this->_matrix.val();
1734
1735 // loop over our own data mirror
1736 for(auto ij : this->_my_data_mir)
1737 val_x[ij.first] = val_a[ij.second];
1738 }
1739
1752 static void _gather_data(BufferVectorType& buffer, const LocalMatrixType& local_matrix,
1753 const MirrorType& data_mirror)
1754 {
1755 XASSERT(buffer.size() == data_mirror.num_indices());
1756 XASSERT(data_mirror.size() == local_matrix.used_elements());
1757
1758 DataType* buf_val = buffer.elements();
1759 const DataType* mat_val = local_matrix.val();
1760 const IndexType* mir_idx = data_mirror.indices();
1761 const Index n = buffer.size();
1762 for(IndexType i(0); i < n; ++i)
1763 buf_val[i] = mat_val[mir_idx[i]];
1764 }
1765
1778 static void _scatter_data(LocalMatrixType& local_matrix, const BufferVectorType& buffer,
1779 const MirrorType& data_mirror)
1780 {
1781 XASSERT(buffer.size() == data_mirror.num_indices());
1782 XASSERT(data_mirror.size() == local_matrix.used_elements());
1783
1784 DataType* mat_val = local_matrix.val();
1785 const DataType* buf_val = buffer.elements();
1786 const IndexType* mir_idx = data_mirror.indices();
1787 const Index n = buffer.size();
1788 for(IndexType i(0); i < n; ++i)
1789 mat_val[mir_idx[i]] += buf_val[i];
1790 }
1791 }; // class AlgDofPartiMatrixCSR
1792 } // namespace Global
1793} // namespace FEAT
#define ASSERT(expr)
Debug-Assertion macro definition.
Definition: assertion.hpp:229
#define XASSERT(expr)
Assertion macro definition.
Definition: assertion.hpp:262
#define XASSERTM(expr, msg)
Assertion macro definition with custom message.
Definition: assertion.hpp:263
Dynamic Adjacency Graph implementation.
bool insert(Index domain_node, Index image_node)
Inserts a new adjacency entry to the graph.
Adjacency Graph implementation.
Definition: graph.hpp:34
Communicator class.
Definition: dist.hpp:1349
void allreduce(const void *sendbuf, void *recvbuf, std::size_t count, const Datatype &datatype, const Operation &op) const
Blocking All-Reduce.
Definition: dist.cpp:655
int size() const
Returns the size of this communicator.
Definition: dist.hpp:1506
void exscan(const void *sendbuf, void *recvbuf, std::size_t count, const Datatype &datatype, const Operation &op) const
Blocking Exclusive Scan.
Definition: dist.cpp:683
Request irecv(void *buffer, std::size_t count, const Datatype &datatype, int source, int tag=0) const
Nonblocking Receive.
Definition: dist.cpp:716
Request isend(const void *buffer, std::size_t count, const Datatype &datatype, int dest, int tag=0) const
Nonblocking Send.
Definition: dist.cpp:704
int rank() const
Returns the rank of this process in this communicator.
Definition: dist.hpp:1494
void allgather(const void *sendbuf, std::size_t sendcount, const Datatype &sendtype, void *recvbuf, std::size_t recvcount, const Datatype &recvtype) const
Blocking gather-to-all.
Definition: dist.cpp:589
Communication Request vector class.
Definition: dist.hpp:640
bool is_null() const
Checks whether all requests are null requests.
Definition: dist.hpp:954
bool wait_any(std::size_t &idx, Status &status)
Blocks until one of the active requests has been fulfilled.
Definition: dist.cpp:329
void wait_all()
Blocks until all active requests are fulfilled.
Definition: dist.cpp:324
const MirrorType & get_donee_mirror(IndexType i) const
Returns a donee neighbor mirror.
const MirrorType & get_owner_mirror(IndexType i) const
Returns an owner neighbor mirror.
void assemble_allgather(bool yes_i_really_want_this=false)
Assembles the required data for the AlgDofPartiVector::allgather() and AlgDofPartiMatrix::apply() fun...
Global::Gate< LocalVectorType, MirrorType > GateType
the global gate type
IndexType map_owned_to_local_index(const IndexType owned_dof_idx) const
Maps an owned DOF index to the corresponding local DOF index.
LAFEM::VectorMirror< DataType, IndexType > MirrorType
the vector mirror type
void assemble_by_gate(const GateType &gate)
Assembles the AlgDofParti object from a given Global::Gate.
std::vector< IndexType > _glob_dof_idx
global dof indices, size = number of local DOFs
std::vector< std::pair< int, MirrorType > > _neighbors_owner
rank/mirror-pair of DOF-owner processes
LAFEM::DenseVector< DataType, IndexType > LocalVectorType
the local vector type
Global::Vector< LocalVectorType, MirrorType > GlobalVectorType
the global vector type
IndexType map_local_to_global_index(const IndexType local_dof_idx) const
Maps a local DOF index to the corresponding global DOF index.
const std::vector< IndexType > get_global_dof_indices() const
Returns the global-dof-indices array.
const MirrorType & get_owned_mirror() const
Returns the Mirror of the owned DOFs.
const std::vector< int > & get_all_global_dof_counts() const
Returns the all-global-dof-counts vector.
const std::vector< int > & get_all_global_dof_offsets() const
Returns the all-global-dof-offsets vector.
std::vector< std::pair< int, MirrorType > > _neighbors_donee
rank/mirror-pair of DOF-donee processes
Algebraic DOF Partitioning implementation class template.
Algebraic DOF Partitioned CSR matrix class template.
void upload_symbolic(const GlobalMatrixType &mat_a)
Initializes this algebraic dof-partitioned matrix from a global matrix.
void filter_matrix(const LAFEM::UnitFilter< DataType, IndexType > &filter)
Applies a unit-filter into this matrix.
static BufferMatrixType _asm_mat_buf(const LocalMatrixType &local_matrix, const MirrorType &mirror, const std::vector< IndexType > &gdi, const IndexType num_glob_dofs)
Auxiliary function: creates a matrix buffer for a given owner mirror.
AlgDofPartiMatrix(const AlgDofPartiType *adp_in)
Creates a ADP matrix based on an algebraic dof partitioning.
std::vector< std::pair< IndexType, IndexType > > _my_data_mir
the data array "mirror" for this process
AlgDofPartiMatrix()
default constructor
static MirrorType _asm_donee_data_mir(const LocalMatrixType &local_matrix, const MirrorType &mirror, const BufferMatrixType &buffer)
Auxiliary function: assembles a data-mirror for a donee-neighbor.
LocalMatrix_::DataType DataType
our data type
LocalMatrix_ LocalMatrixType
the local matrix type
void _assemble_buffers(const LocalMatrixType &local_matrix)
Assembles the owner and donee matrix buffers.
const AlgDofPartiType * _alg_dof_parti
the algebraic dof-partitioning
void _upload_own(const LocalMatrixType &local_matrix)
Uploads the values of the local matrix into our matrix partition.
static void _gather_data(BufferVectorType &buffer, const LocalMatrixType &local_matrix, const MirrorType &data_mirror)
Gathers the values of a local matrix into an owner-neighbor buffer-vector.
LAFEM::MatrixMirrorBuffer< DataType, IndexType > BufferMatrixType
the buffer matrix type used for communication
void _assemble_structure(const LocalMatrixType &local_matrix)
Assembles the matrix structure of the algebraic-DOF-partitioned matrix.
Global::AlgDofPartiVector< LocalVectorType, MirrorType > AlgDofPartiVectorType
the compatible alg-dof-parti vector type
Mirror_ MirrorType
the vector mirror type
std::vector< MirrorType > _data_donee_mirs
the data array mirrors for our donee and owner neighbors
void upload(const LocalMatrixType &mat_a)
Initializes this algebraic dof-partitioned matrix from a global matrix.
OwnedMatrixType _matrix
the internal matrix storing our owned matrix rows
void upload_numeric(const GlobalMatrixType &mat_a)
Copies the matrix entry values from the input matrix into this algebraic-dof-partitioned matrix.
void upload_numeric(const LocalMatrixType &mat_a)
Copies the matrix entry values from the input matrix into this algebraic-dof-partitioned matrix.
const Dist::Comm * get_comm() const
std::vector< BufferMatrixType > _donee_bufs
the matrix buffers for our donee and owner neighbors
LAFEM::DenseVector< DataType, IndexType > BufferVectorType
the buffer vector type used for communication
LocalMatrix_::IndexType IndexType
our index type
void _assemble_data_mirrors(const LocalMatrixType &local_matrix)
Assembles the data-mirrors for all owner- and donee-neighbors.
LocalMatrixType::VectorTypeR LocalVectorType
the local vector type
void filter_matrix(const Global::Filter< LocalFilter_, MirrorType > &filter)
Applies a global unit-filter into this matrix.
const OwnedMatrixType & owned() const
static void _scatter_data(LocalMatrixType &local_matrix, const BufferVectorType &buffer, const MirrorType &data_mirror)
Scatters the values of a donee-neighbor buffer-vector into a local matrix.
void upload_symbolic(const LocalMatrixType &mat_a)
Initializes this algebraic-dof-partitioned matrix from a local matrix.
static MirrorType _asm_owner_data_mir(const LocalMatrixType &local_matrix, const MirrorType &mirror, const BufferMatrixType &buffer, const std::vector< IndexType > &glob_dof_idx)
Auxiliary function: assembles a data-mirror for an owner-neighbor.
Global::Matrix< LocalMatrixType, MirrorType, MirrorType > GlobalMatrixType
the global matrix type
const AlgDofPartiType * get_alg_dof_parti() const
void upload(const GlobalMatrixType &mat_a)
Initializes this algebraic dof-partitioned matrix from a global matrix.
Global::AlgDofParti< LocalVectorType, MirrorType > AlgDofPartiType
the algebraic DOF partitioning
void apply(AlgDofPartiVectorType &vec_r, const AlgDofPartiVectorType &vec_x) const
Performs a global matrix-vector product with this matrix.
void clear()
Resets the whole object.
LAFEM::SparseMatrixCSR< DataType, IndexType > OwnedMatrixType
the matrix type used for the internal storage of our owned matrix rows
Algebraic DOF Partitioned Vector class template.
LocalVector_::IndexType IndexType
our index type
const AlgDofPartiType * _alg_dof_parti
the algebraic dof-partitioning
const Dist::Comm * get_comm() const
Global::AlgDofParti< LocalVector_, Mirror_ > AlgDofPartiType
the algebraic DOF partitioning type
AlgDofPartiVector(const AlgDofPartiType *adp_in)
Creates a ADP vector based on an algebraic dof partitioning.
LAFEM::DenseVector< DataType, IndexType > BufferVectorType
the buffer vector type used for communication
void upload(const GlobalVectorType &global_vector)
Copies the DOF values from a global vector to this algebraic-dof-partitioned vector.
void download(LocalVectorType &local_vector) const
Copies the DOF values from this algebraic-dof-partitioned vector into a local vector.
void download(GlobalVectorType &global_vector) const
Copies the DOF values from this algebraic-dof-partitioned vector into a global vector.
LocalVector_::DataType DataType
our data type
void clear()
Resets the whole object.
void upload(const LocalVectorType &local_vector)
Copies the DOF values from a local vector to this algebraic-dof-partitioned vector.
AlgDofPartiVector()
default constructor
OwnedVectorType _vector
the internal vector object storing our owned dofs
LAFEM::DenseVector< DataType, IndexType > OwnedVectorType
the vector type used for the internal storage of our owned DOFs
LocalVector_ LocalVectorType
the local vector type
Mirror_ MirrorType
the vector mirror type
const OwnedVectorType & owned() const
void allgather(BufferVectorType &vec_full) const
Gathers the full global vector on all processes.
Global::Vector< LocalVector_, Mirror_ > GlobalVectorType
the global vector type
const AlgDofPartiType * get_alg_dof_parti() const
Global Filter wrapper class template.
Definition: filter.hpp:21
Global gate implementation.
Definition: gate.hpp:51
const Dist::Comm * get_comm() const
Returns a const pointer to the underlying communicator.
Definition: gate.hpp:138
std::vector< Mirror_ > _mirrors
vector mirrors
Definition: gate.hpp:73
LocalVector_ _freqs
frequency vector
Definition: gate.hpp:75
std::vector< int > _ranks
communication ranks
Definition: gate.hpp:71
Global Matrix wrapper class template.
Definition: matrix.hpp:40
LocalMatrix_ & local()
Returns a reference to the internal local LAFEM matrix object.
Definition: matrix.hpp:126
Global vector wrapper class template.
Definition: vector.hpp:68
LocalVector_ & local()
Returns a reference to the internal local LAFEM vector object.
Definition: vector.hpp:121
Index size() const
Returns the containers size.
Definition: container.hpp:1136
virtual void clear()
Free all allocated arrays.
Definition: container.hpp:875
void format(DT_ value=DT_(0))
Reset all elements of the container to a given value or zero if missing.
Definition: container.hpp:851
Dense data vector class template.
DT_ * elements()
Get a pointer to the data array.
Matrix Mirror Buffer class template.
Index columns() const
Retrieve matrix column count.
IT_ * row_ptr()
Retrieve row start index array.
IT_ * col_ind()
Retrieve column indices array.
Index entries_per_nonzero() const
Retrieve entries per non zero element count.
Index used_elements() const
Retrieve non zero element count.
Index rows() const
Retrieve matrix row count.
CSR based sparse matrix.
IT_ * col_ind()
Retrieve column indices array.
DT_ * val()
Retrieve non zero element array.
Index rows() const
Retrieve matrix row count.
void apply(DenseVector< DT_, IT_ > &r, const DenseVector< DT_, IT_ > &x) const
Calculate .
Index used_elements() const
Retrieve non zero element count.
IT_ * row_ptr()
Retrieve row start index array.
Unit Filter class template.
Definition: unit_filter.hpp:29
Handles vector prolongation, restriction and serialization.
IT_ * indices()
Get a pointer to the non zero indices array.
Index num_indices() const
Returns the number of indices in the mirror.
@ as_is
Render-As-Is mode.
const Operation op_sum(MPI_SUM)
Operation wrapper for MPI_SUM.
Definition: dist.hpp:271
FEAT namespace.
Definition: adjactor.hpp:12
std::uint64_t Index
Index data type.