Skip to main content

Overview

Dedalus uses coordinate systems and domains to define the spatial structure of problems. The framework supports Cartesian, spherical, and polar coordinate systems, along with sophisticated domain construction and parallel data distribution.

Coordinate Systems

Coordinate systems define the geometric structure of the computational domain. All coordinate system classes are defined in dedalus/core/coords.py.

CartesianCoordinates

The simplest coordinate system for rectangular geometries.
import dedalus.public as d3

# 1D Cartesian coordinates
coords = d3.CartesianCoordinates('x')

# 2D Cartesian coordinates
coords = d3.CartesianCoordinates('x', 'z')

# 3D Cartesian coordinates
coords = d3.CartesianCoordinates('x', 'y', 'z')
Properties:
  • curvilinear: False (flat geometry)
  • dim: Number of dimensions
  • coords: Tuple of coordinate objects
  • right_handed: True by default for 3D systems

SphericalCoordinates

For problems in spherical geometry.
# Spherical coordinates: (azimuth, colatitude, radius)
coords = d3.SphericalCoordinates('phi', 'theta', 'r')
Properties:
  • curvilinear: True
  • dim: 3
  • spin_ordering: (-1, +1, 0) for vector components
  • default_nonconst_groups: (0, 1, 0) - ell=0 has different validity
Coordinate ranges:
  • Azimuth (phi): [0, 2π]
  • Colatitude (theta): [0, π]
  • Radius (r): [r_inner, r_outer], must be non-negative
Cartesian conversion:
x, y, z = SphericalCoordinates.cartesian(phi, theta, r)
# x = r * sin(theta) * cos(phi)
# y = r * sin(theta) * sin(phi)
# z = r * cos(theta)

PolarCoordinates

For 2D problems with radial symmetry.
# Polar coordinates: (azimuth, radius)
coords = d3.PolarCoordinates('phi', 'r')
Properties:
  • curvilinear: True
  • dim: 2
  • spin_ordering: (-1, +1) for vector components
Cartesian conversion:
x, y = PolarCoordinates.cartesian(phi, r)
# x = r * cos(phi)
# y = r * sin(phi)

S2Coordinates

For problems on the surface of a sphere.
# S2 coordinates: (azimuth, colatitude)
coords = d3.S2Coordinates('phi', 'theta')
Properties:
  • curvilinear: True
  • dim: 2
  • spin_ordering: (-1, +1)

DirectProduct

Combine different coordinate systems into a product space.
# Combine Cartesian and spherical
coords1 = d3.CartesianCoordinates('x', 'y')
coords2 = d3.Coordinate('z')
product = d3.DirectProduct(coords1, coords2)

Domain Construction

Domains combine bases on specified coordinate systems to define the computational space. The Domain class (in dedalus/core/domain.py) represents the direct product of bases.

Creating Domains

import numpy as np
import dedalus.public as d3

# Define coordinates
coords = d3.CartesianCoordinates('x', 'z')

# Create distributor
dist = d3.Distributor(coords, dtype=np.float64)

# Create bases
xbasis = d3.RealFourier(coords['x'], size=256, bounds=(0, 4))
zbasis = d3.ChebyshevT(coords['z'], size=64, bounds=(0, 1))

# Domain is automatically created when fields use multiple bases
field = dist.Field(bases=(xbasis, zbasis))
domain = field.domain

Domain Properties

# Domain dimensions
print(domain.dim)  # Total dimensions

# Volume
print(domain.volume)  # Product of basis volumes

# Bases organization
print(domain.bases)  # Tuple of bases
print(domain.bases_by_axis)  # Dictionary mapping axes to bases

# Grid and coefficient shapes
print(domain.coeff_shape)  # Shape in coefficient space
print(domain.grid_shape(scales=1))  # Shape in grid space

# Dealiasing factors
print(domain.dealias)  # Tuple of dealias factors per axis

Accessing Domain Information

# Get basis for a coordinate
basis = domain.get_basis(coords['x'])

# Get coordinate by name
coord = domain.get_coord('x')

# Enumerate unique bases
for axis, basis in domain.enumerate_unique_bases():
    print(f"Axis {axis}: {basis}")

Distributor

The Distributor class (in dedalus/core/distributor.py) manages parallel data distribution and transformations across MPI processes.

Creating a Distributor

from mpi4py import MPI
import numpy as np
import dedalus.public as d3

# Create coordinate system
coords = d3.CartesianCoordinates('x', 'y', 'z')

# Create distributor with default settings
dist = d3.Distributor(coords, dtype=np.float64)

# Create with custom MPI communicator and mesh
comm = MPI.COMM_WORLD
mesh = [4, 2]  # 2D process mesh
dist = d3.Distributor(coords, comm=comm, mesh=mesh, dtype=np.complex128)

Distributor Parameters

  • coordsystems: Coordinate system(s) for the problem
  • comm: MPI communicator (default: MPI.COMM_WORLD)
  • mesh: Process mesh for parallelization (default: 1D mesh)
  • dtype: Data type (np.float64 or np.complex128)

Distributor Properties

# Dimensionality
print(dist.dim)  # Number of dimensions

# MPI information
print(dist.comm)  # MPI communicator
print(dist.mesh)  # Process mesh shape
print(dist.comm_cart)  # Cartesian MPI communicator
print(dist.comm_coords)  # Process coordinates in mesh

# Layouts for transforms
print(dist.coeff_layout)  # Coefficient space layout
print(dist.grid_layout)   # Grid space layout
print(dist.layouts)       # All intermediate layouts

Data Distribution Strategy

Dedalus uses a sophisticated distribution strategy:
  1. Coefficient space: First R dimensions distributed over mesh
  2. Grid space: First dimension local, next R dimensions distributed
  3. Transforms: Loop backwards over dimensions, transforming local axes and transposing distributed axes
# For D=3 dimensions, R=2 mesh dimensions:
# Coefficient space: [distributed, distributed, local]
# After 1st transpose: [distributed, local, local]
# After transform:     [distributed, grid, local]
# After 2nd transpose: [local, grid, local]
# After transform:     [grid, grid, local]
# Grid space:          [local, distributed, distributed]

Creating Fields

The distributor provides factory methods for creating fields:
# Generic field
f = dist.Field(name='f', bases=(xbasis, zbasis))

# Scalar field
s = dist.ScalarField(name='s', bases=(xbasis, zbasis))

# Vector field
u = dist.VectorField(coords, name='u', bases=(xbasis, zbasis))

# Tensor field
T = dist.TensorField((coords, coords), name='T', bases=(xbasis, zbasis))

Grid and Mode Access

# Get local grids (includes only data on this process)
x, z = dist.local_grids(xbasis, zbasis, scales=1)

# Get global grids (all grid points)
x_global = xbasis.global_grid(dist, scale=1)

# Get local modes for a basis
modes = dist.local_modes(xbasis)

Complete Example

import numpy as np
import dedalus.public as d3
from mpi4py import MPI

# Setup
Lx, Lz = 4, 1
Nx, Nz = 256, 64
dtype = np.float64

# Create coordinate system
coords = d3.CartesianCoordinates('x', 'z')

# Create distributor with 1D process mesh
comm = MPI.COMM_WORLD
dist = d3.Distributor(coords, comm=comm, dtype=dtype)

# Create bases
xbasis = d3.RealFourier(coords['x'], size=Nx, bounds=(0, Lx), dealias=3/2)
zbasis = d3.ChebyshevT(coords['z'], size=Nz, bounds=(0, Lz), dealias=3/2)

# Create fields
f = dist.Field(name='f', bases=(xbasis, zbasis))
u = dist.VectorField(coords, name='u', bases=(xbasis, zbasis))

# Get local grids
x, z = dist.local_grids(xbasis, zbasis)

# Access domain properties
print(f"Domain dimension: {f.domain.dim}")
print(f"Domain volume: {f.domain.volume}")
print(f"Coefficient shape: {f.domain.coeff_shape}")
print(f"Grid shape: {f.domain.grid_shape(scales=1)}")

# Set field values in grid space
f['g'] = np.sin(2*np.pi*x/Lx) * np.cos(np.pi*z/Lz)

# Transform to coefficient space
f.change_layout('c')

# Get coordinate unit vectors
ex, ez = coords.unit_vector_fields(dist)
print(f"Unit vector ex at z=0: {ex['g'][:, 0]}")

Best Practices

Coordinate System Selection:
  • Use CartesianCoordinates for rectangular domains
  • Use SphericalCoordinates for full sphere problems
  • Use PolarCoordinates for disk/annulus problems
  • Use S2Coordinates for spherical shell surfaces
Parallel Distribution: The process mesh must have fewer dimensions than the problem dimension, and the product of mesh dimensions must equal the number of MPI processes.
Curvilinear Coordinates: Problems in curvilinear coordinates use spin/regularity component orderings for vectors and tensors. Use the provided unit_vector_fields() method to work with coordinate directions.

See Also

Build docs developers (and LLMs) love