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_MESH_HPP_
7 #define MINT_MESH_HPP_
8 
9 #include "axom/core/Macros.hpp"  // for Axom macros
10 
11 #include "axom/mint/mesh/CellTypes.hpp"         // for CellType enum
12 #include "axom/mint/config.hpp"                 // for mint compile-time type
13 #include "axom/mint/mesh/FieldAssociation.hpp"  // for FieldAssociation enum
14 #include "axom/mint/mesh/FieldData.hpp"         // for mint::FieldData
15 #include "axom/mint/mesh/MeshCoordinates.hpp"   // for mint::MeshCoordinates
16 #include "axom/mint/mesh/MeshTypes.hpp"         // for MeshType enum
17 
18 #include "axom/slic/interface/slic.hpp"  // for SLIC macros
19 
20 namespace axom
21 {
22 /* Forward declarations */
23 #ifdef AXOM_MINT_USE_SIDRE
24 namespace sidre
25 {
26 class Group;
27 }
28 #endif
29 
30 namespace mint
31 {
32 /* Forward declarations */
33 class Field;
34 class Mesh;
35 
36 /// \name Free Methods
37 /// @{
38 
39 #ifdef AXOM_MINT_USE_SIDRE
40 
41 /*!
42  * \brief Creates a mesh instance from the given Sidre group.
43  *
44  * \param [in] group pointer to the root group of the mesh in Sidre.
45  * \param [in] topo topology associated with the requested mesh (optional)
46  *
47  * \return m pointer to a mesh instance corresponding to the specified group.
48  *
49  * \note If a topology name is not provided, the implementation will construct
50  *  a mesh based on the 1st topology group under the parent "topologies"
51  *  group.
52  *
53  * \note Ownership of the resulting mesh object is passed to the caller.
54  *
55  * \note When using Mint with Sidre, Sidre maintains ownership of the data.
56  *  Although the data can be modified via calls to Mint, groups and views
57  *  cannot be deleted. The data remains persistent in Sidre once the mesh
58  *  object goes out-of-scope.
59  *
60  * \pre  group != nullptr
61  * \pre  blueprint::isValidRootGroup( group ) == true
62  * \post m != nullptr
63  */
64 Mesh* getMesh(sidre::Group* group, const std::string& topo = "");
65 
66 #endif
67 
68 /// @}
69 
70 /*!
71  * \class Mesh
72  *
73  * \brief Base class that defines the core API common to all Mesh types.
74  *
75  * A Mesh, \f$ \mathcal{M}(\Omega) \f$, provides an approximation of a physical
76  * domain, \f$ \Omega \in \mathcal{R}^d \f$, where \f$ d \in [1,3] \f$ . The
77  * Mesh is essentially a discrete representation of a problem and is used to
78  * facilitate the analysis, e.g., FEA, CFD, etc. The solution domain is
79  * approximated by dividing it into a finite number of <em> nodes </em> and
80  * <em> cells </em> at which the variables of the underlying mathematical model
81  * (i.e., a PDE) are then computed via a numerical method, such as,
82  * Finite Difference (FD), Finite Volume (FV) or Finite Element (FE), chief
83  * among them.
84  *
85  * There are a variety of mesh types. Mint supports the following mesh types:
86  *
87  *  * <b> Structured (Curvilinear) Mesh </b> <br />
88  *
89  *    A <em> structured mesh </em> divides the solution domain according to a
90  *    logical grid where each node/cell of the mesh can be uniquely identified
91  *    by a corresponding logical ijk-index. The nodes of the mesh are found at
92  *    the intersection of the grid lines, but, are explicitly defined via a
93  *    mapping onto the physical Cartesian coordinate system of the domain. For
94  *    this reason, these types of meshes are also called <em> mapped </em> or
95  *    <em> body-fitted </em> meshes.
96  *
97  *    However, the mesh topology (e.g., connectivity, neighbor information) is
98  *    implicitly defined by the logical indexing scheme. For example,  a
99  *    structured mesh is composed of <em> quadrilateral </em> cells in 2-D and
100  *    <em> hexahedron </em> cells in 3-D. Given the logical index of a cell
101  *    one can compute the node indices of the cell and neighbor information by
102  *    performing simple shift operations on the associated index.
103  *
104  *  * <b> Rectilinear Mesh </b> <br />
105  *
106  *    A <em> rectilinear mesh </em>, also known as a product mesh, is similar
107  *    to the <em> structured mesh </em> in that it is also defined according to
108  *    a logical indexing scheme and has implicit topology.
109  *
110  *    However, the nodes and cells on a <em> rectilinear mesh </em> are arranged
111  *    on a regular lattice that is axis-aligned with the Cartesian coordinate
112  *    system. In contrast to the general <em> structured mesh </em>, the
113  *    <em> rectilinear mesh </em> does not explicitly define **all** the nodes
114  *    of the mesh. Instead, the nodes are only defined along each coordinate
115  *    axis and may have variable spacing between nodes. Given a logical index,
116  *    the corresponding physical position of a node can be evaluated by taking
117  *    the cartesian product of the corresponding coordinate along each
118  *    coordinate axis.
119  *
120  *  * <b> Uniform Mesh </b> <br />
121  *
122  *    A <em> uniform mesh </em>, also called a regular mesh, subdivides the
123  *    domain in cells that have uniform spacing across each coordinate axis.
124  *    Similar to the <em> structured mesh </em>, a <em> uniform mesh </em>
125  *    adheres to the same logical indexing scheme and implicit topology
126  *    representation. Moreover, The nodes and cells of a <em> uniform </em> mesh
127  *    are arranged on a regular lattice as with the <em> rectilinear mesh </em>.
128  *    However, both topology and geometry is implicitly defined on a
129  *    <em> uniform mesh </em>. The geometry is solely defined by an origin,
130  *    \f$ \hat{x_0} \f$ and spacing, \f$ \hat{h} \f$, along each axis. The
131  *    coordinates of a node can be evaluated algebraically by the following:
132  *    \f$ \hat{p} = \hat{x_0} + \hat{i} \times \hat{h} \f$, where \f$\hat{i}\f$
133  *    is the logical ijk-index of the corresponding node.
134  *
135  *  * <b> Unstructured Mesh </b> <br />
136  *
137  *    An <em> unstructured mesh </em> stores both node and topology information
138  *    explicitly. This allows the flexibility of discretizing the solution
139  *    domain using a variety of cell types not just quadrilateral (in 2D) or
140  *    hexahedral (in 3D) cells. Due to this added flexibility, the use of
141  *    <em> unstructured meshes </em> is more common when dealing with complex
142  *    geometries. However, <em> unstructured meshes </em> require additional
143  *    storage and generally incur some performance penalty to store, create and
144  *    access mesh topology information respectively.
145  *
146  *    Mint classifies <em> unstructured meshes </em> in two basic types based on
147  *    the underlying mesh topology:
148  *
149  *    * <b> Single Cell Topology </b>
150  *
151  *      In this case, the <em> unstructured mesh </em> consists of a single cell
152  *      type, e.g., a quad or triangle mesh in 2D, or, a hex or tet mesh in 3D.
153  *      In this case  the underlying implementation is optimized for the
154  *      specified cell type (specified in the constructor).
155  *
156  *    * <b> Mixed Cell Topology </b>
157  *
158  *      When <em> mixed cell topology </em> is specified, the <em> unstructured
159  *      mesh </em> can be composed of any of the supported cell types, e.g.,
160  *      a mesh consisting of both quads and triangles. This mode incurs
161  *      additional overhead for storage and access to mesh topology information,
162  *      since it requires indirection.
163  *
164  *    The list of supported cell types for an <em> unstructured mesh </em> is
165  *    available in CellTypes.hpp
166  *
167  *  * <b> Particle Mesh </b> <br />
168  *
169  *    A <em> particle mesh </em> discretizes the solution domain using
170  *    a set of discrete particle elements which correspond to the the nodes
171  *    of the mesh. There is no ordering imposed on the particles and the
172  *    coordinates of the particles are explicitly defined. A particle mesh
173  *    has no connectivity information connecting the particles, which is why
174  *    in literature methods using a particle discretization are also referred
175  *    to as <em> meshless </em> or <em> meshfree </em> methods.
176  *
177  * The Mesh class provides the means to create, access and remove fields on
178  * a mesh given the field name and its association. The field association
179  * designates the corresponding mesh entity at which the field is stored, e.g.
180  * whether the field is stored at the nodes or cell centers. A Field may be a
181  * scalar quantity, e.g., pressure, or a vector field, such as, velocity.
182  *
183  * \warning When using Sidre, field names have to be unique. For example, if
184  *  there exists a "pressure" node-centered field, there cannot be a
185  *  corresponding cell-centered field.
186  *
187  * \note Mesh is a base class and cannot be instantiated directly
188  *
189  * \note Typically, the computational mesh can be defined across one or more
190  *  <em> blocks </em>, e.g., for multi-block problems, where each block is
191  *  subsequently decomposed into several <em> partitions </em> for parallel
192  *  computation. A Mesh instance represents a <em> single </em> partition for
193  *  a given block.
194  *
195  * \see mint::UnstructuredMesh
196  * \see mint::StructuredMesh
197  * \see mint::CurvilinearMesh
198  * \see mint::RectilinearMesh
199  * \see mint::UniformMesh
200  * \see mint::Field
201  * \see mint::FieldData
202  * \see mint::MeshTypes
203  */
204 class Mesh
205 {
206 public:
207   /*!
208    * \brief Default constructor. Disabled.
209    */
210   Mesh() = delete;
211 
212   /// \name Virtual methods
213   /// @{
214 
215   /*!
216    * \brief Destructor.
217    */
218   virtual ~Mesh();
219 
220   /// \name Cells
221   /// @{
222 
223   /*!
224    * \brief Returns the number of cells in this mesh instance.
225    * \return N the number of cells
226    * \post N >= 0
227    */
228   virtual IndexType getNumberOfCells() const = 0;
229 
230   /*!
231    * \brief Returns the capacity for number of cell in this mesh instance.
232    * \return N the cell capacity
233    * \post N >= 0
234    */
getCellCapacity() const235   virtual IndexType getCellCapacity() const { return getNumberOfCells(); }
236 
237   /*!
238    * \brief Return the type of the given cell.
239    *
240    * \param [in] cellID the ID of the cell in question, this parameter is
241    *  ignored if hasMixedCellTypes() == false.
242    *
243    * \pre 0 <= cellID < getNumberOfCells()
244    */
245   virtual CellType getCellType(IndexType cellID = 0) const = 0;
246 
247   /*!
248    * \brief Return the number of nodes associated with the given cell.
249    *
250    * \param [in] cellID the ID of the cell in question, this parameter is
251    *  ignored unless hasMixedCellTypes() == true.
252    *
253    * \pre 0 <= cellID < getNumberOfCells()
254    */
255   virtual IndexType getNumberOfCellNodes(IndexType cellID = 0) const = 0;
256 
257   /*!
258    * \brief Copy the connectivity of the given cell into the provided buffer.
259    *  The buffer must be of length at least getNumberOfCellNodes( cellID ).
260    *
261    * \param [in] cellID the ID of the cell in question.
262    * \param [out] nodes the buffer into which the connectivity is copied, must
263    *  be of length at least getNumberOfCellNodes( cellID ).
264    *
265    * \return The number of nodes for the given cell.
266    *
267    * \pre nodes != nullptr
268    * \pre 0 <= cellID < getNumberOfCells()
269    */
270   virtual IndexType getCellNodeIDs(IndexType AXOM_UNUSED_PARAM(cellID),
271                                    IndexType* AXOM_UNUSED_PARAM(nodes)) const = 0;
272 
273   /*!
274    * \brief Return the number of faces associated with the given cell.
275    *
276    * \param [in] cellID the ID of the cell in question.
277    */
278   virtual IndexType getNumberOfCellFaces(
279     IndexType AXOM_UNUSED_PARAM(cellID) = 0) const = 0;
280 
281   /*!
282    * \brief Populates the given buffer with the IDs of the faces of the given
283    *  cell and returns the number of faces.
284    *
285    * \param [in] cellID the ID of the cellID in question.
286    * \param [out] faces buffer to populate with the face IDs. Must be of length
287    *  at least getNumberOfCellFaces( cellID ).
288    *
289    * \pre faces != nullptr
290    * \pre 0 <= cellID < getNumberOfCells()
291    */
292   virtual IndexType getCellFaceIDs(IndexType AXOM_UNUSED_PARAM(cellID),
293                                    IndexType* AXOM_UNUSED_PARAM(faces)) const = 0;
294 
295   /// @}
296 
297   /// \name Nodes
298   /// @{
299 
300   /*!
301    * \brief Returns the number of nodes in this mesh instance.
302    * \return N the number of nodes
303    * \post N >= 0
304    */
305   virtual IndexType getNumberOfNodes() const = 0;
306 
307   /*!
308    * \brief Returns the capacity for number of nodes in this mesh instance.
309    * \return N the node capacity
310    * \post N >= 0
311    */
getNodeCapacity() const312   virtual IndexType getNodeCapacity() const { return getNumberOfNodes(); }
313 
314   /*!
315    * \brief Copy the coordinates of the given node into the provided buffer.
316    *
317    * \param [in] nodeID the ID of the node in question.
318    * \param [in] coords the buffer to copy the coordinates into, of length at
319    *  least getDimension().
320    *
321    * \pre 0 <= nodeID < getNumberOfNodes()
322    * \pre coords != nullptr
323    */
324   virtual void getNode(IndexType nodeID, double* node) const = 0;
325 
326   /*!
327    * \brief Returns pointer to the requested mesh coordinate buffer.
328    *
329    * \param [in] dim the dimension of the requested coordinate buffer
330    * \return ptr pointer to the coordinate buffer.
331    *
332    * \note if hasExplicitCoordinates() == true then the length of the returned
333    *  buffer is getNumberOfNodes(). Otherwise the UniformMesh returns
334    *  nullptr and the RectilinearMesh returns a pointer to the associated
335    *  dimension scale which is of length
336    *  static_cast< RectilinearMesh* >( this )->getNodeResolution().
337    *
338    * \pre dim >= 0 && dim < dimension()
339    * \pre dim == X_COORDINATE || dim == Y_COORDINATE || dim == Z_COORDINATE
340    */
341   /// @{
342   virtual double* getCoordinateArray(int dim) = 0;
343   virtual const double* getCoordinateArray(int dim) const = 0;
344   /// @}
345 
346   /// @}
347 
348   /// \name Faces
349   /// @{
350 
351   /*!
352    * \brief Returns the number of faces in this mesh instance.
353    * \return N the number of faces
354    * \post N >= 0
355    */
356   virtual IndexType getNumberOfFaces() const = 0;
357 
358   /*!
359    * \brief Returns the capacity for number of faces in this mesh instance.
360    * \return N the face capacity
361    * \post N >= 0
362    */
getFaceCapacity() const363   virtual IndexType getFaceCapacity() const { return getNumberOfFaces(); }
364 
365   /*!
366    * \brief Return the type of the given face.
367    *
368    * \param [in] faceID the ID of the face in question.
369    */
370   virtual CellType getFaceType(IndexType AXOM_UNUSED_PARAM(faceID)) const = 0;
371 
372   /*!
373    * \brief Return the number of nodes associated with the given face.
374    *
375    * \param [in] faceID the ID of the face in question.
376    */
377   virtual IndexType getNumberOfFaceNodes(
378     IndexType AXOM_UNUSED_PARAM(faceID)) const = 0;
379 
380   /*!
381    * \brief Copy the IDs of the nodes that compose the given face into the
382    *  provided buffer.
383    *
384    * \param [in] faceID the ID of the face in question.
385    * \param [out] nodes the buffer into which the node IDs are copied, must
386    *  be of length at least getNumberOfFaceNodes().
387    *
388    * \return The number of nodes for the given face.
389    *
390    * \pre nodes != nullptr
391    * \pre 0 <= faceID < getNumberOfFaces()
392    */
393   virtual IndexType getFaceNodeIDs(IndexType AXOM_UNUSED_PARAM(faceID),
394                                    IndexType* AXOM_UNUSED_PARAM(nodes)) const = 0;
395 
396   /*!
397    * \brief Copy the IDs of the cells adjacent to the given face into the
398    *  provided indices.
399    *
400    * \param [in] faceID the ID of the face in question.
401    * \param [out] cellIDOne the ID of the first cell.
402    * \param [out] cellIDTwo the ID of the second cell.
403    *
404    * \note If no cell exists (the face is external) then the ID will be set to
405    * -1.
406    *
407    * \pre 0 <= faceID < getNumberOfFaces()
408    */
409   virtual void getFaceCellIDs(IndexType AXOM_UNUSED_PARAM(faceID),
410                               IndexType& AXOM_UNUSED_PARAM(cellIDOne),
411                               IndexType& AXOM_UNUSED_PARAM(cellIDTwo)) const = 0;
412 
413   /// @}
414 
415   /// \name Edges
416   /// @{
417 
418   /*!
419    * \brief Returns the number of edges in this mesh instance.
420    * \return N the number of edges
421    * \post N >= 0
422    */
423   virtual IndexType getNumberOfEdges() const = 0;
424 
425   /*!
426    * \brief Returns the capacity for number of edges in this mesh instance.
427    * \return N the edge capacity
428    * \post N >= 0
429    */
getEdgeCapacity() const430   virtual IndexType getEdgeCapacity() const { return getNumberOfEdges(); }
431 
432   /*!
433    * \brief Returns true iff the mesh was constructed with external arrays.
434    * \return status true if the mesh points to external buffers, else, false.
435    */
436   virtual bool isExternal() const = 0;
437 
438   /// @}
439 
440   /// @}
441 
442   /// \name Mesh Attribute get/set Methods
443   /// @{
444 
445   /*!
446    * \brief Returns the dimension for this mesh instance.
447    * \return ndims the dimension of this mesh instance.
448    * \post ndims >= 1 && ndims <= 3
449    */
getDimension() const450   inline int getDimension() const { return m_ndims; }
451 
452   /*!
453    * \brief Returns the ID of this mesh instance.
454    * \return Id the ID of the mesh.
455    */
getBlockId() const456   inline int getBlockId() const { return m_block_idx; }
457 
458   /*!
459    * \brief set the block ID of this mesh instance.
460    *
461    * \param [in] ID the new block ID.
462    *
463    * \post getBlockId() == ID
464    */
465   void setBlockId(int ID);
466 
467   /*!
468    * \brief Returns the partition ID of this mesh instance.
469    * \return partitionId the partition ID of the mesh.
470    */
getPartitionId() const471   inline int getPartitionId() const { return m_part_idx; }
472 
473   /*!
474    * \brief set the partition ID of this mesh instance.
475    *
476    * \param [in] ID the new partition ID.
477    *
478    * \post getPartitionId() == ID
479    */
480   void setPartitionId(int ID);
481 
482   /*!
483    * \brief Returns the mesh type of this mesh instance.
484    * \return meshType the mesh type
485    * \see MeshType
486    */
getMeshType() const487   inline int getMeshType() const { return m_type; }
488 
489   /*!
490    * \brief Checks if this mesh instance has explicit coordinates.
491    * \return status true iff the mesh defines coordinates explicitly.
492    */
hasExplicitCoordinates() const493   inline bool hasExplicitCoordinates() const { return m_explicit_coords; }
494 
495   /*!
496    * \brief Checks if this mesh instance has explicit connectivity.
497    * \return status true iff the mesh defines cell connectivity explicitly.
498    */
hasExplicitConnectivity() const499   inline bool hasExplicitConnectivity() const
500   {
501     return m_explicit_connectivity;
502   }
503 
504   /*!
505    * \brief Checks if the mesh has mixed cell types, e.g., consisting of both
506    *  triangle and quad elements or hex,pyramid,prisms and tets in 3-D.
507    * \return status true iff the mesh has mixed cell types.
508    */
hasMixedCellTypes() const509   inline bool hasMixedCellTypes() const { return m_has_mixed_topology; }
510 
511   /*!
512    * \brief Returns true if the mesh type is structured.
513    * \return status true if the mesh type is structured, else, false.
514    */
isStructured() const515   inline bool isStructured() const
516   {
517     return ((m_type == STRUCTURED_CURVILINEAR_MESH) ||
518             (m_type == STRUCTURED_RECTILINEAR_MESH) ||
519             (m_type == STRUCTURED_UNIFORM_MESH));
520   }
521 
522   /*!
523    * \brief Returns true if the mesh type is unstructured.
524    * \return status true if the mesh type is unstructured, else, false.
525    */
isUnstructured() const526   inline bool isUnstructured() const { return (m_type == UNSTRUCTURED_MESH); }
527 
528   /*!
529    * \brief Checks if this Mesh instance is associated with a Sidre Group.
530    * \return status true if the Mesh is associated with a group in a Sidre
531    *  hierarchy, else, false.
532    */
533   inline bool hasSidreGroup() const;
534 
535 #ifdef AXOM_MINT_USE_SIDRE
536   /*!
537    * \brief Return a pointer to the sidre::Group associated with this Mesh
538    *  instance or nullptr if none exists.
539    */
getSidreGroup()540   inline sidre::Group* getSidreGroup() { return m_group; }
541 
542   /*!
543    * \brief Return the name of the topology associated with this Mesh instance,
544    *  the return value is undefined if the mesh is not in sidre.
545    */
getTopologyName() const546   inline const std::string& getTopologyName() const { return m_topology; }
547 
548   /*!
549    * \brief Return the name of the coordset associated with this Mesh instance,
550    *  the return value is undefined if the mesh is not in sidre.
551    */
getCoordsetName() const552   inline const std::string& getCoordsetName() const { return m_coordset; }
553 #endif
554 
555   /// @}
556 
557   /// \name Methods to Create, Access & Remove Fields from a Mesh
558   /// @{
559 
560   /*!
561    * \brief Returns const pointer to the FieldData instance with the specified
562    *  mesh field association, e.g., NODE_CENTERED, CELL_CENTERED, etc.
563    *
564    * \param [in] association the specified mesh field association
565    * \return fd pointer to the requested FieldData instance
566    *
567    * \pre association >= 0 && association < NUM_FIELD_ASSOCIATION
568    * \post fd != nullptr
569    *
570    * \see FieldAssociation
571    * \see FieldData
572    */
573   inline const FieldData* getFieldData(int association) const;
574 
575   /*!
576    * \brief Check if a field with the given name and association exists.
577    *
578    * \param [in] name the name of the field in query.
579    * \param [in] association the field association (optional)
580    *
581    * \return status true if the field exists, else, false.
582    *
583    * \note If an association is not explicitly specified, the code will check
584    *  if a field by the given name exists in any available centeering.
585    *
586    * \pre name.empty()==false
587    * \pre association >= 0 && association < NUM_FIELD_ASSOCIATION
588    *
589    * \see FieldAssociation
590    */
591   inline bool hasField(const std::string& name,
592                        int association = ANY_CENTERING) const;
593 
594   /*!
595    * \brief Creates a new field with the given name and specified mesh field
596    *  association, e.g., NODE_CENTERED, CELL_CENTERED, etc.
597    *
598    * \param [in] name the name of the new field.
599    * \param [in] association the mesh field association.
600    * \param [in] num_components number of components of the field (optional).
601    * \param [in] storeInSidre indicates whether to store the field in the
602    *  corresponding Sidre group (optional).
603    * \param [in] capacity
604    *
605    * \return ptr raw pointer to the data buffer of the new field.
606    *
607    * \note This method throws an error and aborts if any of the pre-conditions
608    *  is not satisfied.
609    *
610    * \pre name.empty() == false
611    * \pre hasField( name ) == false
612    * \pre association >= 0 && association < NUM_FIELD_ASSOCIATION
613    *
614    * \post ptr != nullptr
615    * \post hasField( name ) == true
616    *
617    * \see FieldAssociation
618    */
619   template <typename T>
620   inline T* createField(const std::string& name,
621                         int association,
622                         IndexType num_components = 1,
623                         bool storeInSidre = true);
624 
625   /*!
626    * \brief Creates a new field from an external buffer that has the given name
627    *  and specified mesh field association, e.g., NODE_CENTERED, CELL_CENTERED,
628    *  etc.
629    *
630    * \param [in] name the name of the new field.
631    * \param [in] association the mesh field association.
632    * \param [in] data pointer to the external data buffer.
633    * \param [in] num_components number of components of the field (optional).
634    *
635    * \return ptr raw pointer to the data buffer of the new field.
636    *
637    * \note This method throws an error and aborts if any of the pre-conditions
638    *  is not satisfied.
639    *
640    * \pre name.empty() == false
641    * \pre hasField( name ) == false
642    * \pre data != nullptr
643    * \pre association >= 0 && association < NUM_FIELD_ASSOCIATION
644    *
645    * \post ptr != nullptr
646    * \post ptr == data
647    * \post hasField( name ) == true
648    *
649    * \see FieldAssociation
650    */
651   template <typename T>
652   inline T* createField(const std::string& name,
653                         int association,
654                         T* data,
655                         IndexType num_components = 1,
656                         IndexType capacity = USE_DEFAULT);
657 
658   /*!
659    * \brief Removes the field with the given name and specified association.
660    *
661    * \param [in] name the name of the field to remove.
662    * \param [in] association the mesh field association.
663    *
664    * \return status true if the field is removed successfully, else, false.
665    *
666    * \pre name.emtpy() == false
667    * \pre association >= 0 && association < NUM_FIELD_ASSOCIATION
668    *
669    * \see FieldAssociation
670    */
671   inline bool removeField(const std::string& name, int association);
672 
673   /*!
674    * \brief Returns pointer to buffer of the field with the given ane and
675    *  specified mesh field association.
676    *
677    * \param [in] name the name of the requested field.
678    * \param [in] association the mesh field association.
679    * \param [out] num_components the number of components per tuple (optional).
680    *
681    * \return ptr raw pointer to the data buffer of the requested field.
682    *
683    * \pre name.empty() == false
684    * \pre hasField( name )
685    * \pre association >= 0 && association < NUM_FIELD_ASSOCIATION
686    *
687    * \see FieldAssociation
688    */
689   /// @{
690   template <typename T>
691   inline T* getFieldPtr(const std::string& name,
692                         int association,
693                         IndexType& num_components);
694 
695   template <typename T>
696   inline T* getFieldPtr(const std::string& name, int association);
697 
698   template <typename T>
699   inline const T* getFieldPtr(const std::string& name,
700                               int association,
701                               IndexType& num_components) const;
702 
703   template <typename T>
704   inline const T* getFieldPtr(const std::string& name, int association) const;
705   /// @}
706 
707   /// @}
708 
709 protected:
710   /// \name Protected Members
711   /// @{
712 
713   int m_ndims;     /*! mesh dimension */
714   int m_type;      /*! the type of the mesh */
715   int m_block_idx; /*! the Block ID of the mesh */
716   int m_part_idx;  /*! the partition ID of the mesh */
717 
718   bool m_explicit_coords;
719   bool m_explicit_connectivity;
720   bool m_has_mixed_topology;
721 
722   FieldData* m_mesh_fields[NUM_FIELD_ASSOCIATIONS];
723 
724 #ifdef AXOM_MINT_USE_SIDRE
725   sidre::Group* m_group;
726   std::string m_topology;
727   std::string m_coordset;
728 #endif
729 
730   /// @}
731 
732   /// \name Protected Constructors (used in derived classes )
733   /// @{
734 
735   /*!
736    * \brief Mesh Constructor.
737    *
738    * \param [in] ndims the number of dimensions
739    * \param [in] type the mesh type.
740    * \param [in] blockId the block ID for this mesh instance.
741    * \param [in] partId the partition ID for this mesh instance.
742    */
743   Mesh(int ndims, int type);
744 
745 #ifdef AXOM_MINT_USE_SIDRE
746 
747   /*!
748    * \brief Constructor for use with a group that already has data.
749    *
750    * \param [in] group the sidre::Group to use.
751    * \param [in] topo optional argument specifying the name of the topology
752    *  associated with this Mesh instance.
753    *
754    * \note If a topology name is not provided, the implementation will construct
755    *  a mesh based on the 1st topology group under the parent "topologies"
756    *  group.
757    *
758    * \pre group != nullptr.
759    * \pre blueprint::isValidRootGroup( group ) == true
760    *
761    * \see sidre::Group
762    */
763   Mesh(sidre::Group* group, const std::string& topo = "");
764 
765   /*!
766    * \brief Constructor for use with an empty group.
767    *
768    * \param [in] ndims the number of dimensions
769    * \param [in] type the mesh type.
770    * \param [in] group the sidre::Group to use.
771    * \param [in] topo the name of the associated topology group.
772    * \param [in] coordset the name of the associated coordset group.
773    *
774    * \note If a topology and coordset name is not provided a default name is
775    *  used by the implementation.
776    *
777    * \pre group != nullptr.
778    * \pre group->getNumGroups() == 0
779    * \pre group->getNumViews() == 0
780    * \post blueprint::isValidRootGroup( group )
781    *
782    * \see sidre::Group
783    */
784   Mesh(int ndims,
785        int type,
786        sidre::Group* group,
787        const std::string& topo,
788        const std::string& coordset);
789 
790   /*!
791    * \brief Helper method to return the associated coordset group.
792    * \return coordset the associated coordset group.
793    *
794    * \pre  m_group != nullptr
795    * \pre  blueprint::isValidRootGroup( m_group )
796    * \post blueprint::isValidCoordsetGroup( coordset )
797    */
798   sidre::Group* getCoordsetGroup();
799 
800   /*!
801    * \brief Helper method to return the associated topology group.
802    * \return topology the associated topology group.
803    *
804    * \pre  m_group != nullptr
805    * \pre  blueprint::isValidRootGroup( m_group )
806    * \post blueprint::isValidTopologyGroup( topology )
807    */
808   sidre::Group* getTopologyGroup();
809 
810 #endif
811 
812   /// @}
813 
814 private:
815   /*!
816    * \brief Get the info corresponding to the given mesh field association.
817    *
818    * \param [in] association the mesh field association, e.g., NODE_CENTERED.
819    * \param [out] num_tuples the number of tuples in the associated FieldData.
820    * \param [out] capacity the capacity of the associated FieldData.
821    */
822   void getFieldInfo(int association,
823                     IndexType& num_tuples,
824                     IndexType& capacity) const;
825 
826   /*!
827    * \brief Helper method to check if the mesh type is valid.
828    * \return status true if the mesh type is valie, else, false.
829    */
validMeshType() const830   inline bool validMeshType() const
831   {
832     return ((m_type >= 0) && (m_type < mint::NUM_MESH_TYPES));
833   }
834 
835   /*!
836    * \brief Helper method to check if the mesh dimension is valid.
837    * \return status true if the mesh dimension is valid, else, false.
838    */
validDimension() const839   inline bool validDimension() const { return (m_ndims >= 1 && m_ndims <= 3); }
840 
841   /*!
842    * \brief Allocates the FieldData internal data-structures.
843    * \note Helper method that is called from the constructor.
844    */
845   void allocateFieldData();
846 
847   /*!
848    * \brief Deallocates the FieldData internal data-structures.
849    * \note Helper method that is called by the destructor.
850    */
851   void deallocateFieldData();
852 
853   DISABLE_COPY_AND_ASSIGNMENT(Mesh);
854   DISABLE_MOVE_AND_ASSIGNMENT(Mesh);
855 };
856 
857 //------------------------------------------------------------------------------
858 //  IMPLEMENTATION OF TEMPLATE & IN-LINE METHODS
859 //------------------------------------------------------------------------------
860 
861 //------------------------------------------------------------------------------
hasSidreGroup() const862 inline bool Mesh::hasSidreGroup() const
863 {
864 #ifdef AXOM_MINT_USE_SIDRE
865   return (m_group != nullptr);
866 #else
867   return false;
868 #endif
869 }
870 
871 //------------------------------------------------------------------------------
getFieldData(int association) const872 inline const FieldData* Mesh::getFieldData(int association) const
873 {
874   SLIC_ERROR_IF(association < 0 || association >= NUM_FIELD_ASSOCIATIONS,
875                 "invalid field association [" << association << "]");
876   SLIC_ERROR_IF(m_mesh_fields[association] == nullptr,
877                 "null field data object w/association [" << association << "]");
878   SLIC_ERROR_IF(m_type == PARTICLE_MESH && association != NODE_CENTERED,
879                 "a particle mesh may only store node-centered fields");
880 
881   return m_mesh_fields[association];
882 }
883 
884 //------------------------------------------------------------------------------
hasField(const std::string & name,int association) const885 inline bool Mesh::hasField(const std::string& name, int association) const
886 {
887   bool found = false;
888 
889   if(association == mint::ANY_CENTERING)
890   {
891     int N = (m_type == mint::PARTICLE_MESH) ? 1 : mint::NUM_FIELD_ASSOCIATIONS;
892     for(int i = 0; !found && i < N; ++i)
893     {
894       const FieldData* fd = getFieldData(i);
895       SLIC_ASSERT(fd != nullptr);
896       found = fd->hasField(name);
897     }
898   }
899   else
900   {
901     const FieldData* fd = getFieldData(association);
902     SLIC_ASSERT(fd != nullptr);
903     found = fd->hasField(name);
904   }
905 
906   return (found);
907 }
908 
909 //------------------------------------------------------------------------------
910 template <typename T>
createField(const std::string & name,int association,IndexType num_components,bool storeInSidre)911 inline T* Mesh::createField(const std::string& name,
912                             int association,
913                             IndexType num_components,
914                             bool storeInSidre)
915 {
916   SLIC_ERROR_IF(hasField(name), "a field with the same name already exists!");
917 
918   FieldData* fd = const_cast<FieldData*>(getFieldData(association));
919   SLIC_ASSERT(fd != nullptr);
920 
921   IndexType num_tuples, capacity;
922   getFieldInfo(association, num_tuples, capacity);
923   T* ptr =
924     fd->createField<T>(name, num_tuples, num_components, capacity, storeInSidre);
925   if(num_tuples > 0)
926   {
927     SLIC_ASSERT(ptr != nullptr);
928   }
929 
930   return (ptr);
931 }
932 
933 //------------------------------------------------------------------------------
934 template <typename T>
createField(const std::string & name,int association,T * data,IndexType num_components,IndexType capacity)935 inline T* Mesh::createField(const std::string& name,
936                             int association,
937                             T* data,
938                             IndexType num_components,
939                             IndexType capacity)
940 {
941   SLIC_ERROR_IF(hasField(name), "a field with the same name already exists!");
942 
943   SLIC_ASSERT(data != nullptr);
944 
945   FieldData* fd = const_cast<FieldData*>(getFieldData(association));
946   SLIC_ASSERT(fd != nullptr);
947 
948   IndexType num_tuples, dummy1;
949   getFieldInfo(association, num_tuples, dummy1);
950   T* ptr = fd->createField<T>(name, data, num_tuples, num_components, capacity);
951   SLIC_ASSERT(ptr == data);
952 
953   return (ptr);
954 }
955 
956 //------------------------------------------------------------------------------
removeField(const std::string & name,int association)957 inline bool Mesh::removeField(const std::string& name, int association)
958 {
959   bool status = false;
960   FieldData* fd = const_cast<FieldData*>(getFieldData(association));
961 
962   const bool hasField = fd->hasField(name);
963   SLIC_WARNING_IF(!hasField, "field [" << name << "] does not exist!");
964 
965   if(hasField)
966   {
967     fd->removeField(name);
968     status = true;
969   }
970 
971   return (status);
972 }
973 
974 //------------------------------------------------------------------------------
975 template <typename T>
getFieldPtr(const std::string & name,int association)976 inline T* Mesh::getFieldPtr(const std::string& name, int association)
977 {
978   IndexType num_components = 0;
979   return getFieldPtr<T>(name, association, num_components);
980 }
981 
982 //------------------------------------------------------------------------------
983 template <typename T>
getFieldPtr(const std::string & name,int association,IndexType & num_components)984 inline T* Mesh::getFieldPtr(const std::string& name,
985                             int association,
986                             IndexType& num_components)
987 {
988   const Mesh* self = const_cast<const Mesh*>(this);
989   const T* ptr = self->getFieldPtr<T>(name, association, num_components);
990   return (const_cast<T*>(ptr));
991 }
992 
993 //------------------------------------------------------------------------------
994 template <typename T>
getFieldPtr(const std::string & name,int association) const995 inline const T* Mesh::getFieldPtr(const std::string& name, int association) const
996 {
997   IndexType num_components = 0;
998   return getFieldPtr<T>(name, association, num_components);
999 }
1000 
1001 //------------------------------------------------------------------------------
1002 template <typename T>
getFieldPtr(const std::string & name,int association,IndexType & num_components) const1003 inline const T* Mesh::getFieldPtr(const std::string& name,
1004                                   int association,
1005                                   IndexType& num_components) const
1006 {
1007   const FieldData* fd = getFieldData(association);
1008   SLIC_ASSERT(fd != nullptr);
1009 
1010   IndexType num_tuples = 0;
1011   const T* ptr = fd->getFieldPtr<T>(name, num_tuples, num_components);
1012   SLIC_ASSERT(ptr != nullptr);
1013 
1014   return (ptr);
1015 }
1016 
1017 //------------------------------------------------------------------------------
getFieldInfo(int association,IndexType & num_tuples,IndexType & capacity) const1018 inline void Mesh::getFieldInfo(int association,
1019                                IndexType& num_tuples,
1020                                IndexType& capacity) const
1021 {
1022   switch(association)
1023   {
1024   case NODE_CENTERED:
1025     num_tuples = getNumberOfNodes();
1026     capacity = getNodeCapacity();
1027     break;
1028   case CELL_CENTERED:
1029     num_tuples = getNumberOfCells();
1030     capacity = getCellCapacity();
1031     break;
1032   case FACE_CENTERED:
1033     num_tuples = getNumberOfFaces();
1034     capacity = getFaceCapacity();
1035     break;
1036   default:
1037     SLIC_ASSERT(association == EDGE_CENTERED);
1038     num_tuples = getNumberOfEdges();
1039     capacity = getEdgeCapacity();
1040     break;
1041   }  // END switch
1042 }
1043 
1044 } /* namespace mint */
1045 } /* namespace axom */
1046 
1047 #endif /* MINT_MESH_HPP_ */
1048