1 // Copyright (c) 2017-2021, Lawrence Livermore National Security, LLC and
2 // other Axom Project Developers. See the top-level LICENSE file for details.
3 //
4 // SPDX-License-Identifier: (BSD-3-Clause)
5 
6 #ifndef MINT_UNIFORMMESH_HPP_
7 #define MINT_UNIFORMMESH_HPP_
8 
9 #include "axom/core/StackArray.hpp"
10 #include "axom/mint/config.hpp"
11 #include "axom/mint/mesh/StructuredMesh.hpp"
12 
13 #include "axom/slic/interface/slic.hpp"
14 
15 namespace axom
16 {
17 namespace mint
18 {
19 /*!
20  * \class UniformMesh
21  *
22  * \brief Provides the ability to represent and operate on a UniformMesh
23  *
24  *  The UniformMesh extends from the StructuredMesh base class and provides the
25  *  ability to represent and operate on a uniform mesh and associated data.
26  *
27  *  A <em> uniform mesh </em>, also called a regular mesh, subdivides the domain
28  *  in cells that have uniform spacing across each coordinate axis.The nodes and
29  *  cells of a <em> uniform </em> mesh are arranged on a regular grid lattice,
30  *  where the topology is implicitly defined according to the ordering of the
31  *  nodes in the logical i-j-k index space. Moreover, the geometry is also
32  *  implicitly defined on a <em> uniform mesh </em>. Given an origin point,
33  *  \f$ \hat{x_0} \f$ and spacing, \f$ \hat{h} \f$, along each axis, the
34  *  coordinates of a node can be evaluated algebraically by the following:
35  *
36  *    \f$ \hat{p} = \hat{x_0} + \hat{i} \times \hat{h} \f$,
37  *
38  *  where \f$\hat{i}\f$ is the logical i-j-k index of the corresponding node.
39  *
40  *  A UniformMesh object may be constructed using (a) a native constructor, or
41  *  (b) from a a Sidre group when Mint is compiled with Sidre support.
42  *
43  *  * <b> Native Constructor </b> <br />
44  *
45  *    When using native constructor, the UniformMesh object owns all memory
46  *    that is associated with the mesh. Once the UniformMesh object goes
47  *    out-of-scope all memory associated with it is returned to the system.
48  *
49  *  * <b> Sidre Constructor </b> <br />
50  *
51  *    When a UniformMesh is associated with a Sidre Group, Sidre owns all the
52  *    memory. Once the UniformMesh goes out-of-scope the data remains persistent
53  *    in Sidre.
54  *
55  * \note When using Sidre, the specified group must conform to the conventions
56  *  defined by the <a href="http://llnl-conduit.readthedocs.io/en/latest/">
57  *  computational mesh blueprint </a>
58  *
59  * \see StructuredMesh
60  * \see Extent
61  */
62 class UniformMesh : public StructuredMesh
63 {
64 public:
65   /*!
66    * \brief Default constructor. Disabled.
67    */
68   UniformMesh() = delete;
69 
70   /// \name Native Constructors
71   /// @{
72 
73   /*!
74    * \brief Constructs a uniform mesh within a specified rectangular region,
75    *  defined by its lower and upper corner points, and mesh dimensions, Ni,
76    *  Nj, Nk.
77    *
78    * \param [in] lower_bound lower corner coordinates of rectangular region.
79    * \param [in] upper_bound upper corner coordinates of rectangular region.
80    * \param [in] Ni the number of nodes in the i-direction
81    * \param [in] Nj the number of nodes in the j-direction (if dimension >= 2)
82    * \param [in] Nk the number of nodes in the k-direction (if dimension == 3)
83    *
84    * \pre lower_bound != nullptr
85    * \pre upper_bound != nullptr
86    * \pre Ni >= 1
87    * \pre Nj >= 1 iff dimension >= 2
88    * \pre Nk >= 1 iff dimension == 3
89    *
90    * \post getOrigin() != nullptr
91    * \post getSpacing() != nullptr
92    * \post getOrigin()[ i ] == lower_bound[ i ] \f$ \forall i \f$
93    */
94   UniformMesh(const double* lower_bound,
95               const double* upper_bound,
96               IndexType Ni,
97               IndexType Nj = -1,
98               IndexType Nk = -1);
99   /// @}
100 
101 #ifdef AXOM_MINT_USE_SIDRE
102 
103   /// \name Sidre Constructors
104   /// @{
105 
106   /*!
107    * \brief Creates a uniform mesh instance from the given Sidre group that
108    *  holds uniform mesh data according to the computational mesh blueprint
109    *  conventions.
110    *
111    * \param [in] group pointer to the root group within a Sidre hierarchy.
112    * \param [in] topo the name of the topology for this mesh (optional).
113    *
114    * \note The supplied group is expected to contain uniform mesh data that is
115    *  valid & conforming to the conventions described by conduit's
116    *  <a href="http://llnl-conduit.readthedocs.io/en/latest/"> computational
117    *  mesh blueprint </a>.
118    *
119    * \note If a topology name is not provided, the implementation will use the
120    *  1st topology group under the parent "topologies" group.
121    *
122    * \note When using this constructor, all data is owned by Sidre. Once the
123    *  mesh object goes out-of-scope, the data will remain persistent in Sidre.
124    *
125    * \pre group != nullptr
126    * \pre blueprint::isValidRootGroup( group )
127    * \post hasSidreGroup() == true
128    */
129   explicit UniformMesh(sidre::Group* group, const std::string& topo = "");
130 
131   /*!
132    * \brief Constructs a uniform mesh object, on an empty Sidre group, that
133    *  covers a rectangular region, defined by its lower and upper corner points,
134    *  and desired mesh dimensions, \f$ N_i, N_j, N_k f\$
135    *
136    * \param [in] group pointer to the Sidre group
137    * \param [in] topo the name of the topology (optional)
138    * \param [in] coordset name of the corresponding coordset group (optional)
139    * \param [in] lower_bound lower corner coordinates of rectangular region
140    * \param [in] upper_bound upper corner coordinates of rectangular region
141    * \param [in] Ni number of nodes in the i-direction
142    * \param [in] Nj number of nodes in the j-direction (if dimension >= 2)
143    * \param [in] Nk number of nodes in the k-direction (if dimension == 3)
144    *
145    * \note The supplied `lower_bound` and `upper_bound` must point to buffer
146    *  that have at least N entries, where N is the dimension of the mesh.
147    *
148    * \note When using this constructor, all data is owned by Sidre. Once the
149    *  UniformMesh object goes out-of-scope, the data will remain persistent in
150    *  Sidre.
151    *
152    * \note If a topology and/or coordset name are not provided by the caller,
153    *  internal defaults will be used by the implementation.
154    *
155    * \pre lower_bound != nullptr
156    * \pre upper_bound != nullptr
157    * \pre Ni >= 1
158    * \pre Nj >= 1 iff dimension >= 2
159    * \pre Nk >= 1 iff dimension == 3
160    * \pre group != nullptr
161    * \pre group->getNumViews()==0
162    * \pre group->getNumGroups()==0
163    *
164    * \post getOrigin() != nullptr
165    * \post getSpacing() != nullptr
166    * \post getOrigin()[ i ] == lower_bound[ i ] \f$ \forall i \f$
167    * \post hasSidreGroup() == true
168    */
169   /// @{
170   UniformMesh(sidre::Group* group,
171               const std::string& topo,
172               const std::string& coordset,
173               const double* lower_bound,
174               const double* upper_bound,
175               IndexType Ni,
176               IndexType Nj = -1,
177               IndexType Nk = -1);
178 
UniformMesh(sidre::Group * group,const double * lower_bound,const double * upper_bound,IndexType Ni,IndexType Nj=-1,IndexType Nk=-1)179   UniformMesh(sidre::Group* group,
180               const double* lower_bound,
181               const double* upper_bound,
182               IndexType Ni,
183               IndexType Nj = -1,
184               IndexType Nk = -1)
185     : UniformMesh(group, "", "", lower_bound, upper_bound, Ni, Nj, Nk)
186   { }
187     /// @}
188 
189 /// @}
190 #endif
191 
192   /// \name Virtual methods
193   /// @{
194 
195   /*!
196    * \brief Destructor.
197    */
~UniformMesh()198   virtual ~UniformMesh() { }
199 
200   /// \name Nodes
201   /// @{
202 
203   /*!
204    * \brief Copy the coordinates of the given node into the provided buffer.
205    *
206    * \param [in] nodeID the ID of the node in question.
207    * \param [in] coords the buffer to copy the coordinates into, of length at
208    *  least getDimension().
209    *
210    * \note provided only for convenience, do not use inside a loop. Instead use
211    *  getOrigin() and getSpacing() to calculate the nodal coordinates.
212    *
213    * \pre 0 <= nodeID < getNumberOfNodes()
214    * \pre coords != nullptr
215    */
216   virtual void getNode(IndexType nodeID, double* node) const final override;
217 
218   /*!
219    * \brief Return a pointer to the nodal positions in the specified dimension.
220    *  Since the UniformMesh holds no such array it return nullptr.
221    */
222   /// @{
223 
getCoordinateArray(int AXOM_UNUSED_PARAM (dim))224   virtual double* getCoordinateArray(int AXOM_UNUSED_PARAM(dim)) final override
225   {
226     SLIC_ERROR("getCoordinateArray() is not supported for UniformMesh");
227     return nullptr;
228   }
229 
getCoordinateArray(int AXOM_UNUSED_PARAM (dim)) const230   virtual const double* getCoordinateArray(int AXOM_UNUSED_PARAM(dim)) const final override
231   {
232     SLIC_ERROR("getCoordinateArray() is not supported for UniformMesh");
233     return nullptr;
234   }
235 
236   /// @}
237 
238   /// @}
239 
240   /// @}
241 
242   /// \name Attribute Querying Methods
243   /// @{
244 
245   /*!
246    * \brief Returns a const pointer to origin of the Uniform Mesh
247    * \return origin pointer to the buffer storing the coordinates of the origin
248    * \post origin != nullptr
249    */
getOrigin() const250   const StackArray<double, 3>& getOrigin() const { return m_origin; }
251 
252   /*!
253    * \brief Returns a const pointer to spacing of the Uniform Mesh.
254    * \return h user-supplied buffer to store the spacing of the mesh.
255    */
getSpacing() const256   const StackArray<double, 3>& getSpacing() const { return m_h; }
257 
258   /// @}
259 
260   /*!
261    * \brief Evaluates the physical coordinate of a node along a given diction.
262    *
263    * \param [in] i the node's logical grid index along the specified direction
264    * \param [in] direction the direction in query, e.g, I_DIRECTION, etc.
265    *
266    * \return x the physical coordinate of the node in the specified direction.
267    *
268    * \pre i >= 0 && i < getNumberNodeAlongDim( direction )
269    * \pre direction >= 0 && direction < getDimension()
270    */
evaluateCoordinate(IndexType i,int direction) const271   inline double evaluateCoordinate(IndexType i, int direction) const
272   {
273     SLIC_ASSERT(direction >= 0 && direction < getDimension());
274     SLIC_ASSERT(i >= 0 && i < getNodeResolution(direction));
275 
276     const double* x0 = getOrigin();
277     const double* h = getSpacing();
278     return x0[direction] + i * h[direction];
279   }
280 
281 private:
282   void setSpacingAndOrigin(const double* lo, const double* hi);
283 
284   StackArray<double, 3> m_origin = {{0.0, 0.0, 0.0}};
285   StackArray<double, 3> m_h = {{1.0, 1.0, 1.0}};
286 
287   DISABLE_COPY_AND_ASSIGNMENT(UniformMesh);
288   DISABLE_MOVE_AND_ASSIGNMENT(UniformMesh);
289 };
290 
291 } /* namespace mint */
292 } /* namespace axom */
293 
294 #endif /* MINT_UNIFORMMESH_HPP_ */
295