Component Architecture¶
This section links the core concepts, presented in the Mesh Representation and Mesh Types sections, to the underlying implementation of the Mint mesh data model. The Component Architecture of Mint’s mesh data model consists of a class hierarchy that follows directly the taxonomy of Mesh Types discussed earlier. The constituent classes of the mesh data model are combined using a mix of class inheritance and composition, as illustrated in the class diagram depicted in Fig. 18.
At the top level, The Mesh Base Class, implemented in mint::Mesh
,
stores common mesh attributes and fields. Moreover, it defines a unified
Application Programming Interface (API) for the various Mesh Types. See the
Mint Doxygen API Documentation for a complete specification of
the API. The Concrete Mesh Classes extend The Mesh Base Class
and implement the Mesh Representation for each of the
Mesh Types respectively. The mint::ConnectivityArray
and
mint::MeshCoordinates
classes, are the two main internal support classes
that underpin the implementation of the Concrete Mesh Classes and facilitate
the representation of the constituent Geometry and Topology
of the mesh.
Note
All Mint classes and functions are encapsulated in the axom::mint
namespace.
The Mesh Base Class¶
The Mesh Base Class stores common attributes associated with a mesh.
Irrespective of the mesh type, a Mint mesh has two identifiers. The mesh
BlockID and mesh DomainID, which are assigned by domain decomposition.
Notably, the computational domain can consist of one or more blocks, which are
usually defined by the user or application. Each block is then subsequently
partitioned to multiple domains that are distributed across processing units
for parallel computation. For example, a sample block and domain decomposition
is depicted in Fig. 19. Each of the constituent domains is
represented by a corresponding mint::Mesh
instance, which in aggregate
define the entire problem domain.
Note
A mint::Mesh
instance provides the means to store the mesh
BlockID and DomainID respectively. However, Mint does not impose a
numbering or partitioning scheme. Assignment of the BlockID and DomainID
is handled at the application level and by the underlying mesh partitioner
that is being employed.
Moreover, each mint::Mesh
instance has associated Mesh Field Data,
represented by the mint::FieldData
class. Each of the constituent
topological mesh entities, i.e. the Cells, Faces and Nodes
comprising the mesh, has a handle to a corresponding mint::FieldData
instance. The mint::FieldData
object essentialy provides a container to
store and manage a collection of fields, defined over the corresponding mesh
entity.
Warning
Since a Particle Mesh is defined by a set of Nodes, it can only store Field Data at its constituent Nodes. All other supported Mesh Types can have Field Data associated with their constituent Cells, Faces and Nodes.
Mesh Field Data¶
A mint::FieldData
instance typically stores multiple fields.
Each field is represented by an instance of a mint::Field
object
and defines a named numerical quantity, such as mass, velocity,
temperature, etc., defined on a given mesh. Moreover, a field can be either
single-component, i.e. a scalar quantity, or, multi-component, e.g.
a vector or tensor quantity. Typically, a field represents some physical
quantity that is being modeled, or, an auxiliary quantity that is needed to
perform a particular calculation.
In addition, each mint::Field
instance can be of different data type.
The mint::FieldData
object can store different types of fields.
For example, floating point quantities i.e., float
or double
,
as well as, integral quantities, i.e. int32_t
, int64_t
, etc. This is
accomplished using a combination of C++ templates and inheritance. The
mint::Field
object is an abstract base class that defines a type-agnostic
interface to encapsulate a field. Since mint::Field
is an abstract
base class, it is not instantiated directly. Instead, all fields are created by
instantiating a mint::FieldVariable
object, a class templated on data type,
that derives from the mint::Field
base class. For example, the code snippet
below illustrates how fields of different type can be instantiated.
...
// create a scalar field to store mass as a single precision quantity
mint::Field* mass = new mint::FieldVariable< float >( "mass", size );
// create a velocity vector field as a double precision floating point quantity
constexpr int NUM_COMPONENTS = 3;
mint::Field* vel = new mint::FieldVariable< double >( "vel", size, NUM_COMPONENTS );
...
Generally, in application code, it is not necessary to create fields using the
mint::FieldVariable
class directly. The mint::Mesh
object provides
convenience methods for adding, removing and accessing fields on a mesh.
Consult the Tutorial for more details on
Working with Fields on a Mesh.
Concrete Mesh Classes¶
The Concrete Mesh Classes, extend The Mesh Base Class and implement the underlying Mesh Representation of the various Mesh Types, depicted in Fig. 20.
Structured Mesh¶
All Structured Mesh types in Mint can be represented by an instance of
the mint::StructuredMesh
class, which derives directly from The Mesh Base Class,
mint::Mesh
. The mint::StructuredMesh
class is also an abstract base class
that encapsulates the implementation of the implicit, ordered and regular
Topology that is common to all Structured Mesh types. The
distinguishing characteristic of the different Structured Mesh types is
the representation of the constituent Geometry. Mint implements each of
the different Structured Mesh types by a corresponding class, which
derives directly from mint::StructuredMesh
and thereby inherit its
implicit Topology representation.
Consequently, support for the Uniform Mesh is implemented in
mint::UniformMesh
. The Geometry of a Uniform Mesh is
implicit, given by two attributes, the mesh origin and spacing.
Consequently, the mint::UniformMesh
consists of two data members to
store the origin and spacing of the Uniform Mesh and provides
functionality for evaluating the spatial coordinates of a node given its
corresponding IJK lattice coordinates.
Similarly, support for the Rectilinear Mesh is implemented in
mint::RectilinearMesh
. The constituent Geometry representation of
the Rectilinear Mesh is semi-implicit. The spatial coordinates
of the Nodes along each axis are specified explicitly while the
coordinates of the interior Nodes are evaluated by taking the
Cartesian product of the corresponding coordinate along each coordinate
axis. The mint::RectilinearMesh
consists of seperate arrays to
store the coordinates along each axis for the semi-implicit Geometry
representation of the Rectilinear Mesh.
Support for the Curvilinear Mesh is implemented by the
mint::CurvilinearMesh
class. The Curvilinear Mesh requires explicit
representation of its constituent Geometry. The mint::CurvilinearMesh
makes use of the mint::MeshCoordinates
class to explicitly represent the
spatial coordinates associated with the constituent Nodes of the mesh.
Unstructured Mesh¶
Mint’s Unstructured Mesh representation is provided by the
mint::UnstructuredMesh
class, which derives directly from the
The Mesh Base Class, mint::Mesh
. An Unstructured Mesh has both
explicit Geometry and Topology. As with the
mint::CurvilinearMesh
class, the explicit Geometry representation
of the Unstructured Mesh employs the mint::MeshCoordinates
. The
constituent Topology is handled by the mint::ConnectivityArray
,
which is employed for the representation of all the topological
Connectivity information, i.e. cell-to-node, face-to-node,
face-to-cell, etc.
Note
Upon construction, a mint::UnstructuredMesh
instance consists of the
minimum sufficient representation for an Unstructured Mesh comprised
of the cell-to-node Connectivity information.
Applications that require face Connectivity information must
explicitly call the initializeFaceConnectivity()
method on the
corresponding Unstructured Mesh object.
Depending on the cell Topology being employed, an Unstructured Mesh
can be classified as either a Single Cell Type Topology Unstructured Mesh
or a Mixed Cell Type Topology Unstructured Mesh. To accomodate these
two different representations, the mint::UnstructuredMesh
class, is templated
on CELL_TOPOLOGY
. Internally, the template argument is used to indicate
the type of mint::ConnectivityArray
to use, i.e. whether,
stride access addressing or indirect addressing is used, for
Single Cell Type Topology and Mixed Cell Type Topology respectively.
Particle Mesh¶
Support for the Particle Mesh representation is implemented in
mint::ParticleMesh
, which derives directly from The Mesh Base Class,
mint::Mesh
. A Particle Mesh discretizes the domain by a set
of particles, which correspond to the constituent Nodes of the mesh.
The Nodes of a Particle Mesh can also be thought of as Cells,
however, since this information is trivially obtrained, there is not need
to be stored explicitly, e.g. using a Single Cell Type Topology
Unstructured Mesh representation. Consequently, the Particle Mesh
representation consists of explicit Geometry and implicit
Topology. As with the mint::CurvilinearMesh
and
mint::UnstructuredMesh
, the explicit Geometry of the
Particle Mesh is represented by employing the mint::MeshCoordinates
as an internal class member.
The following code snippet provides a simple examples illustrating how to construct and operate on a Particle Mesh.
1// Copyright (c) 2017-2024, 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/*!
7 * \file
8 *
9 * \brief Illustrates how to construct and use a ParticleMesh to perform
10 * operations on a set of particles.
11 */
12
13// Axom utilities
14#include "axom/core.hpp"
15#include "axom/mint.hpp"
16
17// namespace aliases
18namespace mint = axom::mint;
19namespace utilities = axom::utilities;
20
21//------------------------------------------------------------------------------
22int main(int AXOM_UNUSED_PARAM(argc), char** AXOM_UNUSED_PARAM(argv))
23{
24 using int64 = axom::IndexType;
25 const axom::IndexType NUM_PARTICLES = 100;
26 const int DIMENSION = 3;
27
28 const double HI = 10.0;
29 const double LO = -10.0;
30 const double VLO = 0.0;
31 const double VHI = 1.0;
32
33 // STEP 0: create the ParticleMesh
34 mint::ParticleMesh particles(DIMENSION, NUM_PARTICLES);
35
36 // STEP 1: Add fields to the Particles
37 double* vx = particles.createField<double>("vx", mint::NODE_CENTERED);
38 double* vy = particles.createField<double>("vy", mint::NODE_CENTERED);
39 double* vz = particles.createField<double>("vz", mint::NODE_CENTERED);
40 int64* id = particles.createField<int64>("id", mint::NODE_CENTERED);
41
42 // STEP 2: grab handle to the particle position arrays
43 double* px = particles.getCoordinateArray(mint::X_COORDINATE);
44 double* py = particles.getCoordinateArray(mint::Y_COORDINATE);
45 double* pz = particles.getCoordinateArray(mint::Z_COORDINATE);
46
47 // STEP 3: loop over the particle data
48 const int64 numParticles = particles.getNumberOfNodes();
49 for(int64 i = 0; i < numParticles; ++i)
50 {
51 px[i] = utilities::random_real(LO, HI);
52 py[i] = utilities::random_real(LO, HI);
53 pz[i] = utilities::random_real(LO, HI);
54
55 vx[i] = utilities::random_real(VLO, VHI);
56 vy[i] = utilities::random_real(VLO, VHI);
57 vz[i] = utilities::random_real(VLO, VHI);
58
59 id[i] = i;
60
61 } // END
62
63 // STEP 4: write the particle mesh in VTK format for visualization
64 mint::write_vtk(&particles, "particles.vtk");
65
66 return 0;
67}
Mesh Storage Management¶
Mint provides a flexible Mesh Storage Management system that can optionally interoperate with Sidre as the underlying, in-memory, hierarchichal datastore. This enables Mint to natively conform to Conduit’s Blueprint protocol for representing a computational mesh in memory and thereby, facilitate with the integration across different physics packages.
Mint’s Mesh Storage Management substrate supports three storage options. The applicable operations and ownership state of each storage option are summarized in the table below, followed by a brief description of each option.
Modify |
Reallocate |
Ownership |
|
---|---|---|---|
✓ |
✓ |
Mint |
|
✓ |
Application |
||
✓ |
✓ |
Native Storage¶
A Mint object using Native Storage owns all memory and associated data. The data can be modified and the associated memory space can be reallocated to grow and shrink as needed. However, once the Mint object goes out-of-scope, all data is deleted and the memory is returned to the system.
See the Tutorial for more information and a set of concrete examples on how to create a mesh using Native Storage.
External Storage¶
A Mint object using External Storage has a pointer to a supplied application buffer. In this case, the data can be modified, but the application maintains ownership of the underlying memory. Consequently, the memory space cannot be reallocated and once the Mint object goes out-of-scope, the data is not deleted. The data remains persistent in the application buffers until it is deleted by the application.
See the Tutorial for more information on Using External Storage.
Sidre Storage¶
A Mint object using Sidre Storage is associated with a Sidre Group object which has owneship of the mesh data. In this case the data can be modified and the associated memory can be reallocated to grow and shrink as needed. However, when the Mint object goes out-of-scope, the data remains persistent in Sidre.
See the Tutorial for more information and a set of concrete examples on Using Sidre.