1 // Copyright(C) 1999-2021 National Technology & Engineering Solutions
2 // of Sandia, LLC (NTESS).  Under the terms of Contract DE-NA0003525 with
3 // NTESS, the U.S. Government retains certain rights in this software.
4 //
5 // See packages/seacas/LICENSE for details
6 
7 #ifndef IOSS_Ioss_StructuredBlock_h
8 #define IOSS_Ioss_StructuredBlock_h
9 
10 #include "vtk_ioss_mangle.h"
11 
12 #include <Ioss_BoundingBox.h>
13 #include <Ioss_CodeTypes.h>
14 #include <Ioss_EntityBlock.h>
15 #include <Ioss_NodeBlock.h>
16 #include <Ioss_Property.h>
17 #include <Ioss_ZoneConnectivity.h>
18 #include <array>
19 #include <cassert>
20 #include <string>
21 
22 #if defined(SEACAS_HAVE_CGNS) && !defined(BUILT_IN_SIERRA)
23 using INT = std::int64_t;
24 #else
25 // If this is not being built with CGNS, then default to using 32-bit integers.
26 // Currently there is no way to input/output a structured mesh without CGNS,
27 // so this block is simply to get things to compile and probably has no use.
28 using INT = int;
29 #endif
30 
31 namespace Ioss {
32   class Region;
33 
34   struct BoundaryCondition
35   {
BoundaryConditionBoundaryCondition36     BoundaryCondition(std::string name, std::string fam_name, Ioss::IJK_t range_beg,
37                       Ioss::IJK_t range_end)
38         : m_bcName(std::move(name)), m_famName(std::move(fam_name)), m_rangeBeg(range_beg),
39           m_rangeEnd(range_end)
40     {
41     }
42 
43     // Deprecated... Use the constructor above with both name and fam_name
BoundaryConditionBoundaryCondition44     BoundaryCondition(std::string name, Ioss::IJK_t range_beg, Ioss::IJK_t range_end)
45         : m_bcName(name), m_famName(std::move(name)), m_rangeBeg(range_beg), m_rangeEnd(range_end)
46     {
47     }
48 
49     // cereal requires a default constructor when de-serializing vectors of objects.  Because
50     // StructuredBlock contains a vector of BoundaryCondition objects, this default constructor is
51     // necessary.
52     BoundaryCondition() = default;
53 
54     BoundaryCondition(const BoundaryCondition &copy_from) = default;
55 
56     // Determine which "face" of the parent block this BC is applied to.
57     int which_face() const;
58 
59     // Does range specify a valid face
60     bool is_valid() const;
61 
62     // Return number of cell faces in the BC
63     size_t get_face_count() const;
64 
65     bool operator==(const Ioss::BoundaryCondition &rhs) const;
66     bool operator!=(const Ioss::BoundaryCondition &rhs) const;
67     bool equal(const Ioss::BoundaryCondition &rhs) const;
68 
69     std::string m_bcName{};
70     std::string m_famName{};
71 
72     // These are potentially subsetted due to parallel decompositions...
73     Ioss::IJK_t m_rangeBeg{};
74     Ioss::IJK_t m_rangeEnd{};
75 
76     mutable int m_face{-1};
77 
serializeBoundaryCondition78     template <class Archive> void serialize(Archive &archive)
79     {
80       archive(m_bcName, m_famName, m_rangeBeg, m_rangeEnd, m_face);
81     }
82 
83     friend std::ostream &operator<<(std::ostream &os, const BoundaryCondition &bc);
84 
85   private:
86     bool equal_(const Ioss::BoundaryCondition &rhs, bool quiet) const;
87   };
88 
89   class DatabaseIO;
90 
91   /** \brief A structured zone -- i,j,k
92    */
93   class StructuredBlock : public EntityBlock
94   {
95   public:
96     StructuredBlock(DatabaseIO *io_database, const std::string &my_name, int index_dim, int ni,
97                     int nj, int nk, int off_i, int off_j, int off_k, int glo_ni, int glo_nj,
98                     int glo_nk);
99     StructuredBlock(DatabaseIO *io_database, const std::string &my_name, int index_dim,
100                     Ioss::IJK_t &ordinal, Ioss::IJK_t &offset, Ioss::IJK_t &global_ordinal);
101 
102     // Useful for serial
103     StructuredBlock(DatabaseIO *io_database, const std::string &my_name, int index_dim, int ni,
104                     int nj, int nk);
105     StructuredBlock(DatabaseIO *io_database, const std::string &my_name, int index_dim,
106                     Ioss::IJK_t &ordinal);
107 
108     StructuredBlock *clone(DatabaseIO *database) const;
109 
110     ~StructuredBlock() override;
111 
type_string()112     std::string type_string() const override { return "StructuredBlock"; }
short_type_string()113     std::string short_type_string() const override { return "structuredblock"; }
contains_string()114     std::string contains_string() const override { return "Cell"; }
type()115     EntityType  type() const override { return STRUCTUREDBLOCK; }
116 
get_node_block()117     const Ioss::NodeBlock &get_node_block() const { return m_nodeBlock; }
get_node_block()118     Ioss::NodeBlock &      get_node_block() { return m_nodeBlock; }
119 
120     /** \brief Does block contain any cells
121      */
is_active()122     bool is_active() const { return m_ni * m_nj * m_nk > 0; }
123 
124     // Handle implicit properties -- These are calcuated from data stored
125     // in the grouping entity instead of having an explicit value assigned.
126     // An example would be 'element_block_count' for a region.
127     Property get_implicit_property(const std::string &my_name) const override;
128 
129     AxisAlignedBoundingBox get_bounding_box() const;
130 
131     /** \brief Set the 'offset' for the block.
132      *
133      *  The 'offset' is used to map a cell or node location within a
134      *  structured block to the model implicit cell or node location
135      *  on a single processor.  zero-based.
136      *
137      *  The 'global' offsets do the same except for they apply over
138      *  the entire model on all processors. zero-based.
139      *
140      *  For example, the file descriptor (1-based) of
141      *  the 37th cell in the 4th block is calculated by:
142      *
143      *  file_descriptor = offset of block 4 + 37
144      *
145      *  This can also be used to determine which structured block
146      *  a cell with a file_descriptor maps into. An particular
147      *  structured block contains all cells in the range:
148      *
149      *  offset < file_descriptor <= offset+number_cells_per_block
150      *
151      *  Note that for nodes, the nodeOffset does not take into account
152      *  the nodes that are shared between blocks.
153      */
set_node_offset(size_t offset)154     void set_node_offset(size_t offset) { m_nodeOffset = offset; }
set_cell_offset(size_t offset)155     void set_cell_offset(size_t offset) { m_cellOffset = offset; }
set_node_global_offset(size_t offset)156     void set_node_global_offset(size_t offset) { m_nodeGlobalOffset = offset; }
set_cell_global_offset(size_t offset)157     void set_cell_global_offset(size_t offset) { m_cellGlobalOffset = offset; }
158 
get_node_offset()159     size_t get_node_offset() const { return m_nodeOffset; }
get_cell_offset()160     size_t get_cell_offset() const { return m_cellOffset; }
get_node_global_offset()161     size_t get_node_global_offset() const { return m_nodeGlobalOffset; }
get_cell_global_offset()162     size_t get_cell_global_offset() const { return m_cellGlobalOffset; }
163 
164     // Get the global (over all processors) cell
165     // id at the specified i,j,k location (1 <= i,j,k <= ni,nj,nk).  1-based.
get_global_cell_id(int i,int j,int k)166     size_t get_global_cell_id(int i, int j, int k) const
167     {
168       return m_cellGlobalOffset + static_cast<size_t>(k - 1) * m_niGlobal * m_njGlobal +
169              static_cast<size_t>(j - 1) * m_niGlobal + i;
170     }
171 
get_global_cell_id(IJK_t index)172     size_t get_global_cell_id(IJK_t index) const
173     {
174       return get_global_cell_id(index[0], index[1], index[2]);
175     }
176 
177     // Get the global (over all processors) node
178     // offset at the specified i,j,k location (1 <= i,j,k <= ni,nj,nk).  0-based, does not account
179     // for shared nodes.
get_global_node_offset(int i,int j,int k)180     size_t get_global_node_offset(int i, int j, int k) const
181     {
182       return m_nodeGlobalOffset + static_cast<size_t>(k - 1) * (m_niGlobal + 1) * (m_njGlobal + 1) +
183              static_cast<size_t>(j - 1) * (m_niGlobal + 1) + i - 1;
184     }
185 
get_global_node_offset(IJK_t index)186     size_t get_global_node_offset(IJK_t index) const
187     {
188       return get_global_node_offset(index[0], index[1], index[2]);
189     }
190 
191     // Get the local (relative to this block on this processor) node id at the specified
192     // i,j,k location (1 <= i,j,k <= ni+1,nj+1,nk+1).  0-based.
get_block_local_node_offset(int ii,int jj,int kk)193     size_t get_block_local_node_offset(int ii, int jj, int kk) const
194     {
195       auto i = ii - m_offsetI;
196       auto j = jj - m_offsetJ;
197       auto k = kk - m_offsetK;
198       assert(i > 0 && i <= m_ni + 1 && j > 0 && j <= m_nj + 1 && k > 0 && k <= m_nk + 1);
199       return static_cast<size_t>(k - 1) * (m_ni + 1) * (m_nj + 1) +
200              static_cast<size_t>(j - 1) * (m_ni + 1) + i - 1;
201     }
202 
get_block_local_node_offset(IJK_t index)203     size_t get_block_local_node_offset(IJK_t index) const
204     {
205       return get_block_local_node_offset(index[0], index[1], index[2]);
206     }
207 
208     // Get the local (on this processor) cell-node offset at the specified
209     // i,j,k location (1 <= i,j,k <= ni+1,nj+1,nk+1).  0-based.
get_local_node_offset(int i,int j,int k)210     size_t get_local_node_offset(int i, int j, int k) const
211     {
212       return get_block_local_node_offset(i, j, k) + m_nodeOffset;
213     }
214 
get_local_node_offset(IJK_t index)215     size_t get_local_node_offset(IJK_t index) const
216     {
217       return get_local_node_offset(index[0], index[1], index[2]);
218     }
219 
get_cell_node_ids(bool add_offset)220     std::vector<INT> get_cell_node_ids(bool add_offset) const
221     {
222       size_t           node_count = get_property("node_count").get_int();
223       std::vector<INT> ids(node_count);
224       get_cell_node_ids(ids.data(), add_offset);
225       return ids;
226     }
227 
get_cell_node_ids(INT_t * idata,bool add_offset)228     template <typename INT_t> size_t get_cell_node_ids(INT_t *idata, bool add_offset) const
229     {
230       // Fill 'idata' with the cell node ids which are the
231       // 1-based location of each node in this zone
232       // The location is based on the "model" (all processors) zone.
233       // If this is a parallel decomposed model, then
234       // this block may be a subset of the "model" zone
235       //
236       // if 'add_offset' is true, then add the m_cellGlobalOffset
237       // which changes the location to be the location in the
238       // entire "mesh" instead of within a "zone" (all processors)
239 
240       size_t index  = 0;
241       size_t offset = add_offset ? m_nodeGlobalOffset : 0;
242 
243       if (m_nk == 0 && m_nj == 0 && m_ni == 0) {
244         return index;
245       }
246 
247       for (int kk = 0; kk < m_nk + 1; kk++) {
248         size_t k = m_offsetK + kk;
249         for (int jj = 0; jj < m_nj + 1; jj++) {
250           size_t j = m_offsetJ + jj;
251           for (int ii = 0; ii < m_ni + 1; ii++) {
252             size_t i = m_offsetI + ii;
253 
254             size_t ind = k * (m_niGlobal + 1) * (m_njGlobal + 1) + j * (m_niGlobal + 1) + i;
255 
256             idata[index++] = ind + offset + 1;
257           }
258         }
259       }
260 
261       for (auto idx_id : m_globalIdMap) {
262         idata[idx_id.first] = idx_id.second;
263       }
264 
265       return index;
266     }
267 
get_cell_ids(INT_t * idata,bool add_offset)268     template <typename INT_t> size_t get_cell_ids(INT_t *idata, bool add_offset) const
269     {
270       // Fill 'idata' with the cell ids which are the
271       // 1-based location of each cell in this zone
272       // The location is based on the "model" zone.
273       // If this is a parallel decomposed model, then
274       // this block may be a subset of the "model" zone
275       //
276       // if 'add_offset' is true, then add the m_cellGlobalOffset
277       // which changes the location to be the location in the
278       // entire "mesh" instead of within a "zone"
279 
280       size_t index  = 0;
281       size_t offset = add_offset ? m_cellGlobalOffset : 0;
282 
283       if (m_nk == 0 && m_nj == 0 && m_ni == 0) {
284         return index;
285       }
286 
287       for (int kk = 0; kk < m_nk; kk++) {
288         size_t k = m_offsetK + kk;
289         for (int jj = 0; jj < m_nj; jj++) {
290           size_t j = m_offsetJ + jj;
291           for (int ii = 0; ii < m_ni; ii++) {
292             size_t i = m_offsetI + ii;
293 
294             size_t ind = k * m_niGlobal * m_njGlobal + j * m_niGlobal + i;
295 
296             idata[index++] = ind + offset + 1;
297           }
298         }
299       }
300       return index;
301     }
302 
contains(size_t global_offset)303     bool contains(size_t global_offset) const
304     {
305       return (global_offset >= m_nodeOffset &&
306               global_offset < m_nodeOffset + get_property("node_count").get_int());
307     }
308 
309     /* COMPARE two StructuredBlocks */
310     bool operator==(const Ioss::StructuredBlock &rhs) const;
311     bool operator!=(const Ioss::StructuredBlock &rhs) const;
312     bool equal(const Ioss::StructuredBlock &rhs) const;
313 
314   protected:
315     int64_t internal_get_field_data(const Field &field, void *data,
316                                     size_t data_size) const override;
317 
318     int64_t internal_put_field_data(const Field &field, void *data,
319                                     size_t data_size) const override;
320 
321   private:
322     bool equal_(const Ioss::StructuredBlock &rhs, bool quiet) const;
323     int  m_ni{};
324     int  m_nj{};
325     int  m_nk{};
326 
327     int m_offsetI{}; // Valid 'i' ordinal runs from m_offsetI+1 to m_offsetI+m_ni
328     int m_offsetJ{};
329     int m_offsetK{};
330 
331     int m_niGlobal{}; // The ni,nj,nk of the master block this is a subset of.
332     int m_njGlobal{};
333     int m_nkGlobal{};
334 
335     size_t m_nodeOffset{};
336     size_t m_cellOffset{};
337 
338     size_t m_nodeGlobalOffset{};
339     size_t m_cellGlobalOffset{};
340 
341     Ioss::NodeBlock m_nodeBlock;
342 
343   public:
344     std::vector<ZoneConnectivity>          m_zoneConnectivity;
345     std::vector<BoundaryCondition>         m_boundaryConditions;
346     std::vector<size_t>                    m_blockLocalNodeIndex;
347     std::vector<std::pair<size_t, size_t>> m_globalIdMap;
348 
serialize(Archive & archive)349     template <class Archive> void serialize(Archive &archive)
350     {
351       archive(m_zoneConnectivity, m_boundaryConditions, m_blockLocalNodeIndex, m_globalIdMap);
352     }
353   };
354 } // namespace Ioss
355 #endif
356