1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
#pragma once

#include <algorithm>
#include <array>
#include <bitset>
#include <cmath>
#include <cstddef>
#include <optional>
#include <stdexcept>
#include <vector>

#include <vtkRectilinearGrid.h>
#include <vtkSmartPointer.h>
#include <vtkType.h>
#include <vtkUnsignedCharArray.h>

#include "../Math/Vector.h"
#include "GridTypes.h"

class vtkMPIController;

namespace VofFlow {
    class DomainInfo {
    public:
        struct NeighborInfo {
            int processId = 0;
            std::bitset<6> flags;
            extent_t zeroExtent{};
            bounds_t zeroBounds{};
        };

        explicit DomainInfo(vtkRectilinearGrid* grid, vtkMPIController* mpiController = nullptr);

        [[nodiscard]] inline vtkRectilinearGrid* grid() const {
            return grid_;
        }

        [[nodiscard]] inline const extent_t& localExtent() const {
            return localExtent_;
        }

        [[nodiscard]] inline const extent_t& localZeroExtent() const {
            return localZeroExtent_;
        }

        [[nodiscard]] inline const extent_t& globalExtent() const {
            return globalExtent_;
        }

        [[nodiscard]] inline const bounds_t& localBounds() const {
            return localBounds_;
        }

        [[nodiscard]] inline const bounds_t& localZeroBounds() const {
            return localZeroBounds_;
        }

        [[nodiscard]] inline const bounds_t& globalBounds() const {
            return globalBounds_;
        }

        [[nodiscard]] inline const dim_t& cellDims() const {
            return dims_;
        }

        [[nodiscard]] inline bool isUniform() const {
            return isUniform_;
        }

        [[nodiscard]] inline bool isParallel() const {
            return isParallel_;
        }

        [[nodiscard]] inline const vtkSmartPointer<vtkUnsignedCharArray>& ghostArray() const {
            return ghostArray_;
        }

        [[nodiscard]] inline const std::vector<double>& coordsX() const {
            return coordsX_;
        }

        [[nodiscard]] inline const std::vector<double>& coordsY() const {
            return coordsY_;
        }

        [[nodiscard]] inline const std::vector<double>& coordsZ() const {
            return coordsZ_;
        }

        [[nodiscard]] inline const std::vector<double>& cellSizesX() const {
            return cellSizesX_;
        }

        [[nodiscard]] inline const std::vector<double>& cellSizesY() const {
            return cellSizesY_;
        }

        [[nodiscard]] inline const std::vector<double>& cellSizesZ() const {
            return cellSizesZ_;
        }

        [[nodiscard]] inline const std::vector<double>& cellCentersX() const {
            return cellCentersX_;
        }

        [[nodiscard]] inline const std::vector<double>& cellCentersY() const {
            return cellCentersY_;
        }

        [[nodiscard]] inline const std::vector<double>& cellCentersZ() const {
            return cellCentersZ_;
        }

        [[nodiscard]] inline const std::vector<NeighborInfo>& neighbors() const {
            return neighbors_;
        }

        [[nodiscard]] inline const std::array<std::vector<int>, 6>& neighborsByDirection() const {
            return neighborsByDirection_;
        }

        [[nodiscard]] inline vtkIdType numCells() const {
            return static_cast<vtkIdType>(dims_[0]) * static_cast<vtkIdType>(dims_[1]) *
                   static_cast<vtkIdType>(dims_[2]);
        }

        [[nodiscard]] inline posCoords_t coords(int x, int y, int z) const {
            return {coordsX_[x], coordsY_[y], coordsZ_[z]};
        }

        [[nodiscard]] inline posCoords_t coords(gridCoords_t g_coords) const {
            return coords(g_coords[0], g_coords[1], g_coords[2]);
        }

        [[nodiscard]] inline vec3 coordsVec3(int x, int y, int z) const {
            return {
                static_cast<float>(coordsX_[x]),
                static_cast<float>(coordsY_[y]),
                static_cast<float>(coordsZ_[z]),
            };
        }

        [[nodiscard]] inline vec3 coordsVec3(gridCoords_t g_coords) const {
            return coordsVec3(g_coords[0], g_coords[1], g_coords[2]);
        }

        [[nodiscard]] inline posCoords_t cellSize(int g_x, int g_y, int g_z) const {
            return {cellSizesX_[g_x], cellSizesY_[g_y], cellSizesZ_[g_z]};
        }

        [[nodiscard]] inline posCoords_t cellSize(gridCoords_t g_coords) const {
            return cellSize(g_coords[0], g_coords[1], g_coords[2]);
        }

        [[nodiscard]] inline vec3 cellSizeVec3(int g_x, int g_y, int g_z) const {
            return {
                static_cast<float>(cellSizesX_[g_x]),
                static_cast<float>(cellSizesY_[g_y]),
                static_cast<float>(cellSizesZ_[g_z]),
            };
        }

        [[nodiscard]] inline vec3 cellSizeVec3(gridCoords_t g_coords) const {
            return cellSizeVec3(g_coords[0], g_coords[1], g_coords[2]);
        }

        [[nodiscard]] inline posCoords_t cellCenter(int g_x, int g_y, int g_z) const {
            return {cellCentersX_[g_x], cellCentersY_[g_y], cellCentersZ_[g_z]};
        }

        [[nodiscard]] inline posCoords_t cellCenter(gridCoords_t g_coords) const {
            return cellCenter(g_coords[0], g_coords[1], g_coords[2]);
        }

        [[nodiscard]] inline vec3 cellCenterVec3(int g_x, int g_y, int g_z) const {
            return {
                static_cast<float>(cellCentersX_[g_x]),
                static_cast<float>(cellCentersY_[g_y]),
                static_cast<float>(cellCentersZ_[g_z]),
            };
        }

        [[nodiscard]] inline vec3 cellCenterVec3(gridCoords_t g_coords) const {
            return cellCenterVec3(g_coords[0], g_coords[1], g_coords[2]);
        }

        [[nodiscard]] inline bool isInLocalExtent(const gridCoords_t& e_coords) const {
            return Grid::isInExtent(localExtent_, e_coords);
        }

        [[nodiscard]] inline bool isInLocalExtent(int e_x, int e_y, int e_z) const {
            return isInLocalExtent(gridCoords_t{e_x, e_y, e_z});
        }

        [[nodiscard]] inline bool isInZeroExtent(const gridCoords_t& e_coords) const {
            return Grid::isInExtent(localZeroExtent_, e_coords);
        }

        [[nodiscard]] inline bool isInZeroExtent(int e_x, int e_y, int e_z) const {
            return isInZeroExtent(gridCoords_t{e_x, e_y, e_z});
        }

        [[nodiscard]] inline bool isInZeroExtent(vtkIdType idx) const {
            return isInZeroExtent(idxToExtentCoord(idx));
        }

        [[nodiscard]] inline vtkIdType gridCoordToIdx(int g_x, int g_y, int g_z) const {
            return static_cast<vtkIdType>(g_x) + static_cast<vtkIdType>(g_y) * static_cast<vtkIdType>(dims_[0]) +
                   static_cast<vtkIdType>(g_z) * static_cast<vtkIdType>(dims_[0]) * static_cast<vtkIdType>(dims_[1]);
        }

        [[nodiscard]] inline vtkIdType gridCoordToIdx(const gridCoords_t& g_coords) const {
            return gridCoordToIdx(g_coords[0], g_coords[1], g_coords[2]);
        }

        [[nodiscard]] inline gridCoords_t idxToGridCoord(vtkIdType idx) const {
            const int g_x = static_cast<int>(idx % dims_[0]);
            const int g_y = static_cast<int>((idx / dims_[0]) % dims_[1]);
            const int g_z = static_cast<int>(idx / (dims_[0] * dims_[1]));
            return {g_x, g_y, g_z};
        }

        [[nodiscard]] inline vtkIdType localExtentCoordToIdx(int e_x, int e_y, int e_z) const {
            if (!isInLocalExtent(e_x, e_y, e_z)) {
                throw std::runtime_error("Extent coord out of bounds!");
            }
            return (e_x - localExtent_[0]) + (e_y - localExtent_[2]) * dims_[0] +
                   (e_z - localExtent_[4]) * dims_[0] * dims_[1];
        }

        [[nodiscard]] inline vtkIdType localExtentCoordToIdx(const gridCoords_t& e_coords) const {
            return localExtentCoordToIdx(e_coords[0], e_coords[1], e_coords[2]);
        }

        [[nodiscard]] inline gridCoords_t idxToExtentCoord(vtkIdType idx) const {
            const auto [g_x, g_y, g_z] = idxToGridCoord(idx);
            return {localExtent_[0] + g_x, localExtent_[2] + g_y, localExtent_[4] + g_z};
        }

        [[nodiscard]] inline gridCoords_t localExtentCoordToGridCoord(int e_x, int e_y, int e_z) const {
            if (!isInLocalExtent(e_x, e_y, e_z)) {
                throw std::runtime_error("Extent coord out of bounds!");
            }
            return {e_x - localExtent_[0], e_y - localExtent_[2], e_z - localExtent_[4]};
        }

        [[nodiscard]] inline gridCoords_t localExtentCoordToGridCoord(const gridCoords_t& e_coords) const {
            return localExtentCoordToGridCoord(e_coords[0], e_coords[1], e_coords[2]);
        }

        [[nodiscard]] inline bool isInLocalBounds(const posCoords_t& pos) const {
            return Grid::isInBounds(localBounds_, pos);
        }

        [[nodiscard]] inline bool isInLocalBounds(double x, double y, double z) const {
            return isInLocalBounds(posCoords_t{x, y, z});
        }

        [[nodiscard]] inline bool isInLocalBounds(const vec3& p) const {
            return isInLocalBounds(p.x, p.y, p.z);
        }

        [[nodiscard]] inline bool isInZeroBounds(const posCoords_t& pos) const {
            return Grid::isInBounds(localZeroBounds_, pos);
        }

        [[nodiscard]] inline bool isInZeroBounds(double x, double y, double z) const {
            return isInZeroBounds(posCoords_t{x, y, z});
        }

        [[nodiscard]] inline bool isInZeroBounds(const vec3& p) const {
            return isInZeroBounds(p.x, p.y, p.z);
        }

        [[nodiscard]] inline bool isInGlobalBounds(const posCoords_t& pos) const {
            return Grid::isInBounds(globalBounds_, pos);
        }

        [[nodiscard]] inline bool isInGlobalBounds(double x, double y, double z) const {
            return isInGlobalBounds(posCoords_t{x, y, z});
        }

        [[nodiscard]] inline bool isInGlobalBounds(const vec3& p) const {
            return isInGlobalBounds(p.x, p.y, p.z);
        }

        [[nodiscard]] inline int isOutOfZeroBoundsDirection(const vec3& p) const {
            return Grid::isOutOfBoundsDirection(localZeroBounds_, posCoords_t{p.x, p.y, p.z});
        }

        [[nodiscard]] inline std::optional<StructuredCoordinates> computeStructuredCoordinates(
            const posCoords_t& p) const {
            StructuredCoordinates sc;
            if (computeStructuredCoordinates(coordsX_, p[0], sc.cellCoords[0], sc.relativeCoords[0]) &&
                computeStructuredCoordinates(coordsY_, p[1], sc.cellCoords[1], sc.relativeCoords[1]) &&
                computeStructuredCoordinates(coordsZ_, p[2], sc.cellCoords[2], sc.relativeCoords[2])) {
                return sc;
            }
            return std::nullopt;
        }

        [[nodiscard]] inline std::optional<StructuredCoordinates> computeStructuredCoordinates(const vec3& p) const {
            return computeStructuredCoordinates(posCoords_t{p.x, p.y, p.z});
        }

        [[nodiscard]] inline StructuredCoordinates computeNearestStructuredCoordinates(const posCoords_t& p) const {
            StructuredCoordinates sc;
            computeStructuredCoordinates(coordsX_, p[0], sc.cellCoords[0], sc.relativeCoords[0]);
            computeStructuredCoordinates(coordsY_, p[1], sc.cellCoords[1], sc.relativeCoords[1]);
            computeStructuredCoordinates(coordsZ_, p[2], sc.cellCoords[2], sc.relativeCoords[2]);
            return sc;
        }

        [[nodiscard]] inline StructuredCoordinates computeNearestStructuredCoordinates(const vec3& p) const {
            return computeNearestStructuredCoordinates(posCoords_t{p.x, p.y, p.z});
        }

        [[nodiscard]] inline std::optional<gridCoords_t> posToGridCoord(const posCoords_t& p) const {
            auto coords = computeStructuredCoordinates(p);
            if (coords.has_value()) {
                return coords.value().cellCoords;
            }
            return std::nullopt;
        }

        [[nodiscard]] inline std::optional<gridCoords_t> posToGridCoord(const vec3& p) const {
            return posToGridCoord(posCoords_t{p.x, p.y, p.z});
        }

        [[nodiscard]] inline gridCoords_t posToGridCoordEx(const posCoords_t& p) const {
            const auto cell = posToGridCoord(p);
            if (cell.has_value()) {
                return cell.value();
            }
            throw std::runtime_error("Position is outside of grid bounds!");
        }

        [[nodiscard]] inline gridCoords_t posToGridCoordEx(const vec3& p) const {
            return posToGridCoordEx(posCoords_t{p.x, p.y, p.z});
        }

        [[nodiscard]] inline std::optional<vtkIdType> posToIdx(const posCoords_t& p) const {
            const auto g_coords = posToGridCoord(p);
            if (g_coords.has_value()) {
                return gridCoordToIdx(g_coords.value());
            }
            return std::nullopt;
        }

        [[nodiscard]] inline std::optional<vtkIdType> posToIdx(const vec3& p) const {
            return posToIdx(posCoords_t{p.x, p.y, p.z});
        }

        [[nodiscard]] inline vtkIdType posToIdxEx(const posCoords_t& p) const {
            return gridCoordToIdx(posToGridCoordEx(p));
        }

        [[nodiscard]] inline vtkIdType posToIdxEx(const vec3& p) const {
            return gridCoordToIdx(posToGridCoordEx(p));
        }

    private:
        /**
         * VTK's `ComputeStructuredCoordinates()` is very slow. The two major points are the linear search and the
         * overhead from the GetComponent interface of the arrays. Here, we implement binary search and directly use
         * the std::vector coords.
         * Assumptions: coords is sorted and size >= 2. (Both checked in constructor.)
         */
        inline bool computeStructuredCoordinates(const std::vector<double>& coords, double pos, int& cell,
            double& pcoord) const {
            // If out of bounds return closest point.
            if (pos < coords.front()) {
                cell = 0;
                pcoord = 0.0;
                return false;
            } else if (pos >= coords.back()) {
                cell = static_cast<int>(coords.size()) - 2;
                pcoord = 1.0;
                return false;
            }

            // Shortcut for uniform grids
            if (isUniform_) {
                double relative_pos = (pos - coords.front()) / (coords.back() - coords.front()); // [0, 1)
                relative_pos *= static_cast<double>(coords.size() - 1);                          // [0, num_cells)
                double iptr = 0.0;
                pcoord = std::clamp(std::modf(relative_pos, &iptr), 0.0, 1.0);
                cell = std::clamp(static_cast<int>(iptr), 0, static_cast<int>(coords.size() - 1));
                return true;
            }

            std::size_t left = 0;
            std::size_t right = coords.size() - 1;
            while (left + 1 < right) {
                const std::size_t mid = left + (right - left) / 2;
                if (pos < coords[mid]) {
                    right = mid;
                } else {
                    left = mid;
                }
            }
            cell = static_cast<int>(left);
            pcoord = (pos - coords[left]) / (coords[right] - coords[left]);
            return true;
        }

        vtkSmartPointer<vtkRectilinearGrid> grid_;
        extent_t localExtent_;     // Extent of thread local grid.
        extent_t localZeroExtent_; // Extent of the thread local grid without ghost cells.
        extent_t globalExtent_;    // Extent of the global grid across all threads.
        bounds_t localBounds_;     // Bounds of the thread local grid domain.
        bounds_t localZeroBounds_; // Bounds of the thread local grid domain without ghost cells.
        bounds_t globalBounds_;    // Bounds of the global grid domain across all threads.
        dim_t dims_;
        bool isUniform_;
        bool isParallel_;
        vtkSmartPointer<vtkUnsignedCharArray> ghostArray_;

        std::vector<double> coordsX_;
        std::vector<double> coordsY_;
        std::vector<double> coordsZ_;
        std::vector<double> cellSizesX_;
        std::vector<double> cellSizesY_;
        std::vector<double> cellSizesZ_;
        std::vector<double> cellCentersX_;
        std::vector<double> cellCentersY_;
        std::vector<double> cellCentersZ_;

        std::vector<NeighborInfo> neighbors_;
        std::array<std::vector<int>, 6> neighborsByDirection_; // list neighbors by direction
    };
} // namespace VofFlow