Line | Branch | Exec | Source |
---|---|---|---|
1 | // -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- | ||
2 | // vi: set et ts=4 sw=4 sts=4: | ||
3 | // | ||
4 | // SPDX-FileCopyrightText: Copyright © DuMux Project contributors, see AUTHORS.md in root folder | ||
5 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
6 | // | ||
7 | /*! | ||
8 | * \file | ||
9 | * \ingroup InputOutput | ||
10 | * \brief A data handle for communicating grid data for VTK grids | ||
11 | */ | ||
12 | #ifndef DUMUX_VTK_GRID_DATA_HANDLE_HH | ||
13 | #define DUMUX_VTK_GRID_DATA_HANDLE_HH | ||
14 | |||
15 | #include <memory> | ||
16 | #include <algorithm> | ||
17 | #include <map> | ||
18 | #include <numeric> | ||
19 | #include <array> | ||
20 | #include <vector> | ||
21 | #include <string> | ||
22 | #include <unordered_map> | ||
23 | #include <utility> | ||
24 | #include <iostream> | ||
25 | #include <ranges> | ||
26 | |||
27 | #include <dune/common/parallel/communication.hh> | ||
28 | #include <dune/common/parallel/future.hh> | ||
29 | #include <dune/geometry/dimension.hh> | ||
30 | #include <dune/grid/common/partitionset.hh> | ||
31 | #include <dune/grid/common/datahandleif.hh> | ||
32 | |||
33 | #include <dumux/io/vtk/vtkreader.hh> | ||
34 | |||
35 | namespace Dumux { | ||
36 | |||
37 | /*! | ||
38 | * \ingroup InputOutput | ||
39 | * \brief A data handle for communicating grid data for VTK grids | ||
40 | */ | ||
41 | template<class Grid, class GridInput, class Data> | ||
42 | struct VtkGridDataHandle | ||
43 | : public Dune::CommDataHandleIF<VtkGridDataHandle<Grid, GridInput, Data>, typename Data::value_type> | ||
44 | { | ||
45 | using GridView = typename Grid::LevelGridView; | ||
46 | |||
47 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
|
4 | VtkGridDataHandle(const Grid& grid, const GridInput& gridInput, VTKReader::Data& cellData, VTKReader::Data& pointData) |
48 | 4 | : gridView_(grid.levelGridView(0)) | |
49 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
|
4 | , idSet_(grid.localIdSet()) |
50 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
|
4 | , userCellData_(cellData) |
51 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
|
4 | , userPointData_(pointData) |
52 | { | ||
53 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
|
4 | const auto rank = grid.comm().rank(); |
54 | |||
55 | // For the following to work we assume a sorted map of keys to values in the user data. | ||
56 | // This is not guaranteed by the VTKReader, so we need to sort the data first. | ||
57 |
3/4✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 4 times.
|
8 | for (const auto& [key, data] : userCellData_) |
58 |
2/4✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
|
2 | cellData_[key] = std::move(userCellData_[key]); |
59 |
3/4✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 4 times.
|
8 | for (const auto& [key, data] : userPointData_) |
60 |
2/4✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
|
2 | pointData_[key] = std::move(userPointData_[key]); |
61 | |||
62 | #if DUMUX_HAVE_GRIDFORMAT | ||
63 | // compute the number of components for each cell and point data array | ||
64 | // and the serialization size per entity | ||
65 | std::array<std::size_t, 2> numKeys{{ cellData_.size(), pointData_.size() }}; | ||
66 | std::vector<std::size_t> keyComponents(numKeys[0] + numKeys[1], 0); | ||
67 | { | ||
68 | int n = 0; | ||
69 | for (const auto& [key, data] : cellData_) | ||
70 | keyComponents[n++] = rank == 0 ? data.size()/gridInput.numElements() : 0; | ||
71 | for (const auto& [key, data] : pointData_) | ||
72 | keyComponents[n++] = rank == 0 ? data.size()/gridInput.numVertices() : 0; | ||
73 | grid.comm().broadcast(keyComponents.data(), keyComponents.size(), 0); | ||
74 | } | ||
75 | |||
76 | const auto begin = keyComponents.begin(); | ||
77 | cellDataComponents_.assign(begin, begin + numKeys[0]); | ||
78 | pointDataComponents_.assign(begin + numKeys[0], keyComponents.end()); | ||
79 | numCellDataPerElement_ = std::accumulate(cellDataComponents_.begin(), cellDataComponents_.end(), 0UL); | ||
80 | numPointDataPerVertex_ = std::accumulate(pointDataComponents_.begin(), pointDataComponents_.end(), 0UL); | ||
81 | #else | ||
82 | // assume all data is on rank 0 (see grid manager) | ||
83 | // First broadcast how many keys we have | ||
84 |
1/3✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
4 | std::array<std::size_t, 2> numKeys{{ cellData_.size(), pointData_.size() }}; |
85 |
3/8✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
✓ Branch 3 taken 4 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 4 times.
✗ Branch 7 not taken.
✗ Branch 2 not taken.
✗ Branch 5 not taken.
|
4 | grid.comm().broadcast(numKeys.data(), 2, 0); |
86 | |||
87 | // then broadcast the length of the individual key strings | ||
88 | // and the number of component associated with each key (e.g. vector/tensor fields) | ||
89 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
4 | std::vector<std::size_t> keyLengthAndComponents(2*(numKeys[0] + numKeys[1]), 0); |
90 | { | ||
91 | 4 | int n = 0; | |
92 | |||
93 | // key length | ||
94 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 4 times.
|
6 | for (const auto& [key, data] : cellData_) |
95 | 2 | keyLengthAndComponents[n++] = key.size(); | |
96 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 4 times.
|
6 | for (const auto& [key, data] : pointData_) |
97 | 2 | keyLengthAndComponents[n++] = key.size(); | |
98 | |||
99 | // number of components | ||
100 |
3/4✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 4 times.
|
6 | for (const auto& [key, data] : cellData_) |
101 |
1/2✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
|
2 | keyLengthAndComponents[n++] = rank == 0 ? data.size()/gridInput.numElements() : 0; |
102 |
3/4✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 4 times.
|
6 | for (const auto& [key, data] : pointData_) |
103 |
1/2✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
|
2 | keyLengthAndComponents[n++] = rank == 0 ? data.size()/gridInput.numVertices() : 0; |
104 | |||
105 |
3/8✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
✓ Branch 3 taken 4 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 4 times.
✗ Branch 7 not taken.
✗ Branch 2 not taken.
✗ Branch 5 not taken.
|
4 | grid.comm().broadcast(keyLengthAndComponents.data(), keyLengthAndComponents.size(), 0); |
106 | } | ||
107 | |||
108 | // save the number of components for each cell and point data array | ||
109 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
4 | const auto begin = keyLengthAndComponents.begin() + numKeys[0] + numKeys[1]; |
110 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
4 | cellDataComponents_.assign(begin, begin + numKeys[0]); |
111 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
4 | pointDataComponents_.assign(begin + numKeys[0], keyLengthAndComponents.end()); |
112 | 4 | numCellDataPerElement_ = std::accumulate(cellDataComponents_.begin(), cellDataComponents_.end(), 0UL); | |
113 | 8 | numPointDataPerVertex_ = std::accumulate(pointDataComponents_.begin(), pointDataComponents_.end(), 0UL); | |
114 | |||
115 | // then broadcast the actual keys | ||
116 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
8 | std::string keys; keys.resize(std::accumulate(keyLengthAndComponents.begin(), begin, 0UL)); |
117 | { | ||
118 | 4 | int n = 0; | |
119 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 4 times.
|
6 | for (const auto& [key, data] : cellData_) |
120 |
2/2✓ Branch 0 taken 26 times.
✓ Branch 1 taken 2 times.
|
28 | for (const auto& c : key) |
121 | 26 | keys[n++] = c; | |
122 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 4 times.
|
6 | for (const auto& [key, data] : pointData_) |
123 |
2/2✓ Branch 0 taken 20 times.
✓ Branch 1 taken 2 times.
|
22 | for (const auto& c : key) |
124 | 20 | keys[n++] = c; | |
125 | |||
126 |
2/5✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
✓ Branch 3 taken 4 times.
✗ Branch 4 not taken.
✗ Branch 2 not taken.
|
4 | grid.comm().broadcast(keys.data(), keys.size(), 0); |
127 | } | ||
128 | |||
129 | // create the entries in the cellData and pointData maps on all processes | ||
130 | ✗ | std::size_t offset = 0; | |
131 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 4 times.
|
8 | for (int keyIdx = 0; keyIdx < numKeys[0]; ++keyIdx) |
132 | { | ||
133 |
3/4✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 2 times.
|
4 | if (std::string key{ keys, offset, keyLengthAndComponents[keyIdx] }; cellData_.count(key) == 0) |
134 |
2/4✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 2 times.
|
2 | cellData_[key] = Data{}; |
135 | |||
136 | 4 | offset += keyLengthAndComponents[keyIdx]; | |
137 | } | ||
138 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 4 times.
|
8 | for (int keyIdx = numKeys[0]; keyIdx < numKeys[0] + numKeys[1]; ++keyIdx) |
139 | { | ||
140 |
3/4✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 2 times.
|
4 | if (std::string key{ keys, offset, keyLengthAndComponents[keyIdx] }; pointData_.count(key) == 0) |
141 |
2/4✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 2 times.
|
2 | pointData_[key] = Data{}; |
142 | |||
143 | 4 | offset += keyLengthAndComponents[keyIdx]; | |
144 | } | ||
145 | #endif | ||
146 | |||
147 | // write data into an id map | ||
148 |
6/12✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 4 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 5088 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 5088 times.
✓ Branch 13 taken 4 times.
✗ Branch 3 not taken.
✗ Branch 6 not taken.
|
10192 | for (const auto& element : elements(gridView_, Dune::Partitions::interior)) |
149 | { | ||
150 |
3/6✓ Branch 1 taken 5088 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5088 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 5088 times.
✗ Branch 8 not taken.
|
5088 | data_[idSet_.id(element)].resize(numCellDataPerElement_); |
151 | |||
152 | 5088 | int n = 0, l = 0; | |
153 |
2/2✓ Branch 0 taken 5088 times.
✓ Branch 1 taken 5088 times.
|
10176 | for (const auto& [key, data] : cellData_) |
154 | { | ||
155 | 5088 | const auto nComp = cellDataComponents_[l++]; | |
156 |
2/2✓ Branch 0 taken 5088 times.
✓ Branch 1 taken 5088 times.
|
10176 | for (int k = 0; k < nComp; ++k) |
157 |
4/8✓ Branch 1 taken 5088 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5088 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 5088 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 5088 times.
✗ Branch 11 not taken.
|
5088 | std::swap(cellData_[key][k + nComp*gridInput.insertionIndex(element)], data_[idSet_.id(element)][n++]); |
158 | } | ||
159 | |||
160 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 5088 times.
|
5088 | assert(n == numCellDataPerElement_); |
161 | } | ||
162 | |||
163 |
6/12✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 4 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 2650 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 4 times.
✓ Branch 13 taken 2650 times.
✗ Branch 3 not taken.
✗ Branch 6 not taken.
|
5324 | for (const auto& vertex : vertices(gridView_)) |
164 | { | ||
165 |
3/6✓ Branch 1 taken 2650 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2650 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 2650 times.
✗ Branch 8 not taken.
|
2650 | data_[idSet_.id(vertex)].resize(numPointDataPerVertex_); |
166 | |||
167 | 2650 | int n = 0, l = 0; | |
168 |
2/2✓ Branch 0 taken 2650 times.
✓ Branch 1 taken 2650 times.
|
5300 | for (const auto& [key, data] : pointData_) |
169 | { | ||
170 | 2650 | const auto nComp = pointDataComponents_[l++]; | |
171 |
2/2✓ Branch 0 taken 2650 times.
✓ Branch 1 taken 2650 times.
|
5300 | for (int k = 0; k < nComp; ++k) |
172 |
4/8✓ Branch 1 taken 2650 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2650 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 2650 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 2650 times.
✗ Branch 11 not taken.
|
2650 | std::swap(pointData_[key][k + nComp*gridInput.insertionIndex(vertex)], data_[idSet_.id(vertex)][n++]); |
173 | } | ||
174 | |||
175 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2650 times.
|
2650 | assert(n == numPointDataPerVertex_); |
176 | } | ||
177 | 4 | } | |
178 | |||
179 | 4 | ~VtkGridDataHandle() | |
180 | { | ||
181 | // resize arrays and unpack communicated data | ||
182 | 4 | const auto& indexSet = gridView_.indexSet(); | |
183 | |||
184 | { | ||
185 | 4 | int n = 0; | |
186 |
2/2✓ Branch 1 taken 4 times.
✓ Branch 2 taken 4 times.
|
8 | for (const auto& [key, data] : cellData_) |
187 | 4 | cellData_[key].resize(indexSet.size(0)*cellDataComponents_[n++]); | |
188 | } | ||
189 | { | ||
190 | 4 | int n = 0; | |
191 |
2/2✓ Branch 1 taken 4 times.
✓ Branch 2 taken 4 times.
|
8 | for (const auto& [key, data] : pointData_) |
192 | 4 | pointData_[key].resize(indexSet.size(GridView::dimension)*pointDataComponents_[n++]); | |
193 | } | ||
194 | |||
195 |
2/5✓ Branch 4 taken 5088 times.
✓ Branch 5 taken 4 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
10188 | for (const auto& element : elements(gridView_)) |
196 | { | ||
197 | 5088 | int n = 0, l = 0; | |
198 |
2/2✓ Branch 0 taken 5088 times.
✓ Branch 1 taken 5088 times.
|
10176 | for (const auto& [key, data] : cellData_) |
199 | { | ||
200 | 5088 | const auto nComp = cellDataComponents_[l++]; | |
201 |
2/2✓ Branch 0 taken 5088 times.
✓ Branch 1 taken 5088 times.
|
10176 | for (int k = 0; k < nComp; ++k) |
202 | 5088 | std::swap(cellData_[key][k + nComp*indexSet.index(element)], data_[idSet_.id(element)][n++]); | |
203 | } | ||
204 | } | ||
205 | |||
206 |
2/5✓ Branch 4 taken 4 times.
✓ Branch 5 taken 2796 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
5604 | for (const auto& vertex : vertices(gridView_)) |
207 | { | ||
208 | 2796 | int n = 0, l = 0; | |
209 |
2/2✓ Branch 0 taken 2796 times.
✓ Branch 1 taken 2796 times.
|
5592 | for (const auto& [key, data] : pointData_) |
210 | { | ||
211 | 2796 | const auto nComp = pointDataComponents_[l++]; | |
212 |
2/2✓ Branch 0 taken 2796 times.
✓ Branch 1 taken 2796 times.
|
5592 | for (int k = 0; k < nComp; ++k) |
213 | 2796 | std::swap(pointData_[key][k + nComp*indexSet.index(vertex)], data_[idSet_.id(vertex)][n++]); | |
214 | } | ||
215 | } | ||
216 | |||
217 | // move data back from sorted internal storage to user data | ||
218 |
2/2✓ Branch 1 taken 4 times.
✓ Branch 2 taken 4 times.
|
8 | for (const auto& [key, data] : cellData_) |
219 | 4 | userCellData_[key] = std::move(cellData_[key]); | |
220 |
2/2✓ Branch 1 taken 4 times.
✓ Branch 2 taken 4 times.
|
8 | for (const auto& [key, data] : pointData_) |
221 | 4 | userPointData_[key] = std::move(pointData_[key]); | |
222 | 4 | } | |
223 | |||
224 | Dune::CommDataHandleIF<VtkGridDataHandle<Grid, GridInput, Data>, typename Data::value_type>& interface() | ||
225 | { return *this; } | ||
226 | |||
227 | 12 | bool contains (int dim, int codim) const | |
228 | 12 | { return codim == 0 || codim == dim; } | |
229 | |||
230 | //! returns true if size per entity of given dim and codim is a constant | ||
231 | bool fixedSize(int dim, int codim) const | ||
232 | { return true; } | ||
233 | |||
234 | template<class Entity> | ||
235 | 10468 | std::size_t size (const Entity&) const | |
236 | { | ||
237 | if constexpr (Entity::codimension == 0) | ||
238 |
0/2✗ Branch 0 not taken.
✗ Branch 1 not taken.
|
2544 | return numCellDataPerElement_; |
239 | else if constexpr (Entity::codimension == GridView::dimension) | ||
240 |
0/2✗ Branch 0 not taken.
✗ Branch 1 not taken.
|
7924 | return numPointDataPerVertex_; |
241 | else | ||
242 | return 0; | ||
243 | } | ||
244 | |||
245 | template<class MessageBufferImp, class Entity> | ||
246 | 20936 | void gather (MessageBufferImp& buff, const Entity& e) const | |
247 | { | ||
248 | if constexpr (Entity::codimension == 0) | ||
249 | { | ||
250 | 5088 | const auto& data = data_[idSet_.id(e)]; | |
251 |
2/2✓ Branch 0 taken 2544 times.
✓ Branch 1 taken 2544 times.
|
10176 | for (int n = 0; n < numCellDataPerElement_; ++n) |
252 | 5088 | buff.write(data[n]); | |
253 | } | ||
254 | |||
255 | if constexpr (Entity::codimension == GridView::dimension) | ||
256 | { | ||
257 | 15848 | const auto& data = data_[idSet_.id(e)]; | |
258 |
2/2✓ Branch 0 taken 7924 times.
✓ Branch 1 taken 7924 times.
|
31696 | for (int n = 0; n < numPointDataPerVertex_; ++n) |
259 | 15848 | buff.write(data[n]); | |
260 | } | ||
261 | 20936 | } | |
262 | |||
263 | template<class MessageBufferImp, class Entity> | ||
264 | 20936 | void scatter (MessageBufferImp& buff, const Entity& e, std::size_t n) | |
265 | { | ||
266 | 20936 | auto& data = data_[idSet_.id(e)]; | |
267 | 20936 | data.resize(n); | |
268 | |||
269 | if constexpr (Entity::codimension == 0) | ||
270 |
2/2✓ Branch 1 taken 2544 times.
✓ Branch 2 taken 2544 times.
|
10176 | for (int k = 0; k < numCellDataPerElement_; ++k) |
271 | 5088 | buff.read(data[k]); | |
272 | |||
273 | if constexpr (Entity::codimension == GridView::dimension) | ||
274 |
2/2✓ Branch 1 taken 7924 times.
✓ Branch 2 taken 7924 times.
|
31696 | for (int k = 0; k < numPointDataPerVertex_; ++k) |
275 | 15848 | buff.read(data[k]); | |
276 | 20936 | } | |
277 | |||
278 | private: | ||
279 | using IdSet = typename Grid::LocalIdSet; | ||
280 | |||
281 | const GridView gridView_; | ||
282 | const IdSet &idSet_; | ||
283 | |||
284 | VTKReader::Data& userCellData_; | ||
285 | VTKReader::Data& userPointData_; | ||
286 | |||
287 | std::map<std::string, VTKReader::Data::mapped_type> cellData_; | ||
288 | std::map<std::string, VTKReader::Data::mapped_type> pointData_; | ||
289 | |||
290 | std::vector<std::size_t> cellDataComponents_; | ||
291 | std::vector<std::size_t> pointDataComponents_; | ||
292 | |||
293 | std::size_t numCellDataPerElement_; | ||
294 | std::size_t numPointDataPerVertex_; | ||
295 | |||
296 | mutable std::map< typename IdSet::IdType, std::vector<typename Data::value_type> > data_; | ||
297 | }; | ||
298 | |||
299 | } // namespace Dumux | ||
300 | |||
301 | namespace Dumux::Detail::VtkData { | ||
302 | |||
303 | // for structured vtk data, we manually distribute the data to the other ranks | ||
304 | template<class Grid, class GridInput> | ||
305 | ✗ | void communicateStructuredVtkData(const Grid& grid, const GridInput& gridInput, ::Dumux::VTKReader::Data& cellData, ::Dumux::VTKReader::Data& pointData) | |
306 | { | ||
307 | #if HAVE_MPI // needed due to oversight in Dune::Communication interface | ||
308 | ✗ | const auto commSize = grid.comm().size(); | |
309 | ✗ | if (commSize <= 1) | |
310 | ✗ | return; | |
311 | |||
312 | ✗ | const auto rank = grid.comm().rank(); | |
313 | |||
314 | #ifndef NDEBUG | ||
315 | ✗ | if (rank == 0) | |
316 | { | ||
317 | ✗ | std::cout << "Communicating structured VTK data...\n\n" << std::endl; | |
318 | ✗ | std::cout << "Grid has " << gridInput.numElements() << " elements and " << gridInput.numVertices() << " vertices." << std::endl; | |
319 | } | ||
320 | #endif | ||
321 | |||
322 | // first some preliminary steps | ||
323 | // we need to sort the data | ||
324 | ✗ | std::map<std::string, ::Dumux::VTKReader::Data::mapped_type> sortedCellData, sortedPointData; | |
325 | ✗ | for (const auto& [key, data] : cellData) | |
326 | ✗ | sortedCellData[key] = std::move(cellData[key]); | |
327 | ✗ | for (const auto& [key, data] : pointData) | |
328 | ✗ | sortedPointData[key] = std::move(pointData[key]); | |
329 | |||
330 | // and we need to compute the number of components for each cell and point data array | ||
331 | // to know the message sizes | ||
332 | ✗ | std::array<std::size_t, 2> numKeys{{ sortedCellData.size(), sortedPointData.size() }}; | |
333 | ✗ | std::vector<std::size_t> keyComponents(numKeys[0] + numKeys[1], 0); | |
334 | { | ||
335 | ✗ | int n = 0; | |
336 | ✗ | for (const auto& [key, data] : sortedCellData) | |
337 | ✗ | keyComponents[n++] = rank == 0 ? data.size()/gridInput.numElements() : 0; | |
338 | ✗ | for (const auto& [key, data] : sortedPointData) | |
339 | ✗ | keyComponents[n++] = rank == 0 ? data.size()/gridInput.numVertices() : 0; | |
340 | ✗ | grid.comm().broadcast(keyComponents.data(), keyComponents.size(), 0); | |
341 | } | ||
342 | |||
343 | ✗ | const auto begin = keyComponents.begin(); | |
344 | ✗ | const std::size_t numCellDataPerElement = std::accumulate(begin, begin + numKeys[0], 0UL); | |
345 | ✗ | const std::size_t numPointDataPerVertex = std::accumulate(begin + numKeys[0], keyComponents.end(), 0UL); | |
346 | |||
347 | #ifndef NDEBUG | ||
348 | ✗ | std::cout << "Rank " << rank << ": numbers of components for each cell and point data array: " | |
349 | ✗ | << numCellDataPerElement << " " << numPointDataPerVertex << std::endl; | |
350 | #endif | ||
351 | |||
352 | // each process knows: | ||
353 | // - total number of cells and vertices | ||
354 | // - its data indices for each cell and vertex (global numbering) | ||
355 | // process 0 has all the data | ||
356 | |||
357 | // each rank decides which data they need | ||
358 | ✗ | const auto& gridView = grid.levelGridView(0); | |
359 | ✗ | std::vector<std::size_t> requestedData(gridView.size(0) + gridView.size(Grid::dimension)); | |
360 | ✗ | const std::size_t numElements = gridView.size(0); | |
361 | ✗ | const std::size_t numVertices = gridView.size(Grid::dimension); | |
362 | ✗ | for (const auto& element : elements(gridView)) | |
363 | { | ||
364 | ✗ | requestedData[gridView.indexSet().index(element)] = gridInput.insertionIndex(element); | |
365 | ✗ | for (const auto& vertex : subEntities(element, Dune::Codim<Grid::dimension>{})) | |
366 | ✗ | requestedData[numElements + gridView.indexSet().index(vertex)] = gridInput.insertionIndex(vertex); | |
367 | } | ||
368 | |||
369 | // gather the sizes of the data on rank 0 | ||
370 | ✗ | std::vector<std::size_t> numData_; | |
371 | ✗ | if (rank == 0) | |
372 | ✗ | numData_.resize(2*commSize); | |
373 | ✗ | std::array<std::size_t, 2> localNumData{{ numElements, numVertices }}; | |
374 | ✗ | grid.comm().gather(localNumData.data(), numData_.data(), 2, 0); | |
375 | |||
376 | #ifndef NDEBUG | ||
377 | ✗ | if (rank == 0) | |
378 | { | ||
379 | ✗ | std::cout << "Number of elements and vertices on each rank: "; | |
380 | ✗ | for (std::size_t i = 0; i < commSize; ++i) | |
381 | ✗ | std::cout << numData_[2*i] << " " << numData_[2*i + 1] << " "; | |
382 | ✗ | std::cout << std::endl; | |
383 | } | ||
384 | #endif | ||
385 | |||
386 | // send the data request to rank 0 | ||
387 | using RequestData = std::vector<std::size_t>; | ||
388 | using FutureIndices = Dune::Future<RequestData>; | ||
389 | ✗ | std::unique_ptr<FutureIndices> sendRequest; | |
390 | if (rank != 0) | ||
391 | ✗ | sendRequest = std::make_unique<FutureIndices>( | |
392 | ✗ | std::move(grid.comm().isend(std::move(requestedData), 0, /*tag*/0)) | |
393 | ); | ||
394 | |||
395 | // receive the data on rank 0 | ||
396 | ✗ | std::vector<RequestData> requestedDataAll(commSize); | |
397 | ✗ | if (rank == 0) | |
398 | { | ||
399 | ✗ | std::vector<std::unique_ptr<FutureIndices>> receiveRequests(commSize-1); | |
400 | ✗ | for (std::size_t i = 0; i < commSize; ++i) | |
401 | { | ||
402 | ✗ | requestedDataAll[i].resize(numData_[2*i] + numData_[2*i + 1]); | |
403 | |||
404 | ✗ | if (i == 0) | |
405 | ✗ | requestedDataAll[i] = std::move(requestedData); | |
406 | else | ||
407 | { | ||
408 | ✗ | receiveRequests[i-1] = std::make_unique<FutureIndices>( | |
409 | ✗ | std::move(grid.comm().irecv(requestedDataAll[i], i, /*tag*/0)) | |
410 | ); | ||
411 | } | ||
412 | } | ||
413 | |||
414 | /// TODO: actually we want to call MPI_Waitall here: how to do this with the futures? | ||
415 | ✗ | std::ranges::for_each(receiveRequests, [](auto& request) { request->wait(); }); | |
416 | |||
417 | #ifndef NDEBUG | ||
418 | ✗ | std::cout << "Received data from all ranks." << std::endl; | |
419 | ✗ | for (std::size_t i = 0; i < commSize; ++i) | |
420 | ✗ | std::cout << "From rank " << i << ": " << requestedDataAll[i].size() << " index size." << std::endl; | |
421 | #endif | ||
422 | ✗ | } | |
423 | |||
424 | // Now we need to pack the data and send it to the other ranks | ||
425 | using PackedData = std::vector<double>; | ||
426 | ✗ | PackedData receivedFlatData(localNumData[0]*numCellDataPerElement + localNumData[1]*numPointDataPerVertex); | |
427 | ✗ | if (rank == 0) | |
428 | { | ||
429 | using FutureData = Dune::Future<PackedData>; | ||
430 | ✗ | std::vector<std::unique_ptr<FutureData>> sendRequests(commSize-1); | |
431 | ✗ | for (std::size_t i = 0; i < commSize; ++i) | |
432 | { | ||
433 | ✗ | const auto& requestedIndices = requestedDataAll[i]; | |
434 | ✗ | PackedData packedData(numData_[2*i]*numCellDataPerElement + numData_[2*i + 1]*numPointDataPerVertex); | |
435 | |||
436 | // pack the data | ||
437 | ✗ | std::size_t n = 0, l = 0; | |
438 | ✗ | for (const auto& [key, data] : sortedCellData) | |
439 | { | ||
440 | ✗ | const auto nComp = keyComponents[l++]; | |
441 | ✗ | for (std::size_t k = 0; k < numData_[2*i]; ++k) | |
442 | { | ||
443 | ✗ | const auto idx = requestedIndices[k]; | |
444 | ✗ | for (std::size_t j = 0; j < nComp; ++j) | |
445 | ✗ | packedData[n++] = data[j + nComp*idx]; | |
446 | } | ||
447 | } | ||
448 | |||
449 | ✗ | const auto pointDataOffsett = numData_[2*i]; | |
450 | ✗ | for (const auto& [key, data] : sortedPointData) | |
451 | { | ||
452 | ✗ | const auto nComp = keyComponents[l++]; | |
453 | ✗ | for (std::size_t k = 0; k < numData_[2*i + 1]; ++k) | |
454 | { | ||
455 | ✗ | const auto idx = requestedIndices[k + pointDataOffsett]; | |
456 | ✗ | for (std::size_t j = 0; j < nComp; ++j) | |
457 | ✗ | packedData[n++] = data[j + nComp*idx]; | |
458 | } | ||
459 | } | ||
460 | |||
461 | ✗ | if (n != packedData.size()) | |
462 | ✗ | DUNE_THROW(Dune::InvalidStateException, "Packed data size does not match expected size."); | |
463 | |||
464 | ✗ | if (i == 0) | |
465 | ✗ | receivedFlatData = std::move(packedData); | |
466 | else | ||
467 | { | ||
468 | // send the data to rank i | ||
469 | ✗ | sendRequests[i-1] = std::make_unique<FutureData>( | |
470 | ✗ | std::move(grid.comm().isend(std::move(packedData), i, /*tag*/1)) | |
471 | ); | ||
472 | } | ||
473 | } | ||
474 | |||
475 | /// TODO: actually we want to call MPI_Waitall here: how to do this with the futures? | ||
476 | ✗ | std::ranges::for_each(sendRequests, [](auto& request) { request->wait(); }); | |
477 | ✗ | } | |
478 | else | ||
479 | { | ||
480 | // receive the data from rank 0 | ||
481 | ✗ | auto receiveRequest = grid.comm().irecv(receivedFlatData, 0, /*tag*/1); | |
482 | |||
483 | // finalize all communication | ||
484 | ✗ | sendRequest->wait(); | |
485 | ✗ | receiveRequest.wait(); | |
486 | ✗ | } | |
487 | |||
488 | #ifndef NDEBUG | ||
489 | ✗ | std::cout << "On rank " << rank << ", the received data size is " << receivedFlatData.size() << std::endl; | |
490 | #endif | ||
491 | |||
492 | // unpack the data on each process into cellData and pointData | ||
493 | ✗ | std::size_t n = 0, l = 0; | |
494 | ✗ | for (const auto& [key, data] : sortedCellData) | |
495 | { | ||
496 | ✗ | auto& out = cellData[key]; | |
497 | ✗ | const auto nComp = keyComponents[l++]; | |
498 | ✗ | out.resize(localNumData[0]*nComp); | |
499 | ✗ | for (std::size_t k = 0; k < localNumData[0]; ++k) | |
500 | ✗ | for (std::size_t j = 0; j < nComp; ++j) | |
501 | ✗ | out[j + nComp*k] = receivedFlatData[n++]; | |
502 | } | ||
503 | |||
504 | ✗ | for (const auto& [key, data] : sortedPointData) | |
505 | { | ||
506 | ✗ | auto& out = pointData[key]; | |
507 | ✗ | const auto nComp = keyComponents[l++]; | |
508 | ✗ | out.resize(localNumData[1]*nComp); | |
509 | ✗ | for (std::size_t k = 0; k < localNumData[1]; ++k) | |
510 | ✗ | for (std::size_t j = 0; j < nComp; ++j) | |
511 | ✗ | out[j + nComp*k] = receivedFlatData[n++]; | |
512 | } | ||
513 | |||
514 | ✗ | if (n != receivedFlatData.size()) | |
515 | ✗ | DUNE_THROW(Dune::InvalidStateException, "Unpacked data size does not match expected size."); | |
516 | |||
517 | #ifndef NDEBUG | ||
518 | ✗ | if (rank == 0) | |
519 | ✗ | std::cout << "\n\n+++++++++++++++++++++++++++++++++++++++++++++" << std::endl; | |
520 | #endif | ||
521 | #endif | ||
522 | ✗ | } | |
523 | |||
524 | } // end namespace Dumux::Detail::VtkData | ||
525 | |||
526 | #endif | ||
527 |