Meshes

Aether.Meshes.MeshType
struct Mesh{FT, C<:Aether.Meshes.AbstractCoordinates, Arch, Comm, BG}
Mesh(architecture, FT = Float64; size, extent = nothing,
     x1 = nothing, x2 = nothing, x3 = nothing,
     cells_per_block = size, nghost = 2,
     coordinates = CartesianCoordinates(),
     communicator = nothing)

Host-side mesh: domain geometry and its decomposition into equal-size blocks. Geometry and decomposition only — no field arrays, no time, no physics. Never passed to kernels whole; kernels receive block_geometry plus the index packs.

Keyword arguments:

  • size: global interior cell counts (nx1, nx2, nx3). Singleton directions (nx == 1) must be trailing: 1D runs use x1, 2D runs use x1–x2.
  • extent: domain lengths (L1, L2, L3), giving bounds [0, L] per direction. Mutually exclusive with x1/x2/x3.
  • x1, x2, x3: explicit (min, max) bounds per direction. All three are required when extent is not given, singleton directions included (a 2D run still has an x3 thickness).
  • cells_per_block: interior cells per block; must divide size evenly, be at least max(nghost, 2) in active directions (a smaller block cannot serve its neighbor's ghost layer), and be 1 in singleton directions. Defaults to size (a single block).
  • nghost: ghost cells per side in active directions (≥ 1).
  • coordinates: the coordinate system (CartesianCoordinates).
  • communicator: nothing for a serial run (the default; the single process owns every block), or an MPI.Comm (with MPI.jl loaded) to distribute the blocks over its ranks. Works on CPU and GPU architectures alike.

Blocks are identified by their global id gid ∈ 1:nblocks1*nblocks2*nblocks3, assigned in Z-order (Morton order) of the logical locations (lx1, lx2, lx3)zorder, realized by block_index / block_location. The Z-ordered gid list is dealt to ranks in contiguous slices (distribute_blocks); every rank computes the same global tables deterministically, so construction needs no communication. Each rank stores geometry only for its owned_gids, and the kernel-facing block slot is mb = gid - first(owned_gids) + 1.

Block bounds are computed from the global domain ends with face_position, so neighboring blocks share bitwise-identical face coordinates, and the cell spacings are bitwise-identical across blocks.

  • architecture::Any: architecture the mesh's device data lives on

  • coordinates::Aether.Meshes.AbstractCoordinates: coordinate system, dispatched on by every geometry accessor

  • communicator::Any: nothing for serial runs, the MPI.Comm for distributed runs

  • rank::Int64: this process's rank in the communicator (0 for serial)

  • nranks::Int64: number of ranks in the communicator (1 for serial)

  • indices::Aether.Meshes.GridIndices: global-domain cell counts and nominal index ranges (no global array exists)

  • block_indices::Aether.Meshes.GridIndices: per-block cell counts and index ranges; identical for every block

  • x1min::Any: lower x1 domain bound

  • x1max::Any: upper x1 domain bound

  • x2min::Any: lower x2 domain bound

  • x2max::Any: upper x2 domain bound

  • x3min::Any: lower x3 domain bound

  • x3max::Any: upper x3 domain bound

  • nblocks1::Int64: number of blocks along x1

  • nblocks2::Int64: number of blocks along x2

  • nblocks3::Int64: number of blocks along x3

  • block_locations::Vector{Tuple{Int64, Int64, Int64}}: host table: logical location (lx1, lx2, lx3) of every block, Z-ordered by gid

  • block_ranks::Vector{Int64}: host table: owning rank of every block, indexed by gid

  • owned_gids::UnitRange{Int64}: gids of the blocks owned by this rank (a contiguous Z-order slice)

  • nblocks::Int64: number of blocks owned by this rank — the kernel-facing block count

  • block_geometry::Any: device vector of BlockGeometry for the owned blocks, indexed by slot mb in kernels

using Aether

Mesh(CPU(); size = (8, 8, 8), extent = (1, 1, 1), cells_per_block = (4, 8, 8))

# output
Mesh{Float64} on CPU()
├── coordinates: CartesianCoordinates()
├── domain: [0.0, 1.0] × [0.0, 1.0] × [0.0, 1.0]
├── cells: 8 × 8 × 8 interior, nghost = 2
└── blocks: 2 × 1 × 1 = 2 of 4 × 8 × 8 cells
source
Aether.Meshes.CartesianCoordinatesType
struct CartesianCoordinates <: Aether.Meshes.AbstractCoordinates
CartesianCoordinates()

Uniform Cartesian coordinates. All geometry accessors fold to compile-time constant expressions of the cell spacings.

using Aether

CartesianCoordinates()

# output
CartesianCoordinates()
source
Aether.Meshes.CylindricalCoordinatesType
struct CylindricalCoordinates <: Aether.Meshes.AbstractCoordinates
CylindricalCoordinates()

Uniform cylindrical coordinates $(R, \varphi, z)$ in the slots $(x_1, x_2, x_3)$: x1 is the cylindrical radius R ≥ 0, x2 the azimuth φ, x3 the height z. Spacings are uniform in each coordinate; the metric lives entirely in the geometry accessors — the x1-face area is R dφ dz, the cell volume is ½(R₊² - R₋²) dφ dz, the x2-edge length is R dφ, and the cell center is the volume-weighted radius. Hydrodynamics and MHD alike: the curvilinear momentum source terms are attached automatically by Simulation, and constrained transport carries the metric through its edge lengths and face areas.

using Aether

CylindricalCoordinates()

# output
CylindricalCoordinates()
source
Aether.Meshes.SphericalPolarCoordinatesType
struct SphericalPolarCoordinates <: Aether.Meshes.AbstractCoordinates
SphericalPolarCoordinates()

Uniform spherical-polar coordinates $(r, \theta, \varphi)$ in the slots $(x_1, x_2, x_3)$: x1 is the spherical radius r ≥ 0, x2 the polar angle θ ∈ [0, π], x3 the azimuth φ. Spacings are uniform in each coordinate; the metric lives entirely in the geometry accessors — the x1-face area is r² sinθ dθ dφ, the cell volume is ⅓(r₊³ - r₋³)(cosθ₋ - cosθ₊) dφ, the x3-edge length is r sinθ dφ, and the cell center is the volume-weighted radius and polar angle. Hydrodynamics and MHD alike: the curvilinear momentum source terms are attached automatically by Simulation, and constrained transport carries the metric through its edge lengths and face areas. A domain reaching the polar axis (θ = 0, π) takes PolarBC on the corresponding x2 face — connectivity through the pole for general flows and fields — on a full periodic azimuth or an axisymmetric (nx3 = 1) mesh.

using Aether

SphericalPolarCoordinates()

# output
SphericalPolarCoordinates()
source
Aether.Meshes.x1vFunction
x1v(
    _::CartesianCoordinates,
    i,
    geometry::Aether.Meshes.BlockGeometry,
    indices::Aether.Meshes.GridIndices
) -> Any

Position of the center of cell i in the block described by geometrycenter_position applied to the block bounds. indices must be the mesh's block_indices.

using Aether

mesh = Mesh(CPU(); size = (4, 1, 1), extent = (1, 1, 1))
geometry = mesh.block_geometry[1]
Meshes.x1v(CartesianCoordinates(), mesh.block_indices.is, geometry, mesh.block_indices)

# output
0.125
source
Aether.Meshes.x2vFunction
x2v(
    _::CartesianCoordinates,
    j,
    geometry::Aether.Meshes.BlockGeometry,
    indices::Aether.Meshes.GridIndices
) -> Any

Position of the center of cell j in x2; see x1v.

source
Aether.Meshes.x3vFunction
x3v(
    _::CartesianCoordinates,
    k,
    geometry::Aether.Meshes.BlockGeometry,
    indices::Aether.Meshes.GridIndices
) -> Any

Position of the center of cell k in x3; see x1v.

source
Aether.Meshes.cell_volumeFunction
cell_volume(
    _::CartesianCoordinates,
    i,
    j,
    k,
    geometry::Aether.Meshes.BlockGeometry,
    indices::Aether.Meshes.GridIndices
) -> Any

Volume of cell (i, j, k). Uniform Cartesian: dx1 * dx2 * dx3.

using Aether

mesh = Mesh(CPU(); size = (4, 1, 1), extent = (1, 1, 1))
geometry = mesh.block_geometry[1]
Meshes.cell_volume(CartesianCoordinates(), mesh.block_indices.is, 1, 1, geometry, mesh.block_indices)

# output
0.25
source