Domains
Domains combine spectral bases with the MPI distributor to define the computational space.
Creating a Domain
using Tarang
# Setup coordinates and distributor
coords = CartesianCoordinates("x", "z")
dist = Distributor(coords; mesh=(2, 2), dtype=Float64)
# Define spectral bases
x_basis = RealFourier(coords["x"]; size=128, bounds=(0.0, 2π))
z_basis = ChebyshevT(coords["z"]; size=64, bounds=(0.0, 1.0))
# Create domain
domain = Domain(dist, (x_basis, z_basis))Domain Properties
domain.distributor # MPI distributor
domain.bases # Tuple of spectral bases
domain.ndim # Number of dimensions
domain.shape # Global grid shape
domain.local_shape # Local grid shape on this processQuick Domain Creation
Tarang provides convenience functions for common domain types:
Periodic Box
# 2D doubly-periodic domain
domain, dist = create_2d_periodic_domain(
Lx=2π, Ly=2π, # Domain size
Nx=128, Ny=128, # Resolution
mesh=(2, 2) # MPI processes
)
# 3D triply-periodic domain
domain, dist = create_3d_periodic_domain(
Lx=2π, Ly=2π, Lz=2π,
Nx=64, Ny=64, Nz=64,
mesh=(2, 2, 2)
)Channel Domain
# Periodic in x, bounded in z
domain, dist = create_channel_domain(
Lx=4.0, Lz=1.0,
Nx=256, Nz=64,
mesh=(2, 2)
)Box Domain
# Bounded in all directions
domain, dist = create_box_domain(
Lx=1.0, Ly=1.0, Lz=1.0,
Nx=64, Ny=64, Nz=64,
mesh=(2, 2, 2)
)Working with Domains
Creating Fields
# ScalarField
T = ScalarField(dist, "T", domain.bases, Float64)
# VectorField
u = VectorField(dist, coords, "u", domain.bases, Float64)Grid Information
# Get global grid points for each axis
for (i, basis) in enumerate(domain.bases)
grid = get_grid(basis)
println("Axis $i: $(length(grid)) points")
end
# Get local grid portion
local_grid = get_local_grid(domain, axis)Domain Dimensions
# Total number of grid points
total_points = prod(domain.shape)
# Number of spectral modes
total_modes = prod(basis.size for basis in domain.bases)
# Domain volume
volume = prod(basis.bounds[2] - basis.bounds[1] for basis in domain.bases)Multi-Dimensional Layouts
Grid Space (:g)
Data stored as real values at collocation points:
Tarang.ensure_layout!(field, :g)
# get_grid_data(field) contains real grid values
# Shape: (Nx, Nz) for 2DCoefficient Space (:c)
Data stored as spectral coefficients:
Tarang.ensure_layout!(field, :c)
# get_coeff_data(field) contains spectral coefficients
# Shape depends on basis typesLayout Transforms
# Grid to spectral
Tarang.ensure_layout!(field, :c)
# Spectral to grid
Tarang.ensure_layout!(field, :g)
# Check current layout
current_layout = field.current_layout # :g or :cDomain Decomposition
The domain is distributed across MPI processes:
2D Domain (128 × 64) on 4 processes (2×2 mesh):
Process 0: x ∈ [0:64], z ∈ [0:32]
Process 1: x ∈ [64:128], z ∈ [0:32]
Process 2: x ∈ [0:64], z ∈ [32:64]
Process 3: x ∈ [64:128], z ∈ [32:64]Accessing Local Data
# Local data array
local_data = get_grid_data(field)
# Local array size
local_size = size(local_data)
# Global index range for this process (internal function)
start_idx, end_idx = Tarang.local_indices(dist, axis, global_size)Coordinate Systems
Cartesian
coords = CartesianCoordinates("x", "y", "z")
# dx, dy, dz derivatives
# grad, div, curl, lap in standard formFuture Support
Spherical and cylindrical coordinates are planned:
# Planned API (not yet implemented)
coords = SphericalCoordinates("r", "θ", "φ")
coords = CylindricalCoordinates("r", "θ", "z")Performance Considerations
Mesh Selection
Match mesh to domain aspect ratio:
| Domain Aspect | Mesh |
|---|---|
| Square (Lx ≈ Lz) | (n, n) |
| Wide (Lx > Lz) | (2n, n) |
| Tall (Lx < Lz) | (n, 2n) |
Resolution Balance
- More modes in directions with:
- Smaller scales
- Sharper gradients
- More active dynamics
Memory Usage
# Estimate memory per field
bytes_per_element = 8 # Float64
total_elements = prod(domain.shape)
memory_per_field = total_elements * bytes_per_element
# Per process
memory_per_process = memory_per_field / dist.sizeSee Also
- Coordinates: Coordinate systems
- Bases: Spectral basis types
- Fields: Creating fields on domains
- API: Domains: Complete reference