The sections/tutorial
section consists of simple examples and code snippets that illustrate how to use Mint's core classes and functions to construct and operate on the various supported MeshTypes
. The examples presented herein aim to illustrate specific Mint concepts and capabilities in a structured and simple format. To quickly learn basic Mint concepts and capabilities through an illustrative walk-through of a complete working code example, see the sections/mint/getting_started
section. Additional code examples, based on Mint mini-apps, are provided in the sections/examples
section. For thorough documentation of the interfaces of the various classes and functions in Mint, developers are advised to consult the Mint Doxygen API Documentation, in conjunction with this sections/tutorial
.
A UniformMesh
is relatively the simplest StructuredMesh
type, but also, the most restrictive mesh out of all MeshTypes
. The constituent Nodes
of the UniformMesh
are uniformly spaced along each axis on a regular lattice. Consequently, a UniformMesh
can be easily constructed by simply specifying the spatial extents of the domain and desired dimensions, e.g. the number of Nodes
along each dimension.
For example, a 50 × 50 UniformMesh
, defined on a bounded domain given by the interval ℐ : [ − 5.0, 5.0] × [ − 5.0, 5.0], can be easily constructed as follows:
../../../examples/user_guide/mint_tutorial.cpp
The resulting mesh is depicted in figs/uniformMesh50x50
.
Resulting Uniform Mesh.
A RectilinearMesh
, also called a product mesh, is similar to a UniformMesh
. However, the constituent Nodes
of a RectilinearMesh
are not uniformly spaced. The spacing between adjacent Nodes
can vary arbitrarily along each axis, but the Topology
of the mesh remains a regular StructuredMesh
Topology
. To allow for this flexibility, the coordinates of the Nodes
along each axis are explicitly stored in separate arrays, i.e. x, y and z, for each coordinate axis respectively.
The following code snippet illustrates how to construct a 25 × 25 RectilinearMesh
where the spacing of the Nodes
grows according to an exponential stretching function along the x and y axis respectively. The resulting mesh is depicted in figs/rectilinearMesh25x25
.
../../../examples/user_guide/mint_tutorial.cpp
Resulting RectilinearMesh
.
A CurvilinearMesh
, also called a body-fitted mesh, is the most general of the StructuredMesh
types. Similar to the UniformMesh
and RectilinearMesh
types, a CurvilinearMesh
also has regular StructuredMesh
Topology
. However, the coordinates of the Nodes
comprising a CurvilinearMesh
are defined explicitly, enabling the use of a StructuredMesh
discretization with more general geometric domains, i.e., the domain may not be necessarily Cartesian. Consequently, the coordinates of the Nodes
are specified explicitly in separate arrays, x, y, and z.
The following code snippet illustrates how to construct a 25 × 25 CurvilinearMesh
. The coordinates of the Nodes
follow from the equation of a cylinder with a radius of 2.5. The resulting mesh is depicted in figs/curvilinearMesh25x25
.
../../../examples/user_guide/mint_tutorial.cpp
Resulting CurvilinearMesh
.
An UnstructuredMesh
with SingleCellTopology
has both explicit Topology
and Geometry
. However, the cell type that the mesh stores is known a priori, allowing for an optimized underlying MeshRepresentation
, compared to the more general MixedCellTopology
MeshRepresentation
.
Since both Geometry
and Topology
are explicit, an UnstructuredMesh
is created by specifying:
- the coordinates of the constituent
Nodes
, and the
Cells
comprising the mesh, defined by the cell-to-nodeConnectivity
Resulting
SingleCellTopology
UnstructuredMesh
The following code snippet illustrates how to create the simple UnstructuredMesh
depicted in figs/triangularMesh
.
../../../examples/user_guide/mint_tutorial.cpp
An UnstructuredMesh
is represented by the mint::UnstructuredMesh
template class. The template argument of the class, mint::SINGLE_SHAPE
indicates the mesh has SingleCellTopology
. The two arguments to the class constructor correspond to the problem dimension and cell type, which in this case, is 2 and mint::TRIANGLE
respectively. Once the mesh is constructed, the Nodes
and Cells
are appended to the mesh by calls to the appendNode()
and appendCell()
methods respectively. The resulting mesh is shown in figs/triangularMesh
.
Tip
The storage for the mint::UstructuredMesh
will grow dynamically as new Nodes
and Cells
are appended on the mesh. However, reallocations tend to be costly operations. For best performance, it is advised the node capacity and cell capacity for the mesh are specified in the constructor if known a priori. Consult the Mint Doxygen API Documentation for more details.
Compared to the SingleCellTopology
UnstructuredMesh
, a MixedCellTopology
UnstructuredMesh
has also explicit Topology
and Geometry
. However, the cell type is not fixed. Notably, the mesh can store different CellTypes
, e.g. triangles and quads, as shown in the simple 2D mesh depicted in figs/mixedMesh
.
Sample
MixedCellTopology
UnstructuredMesh
As with the SingleCellTopology
UnstructuredMesh
, a MixedCellTopology
UnstructuredMesh
is created by specifying:
- the coordinates of the constituent
Nodes
, and - the
Cells
comprising the mesh, defined by the cell-to-nodeConnectivity
The following code snippet illustrates how to create the simple MixedCellTopology
UnstructuredMesh
depicted in figs/mixedMesh
, consisting of 2 triangles and 1 quadrilateral Cells
.
../../../examples/user_guide/mint_tutorial.cpp
Similarly, a MixedCellTopology
UnstructuredMesh
is represented by the mint::UnstructuredMesh
template class. However, the template argument to the class is mint::MIXED_SHAPE
, which indicates that the mesh has MixedCellTopology
. In this case, the class constructor takes only a single argument that corresponds to the problem dimension, i.e. 2. Once the mesh is constructed, the constituent Nodes
of the mesh are specified by calling the appendNode()
method on the mesh. Similarly, the Cells
are specified by calling the appendCell()
method. However, in this case, appendCell()
takes one additional argument that specifies the cell type, since that can vary.
Tip
The storage for the mint::UstructuredMesh
will grow dynamically as new Nodes
and Cells
are appended on the mesh. However, reallocations tend to be costly operations. For best performance, it is advised the node capacity and cell capacity for the mesh are specified in the constructor if known a priori. Consult the Mint Doxygen API Documentation for more details.
A mesh typically has associated FieldData
that store various numerical quantities on the constituent Nodes
, Cells
and Faces
of the mesh.
Warning
Since a ParticleMesh
is defined by a set of Nodes
, it can only store FieldData
at its constituent Nodes
. All other supported MeshTypes
can have FieldData
associated with their constituent Cells
, Faces
and Nodes
.
Given a mint::Mesh
instance, a field is created by specifying:
- The name of the field,
- The field association, i.e. centering, and
- Optionally, the number of components of the field, required if the field is not a scalar quantity.
For example, the following code snippet creates the scalar density field, den
, stored at the cell centers, and the vector velocity field, vel
, stored at the Nodes
:
../../../examples/user_guide/mint_tutorial.cpp
Note
If Sidre is used as the backend MeshStorageManagement
substrate, createField()
will populate the Sidre tree hierarchy accordingly. See usingSidre
for more information.
- Note, the template argument to the
createField()
method indicates the underlying field type, e.g.double
,int
, etc. In this case, both fields are ofdouble
field type. - The name of the field is specified by the first required argument to the
createField()
call. - The field association, is specified by the second argument to the
createField()
call. - A third, optional, argument may be specified to indicate the number of components of the corresponding field. In this case, since
vel
is a vector quantity the number of components must be explicitly specified. - The
createField()
method returns a raw pointer to the data corresponding to the new field, which can be used by the application.
Note
Absence of the third argument when calling createField()
indicates that the number of components of the field defaults to 1 and thereby the field is assumed to be a scalar quantity.
Specific, existing fields can be requested by calling getFieldPtr()
on the target mesh as follows:
../../../examples/user_guide/mint_tutorial.cpp
- As with the
createField()
method, the template argument indicates the underlying field type, e.g.double
,int
, etc. - The first argument specifies the name of the requested field
- The second argument specifies the corresponding association of the requested field.
- The third argument is optional and it can be used to get back the number of components of the field, i.e. if the field is not a scalar quantity.
Note
Calls to getFieldPtr()
assume that the caller knows a priori the:
- Field name,
- Field association, i.e. centering, and
- The underlying field type, e.g.
double
,int
, etc.
An application can also check if a field exists by calling hasField()
on the mesh, which takes as arguments the field name and corresponding field association as follows:
../../../examples/user_guide/mint_tutorial.cpp
The hasField()
method returns true
or false
indicating whether a given field is defined on the mesh.
A field can be removed from a mesh by calling removeField()
on the target mesh, which takes as arguments the field name and corresponding field association as follows:
../../../examples/user_guide/mint_tutorial.cpp
The removeField()
method returns true
or false
indicating whether the corresponding field was removed successfully from the mesh.
In some cases, an application may not always know a priori the name or type of the field, or, we may want to write a function to process all fields, regardless of their type.
The following code snippet illustrates how to do that:
../../../examples/user_guide/mint_tutorial.cpp
- The
mint::FieldData
instance obtained by callinggetFieldData()
on the target mesh holds all the fields with a field association given byFIELD_ASSOCIATION
. - The total number of fields can be obtained by calling
getNumFields()
on themint::FieldData
instance, which allows looping over the fields with a simplefor
loop. - Within the loop, a pointer to a
mint::Field
instance, corresponding to a particular field, can be requested by callinggetField()
on themint::FieldData
instance, which takes the field index,ifield
, as an argument. Given the pointer to a
mint::Field
instance, an application can query the following field metadata:- The field name, by calling
getName
, - The number of tuples of the field, by calling
getNumTuples()
- The number of components of the field, by calling
getNumComponents()
- The underlying field type by calling
getType()
- The field name, by calling
- Given the above metadata, the application can then obtain a pointer to the raw field data by calling
getFieldPtr()
on the target mesh, as shown in the code snippet above.
A Mint mesh may also be constructed from ExternalStorage
. In this case, the application holds buffers that describe the constituent Geometry
, Topology
and FieldData
of the mesh, which are wrapped in Mint for further processing.
The following code snippet illustrates how to use ExternalStorage
using the SingleCellTopology
UnstructuredMesh
used to demonstrate how to createAnUnstructuredMesh
with NativeStorage
:
../../../examples/user_guide/mint_tutorial.cpp
- The application has the following buffers:
x
andy
buffers to hold the coordinates of theNodes
cell_connectivity
, which stores the cell-to-node connectivityden
which holds a scalar density field defined over the constituentCells
of the mesh.
- The mesh is created by calling the
mint::UnstructuredMesh
class constructor, with the following arguments:- The cell type, i.e,
mint::TRIANGLE
, - The total number of cells,
NUM_CELLS
, - The
cell_connectivity
which specifies theTopology
of theUnstructuredMesh
, - The total number of nodes,
NUM_NODES
, and - The
x
,y
coordinate buffers that specify theGeometry
of theUnstructuredMesh
.
- The cell type, i.e,
- The scalar density field is registered with Mint by calling the
createField()
method on the target mesh instance, as before, but also passing the raw pointer to the application buffer in a third argument.
Note
The other MeshTypes
can be similarly constructed using ExternalStorage
by calling the appropriate constructor. Consult the Mint Doxygen API Documentation for more details.
The resulting mesh instance points to the application's buffers. Mint may be used to process the data e.g., outputToVTK
etc. The values of the data may also be modified, however the mesh cannot dynamically grow or shrink when using ExternalStorage
.
Warning
A mesh using ExternalStorage
may modify the values of the application data. However, the data is owned by the application that supplied the external buffers. Mint cannot reallocate external buffers to grow or shrink the the mesh. Once the mesh is deleted, the data remains persistent in the application buffers until it is deleted by the application.
Mint can also use Sidre as the underlying MeshStorageManagement
substrate, thereby, facilitate the integration of packages or codes within the overarching WSC software ecosystem. Sidre is another component of the Axom Toolkit that provides a centralized data management system that enables efficient coordination of data across the constituent packages of a multi-physics application.
There are two primary operations a package/code may want to perform:
createANewMeshInSidre
so that it can be shared with other packages.importAMeshFromSidre
, presumably created by different package or code upstream, to operate on, e.g. evaluate a new field on the mesh, etc.
Code snippets illustrating these two operations are presented in the following sections using a simple UnstructuredMesh
example. However, the basic concepts extend to all supported MeshTypes
.
Note
To use Sidre with Mint, the Axom Toolkit must be compiled with Conduit support and Sidre must be enabled (default). Consult the Axom Quick Start Guide for the details on how to build the Axom Toolkit.
Creating a mesh using Sidre is very similar to creating a mesh that uses NativeStorage
. The key difference is that when calling the mesh constructor, the target sidre::Group
, that will consist of the mesh, must be specified.
Warning
The target sidre::Group
supplied to the mesh constructor is expected to be empty.
The following code snippet illustrates this capability using the SingleCellTopology
UnstructuredMesh
used to demonstrate how to createAnUnstructuredMesh
with NativeStorage
. The key differences in the code are highlighted below:
../../../examples/user_guide/mint_tutorial.cpp
Note
A similar construction follows for all supported MeshTypes
. To createANewMeshInSidre
the target sidre::Group
that will consist of the mesh is specified in the constructor in addition to any other arguments. Consult the Mint Doxygen API Documentation for more details.
When the constructor is called, the target sidre::Group
is populated according to the Conduit Blueprint mesh description. Any subsequent changes to the mesh are reflected accordingly to the corresponding sidre::Group
. The rawSidreData
generated after the above code snippet executes are included for reference in the sections/mint/appendix
.
However, once the mesh object goes out-of-scope the mesh description and any data remains persisted in Sidre. The mesh can be deleted from Sidre using the corresponding Sidre API calls.
Warning
A Mint mesh, bound to a Sidre Group, can only be deleted from Sidre when the Group consisting the mesh is deleted from Sidre, or, when the Sidre Datastore instance that holds the Group is deleted. When a mesh, bound to a Sidre Group is deleted, its mesh representation and any data remain persistent within the corresponding Sidre Group hierarchy.
Support for importing an existing mesh from Sidre, that conforms to the Conduit Blueprint mesh description, is provided by the mint::getMesh()
function. The mint::getMesh()
function takes the sidre::Group
instance consisting of the mesh as an argument and returns a corresponding mint::Mesh
instance. Notably, the returned mint:Mesh
instance can be any of the supported MeshTypes
.
The following code snippet illustrates this capability:
../../../examples/user_guide/mint_tutorial.cpp
- The mesh is imported from Sidre by calling
mint::getMesh()
, passing thesidre::Group
consisting of the mesh as an argument. - The mesh type of the imported mesh can be queried by calling the
getMeshType()
on the imported mesh object. - Moreover, an application can check if the mesh is bound to a Sidre group by calling
hasSidreGroup()
on the mesh. - Once the mesh is imported, the application can operate on it, e.g.
outputToVTK
, etc., as illustrated in the above code snippet. - Any subsequent changes to the mesh are reflected accordingly to the corresponding
sidre::Group
However, once the mesh object goes out-of-scope the mesh description and any data remains persisted in Sidre. The mesh can be deleted from Sidre using the corresponding Sidre API calls.
Warning
- When a Mint mesh bound to a Sidre Group is deleted, its mesh representation and any data remain persistent within the corresponding Sidre Group hierarchy.
- A Mint mesh, bound to a Sidre Group, is deleted from Sidre by deleting the corresponding Sidre Group, or, when the Sidre Datastore instance that holds the Group is deleted.
The NodeTraversalFunctions
iterate over the constituent Nodes
of the mesh and apply a user-supplied kernel operation, often specified with a Lambda Expression. The NodeTraversalFunctions
are implemented by the mint::for_all_nodes()
family of functions, which take an ExecutionPolicy
as the first template argument, and optionally, a second template argument to indicate the ExecutionSignature
of the supplied kernel.
Note
If a second template argument is not specified, the default ExecutionSignature
is set to xargs::index
, which indicates that the supplied kernel takes a single argument corresponding to the index of the iteration space, in this case the node index, nodeIdx
.
The following code snippet illustrates a simple loop over the Nodes
of a 2D mesh that computes the velocity magnitude, vmag
, given the corresponding velocity components, vx
and vy
.
../../../examples/user_guide/mint_tutorial.cpp
The coordinates of a node are sometimes also required in addition to its index. This additional information may be requested by supplying xargs::x
(in 1D), xargs::xy
(in 2D) or xargs::xyz
(in 3D), as the second template argument to the for_all_nodes()
method to specify the executionSignature
for the kernel.
This capability is demonstrated by the following code snippet, consisting of a kernel that updates the nodal velocity components, based on old node positions, stored at the xold
and yold
node-centered fields, respectively.
../../../examples/user_guide/mint_tutorial.cpp
Note
- The second template argument,
mint::xargs::xy
, indicates that the supplied kernel expects thex
andy
node coordinates as arguments in addition to itsnodeIdx
.
When working with a StructuredMesh
, it is sometimes required to expose the regular Topology
of the StructuredMesh
to obtain higher performance for a particular algorithm. This typically entails using the logical IJK ordering of the StructuredMesh
to implement certain operations. The template argument, xargs::ij
or xargs::ijk
, for 2D or 3D respectively, may be used as the second template argument to the for_all_nodes()
function to specify the executionSignature
of the supplied kernel.
For example, the following code snippet illustrates how to obtain a node's i
and j
indices within a sample kernel that computes the linear index of each node and stores the result in a node-centered field, ID
.
../../../examples/user_guide/mint_tutorial.cpp
Warning
In this case, the kernel makes use of the IJK indices and hence it is only applicable for a StructuredMesh
.
The CellTraversalFunctions
iterate over the constituent Cells
of the mesh and apply a user-supplied kernel operation, often specified with a Lambda Expression. The CellTraversalFunctions
are implemented by the mint::for_all_cells()
family of functions, which take an ExecutionPolicy
as the first template argument, and optionally, a second template argument to indicate the ExecutionSignature
of the supplied kernel.
Note
If a second template argument is not specified, the default ExecutionSignature
is set to xargs::index
, which indicates that the supplied kernel takes a single argument corresponding to the index of the iteration space, in this case the cell index, cellIdx
.
The following code snippet illustrates a simple loop over the constituent Cells
of the mesh that computes the cell density (den
), given corresponding mass (mass
) and volume (vol
) quantities.
../../../examples/user_guide/mint_tutorial.cpp
Certain operations may require the IDs of the constituent cell Nodes
for some calculation. The template argument, xargs::nodeids
, may be used as the second template argument to the for_all_cells()
function to specify the executionSignature
for the kernel. The xargs::nodeids
indicates that the supplied kernel also takes the the IDs of the constituent cell Nodes
as an argument.
This feature is demonstrated with the following code snippet, which averages the node-centered velocity components to corresponding cell-centered fields:
../../../examples/user_guide/mint_tutorial.cpp
Note
xargs::nodeids
indicates that the specified kernel takes three arguments:cellIdx
, the ID of the cell,nodeIDs
, an array of the constituent node IDs, andN
, the number ofNodes
for the given cell.
The coordinates of the constituent cell Nodes
are often required in some calculations. A cell's node coordinates may be supplied to the specified kernel as an argument using xargs::coords
as the second template argument to the for_all_cells()
function, to specify the executionSignature
of the supplied kernel.
This feature is demonstrated with the following code snippet, which computes the cell centroid by averaging the coordinates of the constituent cell Nodes
:
Note
Since this kernel does not use the node IDs, the argument to the kernel is annotated using the AXOM_UNUSED_PARAM
macro to silence compiler warnings.
../../../examples/user_guide/mint_tutorial.cpp
Note
xargs::coords
indicates that the specified kernel takes the following arguments:cellIdx
, the ID of the cell,coords
, a matrix that stores the cell coordinates, such that:- The number of rows corresponds to the problem dimension, and,
- The number of columns corresponds to the number of nodes.
- The ith column vector of the matrix stores the coordinates of the ith node.
nodeIdx
array of corresponding node IDs.
The IDs of the constituent cell Faces
are sometimes needed to access the corresponding face-centered quantities for certain operations. The face IDs can be obtained using xargs::faceids
as the second template argument to the for_all_faces()
function, to specify the executionSignature
of the supplied kernel.
This feature is demonstrated with the following code snippet, which computes the perimeter of each cell by summing the pre-computed face areas:
../../../examples/user_guide/mint_tutorial.cpp
Note
xargs::faceids
indicates that the specified kernel takes the following arguments:cellIdx
, the ID of the cell,faceIDs
, an array of the constituent face IDs, and,N
, the number ofFaces
for the given cell.
As with the NodeTraversalFunctions
, when working with a StructuredMesh
, it is sometimes required to expose the regular Topology
of the StructuredMesh
to obtain higher performance for a particular algorithm. This typically entails using the logical IJK ordering of the StructuredMesh
to implement certain operations. The template argument, xargs::ij
(in 2D) or xargs::ijk
(in 3D) may be used as the second template argument to the for_all_cells()
function, to specify the executionSignature
of the supplied kernel.
For example, the following code snippet illustrates to obtain a cell's i
and j
indices within a kernel that computes the linear index of each cell and stores the result in a cell-centered field, ID
.
../../../examples/user_guide/mint_tutorial.cpp
Warning
In this case, the kernel makes use of the IJK indices and hence it is only applicable for a StructuredMesh
.
The FaceTraversalFunctions
functions iterate over the constituent Faces
of the mesh and apply a user-supplied kernel operation, often specified with a Lambda Expression. The FaceTraversalFunctions
are implemented by the mint::for_all_faces()
family of functions. which take an ExecutionPolicy
as the first template argument, and optionally, a second template argument to indicate the ExecutionSignature
of the supplied kernel.
Note
If a second template argument is not specified, the default ExecutionSignature
is set to xargs::index
, which indicates that the supplied kernel takes a single argument corresponding to the index of the iteration space, in this case the face index, faceIdx
.
The following code snippet illustrates a simple loop over the constituent Faces
of a 2D mesh that computes an interpolated face-centered quantity (temp
) based on pre-computed interpolation coefficients t1
, t2
and w
.
../../../examples/user_guide/mint_tutorial.cpp
The IDs of the constituent face Nodes
are sometimes needed to access associated node-centered data for certain calculations. The template argument, xargs::nodeids
, may be used as the second template argument to the for_all_faces()
function to specify the executionSignature
of the supplied kernel. The xargs::nodeids
template argument indicates that the supplied kernel also takes the IDs of the constituent face Nodes as an argument.
This feature is demonstrated with the following code snippet which averages the node-centered velocity components to corresponding face-centered quantities:
../../../examples/user_guide/mint_tutorial.cpp
Note
xargs::nodeids
indicates that the specified kernel takes three arguments:faceIdx
, the ID of the cell,nodeIDs
, an array of the constituent node IDs, andN
, the number ofNodes
for the corresponding face.
The coordinates of the constituent face Nodes
are often required in some calculations. The constituent face node coordinates may be supplied to the specified kernel as an argument using xargs::coords
as the second template argument to the for_all_faces()
function, to specify the executionSignature
of the supplied kernel.
This feature is demonstrated with the following code snippet, which computes the face centroid by averaging the coordinates of the constituent face Nodes
:
Note
Since this kernel does not use the node IDs, the argument to the kernel is annotated using the AXOM_UNUSED_PARAM
macro to silence compiler warnings.
../../../examples/user_guide/mint_tutorial.cpp
Note
xargs::coords
indicates that the specified kernel takes the following arguments:faceIdx
, the ID of the cell,coords
, a matrix that stores the cell coordinates, such that:- The number of rows corresponds to the problem dimension, and,
- The number of columns corresponds to the number of nodes.
- The ith column vector of the matrix stores the coordinates of the ith node.
nodeIdx
array of corresponding node IDs.
The constituent Faces
of a mesh can be bound to either one or two Cells
. The IDs of the Cells
abutting a face are required in order to obtain the corresponding cell-centered quantities, needed by some calculations. The template argument, xargs::cellids
, may be used as the second template argument to the for_all_faces()
function to specify the executionSignature
of the supplied kernel. Thereby, indicate that the supplied kernel also takes the IDs of the two abutting cells as an argument.
Note
External boundary faces are only bound to one cell. By convention, the ID of the second cell for external boundary faces is set to − 1.
This functionality is demonstrated with the following example that loops over the constituent Faces
of a mesh and marks external boundary faces:
../../../examples/user_guide/mint_tutorial.cpp
Note
xargs::coords
indicates that the specified kernel takes the following arguments:faceIdx
, the ID of the cell,c1
, the ID of the first cell,c2
, the ID of the second cell, set to a − 1 if the face is an external boundary face.
Mint provides basic support for sections/fem
consisting of LagrangeBasis
shape functions for commonly employed CellTypes
and associated operations, such as functions to evaluate the Jacobian and compute the forward and inverse IsoparametricMapping
.
Warning
Porting and refactoring of Mint's sections/fem
for GPUs is under development. This feature will be available in future versions of Mint.
All associated functionality with sections/fem
is exposed to the application through the mint::FiniteElement
class. The following code snippet illustrates how to createAFiniteElementObject
using a Linear Lagrangian Quadrilateral Finite Element as an example:
../../../examples/user_guide/mint_tutorial.cpp
- The
mint::FiniteElement
constructor takes two arguments:- An N × M Matrix consisting of the cell coordinates, where, N corresponds to physical dimension of the cell and M corresponds to the number of constituent cell nodes. The cell coordinates are organized in the matrix such that, each column vector stores the coordinates of a corresponding node.
- The cell type, e.g.
mint::QUAD
- Then,
mint::bind_basis()
is called to bind the Finite Element object to theLagrangeBasis
. Effectively, this step wires the pointers to theLagrangeBasis
shape functions for the particular cell type.
A similar construction follows for different CellTypes
and associated supported shape functions.
The Finite Element object, once constructed and bound to a basis, it may be used to perform the following operations:
- Given a point in reference space, ξ̂ ∈ Ω̄:
evaluateShapeFunctions
, Ni(ξ), associated with each of the constituent cell nodes, which are often used as interpolation weights,evaluateTheJacobian
, J(ξ), and- Compute the
forwardIsoparametricMap
x⃗ : Ω̄ → Ωe- Given a point in physical space, x̂ ∈ Ω:
- Compute the
inverseIsoparametricMap
, which attempts to evaluate the corresponding reference coordinates of the point, ξ̂ ∈ Ω̄, with respect to the finite element, Ωe. This operation is only defined for points that are inside the element (within some ϵ).
The shape functions can be readily computed from any mint::FiniteElement
instance by calling the evaluateShapeFunctions()
method on the finite element object. The following code snippet illustrates how to evaluateShapeFunctions
at the isoparametric center of a quadrilateral element, given by ξ = (0.5, 0.5)T:
../../../examples/user_guide/mint_tutorial.cpp
- The
evaluateShapeFunctions()
method takes two arguments:xi
, an input argument corresponding to the reference coordinates of the point, ξ̂, where the shape functions will be evaluated, andN
, an output argument which is an array of length equal to the number of constituent cellNodes
, storing the corresponding shape functions.
Similarly, for a reference point, ξ̂ ∈ Ω̄, the Jacobian matrix, consisting the sums of derivatives of shape functions and the corresponding determinant of the Jacobian, can be readily computed from the finite element object as follows:
../../../examples/user_guide/mint_tutorial.cpp
- The Jacobian matrix is computed by calling the
jacobian()
method on the finite element object, which takes two arguments:xi
, an input argument corresponding to the reference coordinates of the point, ξ̂, where the Jacobian will be evaluated, and- A matrix, represented by the
axom::numerics::Matrix
class, to store the resulting Jacobian.
Note
The Jacobian matrix is not necessarily a square matrix. It can have N × M dimensions, where, N corresponds to the dimension in the reference xi-space and M is the physical dimension. For example, a quadrilateral element is defined in a 2D reference space, but it may be instantiated within a 3D ambient space. Consequently, the dimensions of the corresponding Jacobian would be 2 × 3 in this case.
- The determinant of the Jacobian can then be computed by calling
axom::numerics::determinant()
, with the Jacobian as the input argument.
Given a point in reference space, ξ̂ ∈ Ω̄, the corresponding physical point, x̂ ∈ Ωe is computed by calling the computePhysicalCoords()
method on the finite element object as illustrated below:
../../../examples/user_guide/mint_tutorial.cpp
The computePhysicalCoords()
method takes two arguments:
xi
, an input argument corresponding to the reference coordinates of the point, ξ̂, whose physical coordinates are computed, andxc
, an output array argument that stores the computed physical coordinates, x̂ ∈ Ωe
Similarly, given a point in physical space, x̂ ∈ Ω, a corresponding point in the reference space of the element, ξ̂ ∈ Ω̄, can be obtained by calling the computeReferenceCoords()
method on the finite element object as illustrated by the following:
../../../examples/user_guide/mint_tutorial.cpp
The computeReferenceCoords()
method takes two arguments:
xc
an input argument consisting of the physical point coordinates, whose reference coordinates are computed, andxi
an output array to store the computed reference coordinates, if successful.
The inverseIsoparametricMap
typically requires an iterative, non-linear solve, which is typically implemented with a Newton-Raphson. Moreover, the inverseIsoparametricMap
is only defined for points within the element, Ωe. Consequently, the computeReferenceCoords()
method returns a status that indicates whether the operation was successful. Specifically, computeReferenceCoords()
can return the following statuses:
INVERSE_MAP_FAILED
This typically indicates that the Newton-Raphson iteration did not converge, e.g., negative Jacobian, etc.
OUTSIDE_ELEMENT
This indicates that the Newton-Raphson converged, but the point is outside the element. Consequently, valid reference coordinates do not exist for the given point with respect to the element.
INSIDE_ELEMENT
This indicates the the Newton-Raphson converged and the point is inside the element
Mint provides native support for writing meshes in the ASCII Legacy VTK File Format. Legacy VTK files are popular due to their simplicity and can be read by a variety of visualization tools, such as VisIt and ParaView. Thereby, enable quick visualization of the various MeshTypes
and constituent FieldData
, which can significantly aid in debugging.
Warning
The Legacy VTK File Format does not provide support for face-centered fields. Consequently, the output consists of only the node-centered and cell-centered fields of the mesh.
The functionality for outputting a mesh to VTK is provided by the mint::write_vtk()
function. This is a free function in the axom::mint
namespace, which takes two arguments: (1) a pointer to a mint::Mesh
object, and, (2) the filename of the target VTK file, as illustrated in the code snippet below:
../../../examples/user_guide/mint_tutorial.cpp
This function can be invoked on a mint::Mesh
object, which can correspond to any of the supported MeshTypes
. The concrete mesh type will be reflected in the resulting VTK output file according to the VTK File Format specification.
Note
Support for VTK output is primarily intended for debugging and quick visualization of meshes. This functionality is not intended for routine output or restart dumps from a simulation. Production I/O capabilities in the Axom Toolkit are supported through Sidre. Consult the Sidre documentation for the details.