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/global/gate.hpp>
9#include <kernel/global/vector.hpp>
10#include <kernel/lafem/dense_vector.hpp>
11#include <kernel/lafem/vector_mirror.hpp>
12#include <kernel/lafem/tuple_mirror.hpp>
13#include <kernel/util/omp_util.hpp>
14
15#include <vector>
16
17namespace FEAT
18{
19 namespace Global
20 {
22 namespace Intern
23 {
24 template<typename IdxVector_>
25 class ADPAux;
26 } // namespace Intern
28
190 template<typename LocalVector_, typename Mirror_>
192 {
193 public:
195 typedef LocalVector_ LocalVectorType;
197 typedef Mirror_ MirrorType;
198
200 typedef typename LocalVectorType::DataType DataType;
202 typedef typename LocalVectorType::IndexType IndexType;
203
208
210 typedef typename LocalVectorType::template ContainerTypeByDI<IndexType, IndexType> IndexVectorType;
211
213 typedef Intern::ADPAux<IndexVectorType> ADPAuxType;
214
217
224
225 //protected:
247 std::vector<int> _owner_ranks;
249 std::vector<int> _donee_ranks;
251 std::vector<OwnerMirrorType> _owner_mirrors;
253 std::vector<DoneeMirrorType> _donee_mirrors;
256
257 // Note: the following vectors hold int objects, because these are used as recvcount in an
258 // MPI_Allgatherv call, which only accepts int objects as counts; we have an XASSERT in
259 // place to ensure that we do not overflow the int datatype with more than a billion DOFs
260
262 std::vector<int> _all_global_dof_offset;
264 std::vector<int> _all_global_dof_counts;
265
266 public:
269 _comm(nullptr),
277 {
278 }
279
281 AlgDofParti(const AlgDofParti&) = delete;
284
286 virtual ~AlgDofParti() = default;
287
289 void clear()
290 {
291 _comm = nullptr;
299 _global_dof_idx.clear();
300 _owned_mirror.clear();
301 _owner_mirrors.clear();
302 _donee_mirrors.clear();
305 }
306
308 const Dist::Comm* get_comm() const
309 {
310 return _comm;
311 }
312
314 std::size_t bytes() const
315 {
316 std::size_t r = 0u;
317 r += _global_dof_idx.bytes();
318 r += _owned_mirror.bytes();
319 for(const auto& x : _owner_mirrors)
320 r += x.second.bytes();
321 for(const auto& x : _donee_mirrors)
322 r += x.second.bytes();
323 r += _all_global_dof_offset.size() * 4u;
324 r += _all_global_dof_counts.size() * 4u;
325 return r;
326 }
327
333 {
334 return this->_block_information;
335 }
336
339 {
340 return this->_owned_dof_count;
341 }
342
345 {
346 return this->_local_dof_count;
347 }
348
351 {
352 return this->_global_dof_count;
353 }
354
357 {
358 return this->_global_dof_offset;
359 }
360
364 const IndexVectorType& get_global_dof_indices() const
365 {
366 return this->_global_dof_idx;
367 }
368
372 const OwnedMirrorType& get_owned_mirror() const
373 {
374 return this->_owned_mirror;
375 }
376
379 {
380 return Index(this->_owner_mirrors.size());
381 }
382
385 {
386 return Index(this->_donee_mirrors.size());
387 }
388
399 {
400 return this->_owner_ranks.at(i);
401 }
402
413 {
414 return this->_donee_ranks.at(i);
415 }
416
430 {
431 return this->_owner_mirrors.at(i);
432 }
433
447 {
448 return this->_donee_mirrors.at(i);
449 }
450
460 const std::vector<int>& get_all_global_dof_offsets() const
461 {
462 XASSERTM(!this->_all_global_dof_offset.empty(), "You did not ask to assemble the global dof offsets");
463 return this->_all_global_dof_offset;
464 }
465
475 const std::vector<int>& get_all_global_dof_counts() const
476 {
477 XASSERTM(!this->_all_global_dof_counts.empty(), "You did not ask to assemble the global dof counts");
478 return this->_all_global_dof_counts;
479 }
480
493 void assemble_allgather(bool yes_i_really_want_to_do_this = false)
494 {
495 XASSERTM(yes_i_really_want_to_do_this, "You probably don't want to do this!");
496
497 // ensure that we don't have more than a billion DOFs
498 XASSERTM(this->get_num_global_dofs() < 1'000'000'000,
499 "allgather assembly not possible for more than a billion DOFs!");
500
501 std::size_t num_ranks = std::size_t(this->_comm->size());
502
503 int my_nod = int(this->get_num_owned_dofs());
504 int my_off = int(this->get_global_dof_offset());
505
506 // resize vectors
507 this->_all_global_dof_offset.resize(num_ranks, 0);
508 this->_all_global_dof_counts.resize(num_ranks, 0);
509
510 // allgather offsets + counts
511 this->_comm->allgather(&my_off, 1, this->_all_global_dof_offset.data(), 1);
512 this->_comm->allgather(&my_nod, 1, this->_all_global_dof_counts.data(), 1);
513 }
514
526 void assemble_by_gate(const GateType& gate, Index extra_local = 0u, Index extra_shared = 0u)
527 {
528 // set our communicator
529 this->_comm = gate.get_comm();
530 XASSERTM(this->_comm != nullptr, "need a communicator here!");
531
532 // get the communicator
533 const Dist::Comm& comm = *this->_comm;
534
535 // get my rank and neighbor ranks
536 const int my_rank = comm.rank();
537 const std::vector<int>& neighbor_ranks = gate._ranks;
538
539 // get a template vector
540 const LocalVectorType& vec_tmpl = gate.get_freqs();
541
542 // get number of total local DOFs on our patch
543 this->_local_dof_count = vec_tmpl.template size<LAFEM::Perspective::pod>();
544 this->_extra_local_dof_count = extra_local;
545 this->_extra_shared_dof_count = extra_shared;
546 //this->_local_dof_count += extra_local + extra_shared;
547
548 // As a very first step, we have to decide which process will become the owner of each DOF.
549 // In this implementation, each DOF is owned by by the process with the lowest rank, which
550 // simplifies a lot of things.
551
552 // So, first create a vector, which stores the rank of the owner process of each of our
553 // local DOFs and format the vector to our rank, i.e. at startup assume that we own all
554 // local DOFs.
555 IndexVectorType dof_owners;
556 ADPAuxType::alloc_idx_vector(dof_owners, vec_tmpl, IndexType(my_rank));
557
558 // Now loop over all our neighbor processes
559 for(std::size_t i(0); i < neighbor_ranks.size(); ++i)
560 {
561 // Check whether the neighbors rank is less than our rank, as otherwise that particular
562 // neighbor can not own any of our local DOFs.
563 if(neighbor_ranks.at(i) < my_rank)
564 {
565 // this neighbor has a lower rank than our process, so update the dof owners vector and
566 // update the ownership of these dofs by setting this neighbor as their owner -- unless
567 // another neighbor with an even lower rank is already a candidate for ownership
568 ADPAuxType::update_owners(IndexType(neighbor_ranks.at(i)), dof_owners, gate.get_mirrors().at(i));
569 }
570 }
571
572 // Okay, at this point we (and all other processes) know the owners of each of our local DOFs.
573 // Now, we have to generate the mirror of all local DOF indices of the DOFs that we own:
574 this->_owned_dof_count = ADPAuxType::build_owned_mirror(IndexType(my_rank), this->_owned_mirror, dof_owners);
575
576 // Save current owned DOF count as extra DOF offset
577 this->_extra_dof_offset = this->_owned_dof_count;
578
579 // Now add the extra local DOFs for each process
580 this->_owned_dof_count += _extra_local_dof_count;
581
582 // The shared DOFs are always owned by the last process
583 const bool is_last_process = (this->_comm->rank()+1 == this->_comm->size());
584 if(is_last_process)
585 this->_owned_dof_count += _extra_shared_dof_count;
586
588 XASSERTM(this->_owned_dof_count > Index(0), "this process has no DOFs!");
589
590 // Next, we have to determine the global offset of our first owned DOF, which is easily
591 // obtained by an exclusive-scan over each processes owned DOF count:
592 comm.exscan(&this->_owned_dof_count, &this->_global_dof_offset, 1, Dist::op_sum);
593
594 // Furthermore, we also perform an allreduce to obtain the total number of global DOFs,
595 // which is generally helpful and can be used for sanity checks.
596 comm.allreduce(&this->_owned_dof_count, &this->_global_dof_count, 1, Dist::op_sum);
597
598 // Allocate an index vector that for each local DOF contains the "owned DOF index" if we
599 // own this particular DOF or ~Index(0) if this particular DOF is owned by another process:
600 IndexVectorType own_dof_idx;
601 ADPAuxType::alloc_idx_vector(own_dof_idx, vec_tmpl, ~IndexType(0));
602 ADPAuxType::build_owned_dofs(IndexType(my_rank), own_dof_idx, dof_owners, 0u);
603
604 // Now we also have to create the mirrors for each of our neighbor processes:
605 for(std::size_t i(0); i < neighbor_ranks.size(); ++i)
606 {
607 // get the neighbor rank and its mirror
608 const int neighbor_rank = neighbor_ranks.at(i);
609 const MirrorType& halo_mirror = gate.get_mirrors().at(i);
610
611 // There are two cases now:
612 // 1) The neighbor process has a lower rank, so it is a (potential) owner
613 // of at least one of our local DOFs.
614 // 2) The neighbor process has a greater rank, so we are an owner of at least
615 // one of the neighbor process's local DOFs.
616 if(neighbor_rank < my_rank)
617 {
618 // This is a potential "owner-neighbor", which may own one or several of our local DOFs.
619 // We call a helper function, which will create a vector of all *local* DOF indices,
620 // which are owned by that neighbor process. Note that it is perfectly legit if the
621 // vector is empty, as this simply means that all our local DOFs, which are shared with
622 // that particular neighbor, are owned by some *other* neighbor process(es) and not by
623 // the particular neighbor that we are currently considering.
624 OwnerMirrorType owner_mirror;
625 if(ADPAuxType::build_owner_mirror(IndexType(neighbor_rank), owner_mirror, halo_mirror, dof_owners) > Index(0))
626 {
627 // That neighbor owns at least one of our local DOFs, so create a mirror from the
628 // index-vector and push it into our list of "owner-neighbors".
629 // Note that the indices in the mirror are *local* DOF indices.
630 _owner_ranks.push_back(neighbor_rank);
631 _owner_mirrors.emplace_back(std::move(owner_mirror));
632 }
633 }
634 else
635 {
636 // This is a potential "donee-neighbor", which shares one or several of our local DOFs,
637 // which we might own. We call a helper function, which will create a vector of all our
638 // *owned* DOF indices, which are shared with that particular neighbor process.
639 // Note that it is perfectly legit if the vector is empty, as this simply means that
640 // all our local DOFs, which are shared with that particular neighbor, are owned by
641 // some *other* neighbor process(es) and not by this process (i.e. us).
642 Index num_donee_dofs = ADPAuxType::count_donee_dofs(halo_mirror, own_dof_idx);
643 if(num_donee_dofs > Index(0))
644 {
645 // allocate and build the actual donee mirror
646 DoneeMirrorType donee_mirror(this->_owned_dof_count, num_donee_dofs);
647 ADPAuxType::build_donee_mirror(donee_mirror, halo_mirror, own_dof_idx, 0u);
648
649 // We own at least of one of the neighbor's local DOFs, so create a mirror from the
650 // index-vector and push it into our list of "donee-neighbors".
651 // Note that the indices in the mirror are *owned* DOF indices.
652 _donee_ranks.push_back(neighbor_rank);
653 _donee_mirrors.emplace_back(std::move(donee_mirror));
654 }
655 }
656 }
657
658 // Finally, we have to determine the global DOF index for each of our local DOFs, so that
659 // we can perform a "local-to-global" DOF index lookup. This information is required for
660 // the assembly of matrices later on, so we have to store this in a member-variable vector.
661
662 // Let's create the vector of the required length, initialize all its indices to ~0, which
663 // can be used for a sanity check later on to ensure that we know the global indices of all
664 // our local DOFs:
665 ADPAuxType::alloc_idx_vector(this->_global_dof_idx, vec_tmpl, ~IndexType(0));
666
667 // Next, let's loop over all DOFs that we own and assign their corresponding global DOF
668 // indices, starting with our global DOF offset, which we have already determined before:
669 ADPAuxType::init_global_dof_idx(this->_global_dof_idx, this->_owned_mirror, this->_global_dof_offset);
670
671 // Finally, we also need to query the global DOF indices of all our local DOFs, which are
672 // owned by some neighbor process (i.e. we are the donee for these DOFs).
673 // Of course, we also have to send the global DOF indices of all DOFs that we own
674 // to all of our "donee-neighbors".
675
676 // Allocate index buffers and post receives for all of our owner-neighbors:
677 const Index num_neigh_owner = Index(_owner_mirrors.size());
678 Dist::RequestVector recv_reqs(num_neigh_owner);
679 std::vector<std::vector<IndexType>> recv_bufs(num_neigh_owner);
680 for(Index i(0); i < num_neigh_owner; ++i)
681 {
682 const std::size_t num_idx = _owner_mirrors.at(i).buffer_size(vec_tmpl);
683 recv_bufs.at(i).resize(num_idx, ~IndexType(0));
684 recv_reqs[i] = comm.irecv(recv_bufs.at(i).data(), num_idx, _owner_ranks.at(i));
685 }
686
687 // Allocate index buffers, fill them with the global DOF indices of our owned
688 // DOFs and send them to the donee-neighbors:
689 const Index num_neigh_donee = Index(_donee_mirrors.size());
690 Dist::RequestVector send_reqs(num_neigh_donee);
691 std::vector<std::vector<IndexType>> send_bufs(num_neigh_donee);
692 for(Index i(0); i < num_neigh_donee; ++i)
693 {
694 // Allocate buffer of required size and translate the indices of our mirror,
695 // which are "owned DOF indices", to global DOF indices, where:
696 // global_dof_index := global_dof_offset + owned_dof_index
697 const std::size_t num_idx = _donee_mirrors.at(i).num_indices();
698 send_bufs.at(i).resize(num_idx, ~IndexType(0));
699 _get_donee_dofs(send_bufs.at(i), _donee_mirrors.at(i), this->_global_dof_offset);
700 send_reqs[i] = comm.isend(send_bufs.at(i).data(), num_idx, _donee_ranks.at(i));
701 }
702
703 // Now process all receive requests from our owner-neighbors:
704 for(std::size_t i(0u); recv_reqs.wait_any(i); )
705 {
706 // Scatter the global DOF indices, which our friendly owner-neighbor has
707 // provided us with, into our global DOF index vector:
708 ADPAuxType::set_owner_dofs(this->_global_dof_idx, recv_bufs.at(i), _owner_mirrors.at(i), 0u);
709 }
710
711 // wait for all sends to finish
712 send_reqs.wait_all();
713
714 // get local counts
715 std::vector<Index> local_num;
716 ADPAuxType::get_local_dof_count(local_num, this->_global_dof_idx, this->_owned_mirror);
717
718 // compute global counts
719 std::vector<Index> global_num(local_num.size());
720 comm.allreduce(local_num.data(), global_num.data(), local_num.size(), Dist::op_sum);
721
722 // compute global offsets
723 std::vector<Index> global_first(local_num.size());
724 comm.exscan(local_num.data(), global_first.data(), local_num.size(), Dist::op_sum);
725
726 // build deques
727 std::deque<Index> loc_off, loc_num, glob_first, glob_num;
728 for(Index i(0), off(0); i < Index(local_num.size()); ++i)
729 {
730 loc_off.push_back(off);
731 loc_num.push_back(local_num[i]);
732 glob_num.push_back(global_num[i]);
733 glob_first.push_back(global_first[i]);
734 off += local_num[i];
735 }
736
737 // finally, build the block information
738 this->_block_information = ADPAuxType::build_block_info(this->_global_dof_idx, this->_global_dof_offset,
739 glob_first, glob_num, loc_off, loc_num);
740 }
741
742 template<typename DT2_>
743 void upload_vector(DT2_* owned_dofs, const LocalVectorType& local_vector,
744 const DataType* extra_local = nullptr, const DataType* extra_shared = nullptr) const
745 {
746 // gather owned DOFs
747 ADPAuxType::gather(owned_dofs, local_vector, this->_owned_mirror);
748
749 // gather extra local DOFs
750 if(this->_extra_local_dof_count > Index(0))
751 {
752 XASSERT(extra_local != nullptr);
753 DT2_* dst = &owned_dofs[this->_extra_dof_offset];
754 for(Index i = 0u; i < this->_extra_local_dof_count; ++i)
755 dst[i] = DT2_(extra_local[i]);
756 }
757
758 // gather extra shared DOFs on last process
759 if(this->_extra_shared_dof_count > Index(0))
760 {
761 XASSERT(extra_shared != nullptr);
762 if(this->_comm->rank() + 1 == this->_comm->size())
763 {
764 DT2_* dst = &owned_dofs[this->_extra_dof_offset + this->_extra_local_dof_count];
765 for(Index i = 0u; i < this->_extra_shared_dof_count; ++i)
766 dst[i] = DT2_(extra_shared[i]);
767 }
768 }
769 }
770
771 template<typename DT2_>
772 void download_vector(const DT2_* owned_dofs, LocalVectorType& local_vector,
773 DataType* extra_local = nullptr, DataType* extra_shared = nullptr) const
774 {
775 // ensure we have a communicator
776 XASSERT(this->_comm != nullptr);
777
778 // get the number of owner- and donee-neighbors
779 const std::size_t num_neigh_owner = this->_owner_mirrors.size();
780 const std::size_t num_neigh_donee = this->_donee_mirrors.size();
781
782 // The basic idea is simple:
783 // 1) Send the shared DOFs to all of our donee-neighbors
784 // 2) Receive the shared DOFs from all of our owner-neighbors
785 // 3) Scatter our own owned DOFs into our local vector
786 // 4) Scatter the dofs we received from our owner-neighbors
787
788 // Create receive buffers and post receives for all owner-neighbors
789 Dist::RequestVector recv_reqs(num_neigh_owner);
790 std::vector<BufferVectorType> recv_bufs(num_neigh_owner);
791 for(std::size_t i(0); i < num_neigh_owner; ++i)
792 {
793 // create a vector buffer
794 // note: owner mirrors relate to local DOF indices
795 recv_bufs.at(i) = this->_owner_mirrors.at(i).create_buffer(local_vector);
796 // post receive from owner neighbor
797 recv_reqs[i] = this->_comm->irecv(recv_bufs.at(i).elements(), recv_bufs.at(i).size(), this->_owner_ranks.at(i));
798 }
799
800 // Create send buffers, fill them with our owned DOFs and send this
801 // to our donee-neighbors
802 Dist::RequestVector send_reqs(num_neigh_donee);
803 std::vector<BufferVectorType> send_bufs(num_neigh_donee);
804 for(Index i(0); i < num_neigh_donee; ++i)
805 {
806 // get mirror, create buffer and gather the shared DOFs
807 // note: donee mirrors relate to owned DOF indices
808 const DoneeMirrorType& mir = this->_donee_mirrors.at(i);
809 send_bufs.at(i) = BufferVectorType(mir.num_indices());
810 _gather(send_bufs.at(i), owned_dofs, mir);
811 // post send to donee neighbor
812 send_reqs[i] = this->_comm->isend(send_bufs.at(i).elements(), send_bufs.at(i).size(), this->_donee_ranks.at(i));
813 }
814
815 // format the output vector
816 local_vector.format();
817
818 // scatter our own owned DOFs into our output vector
819 ADPAuxType::scatter(owned_dofs, local_vector, this->_owned_mirror);
820
821 // process receives from our owner-neighbors
822 for(std::size_t idx(0u); recv_reqs.wait_any(idx); )
823 {
824 // scatter received DOFs into our output vector
825 ADPAuxType::scatter(recv_bufs.at(idx).elements(), local_vector, this->_owner_mirrors.at(idx));
826 }
827
828 // at this point, all receives should have finished
829 XASSERT(recv_reqs.is_null());
830
831 // wait for all sends to finish
832 send_reqs.wait_all();
833
834 // copy extra local DOFs
835 if(this->_extra_local_dof_count > Index(0))
836 {
837 XASSERT(extra_local != nullptr);
838 const DT2_* src = &owned_dofs[this->_extra_dof_offset];
839 for(Index i = 0u; i < this->_extra_local_dof_count; ++i)
840 extra_local[i] = DataType(src[i]);
841 }
842
843 // scatter extra shared DOFs
844 if(this->_extra_shared_dof_count > Index(0))
845 {
846 XASSERT(extra_shared != nullptr);
847
848 // the last process owns the shared DOFs
849 if(this->_comm->rank() + 1 == this->_comm->size())
850 {
851 const DT2_* src = &owned_dofs[this->_extra_dof_offset + this->_extra_local_dof_count];
852 for(Index i = 0u; i < this->_extra_shared_dof_count; ++i)
853 extra_shared[i] = DataType(src[i]);
854 }
855
856 // broadcast shared DOFs
857 this->_comm->bcast(extra_shared, std::size_t(this->_extra_shared_dof_count), this->_comm->size() - 1);
858 }
859 }
860
861 protected:
862 static Index _get_donee_dofs(std::vector<IndexType>& send_buf,
863 const LAFEM::VectorMirror<DataType, IndexType>& donee_mir, Index offset)
864 {
865 const Index num_idx = donee_mir.num_indices();
866 const IndexType* mir_idx = donee_mir.indices();
867 for(Index j(0); j < num_idx; ++j)
868 send_buf[j] = IndexType(offset) + mir_idx[j];
869
870 return num_idx;
871 }
872
873 template<typename DT2_>
874 static Index _gather(
875 LAFEM::DenseVector<DataType, IndexType>& buffer,
876 const DT2_* vector,
877 const LAFEM::VectorMirror<DataType, IndexType>& mirror)
878 {
879 const Index n = mirror.num_indices();
880 const IndexType* idx = mirror.indices();
881 DataType* buf = buffer.elements();
882 for(Index i = 0u; i < n; ++i)
883 buf[i] = DataType(vector[idx[i]]);
884 return n;
885 }
886 }; // class AlgDofParti<...>
887
893
895 namespace Intern
896 {
897 template<typename IT_>
898 class ADPAux<LAFEM::DenseVector<IT_, IT_>>
899 {
900 public:
901 template<typename DT_>
902 static Index get_local_dof_count(std::vector<Index>& v,
903 const LAFEM::DenseVector<IT_, IT_>& DOXY(global_dof_idx),
904 const LAFEM::VectorMirror<DT_, IT_>& owned_mirror)
905 {
906 v.push_back(owned_mirror.num_indices());
907 return v.back();
908 }
909
910 static String build_block_info(
911 const LAFEM::DenseVector<IT_, IT_>& DOXY(global_dof_idx), Index global_offset,
912 std::deque<Index>& global_first, std::deque<Index>& global_num,
913 std::deque<Index>& local_offset, std::deque<Index>& local_num)
914 {
915 String s = "<Scalar";
916 s += " gc=\"" + stringify(global_num.front()) + "\"";
917 s += " gf=\"" + stringify(global_first.front()) + "\"";
918 s += " go=\"" + stringify(global_offset + local_offset.front()) + "\"";
919 s += " lc=\"" + stringify(local_num.front()) + "\"";
920 s += " lo=\"" + stringify(local_offset.front()) + "\"";
921 s += "/>";
922 global_first.pop_front();
923 global_num.pop_front();
924 local_offset.pop_front();
925 local_num.pop_front();
926 return s;
927 }
928
929 template<typename DT_>
930 static void alloc_idx_vector(LAFEM::DenseVector<IT_, IT_>& idx_vec,
931 const LAFEM::DenseVector<DT_, IT_>& tmpl_vec, IT_ value)
932 {
933 idx_vec = LAFEM::DenseVector<IT_, IT_>(tmpl_vec.size(), value);
934 }
935
936 template<typename DT_>
937 static void update_owners(const IT_ neighbor_rank,
938 LAFEM::DenseVector<IT_, IT_>& dof_owners,
939 const LAFEM::VectorMirror<DT_, IT_>& mirror)
940 {
941 const Index num_indices = mirror.num_indices();
942 const IT_* mir_idx = mirror.indices();
943 IT_* dof_own = dof_owners.elements();
944 for(Index j(0); j < num_indices; ++j)
945 {
946 IT_& dof_owner = dof_own[mir_idx[j]];
947 if(neighbor_rank < dof_owner)
948 dof_owner = neighbor_rank;
949 }
950 }
951
952 static Index build_owned_dofs(const IT_ my_rank,
953 LAFEM::DenseVector<IT_, IT_>& owned_dofs,
954 const LAFEM::DenseVector<IT_, IT_>& dof_owners, Index offset)
955 {
956 const Index n = dof_owners.size();
957 const IT_* dof_own = dof_owners.elements();
958 IT_* own_idx = owned_dofs.elements();
959
960 Index k = 0;
961 for(Index i = 0; i < n; ++i)
962 {
963 if(dof_own[i] == my_rank)
964 own_idx[i] = IT_(offset + k++);
965 }
966
967 return k;
968 }
969
970 template<typename DT_>
971 static Index build_owned_mirror(const IT_ owner_rank,
972 LAFEM::VectorMirror<DT_, IT_>& owner_mirror,
973 const LAFEM::DenseVector<IT_, IT_>& dof_owners)
974 {
975 // count the number of owned DOFs
976 const Index n = dof_owners.size();
977 const IT_* dof_own = dof_owners.elements();
978 Index num_owned = 0u;
979 for(Index i = 0; i < n; ++i)
980 {
981 if(dof_own[i] == owner_rank)
982 ++num_owned;
983 }
984
985 // allocate mirror
986 owner_mirror = LAFEM::VectorMirror<DT_, IT_>(n, num_owned);
987 IT_* mir_idx = owner_mirror.indices();
988
989 // store owned DOF indices
990 for(Index i = 0, k = 0; i < n; ++i)
991 {
992 if(dof_own[i] == owner_rank)
993 {
994 mir_idx[k++] = IT_(i);
995 }
996 }
997
998 return num_owned;
999 }
1000
1001 template<typename DT_>
1002 static Index build_owner_mirror(const IT_ neighbor_rank,
1003 LAFEM::VectorMirror<DT_, IT_>& owner_mirror,
1004 const LAFEM::VectorMirror<DT_, IT_>& halo_mirror,
1005 const LAFEM::DenseVector<IT_, IT_>& dof_owners)
1006 {
1007 // get halo mirror indices
1008 const Index n = halo_mirror.num_indices();
1009 const IT_* halo_idx = halo_mirror.indices();
1010 const IT_* dof_own = dof_owners.elements();
1011
1012 // count number of owner dofs
1013 Index num_owner = 0u;
1014 for(Index i = 0; i < n; ++i)
1015 {
1016 if(dof_own[halo_idx[i]] == neighbor_rank)
1017 ++num_owner;
1018 }
1019
1020 // allocate mirror
1021 owner_mirror = LAFEM::VectorMirror<DT_, IT_>(dof_owners.size(), num_owner);
1022
1023 if(num_owner <= Index(0))
1024 return Index(0);
1025
1026 // store owner DOF indices
1027 IT_* own_idx = owner_mirror.indices();
1028 for(IT_ i = 0, k = 0; i < n; ++i)
1029 {
1030 if(dof_own[halo_idx[i]] == neighbor_rank)
1031 {
1032 own_idx[k++] = halo_idx[i];
1033 }
1034 }
1035
1036 return num_owner;
1037 }
1038
1039 template<typename DT_>
1040 static Index count_donee_dofs(
1041 const LAFEM::VectorMirror<DT_, IT_>& halo_mirror,
1042 const LAFEM::DenseVector<IT_, IT_>& own_dof_idx)
1043 {
1044 // get halo mirror indices
1045 const Index n = halo_mirror.num_indices();
1046 const IT_* halo_idx = halo_mirror.indices();
1047 const IT_* own_idx = own_dof_idx.elements();
1048
1049 // count number of owner dofs
1050 Index num_donee = 0u;
1051 for(Index i = 0; i < n; ++i)
1052 {
1053 if(own_idx[halo_idx[i]] != ~IT_(0))
1054 ++num_donee;
1055 }
1056
1057 return num_donee;
1058 }
1059
1060 template<typename DT_>
1061 static Index build_donee_mirror(
1062 LAFEM::VectorMirror<DT_, IT_>& donee_mirror,
1063 const LAFEM::VectorMirror<DT_, IT_>& halo_mirror,
1064 const LAFEM::DenseVector<IT_, IT_>& own_dof_idx, Index offset)
1065 {
1066 // get halo mirror indices
1067 const Index n = halo_mirror.num_indices();
1068 const IT_* halo_idx = halo_mirror.indices();
1069 const IT_* own_idx = own_dof_idx.elements();
1070 IT_* donee_idx = donee_mirror.indices();
1071
1072 // store donee DOF indices
1073 Index k = 0u;
1074 for(Index i = 0; i < n; ++i)
1075 {
1076 if(own_idx[halo_idx[i]] != ~IT_(0))
1077 {
1078 donee_idx[offset + k++] = own_idx[halo_idx[i]];
1079 }
1080 }
1081
1082 return k;
1083 }
1084
1085 template<typename DT_>
1086 static Index init_global_dof_idx(
1087 LAFEM::DenseVector<IT_, IT_>& global_dof_idx,
1088 const LAFEM::VectorMirror<DT_, IT_>& owned_mirror, Index offset)
1089 {
1090 IT_* g_dof_idx = global_dof_idx.elements();
1091 const IT_* own_idx = owned_mirror.indices();
1092 const Index n = owned_mirror.num_indices();
1093 for(Index i = 0; i < n; ++i)
1094 g_dof_idx[own_idx[i]] = IT_(offset + i);
1095 return n;
1096 }
1097
1098 template<typename DT_>
1099 static Index set_owner_dofs(
1100 LAFEM::DenseVector<IT_, IT_>& glob_dof_idx,
1101 const std::vector<IT_>& recv_buf,
1102 const LAFEM::VectorMirror<DT_, IT_>& mirror, Index offset)
1103 {
1104 const Index num_indices = mirror.num_indices();
1105 const IT_* mir_idx = mirror.indices();
1106 IT_* g_dof_idx = glob_dof_idx.elements();
1107 for(Index j(0); j < num_indices; ++j)
1108 g_dof_idx[mir_idx[j]] = recv_buf[offset + j];
1109 return num_indices;
1110 }
1111
1112 template<typename DT_, typename DT2_>
1113 static Index gather(DT2_* buf,
1114 const LAFEM::DenseVector<DT_, IT_>& vector,
1115 const LAFEM::VectorMirror<DT_, IT_>& mirror)
1116 {
1117 XASSERT(mirror.size() == vector.size());
1118 const Index n = mirror.num_indices();
1119 const IT_* idx = mirror.indices();
1120 const DT_* val = vector.elements();
1121 for(Index i = 0u; i < n; ++i)
1122 buf[i] = DT2_(val[idx[i]]);
1123 return n;
1124 }
1125
1126 template<typename DT_, typename DT2_>
1127 static Index scatter(const DT2_* buf,
1128 LAFEM::DenseVector<DT_, IT_>& vector,
1129 const LAFEM::VectorMirror<DT_, IT_>& mirror)
1130 {
1131 XASSERT(mirror.size() == vector.size());
1132 const Index n = mirror.num_indices();
1133 const IT_* idx = mirror.indices();
1134 DT_* val = vector.elements();
1135 for(Index i = 0u; i < n; ++i)
1136 val[idx[i]] = DT_(buf[i]);
1137 return n;
1138 }
1139 }; // class ADPAux<LAFEM::DenseVector<IT_, IT_>>
1140
1142
1143 template<typename IT_, int bs_>
1144 class ADPAux<LAFEM::DenseVectorBlocked<IT_, IT_, bs_>>
1145 {
1146 public:
1147 template<typename DT_>
1148 static Index get_local_dof_count(std::vector<Index>& v,
1149 const LAFEM::DenseVectorBlocked<IT_, IT_, bs_>& DOXY(global_dof_idx),
1150 const LAFEM::VectorMirror<DT_, IT_>& owned_mirror)
1151 {
1152 v.push_back(owned_mirror.num_indices() * Index(bs_));
1153 return v.back();
1154 }
1155
1156 static String build_block_info(
1157 const LAFEM::DenseVectorBlocked<IT_, IT_, bs_>& DOXY(global_dof_idx), Index global_offset,
1158 std::deque<Index>& global_first, std::deque<Index>& global_num,
1159 std::deque<Index>& local_offset, std::deque<Index>& local_num)
1160 {
1161 String s = "<Blocked";
1162 s += " bs=\"" + stringify(bs_) + "\"";
1163 s += " gc=\"" + stringify(global_num.front()) + "\"";
1164 s += " gf=\"" + stringify(global_first.front()) + "\"";
1165 s += " go=\"" + stringify(global_offset + local_offset.front()) + "\"";
1166 s += " lc=\"" + stringify(local_num.front()) + "\"";
1167 s += " lo=\"" + stringify(local_offset.front()) + "\"";
1168 s += "/>";
1169 global_first.pop_front();
1170 global_num.pop_front();
1171 local_offset.pop_front();
1172 local_num.pop_front();
1173 return s;
1174 }
1175
1176 template<typename DT_>
1177 static void alloc_idx_vector(LAFEM::DenseVectorBlocked<IT_, IT_, bs_>& idx_vec,
1178 const LAFEM::DenseVectorBlocked<DT_, IT_, bs_>& tmpl_vec, IT_ value)
1179 {
1180 idx_vec = LAFEM::DenseVectorBlocked<IT_, IT_, bs_>(tmpl_vec.size(), value);
1181 }
1182
1183 template<typename DT_>
1184 static void update_owners(const IT_ neighbor_rank,
1185 LAFEM::DenseVectorBlocked<IT_, IT_, bs_>& dof_owners,
1186 const LAFEM::VectorMirror<DT_, IT_>& mirror)
1187 {
1188 const Index num_indices = mirror.num_indices();
1189 const IT_* mir_idx = mirror.indices();
1190 auto* dof_own = dof_owners.elements(); // dof_own is a Tiny::Vector<IT_,...>*
1191 for(Index j(0); j < num_indices; ++j)
1192 {
1193 auto& dof_owner = dof_own[mir_idx[j]];
1194 if(neighbor_rank < dof_owner[0])
1195 {
1196 for(int k = 0; k < bs_; ++k)
1197 dof_owner[k] = neighbor_rank;
1198 }
1199 }
1200 }
1201
1202 static Index build_owned_dofs(const IT_ my_rank,
1203 LAFEM::DenseVectorBlocked<IT_, IT_, bs_>& owned_dofs,
1204 const LAFEM::DenseVectorBlocked<IT_, IT_, bs_>& dof_owners, Index offset)
1205 {
1206 const Index n = dof_owners.size();
1207 const auto* dof_own = dof_owners.elements();
1208 auto* own_idx = owned_dofs.elements();
1209
1210 Index k = 0;
1211 for(Index i = 0; i < n; ++i)
1212 {
1213 if(dof_own[i][0] == my_rank)
1214 {
1215 for(int j = 0; j < bs_; ++j)
1216 own_idx[i][j] = IT_(offset + k++);
1217 }
1218 }
1219
1220 return k;
1221 }
1222
1223 template<typename DT_>
1224 static Index build_owned_mirror(const IT_ owner_rank,
1225 LAFEM::VectorMirror<DT_, IT_>& owner_mirror,
1226 const LAFEM::DenseVectorBlocked<IT_, IT_, bs_>& dof_owners)
1227 {
1228 // count the number of owned DOFs
1229 const Index n = dof_owners.size();
1230 const auto* dof_own = dof_owners.elements();
1231 Index num_owned = 0u;
1232 for(Index i = 0; i < n; ++i)
1233 {
1234 if(dof_own[i][0] == owner_rank)
1235 ++num_owned;
1236 }
1237
1238 // allocate mirror
1239 owner_mirror = LAFEM::VectorMirror<DT_, IT_>(n, num_owned);
1240 IT_* mir_idx = owner_mirror.indices();
1241
1242 // store owned DOF indices
1243 for(Index i = 0, k = 0; i < n; ++i)
1244 {
1245 if(dof_own[i][0] == owner_rank)
1246 {
1247 mir_idx[k++] = IT_(i);
1248 }
1249 }
1250
1251 return num_owned * Index(bs_);
1252 }
1253
1254 template<typename DT_>
1255 static Index build_owner_mirror(const IT_ neighbor_rank,
1256 LAFEM::VectorMirror<DT_, IT_>& owner_mirror,
1257 const LAFEM::VectorMirror<DT_, IT_>& halo_mirror,
1258 const LAFEM::DenseVectorBlocked<IT_, IT_, bs_>& dof_owners)
1259 {
1260 // get halo mirror indices
1261 const Index n = halo_mirror.num_indices();
1262 const IT_* halo_idx = halo_mirror.indices();
1263 const auto* dof_own = dof_owners.elements();
1264
1265 // count number of owner dofs
1266 Index num_owner = 0u;
1267 for(Index i = 0; i < n; ++i)
1268 {
1269 if(dof_own[halo_idx[i]][0] == neighbor_rank)
1270 ++num_owner;
1271 }
1272
1273 // allocate mirror
1274 owner_mirror = LAFEM::VectorMirror<DT_, IT_>(dof_owners.size(), num_owner);
1275
1276 if(num_owner <= Index(0))
1277 return Index(0);
1278
1279 // store owner DOF indices
1280 IT_* own_idx = owner_mirror.indices();
1281 for(IT_ i = 0, k = 0; i < n; ++i)
1282 {
1283 if(dof_own[halo_idx[i]][0] == neighbor_rank)
1284 {
1285 own_idx[k++] = halo_idx[i];
1286 }
1287 }
1288
1289 return num_owner * Index(bs_);
1290 }
1291
1292 template<typename DT_>
1293 static Index count_donee_dofs(
1294 const LAFEM::VectorMirror<DT_, IT_>& halo_mirror,
1295 const LAFEM::DenseVectorBlocked<IT_, IT_, bs_>& own_dof_idx)
1296 {
1297 // get halo mirror indices
1298 const Index n = halo_mirror.num_indices();
1299 const IT_* halo_idx = halo_mirror.indices();
1300 const auto* own_idx = own_dof_idx.elements();
1301
1302 // count number of owner dofs
1303 Index num_donee = 0u;
1304 for(Index i = 0; i < n; ++i)
1305 {
1306 if(own_idx[halo_idx[i]][0] != ~IT_(0))
1307 num_donee += Index(bs_);
1308 }
1309
1310 return num_donee;
1311 }
1312
1313 template<typename DT_>
1314 static Index build_donee_mirror(
1315 LAFEM::VectorMirror<DT_, IT_>& donee_mirror,
1316 const LAFEM::VectorMirror<DT_, IT_>& halo_mirror,
1317 const LAFEM::DenseVectorBlocked<IT_, IT_, bs_>& own_dof_idx, Index offset)
1318 {
1319 // get halo mirror indices
1320 const Index n = halo_mirror.num_indices();
1321 const IT_* halo_idx = halo_mirror.indices();
1322 const auto* own_idx = own_dof_idx.elements();
1323 IT_* donee_idx = donee_mirror.indices();
1324
1325 // store donee DOF indices
1326 Index k = 0u;
1327 for(Index i = 0; i < n; ++i)
1328 {
1329 if(own_idx[halo_idx[i]][0] != ~IT_(0))
1330 {
1331 for(int j = 0; j < bs_; ++j)
1332 donee_idx[offset + k++] = own_idx[halo_idx[i]][j];
1333 }
1334 }
1335
1336 return k;
1337 }
1338
1339 template<typename DT_>
1340 static Index init_global_dof_idx(
1341 LAFEM::DenseVectorBlocked<IT_, IT_, bs_>& global_dof_idx,
1342 const LAFEM::VectorMirror<DT_, IT_>& owned_mirror, Index offset)
1343 {
1344 auto* g_dof_idx = global_dof_idx.elements();
1345 const IT_* own_idx = owned_mirror.indices();
1346 const Index n = owned_mirror.num_indices();
1347 for(Index i = 0; i < n; ++i)
1348 {
1349 for(int j = 0; j < bs_; ++j)
1350 g_dof_idx[own_idx[i]][j] = IT_(offset + i*Index(bs_) + Index(j));
1351 }
1352 return n * Index(bs_);
1353 }
1354
1355 template<typename DT_>
1356 static Index set_owner_dofs(
1357 LAFEM::DenseVectorBlocked<IT_, IT_, bs_>& glob_dof_idx,
1358 const std::vector<IT_>& recv_buf,
1359 const LAFEM::VectorMirror<DT_, IT_>& mirror, Index offset)
1360 {
1361 const Index num_indices = mirror.num_indices();
1362 const IT_* mir_idx = mirror.indices();
1363 auto* g_dof_idx = glob_dof_idx.elements();
1364 for(Index i(0); i < num_indices; ++i)
1365 {
1366 for(int j = 0; j < bs_; ++j)
1367 g_dof_idx[mir_idx[i]][j] = recv_buf[offset + i*IT_(bs_) + IT_(j)];
1368 }
1369 return num_indices * Index(bs_);
1370 }
1371
1372 template<typename DT_, typename DT2_>
1373 static Index gather(DT2_* buf,
1374 const LAFEM::DenseVectorBlocked<DT_, IT_, bs_>& vector,
1375 const LAFEM::VectorMirror<DT_, IT_>& mirror)
1376 {
1377 XASSERT(mirror.size() == vector.size());
1378 const Index n = mirror.num_indices();
1379 const IT_* idx = mirror.indices();
1380 const auto* val = vector.elements();
1381 for(Index i = 0u, k = 0u; i < n; ++i)
1382 {
1383 for(int j = 0; j < bs_; ++j, ++k)
1384 buf[k] = DT2_(val[idx[i]][j]);
1385 }
1386 return n * Index(bs_);
1387 }
1388
1389 template<typename DT_, typename DT2_>
1390 static Index scatter(const DT2_* buf,
1391 LAFEM::DenseVectorBlocked<DT_, IT_, bs_>& vector,
1392 const LAFEM::VectorMirror<DT_, IT_>& mirror)
1393 {
1394 XASSERT(mirror.size() == vector.size());
1395 const Index n = mirror.num_indices();
1396 const IT_* idx = mirror.indices();
1397 auto* val = vector.elements();
1398 for(Index i = 0u, k = 0u; i < n; ++i)
1399 {
1400 for(int j = 0; j < bs_; ++j, ++k)
1401 val[idx[i]][j] = DT_(buf[k]);
1402 }
1403 return n * Index(bs_);
1404 }
1405 }; // class ADPAux<LAFEM::DenseVectorBlocked<IT_, IT_, bs_>>
1406
1408
1409 template<typename FirstIdxVec_, typename... RestIdxVec_>
1410 class ADPAux<LAFEM::TupleVector<FirstIdxVec_, RestIdxVec_...>>
1411 {
1412 public:
1413 typedef typename FirstIdxVec_::IndexType IT_;
1414 typedef ADPAux<FirstIdxVec_> ADPAuxFirst;
1415 typedef ADPAux<LAFEM::TupleVector<RestIdxVec_...>> ADPAuxRest;
1416
1417 template<typename FirstMirror_, typename... RestMirror_>
1418 static Index get_local_dof_count_raw(std::vector<Index>& v,
1419 const LAFEM::TupleVector<FirstIdxVec_, RestIdxVec_...>& global_dof_idx,
1420 const LAFEM::TupleMirror<FirstMirror_, RestMirror_...>& owned_mirror)
1421 {
1422 Index n1 = ADPAuxFirst::get_local_dof_count(v, global_dof_idx.first(), owned_mirror.first());
1423 Index n2 = ADPAuxRest::get_local_dof_count_raw(v, global_dof_idx.rest(), owned_mirror.rest());
1424 return n1 + n2;
1425 }
1426
1427 template<
1428 typename FirstMirror_, typename... RestMirror_>
1429 static Index get_local_dof_count(std::vector<Index>& v,
1430 const LAFEM::TupleVector<FirstIdxVec_, RestIdxVec_...>& global_dof_idx,
1431 const LAFEM::TupleMirror<FirstMirror_, RestMirror_...>& owned_mirror)
1432 {
1433 v.push_back(get_local_dof_count_raw(v, global_dof_idx, owned_mirror));
1434 return v.back();
1435 }
1436
1437 static String build_block_info_raw(
1438 const LAFEM::TupleVector<FirstIdxVec_, RestIdxVec_...>& global_dof_idx, Index global_offset,
1439 std::deque<Index>& global_first, std::deque<Index>& global_num,
1440 std::deque<Index>& local_offset, std::deque<Index>& local_num)
1441 {
1442 String s = ADPAuxFirst::build_block_info(global_dof_idx.first(), global_offset, global_first, global_num, local_offset, local_num);
1443 s += "\n";
1444 s += ADPAuxRest::build_block_info_raw(global_dof_idx.rest(), global_offset, global_first, global_num, local_offset, local_num);
1445 return s;
1446 }
1447
1448 static String build_block_info(
1449 const LAFEM::TupleVector<FirstIdxVec_, RestIdxVec_...>& global_dof_idx, Index global_offset,
1450 std::deque<Index>& global_first, std::deque<Index>& global_num,
1451 std::deque<Index>& local_offset, std::deque<Index>& local_num)
1452 {
1453 // offsets need to be processed BEFORE the call to '_build_block_info_raw' because
1454 // the offsets of the tuple coincide with the offsets of the first tuple entry
1455 String slo = stringify(local_offset.front());
1456 String sgo = stringify(global_offset + local_offset.front());
1457 String sraw = build_block_info_raw(global_dof_idx, global_offset, global_first, global_num, local_offset, local_num);
1458 String s = "<Tuple";
1459 // counts and first need to be processed AFTER the call to '_build_block_info_raw' because
1460 // these correspond to the sums of the corresponding values of all the tuple entries
1461 s += " gc=\"" + stringify(global_num.front()) + "\"";
1462 s += " gf=\"" + stringify(global_first.front()) + "\"";
1463 s += " go=\"" + sgo + "\"";
1464 s += " lc=\"" + stringify(local_num.front()) + "\"";
1465 s += " lo=\"" + slo + "\">\n";
1466 s += sraw;
1467 s += "\n</Tuple>";
1468 global_first.pop_front();
1469 global_num.pop_front();
1470 local_offset.pop_front();
1471 local_num.pop_front();
1472 return s;
1473 }
1474
1475 template<typename FirstTmplVector_, typename... RestTmplVector_>
1476 static void alloc_idx_vector(LAFEM::TupleVector<FirstIdxVec_, RestIdxVec_...>& idx_vec,
1477 const LAFEM::TupleVector<FirstTmplVector_, RestTmplVector_...>& tmpl_vec, IT_ value)
1478 {
1479 ADPAuxFirst::alloc_idx_vector(idx_vec.first(), tmpl_vec.first(), value);
1480 ADPAuxRest::alloc_idx_vector(idx_vec.rest(), tmpl_vec.rest(), value);
1481 }
1482
1483 template<typename FirstMirror_, typename... RestMirror_>
1484 static void update_owners(const IT_ neighbor_rank,
1485 LAFEM::TupleVector<FirstIdxVec_, RestIdxVec_...>& dof_owners,
1486 const LAFEM::TupleMirror<FirstMirror_, RestMirror_...>& mirror)
1487 {
1488 ADPAuxFirst::update_owners(neighbor_rank, dof_owners.first(), mirror.first());
1489 ADPAuxRest::update_owners(neighbor_rank, dof_owners.rest(), mirror.rest());
1490 }
1491
1492 static Index build_owned_dofs(const IT_ my_rank,
1493 LAFEM::TupleVector<FirstIdxVec_, RestIdxVec_...>& owned_dofs,
1494 const LAFEM::TupleVector<FirstIdxVec_, RestIdxVec_...>& dof_owners, Index offset)
1495 {
1496 Index n1 = ADPAuxFirst::build_owned_dofs(my_rank, owned_dofs.first(), dof_owners.first(), offset);
1497 Index n2 = ADPAuxRest::build_owned_dofs(my_rank, owned_dofs.rest(), dof_owners.rest(), offset + n1);
1498 return n1 + n2;
1499 }
1500
1501 template<typename FirstMirror_, typename... RestMirror_>
1502 static Index build_owned_mirror(const IT_ owner_rank,
1503 LAFEM::TupleMirror<FirstMirror_, RestMirror_...>& owner_mirror,
1504 const LAFEM::TupleVector<FirstIdxVec_, RestIdxVec_...>& dof_owners)
1505 {
1506 Index n1 = ADPAuxFirst::build_owned_mirror(owner_rank, owner_mirror.first(), dof_owners.first());
1507 Index n2 = ADPAuxRest::build_owned_mirror(owner_rank, owner_mirror.rest(), dof_owners.rest());
1508 return n1 + n2;
1509 }
1510
1511 template<typename FirstMirror_, typename... RestMirror_>
1512 static Index build_owner_mirror(const IT_ neighbor_rank,
1513 LAFEM::TupleMirror<FirstMirror_, RestMirror_...>& owner_mirror,
1514 const LAFEM::TupleMirror<FirstMirror_, RestMirror_...>& halo_mirror,
1515 const LAFEM::TupleVector<FirstIdxVec_, RestIdxVec_...>& dof_owners)
1516 {
1517 Index n1 = ADPAuxFirst::build_owner_mirror(neighbor_rank, owner_mirror.first(), halo_mirror.first(), dof_owners.first());
1518 Index n2 = ADPAuxRest::build_owner_mirror(neighbor_rank, owner_mirror.rest(), halo_mirror.rest(), dof_owners.rest());
1519 return n1 + n2;
1520 }
1521
1522 template<typename FirstMirror_, typename... RestMirror_>
1523 static Index count_donee_dofs(
1524 const LAFEM::TupleMirror<FirstMirror_, RestMirror_...>& halo_mirror,
1525 const LAFEM::TupleVector<FirstIdxVec_, RestIdxVec_...>& own_dof_idx)
1526 {
1527 Index n1 = ADPAuxFirst::count_donee_dofs(halo_mirror.first(), own_dof_idx.first());
1528 Index n2 = ADPAuxRest::count_donee_dofs(halo_mirror.rest(), own_dof_idx.rest());
1529 return n1 + n2;
1530 }
1531
1532 template<typename DT_, typename FirstMirror_, typename... RestMirror_>
1533 static Index build_donee_mirror(
1534 LAFEM::VectorMirror<DT_, IT_>& donee_mirror,
1535 const LAFEM::TupleMirror<FirstMirror_, RestMirror_...>& halo_mirror,
1536 const LAFEM::TupleVector<FirstIdxVec_, RestIdxVec_...>& own_dof_idx, Index offset)
1537 {
1538 Index n1 = ADPAuxFirst::build_donee_mirror(donee_mirror, halo_mirror.first(), own_dof_idx.first(), offset);
1539 Index n2 = ADPAuxRest::build_donee_mirror(donee_mirror, halo_mirror.rest(), own_dof_idx.rest(), offset + n1);
1540 return n1 + n2;
1541 }
1542
1543 template<typename FirstMirror_, typename... RestMirror_>
1544 static Index init_global_dof_idx(
1545 LAFEM::TupleVector<FirstIdxVec_, RestIdxVec_...>& global_dof_idx,
1546 const LAFEM::TupleMirror<FirstMirror_, RestMirror_...>& owned_mirror, Index offset)
1547 {
1548 Index n1 = ADPAuxFirst::init_global_dof_idx(global_dof_idx.first(), owned_mirror.first(), offset);
1549 Index n2 = ADPAuxRest::init_global_dof_idx(global_dof_idx.rest(), owned_mirror.rest(), offset + n1);
1550 return n1 + n2;
1551 }
1552
1553 template<typename FirstMirror_, typename... RestMirror_>
1554 static Index set_owner_dofs(
1555 LAFEM::TupleVector<FirstIdxVec_, RestIdxVec_...>& glob_dof_idx,
1556 const std::vector<IT_>& recv_buf,
1557 const LAFEM::TupleMirror<FirstMirror_, RestMirror_...>& mirror, Index offset)
1558 {
1559 Index n1 = ADPAuxFirst::set_owner_dofs(glob_dof_idx.first(), recv_buf, mirror.first(), offset);
1560 Index n2 = ADPAuxRest::set_owner_dofs(glob_dof_idx.rest(), recv_buf, mirror.rest(), offset + n1);
1561 return n1 + n2;
1562 }
1563
1564 template<typename DT2_,
1565 typename FirstVector_, typename... RestVector_,
1566 typename FirstMirror_, typename... RestMirror_>
1567 static Index gather(DT2_* buf,
1568 const LAFEM::TupleVector<FirstVector_, RestVector_...>& vector,
1569 const LAFEM::TupleMirror<FirstMirror_, RestMirror_...>& mirror)
1570 {
1571 Index n1 = ADPAuxFirst::gather(buf, vector.first(), mirror.first());
1572 Index n2 = ADPAuxRest::gather(&buf[n1], vector.rest(), mirror.rest());
1573 return n1 + n2;
1574 }
1575
1576 template<typename DT2_,
1577 typename FirstVector_, typename... RestVector_,
1578 typename FirstMirror_, typename... RestMirror_>
1579 static Index scatter(const DT2_* buf,
1580 LAFEM::TupleVector<FirstVector_, RestVector_...>& vector,
1581 const LAFEM::TupleMirror<FirstMirror_, RestMirror_...>& mirror)
1582 {
1583 Index n1 = ADPAuxFirst::scatter(buf, vector.first(), mirror.first());
1584 Index n2 = ADPAuxRest::scatter(&buf[n1], vector.rest(), mirror.rest());
1585 return n1 + n2;
1586 }
1587 }; // class ADPAux<LAFEM::TupleVector<FirstIdxVec_, RestIdxVec_...>>
1588
1590
1591 template<typename FirstIdxVec_>
1592 class ADPAux<LAFEM::TupleVector<FirstIdxVec_>>
1593 {
1594 public:
1595 typedef typename FirstIdxVec_::IndexType IT_;
1596 typedef ADPAux<FirstIdxVec_> ADPAuxFirst;
1597
1598 template<typename FirstMirror_>
1599 static Index get_local_dof_count_raw(std::vector<Index>& v,
1600 const LAFEM::TupleVector<FirstIdxVec_>& global_dof_idx,
1601 const LAFEM::TupleMirror<FirstMirror_>& owned_mirror)
1602 {
1603 return ADPAuxFirst::get_local_dof_count(v, global_dof_idx.first(), owned_mirror.first());
1604 }
1605
1606 template<typename FirstMirror_>
1607 static Index get_local_dof_count(std::vector<Index>& v,
1608 const LAFEM::TupleVector<FirstIdxVec_>& global_dof_idx,
1609 const LAFEM::TupleMirror<FirstMirror_>& owned_mirror)
1610 {
1611 v.push_back(get_local_dof_count_raw(v, global_dof_idx, owned_mirror));
1612 return v.back();
1613 }
1614
1615 static String build_block_info_raw(
1616 const LAFEM::TupleVector<FirstIdxVec_>& global_dof_idx, Index global_offset,
1617 std::deque<Index>& global_first, std::deque<Index>& global_num,
1618 std::deque<Index>& local_offset, std::deque<Index>& local_num)
1619 {
1620 return ADPAuxFirst::build_block_info(global_dof_idx.first(), global_offset, global_first, global_num, local_offset, local_num);
1621 }
1622
1623 static String build_block_info(
1624 const LAFEM::TupleVector<FirstIdxVec_>& global_dof_idx, Index global_offset,
1625 std::deque<Index>& global_first, std::deque<Index>& global_num,
1626 std::deque<Index>& local_offset, std::deque<Index>& local_num)
1627 {
1628 // offsets need to be processed BEFORE the call to '_build_block_info_raw' because
1629 // the offsets of the tuple coincide with the offsets of the first tuple entry
1630 String slo = stringify(local_offset.front());
1631 String sgo = stringify(global_offset + local_offset.front());
1632 String sraw = build_block_info_raw(global_dof_idx, global_offset, global_first, global_num, local_offset, local_num);
1633 String s = "<Tuple";
1634 // counts and first need to be processed AFTER the call to '_build_block_info_raw' because
1635 // these correspond to the sums of the corresponding values of all the tuple entries
1636 s += " gc=\"" + stringify(global_num.front()) + "\"";
1637 s += " gf=\"" + stringify(global_first.front()) + "\"";
1638 s += " go=\"" + sgo + "\"";
1639 s += " lc=\"" + stringify(local_num.front()) + "\"";
1640 s += " lo=\"" + slo + "\">\n";
1641 s += sraw;
1642 s += "\n</Tuple>";
1643 global_first.pop_front();
1644 global_num.pop_front();
1645 local_offset.pop_front();
1646 local_num.pop_front();
1647 return s;
1648 }
1649
1650 template<typename FirstTmplVector_>
1651 static void alloc_idx_vector(LAFEM::TupleVector<FirstIdxVec_>& idx_vec,
1652 const LAFEM::TupleVector<FirstTmplVector_>& tmpl_vec, IT_ value)
1653 {
1654 ADPAuxFirst::alloc_idx_vector(idx_vec.first(), tmpl_vec.first(), value);
1655 }
1656
1657 template<typename FirstMirror_>
1658 static void update_owners(const IT_ neighbor_rank,
1659 LAFEM::TupleVector<FirstIdxVec_>& dof_owners,
1660 const LAFEM::TupleMirror<FirstMirror_>& mirror)
1661 {
1662 ADPAuxFirst::update_owners(neighbor_rank, dof_owners.first(), mirror.first());
1663 }
1664
1665 static Index build_owned_dofs(const IT_ my_rank,
1666 LAFEM::TupleVector<FirstIdxVec_>& owned_dofs,
1667 const LAFEM::TupleVector<FirstIdxVec_>& dof_owners, Index offset)
1668 {
1669 return ADPAuxFirst::build_owned_dofs(my_rank, owned_dofs.first(), dof_owners.first(), offset);
1670 }
1671
1672 template<typename FirstMirror_>
1673 static Index build_owned_mirror(const IT_ owner_rank,
1674 LAFEM::TupleMirror<FirstMirror_>& owner_mirror,
1675 const LAFEM::TupleVector<FirstIdxVec_>& dof_owners)
1676 {
1677 return ADPAuxFirst::build_owned_mirror(owner_rank, owner_mirror.first(), dof_owners.first());
1678 }
1679
1680 template<typename FirstMirror_>
1681 static Index build_owner_mirror(const IT_ neighbor_rank,
1682 LAFEM::TupleMirror<FirstMirror_>& owner_mirror,
1683 const LAFEM::TupleMirror<FirstMirror_>& halo_mirror,
1684 const LAFEM::TupleVector<FirstIdxVec_>& dof_owners)
1685 {
1686 return ADPAuxFirst::build_owner_mirror(neighbor_rank, owner_mirror.first(), halo_mirror.first(), dof_owners.first());
1687 }
1688
1689 template<typename FirstMirror_>
1690 static Index count_donee_dofs(
1691 const LAFEM::TupleMirror<FirstMirror_>& halo_mirror,
1692 const LAFEM::TupleVector<FirstIdxVec_>& own_dof_idx)
1693 {
1694 return ADPAuxFirst::count_donee_dofs(halo_mirror.first(), own_dof_idx.first());
1695 }
1696
1697 template<typename DT_, typename FirstMirror_>
1698 static Index build_donee_mirror(
1699 LAFEM::VectorMirror<DT_, IT_>& donee_mirror,
1700 const LAFEM::TupleMirror<FirstMirror_>& halo_mirror,
1701 const LAFEM::TupleVector<FirstIdxVec_>& own_dof_idx, Index offset)
1702 {
1703 return ADPAuxFirst::build_donee_mirror(donee_mirror, halo_mirror.first(), own_dof_idx.first(), offset);
1704 }
1705
1706 template<typename FirstMirror_>
1707 static Index init_global_dof_idx(
1708 LAFEM::TupleVector<FirstIdxVec_>& global_dof_idx,
1709 const LAFEM::TupleMirror<FirstMirror_>& owned_mirror, Index offset)
1710 {
1711 return ADPAuxFirst::init_global_dof_idx(global_dof_idx.first(), owned_mirror.first(), offset);
1712 }
1713
1714 template<typename FirstMirror_>
1715 static Index set_owner_dofs(
1716 LAFEM::TupleVector<FirstIdxVec_>& glob_dof_idx,
1717 const std::vector<IT_>& recv_buf,
1718 const LAFEM::TupleMirror<FirstMirror_>& mirror, Index offset)
1719 {
1720 return ADPAuxFirst::set_owner_dofs(glob_dof_idx.first(), recv_buf, mirror.first(), offset);
1721 }
1722
1723 template<typename DT2_, typename FirstVector_, typename FirstMirror_>
1724 static Index gather(DT2_* buf,
1725 const LAFEM::TupleVector<FirstVector_>& vector,
1726 const LAFEM::TupleMirror<FirstMirror_>& mirror)
1727 {
1728 return ADPAuxFirst::gather(buf, vector.first(), mirror.first());
1729 }
1730
1731 template<typename DT2_, typename FirstVector_, typename FirstMirror_>
1732 static Index scatter(const DT2_* buf,
1733 LAFEM::TupleVector<FirstVector_>& vector,
1734 const LAFEM::TupleMirror<FirstMirror_>& mirror)
1735 {
1736 return ADPAuxFirst::scatter(buf, vector.first(), mirror.first());
1737 }
1738 }; // class ADPAux<LAFEM::TupleVector<FirstIdxVec_>>
1739 } // namespace Intern
1741 } // namespace Global
1742} // namespace FEAT
#define XASSERT(expr)
Assertion macro definition.
Definition: assertion.hpp:262
#define XASSERTM(expr, msg)
Assertion macro definition with custom message.
Definition: assertion.hpp:263
Communicator class.
Definition: dist.hpp:1349
void bcast(void *buffer, std::size_t count, const Datatype &datatype, int root) const
Blocking broadcast.
Definition: dist.cpp:541
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 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
Algebraic DOF Partitioning implementation.
LAFEM::DenseVector< DataType, IndexType > BufferVectorType
the type for buffers used by our mirrors
Index _extra_shared_dof_count
number of extra shared dofs; must be equal on all processes
Mirror_ MirrorType
the vector mirror type
String get_block_information() const
Returns the block information of the algebraic dof partitioning as an XML string.
Index _local_dof_count
number of local dofs of this process
void clear()
Resets the whole object.
const std::vector< int > & get_all_global_dof_offsets() const
Returns the all-global-dof-offsets vector.
MirrorType OwnerMirrorType
type of mirror for neighbor owner local dof indices
int get_donee_rank(Index i) const
Returns an donee neighbor rank.
Index get_num_local_dofs() const
Index _extra_local_dof_count
number of extra local dofs; may be different for each process
void assemble_by_gate(const GateType &gate, Index extra_local=0u, Index extra_shared=0u)
Assembles the AlgDofParti object from a given Global::Gate.
std::vector< DoneeMirrorType > _donee_mirrors
rank/mirror-pair of DOF-donee processes
Index _global_dof_offset
global dof offset of this process
Global::Vector< LocalVectorType, MirrorType > GlobalVectorType
the global vector type
LocalVector_ LocalVectorType
the local vector type
Index _global_dof_count
global dof count over all processes
std::vector< int > _owner_ranks
ranks of DOF-owner neighbor processes
Index get_global_dof_offset() const
AlgDofParti()
default constructor
std::vector< int > _all_global_dof_offset
global DOF offsets of all processes,
MirrorType _owned_mirror
mirror for this process's owned DOFs
const std::vector< int > & get_all_global_dof_counts() const
Returns the all-global-dof-counts vector.
void assemble_allgather(bool yes_i_really_want_to_do_this=false)
Assembles the required data for the AlgDofPartiSystem::apply() function.
std::vector< int > _all_global_dof_counts
global DOF counts of all processes,
MirrorType OwnedMirrorType
type of mirror for owned local dof indices
Intern::ADPAux< IndexVectorType > ADPAuxType
auxiliary helper class type
const OwnedMirrorType & get_owned_mirror() const
Returns the Mirror of the owned DOFs.
Index _extra_dof_offset
offset of first extra DOF
const OwnerMirrorType & get_owner_mirror(Index i) const
Returns an owner neighbor mirror.
virtual ~AlgDofParti()=default
virtual destructor
LocalVectorType::template ContainerTypeByDI< IndexType, IndexType > IndexVectorType
the index vector type, this one is required for internal computations
const IndexVectorType & get_global_dof_indices() const
Returns the global-dof-indices array.
std::vector< int > _donee_ranks
ranks of DOF-donee neighbor processes
LocalVectorType::DataType DataType
our data type
AlgDofParti(const AlgDofParti &)=delete
no copy, no problems
Index _owned_dof_count
number of owned dofs of this process
int get_owner_rank(Index i) const
Returns an owner neighbor rank.
String _block_information
a string containing the block information in XML format
std::size_t bytes() const
const Dist::Comm * get_comm() const
Index get_num_owner_neighbors() const
LAFEM::VectorMirror< DataType, IndexType > DoneeMirrorType
type of mirror for neighbor donee owned dof indices
Index get_num_donee_neighbors() const
const DoneeMirrorType & get_donee_mirror(Index i) const
Returns a donee neighbor mirror.
Index get_num_global_dofs() const
Index get_num_owned_dofs() const
Global::Gate< LocalVectorType, MirrorType > GateType
the global gate type
IndexVectorType _global_dof_idx
global dof indices for each local dof
const Dist::Comm * _comm
our communicator
LocalVectorType::IndexType IndexType
our index type
AlgDofParti & operator=(const AlgDofParti &)=delete
no copy, no problems
std::vector< OwnerMirrorType > _owner_mirrors
rank/mirror-pair of DOF-owner processes
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
const LocalVector_ & get_freqs() const
Returns a const reference to the frequencies vector.
Definition: gate.hpp:179
const std::vector< Mirror_ > & get_mirrors() const
Returns a const reference to the neighbor mirrors vector.
Definition: gate.hpp:169
std::vector< int > _ranks
communication ranks
Definition: gate.hpp:71
Global vector wrapper class template.
Definition: vector.hpp:68
Dense data vector class template.
Handles vector prolongation, restriction and serialization.
String class implementation.
Definition: string.hpp:47
void pop_front()
Removes the first character from the string.
Definition: string.hpp:240
const Operation op_sum(MPI_SUM)
Operation wrapper for MPI_SUM.
Definition: dist.hpp:271
FEAT namespace.
Definition: adjactor.hpp:12
String stringify(const T_ &item)
Converts an item into a String.
Definition: string.hpp:993
std::uint64_t Index
Index data type.