Structured Mesh Generation in SELF#
Although SELF uses unstructured mesh data structures, we have provided methods to create structured meshes and store them as an unstructured mesh. This can be quite useful as you are getting started with SELF or if the geometry for your problem can be defined using a structured mesh layout.
One Dimension (1-D)#
In one dimension, the only mesh we use is a structured mesh. To generate a structured mesh in one dimension, use the StructuredMesh
generic in the Mesh1D
class.
At the moment, only uniformly spaced structured meshes of elements can be generated. This means that all of the elements are of the same width; keep in mind that within each element, there is a quadrature grid. The points in the quadrature grid are spaced so that spectral accuracy is guaranteed.
To generate a structured grid in 1-D, you need to provide the number of elements and the left and right endpoints of the mesh.
In the example below, we create a 1-D mesh with 20 elements on the domain \(x ∈ [0,1]\). The geometry fields are created from the mesh information and a \(7^{th}\) degree interpolant through the Legendre-Gauss points.
type(Mesh1D),target :: mesh
type(Lagrange),target :: interp
type(Geometry1D),target :: geometry
call mesh % StructuredMesh(nElem=20, &
x=(/0.0_prec,1.0_prec/))
call interp % Init(N=7, controlNodeType=GAUSS, &
M=10, targetNodeType=UNIFORM)
call geometry % Init(interp,mesh%nElem)
call geometry % GenerateFromMesh(mesh)
Notice that initializing the geometry requires an interpolant and the number of elements as input.
Note
Under the hood, the interpolant for the geometry (geometry % interp
) is associated with a pointer to interp
, ie geometry % interp => interp
.
Once the geometry is initialized, the physical positions and metric terms can be calculated and stored using the GenerateFromMesh
method.
Two Dimensions (2-D)#
To generate a structured mesh in two dimensions, use the StructuredMesh
generic in the Mesh2D
class.
At the moment, only uniformly spaced structured meshes of elements can be generated. This means that all of the elements are of the same width; keep in mind that within each element, there is a quadrature grid. The points in the quadrature grid are spaced so that spectral accuracy is guaranteed.
SELF uses a tiled structured grid. Tiled grids divide the 2-D grid into nTilex
×nTiley
tiles of size nxPerTile
×nyPerTile
. The width and height of the elements are defined as dx
and dy
. With these parameters,
nx = nTilex*nxPerTile
is the total number of elements in the x-directionny = nTiley*nyPerTile
is the total number of elements in the y-directionnelem = nx*ny
is the total number of elementsLx = dx*nx
is the domain length in the x-directionLy = dy*ny
is the domain length in the y-direction
You can set boundary conditions for each of the four sides of the structured mesh using a 1-D array of integers of length 4. The boundary conditions must be provided in counter-clockwise order, starting with the "south" boundary (south, east, north, west). The following built-in flags are available for setting boundary conditions
SELF_BC_NONORMALFLOW
SELF_BC_PRESCRIBED
SELF_BC_RADIATION
The tiled layout is convenient for domain decomposition, when you are wanting to scale up your application for distributed memory platforms. Domain decomposition is automatically enabled when you launch your application with mpirun/mpiexec/srun
with more than one rank. In this case, the domain will be automatically divided as evenly as possible across all MPI ranks.
Note
It's good practice to set the total number of tiles equal to the number of MPI ranks that you are running with. Alternatively, you can use fairly small tiles when working with a large number of MPI ranks to increase the chance of minimizing point-to-point communications .
In the example below, we create a 2-D mesh with the following attributes
- \(2 × 2\) tiles for the domain
- \(10 × 10\) elements per tile
- Each element is has dimensions of \(0.05 × 0.05\). The domain dimensions are then \(L_x × L_y = 1 × 1\)
- Domain decomposition is enabled automatically when launched with more than one mpi rank
The geometry fields are created from the mesh information and a \(7^{th}\) degree interpolant through the Legendre-Gauss points.
type(Mesh2D),target :: mesh
type(Lagrange),target :: interp
type(SEMQuad),target :: geometry
integer :: bcids(1:4)
bcids(1:4) = [SELF_BC_PRESCRIBED,& ! south boundary
SELF_BC_PRESCRIBED,& ! east boundary
SELF_BC_PRESCRIBED,& ! north boundary
SELF_BC_PRESCRIBED] ! west boundary
call mesh % StructuredMesh( nxPerTile=10, nyPerTile=10,&
nTileX=2, nTileY=2,&
dx=0.05_prec, dy=0.05_prec, &
bcids)
call interp % Init(N=7, controlNodeType=GAUSS, &
M=10, targetNodeType=UNIFORM)
call geometry % Init(interp,mesh%nElem)
call geometry % GenerateFromMesh(mesh)
Notice that initializing the geometry requires an interpolant and the number of elements as input.
Note
Under the hood, the interpolant for the geometry (geometry % interp
) is associated with a pointer to interp
, ie geometry % interp => interp
.
Once the geometry is initialized, the physical positions and metric terms can be calculated and stored using the GenerateFromMesh
method.
Three Dimensions (3-D)#
To generate a structured mesh in three dimensions, use the StructuredMesh
generic in the Mesh3D
class.
At the moment, only uniformly spaced structured meshes of elements can be generated. This means that all of the elements are of the same length, width, and height; though, the length, width, and height can each be their own value. Keep in mind that within each element, there is a quadrature grid. The points in the quadrature grid are spaced so that spectral accuracy is guaranteed.
SELF uses a tiled structured grid. Tiled grids divide the 3-D grid into nTilex
×nTiley
×nTilez
tiles of size nxPerTile
×nyPerTile
×nzPerTile
. The length, width, and height of the elements are defined as dx
, dy
, and dz
respectively. With these parameters,
nx = nTilex*nxPerTile
is the total number of elements in the x-directionny = nTiley*nyPerTile
is the total number of elements in the y-directionnz = nTilez*nzPerTile
is the total number of elements in the z-directionnelem = nx*ny*nz
is the total number of elementsLx = dx*nx
is the domain length in the x-directionLy = dy*ny
is the domain length in the y-directionLz = dz*nz
is the domain length in the z-direction
You can set boundary conditions for each of the four sides of the structured mesh using a 1-D array of integers of length 6. The boundary conditions must be provided in CGNS ordering (bottom,south, east, north, west,top). The following built-in flags are available for setting boundary conditions
SELF_BC_NONORMALFLOW
SELF_BC_PRESCRIBED
SELF_BC_RADIATION
The tiled layout is convenient for domain decomposition, when you are wanting to scale up your application for distributed memory platforms. Domain decomposition is automatically enabled when you launch your application with mpirun/mpiexec/srun
with more than one rank. In this case, the domain will be automatically divided as evenly as possible across all MPI ranks.
Note
It's good practice to set the total number of tiles equal to the number of MPI ranks that you are running with. Alternatively, you can use fairly small tiles when working with a large number of MPI ranks to increase the chance of minimizing point-to-point communications .
In the example below, we create a 3-D mesh with the following attributes
- \(2 × 2 × 2\) tiles for the domain
- \(10 × 10 × 10\) elements per tile
- Each element is has dimensions of \(0.05 × 0.05 × 0.05\). The domain dimensions are then \(L_x × L_y × L_z = 1 × 1 × 1\)
- Domain decomposition is enabled
The geometry fields are created from the mesh information and a \(7^{th}\) degree interpolant through the Legendre-Gauss points.
type(Mesh3D),target :: mesh
type(Lagrange),target :: interp
type(SEMHex),target :: geometry
integer :: bcids(1:6)
bcids(1:6) = [SELF_BC_PRESCRIBED,& ! bottom boundary
SELF_BC_PRESCRIBED,& ! south boundary
SELF_BC_PRESCRIBED,& ! east boundary
SELF_BC_PRESCRIBED,& ! north boundary
SELF_BC_PRESCRIBED,& ! west boundary
SELF_BC_PRESCRIBED] ! yop boundary
call mesh % StructuredMesh( nxPerTile=10, nyPerTile=10, nzPerTile=10&
nTileX=2, nTileY=2, nTileZ=2, &
dx=0.05_prec, dy=0.05_prec, dz=0.05_prec, &
bcids)
call interp % Init(N=7, controlNodeType=GAUSS, &
M=10, targetNodeType=UNIFORM)
call geometry % Init(interp,mesh%nElem)
call geometry % GenerateFromMesh(mesh)
Notice that initializing the geometry requires an interpolant and the number of elements as input.
Note
Under the hood, the interpolant for the geometry (geometry % interp
) is associated with a pointer to interp
, ie geometry % interp => interp
.
Once the geometry is initialized, the physical positions and metric terms can be calculated and stored using the GenerateFromMesh
method.