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