Skip to main content

Overview

The distributor module provides the Distributor class for managing parallel distribution and transformation of fields across MPI processes. It handles domain decomposition, layout management, and coordinate transforms.

Distributor

Distributor Class

Distributor(coordsystems, comm=None, mesh=None, dtype=None)
Manages parallelized distribution and transformation of fields.

Parameters

  • coordsystems (CoordinateSystem or tuple): Coordinate system(s) for the domain
  • comm (MPI.Comm, optional): MPI communicator (default: MPI.COMM_WORLD)
  • mesh (tuple of ints, optional): Process mesh dimensions (default: 1D mesh)
  • dtype (dtype, optional): Default data type

Attributes

  • dim (int): Number of dimensions
  • coords (tuple): Coordinate objects
  • mesh (ndarray): Process mesh shape
  • comm (MPI.Comm): MPI communicator
  • comm_cart (MPI.Comm): Cartesian communicator
  • layouts (list): Available data layouts
  • coeff_layout (Layout): Coefficient space layout
  • grid_layout (Layout): Grid space layout

Example: Basic Setup

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

# 1D domain
coord = d3.Coordinate('x')
dist = d3.Distributor(coord)

# 2D domain
coords = d3.CartesianCoordinates('x', 'y')  
dist = d3.Distributor(coords)

# 3D domain with custom mesh
coords = d3.CartesianCoordinates('x', 'y', 'z')
mesh = [2, 2]  # 2D process mesh for 3D domain
dist = d3.Distributor(coords, mesh=mesh)  

# Check distribution
print(f"Process {dist.comm.rank}/{dist.comm.size}")
print(f"Mesh: {dist.mesh}")
print(f"Coordinates: {dist.comm_cart.coords}")

Example: Multiple Coordinate Systems

import dedalus.public as d3

# Combine different coordinate systems
cart = d3.CartesianCoordinates('x', 'y')
z_coord = d3.Coordinate('z')

dist = d3.Distributor((cart, z_coord))  
print(f"Total dimension: {dist.dim}")  # 3

Methods

Field Creation

Field(bases=None, …) - Create a field
u = dist.Field(bases=(xbasis, ybasis), name='u')
ScalarField(…) - Create scalar field
T = dist.ScalarField(bases=(xbasis, ybasis), name='T')  
VectorField(coordsys, …) - Create vector field
u = dist.VectorField(coords, bases=(xbasis, ybasis), name='u')
TensorField(coordsys_out, coordsys_in, …) - Create tensor field
τ = dist.TensorField(coords, bases=(xbasis, ybasis), name='tau')
IdentityTensor(coordsys_in, coordsys_out) - Create identity tensor
I = dist.IdentityTensor(coords)

Grid Access

local_grid(basis, scale) - Get local grid for basis
x = dist.local_grid(xbasis, scale=1)  
*local_grids(bases, scales) - Get local grids for multiple bases
x, y = dist.local_grids(xbasis, ybasis)

Mode Access

local_modes(basis) - Get local mode indices
k = dist.local_modes(xbasis)

Layout Management

get_layout_object(layout) - Get layout from identifier
coeff_layout = dist.get_layout_object('c')
grid_layout = dist.get_layout_object('g')

Utility Methods

get_axis(coord) - Get axis index for coordinate
axis = dist.get_axis(coords['x'])
get_basis_axis(basis) - Get first axis for basis
axis = dist.get_basis_axis(xbasis)  
first_axis(basis) - First axis of basis last_axis(basis) - Last axis of basis remedy_scales(scales) - Normalize scale specification
scales = dist.remedy_scales(2)  # (2, 2, 2) for 3D
scales = dist.remedy_scales((1, 2, 1))  # (1, 2, 1)

Layouts

Layout Objects

Layouts represent different transform/distribution states of field data.

Attributes

  • local (ndarray of bool): Which axes are local (not distributed)
  • grid_space (ndarray of bool): Which axes are in grid space
  • index (int): Layout index in transform sequence

Methods

global_shape(domain, scales) - Global data shape local_shape(domain, scales) - Local data shape local_elements(domain, scales) - Local element indices

Transform Sequence

Dedalus uses multiple layouts to transform between coefficient and grid space:
import dedalus.public as d3

coords = d3.CartesianCoordinates('x', 'y', 'z')
mesh = [2, 2]  
dist = d3.Distributor(coords, mesh=mesh)

# Print layout sequence
for i, layout in enumerate(dist.layouts):
    print(f"Layout {i}:")
    print(f"  Local: {layout.local}")  
    print(f"  Grid:  {layout.grid_space}")

# Coefficient layout (first)
print(f"\nCoeff layout index: {dist.coeff_layout.index}")

# Grid layout (last)  
print(f"Grid layout index: {dist.grid_layout.index}")
Example output for 3D domain on 2×2 mesh:
Layout 0 (coefficient):  
  Local: [False, False, True]
  Grid:  [False, False, False]

Layout 1 (transpose):
  Local: [False, True, False]  
  Grid:  [False, False, False]

Layout 2 (transform z):
  Local: [False, True, False]
  Grid:  [False, False, True]

Layout 3 (transpose):  
  Local: [True, False, False]
  Grid:  [False, False, True]

Layout 4 (transform y):
  Local: [True, False, False]
  Grid:  [False, True, True]  

Layout 5 (transform x):
  Local: [True, False, False]
  Grid:  [True, True, True]

Parallelization

Process Mesh

The process mesh determines how data is distributed:
import dedalus.public as d3
from mpi4py import MPI

coords = d3.CartesianCoordinates('x', 'y', 'z')

# 1D mesh (default): all processes along first dimension  
mesh = None  # or [N] for N processes
dist = d3.Distributor(coords, mesh=mesh)

# 2D mesh: distribute first two dimensions
mesh = [2, 4]  # 8 total processes
dist = d3.Distributor(coords, mesh=mesh)  

# Check process position
rank = dist.comm.rank
coords = dist.comm_cart.coords
print(f"Rank {rank} at mesh coordinates {coords}")

Domain Decomposition

Data is distributed in different ways depending on layout: Coefficient space: First R dimensions distributed
Grid space: Dimensions 1 to R distributed
Example for 3D domain on 2×2 mesh:
import dedalus.public as d3
import numpy as np

coords = d3.CartesianCoordinates('x', 'y', 'z')
mesh = [2, 2]
dist = d3.Distributor(coords, mesh=mesh)

xbasis = d3.RealFourier(coords['x'], size=64, bounds=(0, 2*np.pi))  
ybasis = d3.RealFourier(coords['y'], size=64, bounds=(0, 2*np.pi))
zbasis = d3.RealFourier(coords['z'], size=64, bounds=(0, 2*np.pi))

u = dist.Field(bases=(xbasis, ybasis, zbasis))

# Coefficient space: x, y distributed; z local  
u.change_layout('c')
print(f"Coeff shape: {u.data.shape}")  # (~32, ~32, 64)

# Grid space: x local; y, z distributed
u.change_layout('g')
print(f"Grid shape: {u.data.shape}")  # (64, ~32, ~32)  

Advanced Usage

Custom Communicators

import dedalus.public as d3
from mpi4py import MPI  

# Create custom communicator
comm = MPI.COMM_WORLD.Split(color=rank//2, key=rank)

# Use in distributor
coords = d3.CartesianCoordinates('x', 'y')
dist = d3.Distributor(coords, comm=comm)  

Multiple Distributors

import dedalus.public as d3

# Different distributors for different domains
coords_xy = d3.CartesianCoordinates('x', 'y')  
dist_2d = d3.Distributor(coords_xy, mesh=[2, 2])

coords_xyz = d3.CartesianCoordinates('x', 'y', 'z')
dist_3d = d3.Distributor(coords_xyz, mesh=[2, 2])

See Also

Build docs developers (and LLMs) love