Line data Source code
1 : //
2 : // Class SubFieldLayout
3 : // SubFieldLayout provides a layout for a sub-region of a larger field.
4 : // It ensures that the sub-region is partitioned in the same way as the original FieldLayout,
5 : // maintaining consistent parallel decomposition and neighbor relationships within the sub-region.
6 : //
7 : #include "Ippl.h"
8 :
9 : #include <cstdlib>
10 : #include <limits>
11 :
12 : #include "Utility/IpplException.h"
13 : #include "Utility/IpplTimings.h"
14 : #include "Utility/PAssert.h"
15 :
16 : #include "FieldLayout/SubFieldLayout.h"
17 :
18 : namespace ippl {
19 :
20 : /**
21 : * @copydoc SubFieldLayout::SubFieldLayout(const mpi::Communicator&)
22 : *
23 : * Creates a SubFieldLayout without specifying domains. The layout must be initialized
24 : * later using the initialize() methods. This constructor is useful when you need to
25 : * defer the layout configuration until more information is available.
26 : */
27 : template <unsigned Dim>
28 216 : SubFieldLayout<Dim>::SubFieldLayout(const mpi::Communicator& communicator)
29 216 : : FieldLayout<Dim>(communicator) {}
30 :
31 : /**
32 : * @copydoc SubFieldLayout::SubFieldLayout(mpi::Communicator, const NDIndex<Dim>&, const NDIndex<Dim>&, std::array<bool, Dim>, bool)
33 : *
34 : * Implementation details:
35 : * Initializes both the full domain decomposition and the sub-domain layout. The sub-domain
36 : * must be contained within the full domain. All MPI ranks must have non-empty local domains
37 : * after intersection with the sub-domain, otherwise an exception will be thrown.
38 : *
39 : * This constructor sets up the parallel decomposition based on the full domain, then
40 : * restricts the full domain to the specified sub-region while maintaining the same
41 : * partitioning structure.
42 : */
43 : template <unsigned Dim>
44 552 : SubFieldLayout<Dim>::SubFieldLayout(mpi::Communicator communicator, const NDIndex<Dim>& domain,
45 : const NDIndex<Dim>& subDomain, std::array<bool, Dim> isParallel, bool isAllPeriodic)
46 552 : : FieldLayout<Dim>(communicator) {
47 552 : initialize(domain, subDomain, isParallel, isAllPeriodic);
48 552 : }
49 :
50 : /**
51 : * @copydoc SubFieldLayout::SubFieldLayout(mpi::Communicator, const NDIndex<Dim>&, std::array<bool, Dim>, bool)
52 : *
53 : * Implementation details:
54 : * Creates a SubFieldLayout where the sub-domain is the same as the full domain, making it
55 : * functionally equivalent to a regular FieldLayout.
56 : */
57 : template <unsigned Dim>
58 : SubFieldLayout<Dim>::SubFieldLayout(mpi::Communicator communicator, const NDIndex<Dim>& domain,
59 : std::array<bool, Dim> isParallel, bool isAllPeriodic)
60 : : FieldLayout<Dim>(communicator) {
61 : initialize(domain, isParallel, isAllPeriodic);
62 : }
63 :
64 : /**
65 : * @copydoc SubFieldLayout::initialize(const NDIndex<Dim>&, const NDIndex<Dim>&, std::array<bool, Dim>, bool)
66 : *
67 : * Implementation details:
68 : * This method first partitions the full domain for parallel processing,
69 : * then restricts each rank's local domain to the specified sub-domain.
70 : *
71 : * The sub-domain must be contained within the full domain, and all MPI ranks must have
72 : * non-empty local domains after intersection with the sub-domain, otherwise an exception
73 : * will be thrown.
74 : */
75 : template <unsigned Dim>
76 552 : void SubFieldLayout<Dim>::initialize(const NDIndex<Dim>& domain, const NDIndex<Dim>& subDomain, std::array<bool, Dim> isParallel,
77 : bool isAllPeriodic) {
78 :
79 : // Ensure the sub-domain is contained within the main domain
80 552 : PAssert(domain.contains(subDomain));
81 :
82 : // Call the base class initialize method to set up the main domain and parallel decomposition
83 552 : FieldLayout<Dim>::initialize(domain, isParallel, isAllPeriodic);
84 :
85 552 : unsigned int nRanks = this->comm.size();
86 :
87 552 : originDomain_m = domain;
88 :
89 552 : this->gDomain_m = subDomain;
90 :
91 : // Check if all ranks have a valid local domain that intersects with the sub-domain
92 1104 : if (this->hLocalDomains_m(this->comm.rank()).intersect(subDomain).empty()) {
93 0 : throw std::runtime_error("SubFieldLayout:initialize: given subdomain is not valid, rank"
94 0 : + std::to_string(this->comm.rank()) + " has an empty local domain, choose a sub-domain that has content on all ranks");
95 : }
96 :
97 : // If the local domain is not contained in the sub-domain, change it to the intersection of the local domain and the sub-domain
98 : // This ensures that the sub-field layout is consistent with the original layout
99 1656 : for (unsigned int rank = 0; rank < nRanks; ++rank) {
100 2208 : if (!this->gDomain_m.contains(this->hLocalDomains_m(rank))) {
101 3036 : this->hLocalDomains_m(rank) = this->hLocalDomains_m(rank).intersect(this->gDomain_m);
102 : }
103 : }
104 :
105 552 : this->findNeighbors();
106 :
107 552 : Kokkos::deep_copy(this->dLocalDomains_m, this->hLocalDomains_m);
108 :
109 552 : this->calcWidths();
110 552 : }
111 :
112 : /**
113 : * @copydoc SubFieldLayout::initialize(const NDIndex<Dim>&, std::array<bool, Dim>, bool)
114 : *
115 : * Implementation details:
116 : * This method initializes the layout to use the entire domain as both the full domain
117 : * and the sub-domain, making it equivalent to a regular FieldLayout.
118 : */
119 : template <unsigned Dim>
120 : void SubFieldLayout<Dim>::initialize(const NDIndex<Dim>& domain, std::array<bool, Dim> isParallel,
121 : bool isAllPeriodic) {
122 : // Call the base class initialize method to set up the main domain and parallel decomposition
123 : FieldLayout<Dim>::initialize(domain, isParallel, isAllPeriodic);
124 :
125 : originDomain_m = domain;
126 : }
127 :
128 : } // namespace ippl
|