Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/DedalusProject/dedalus/llms.txt

Use this file to discover all available pages before exploring further.

Overview

The tools module provides various utility functions and classes for configuration, parallelization, array operations, and post-processing.

Configuration

Config System

from dedalus.tools.config import config
Dedalus uses a ConfigParser-based configuration system.

Configuration Files

Read in order (later files override earlier):
  1. Package defaults: dedalus/dedalus.cfg
  2. User config: ~/.dedalus/dedalus.cfg
  3. Local config: ./dedalus.cfg

Example Configuration

# ~/.dedalus/dedalus.cfg

[parallelism]  
TRANSPOSE_LIBRARY = fftw
GROUP_TRANSPOSES = True

[linear algebra]
MATRIX_FACTORIZER = SuperluNaturalSpsolve

[memory]  
FIELD_CACHE_SIZE = 128

[logging]
stdout_level = info
file_level = debug

Accessing Config

from dedalus.tools.config import config

# Get values
transpose_lib = config['parallelism'].get('TRANSPOSE_LIBRARY')
cache_size = config['memory'].getint('FIELD_CACHE_SIZE')  

# Check boolean
group_transposes = config['parallelism'].getboolean('GROUP_TRANSPOSES')

Parallel Tools

Sync

from dedalus.tools.parallel import Sync
Context manager for MPI synchronization.

Parameters

  • comm (MPI.Comm, optional): Communicator (default: COMM_WORLD)
  • enter (bool, optional): Barrier on enter (default: True)
  • exit (bool, optional): Barrier on exit (default: True)

Example

from dedalus.tools.parallel import Sync  
from mpi4py import MPI
import time

comm = MPI.COMM_WORLD

# Synchronize before and after block
with Sync(comm):
    # All processes wait here  
    time.sleep(comm.rank * 0.1)
    print(f"Process {comm.rank}")
# All processes wait here before continuing

parallel_mkdir

from dedalus.tools.parallel import parallel_mkdir
Create directory from root process only.

Parameters

  • path (str or Path): Directory path
  • comm (MPI.Comm, optional): Communicator

Example

from dedalus.tools.parallel import parallel_mkdir
import pathlib

output_dir = pathlib.Path('output')  
parallel_mkdir(output_dir)

Post-Processing

Merging Output Files

from dedalus.tools import post  
merge_process_files(base_path, cleanup=False) - Merge distributed output files

Parameters

  • base_path (str): Base path to output directory
  • cleanup (bool, optional): Delete process files after merging

Example

from dedalus.tools import post

# Merge output from parallel run
post.merge_process_files('snapshots', cleanup=True)  
post.merge_process_files('analysis', cleanup=False)

Post-Processing Script Template

import h5py  
import numpy as np
import matplotlib.pyplot as plt
from dedalus.tools import post

# Merge files
post.merge_process_files('snapshots', cleanup=True)

# Read merged data  
with h5py.File('snapshots/snapshots_s1.h5', 'r') as f:
    # Load data
    u = f['tasks']['velocity'][:]
    x = f['tasks']['velocity'].dims[1][0][:]  
    t = f['scales']['sim_time'][:]
    
    # Plot
    plt.figure(figsize=(10, 6))
    for i in range(0, len(t), 10):
        plt.plot(x, u[i], label=f't={t[i]:.2f}')  
    plt.xlabel('x')
    plt.ylabel('u')
    plt.legend()
    plt.savefig('evolution.png', dpi=150)

Array Tools

reshape_vector

from dedalus.tools.array import reshape_vector
Reshape 1D array as multidimensional vector.

Parameters

  • data (ndarray): 1D array
  • dim (int): Target dimensionality
  • axis (int): Axis for data

Example

import numpy as np
from dedalus.tools.array import reshape_vector

x = np.linspace(0, 1, 64)

# Reshape for broadcasting  
x_2d = reshape_vector(x, dim=2, axis=0)  # Shape: (64, 1)
x_3d = reshape_vector(x, dim=3, axis=1)  # Shape: (1, 64, 1)

axslice

from dedalus.tools.array import axslice
Create slice along specific axis.

Parameters

  • axis (int): Axis to slice
  • start (int): Start index
  • stop (int): Stop index
  • step (int, optional): Step size

Example

import numpy as np  
from dedalus.tools.array import axslice

data = np.random.rand(10, 20, 30)

# Slice along axis 1
sliced = data[axslice(1, 5, 15)]  # data[:, 5:15, :]

Exception Classes

from dedalus.tools.exceptions import *
Dedalus-specific exceptions:
  • UnsupportedEquationError: Equation violates problem constraints
  • NonlinearOperatorError: Nonlinear operator used where linear required
  • UndefinedParityError: Parity undefined for operation
  • SymbolicParsingError: Error parsing symbolic expressions

Example

import dedalus.public as d3  
from dedalus.tools.exceptions import UnsupportedEquationError

try:
    problem = d3.LBVP([u])
    # This will raise UnsupportedEquationError (nonlinear RHS)  
    problem.add_equation("dx(u) = u**2")
except UnsupportedEquationError as e:
    print(f"Invalid equation: {e}")

Logging

import logging
logger = logging.getLogger(__name__)
Dedalus uses Python’s logging module.

Configuration

# In dedalus.cfg
[logging]
stdout_level = info
file_level = debug
filename = dedalus.log

Usage in Scripts

import logging
logger = logging.getLogger(__name__)

# Set level for your script
logger.setLevel(logging.INFO)

# Log messages  
logger.debug("Detailed debug information")
logger.info("Iteration %d complete" % iteration)
logger.warning("CFL condition violated")
logger.error("Solver failed to converge")  

Cache Tools

CachedMethod

from dedalus.tools.cache import CachedMethod  
Decorator for caching method results.

Example

from dedalus.tools.cache import CachedMethod

class MyClass:  
    @CachedMethod
    def expensive_computation(self, x):
        # Only computed once per unique x
        return x**2 + 2*x + 1

obj = MyClass()  
result1 = obj.expensive_computation(5)  # Computed
result2 = obj.expensive_computation(5)  # Cached

CachedAttribute

from dedalus.tools.cache import CachedAttribute
Descriptor for caching attribute values.

Example

from dedalus.tools.cache import CachedAttribute  
import numpy as np

class MyClass:
    @CachedAttribute
    def random_array(self):
        # Only generated once
        return np.random.rand(1000)  

obj = MyClass()
arr1 = obj.random_array  # Generated
arr2 = obj.random_array  # Same array

General Utilities

unify

from dedalus.tools.general import unify
Unify values from a collection, ensuring all are equal.

Example

from dedalus.tools.general import unify

# Returns common value if all equal
result = unify([1, 1, 1, 1])  # Returns 1  

# Raises error if not all equal
try:
    result = unify([1, 2, 3])
except ValueError:
    print("Values not unified")  

OrderedSet

from dedalus.tools.general import OrderedSet  
Set that maintains insertion order.

Example

from dedalus.tools.general import OrderedSet

s = OrderedSet([3, 1, 4, 1, 5])  
print(list(s))  # [3, 1, 4, 5]

Random Arrays

ChunkedRandomArray

from dedalus.tools.random_arrays import ChunkedRandomArray
Generate distributed random arrays reproducibly.

Example

from dedalus.tools.random_arrays import ChunkedRandomArray  
import numpy as np

# Create reproducible random array across processes
shape = (64, 64, 64)
seed = 42
random_gen = ChunkedRandomArray(shape, seed=seed)  
local_data = random_gen.get_local_data()

Performance Profiling

Dedalus includes profiling support:
# In dedalus.cfg  
[profiling]
PROFILE_DEFAULT = False
PARALLEL_PROFILE_DEFAULT = False
PROFILE_DIRECTORY = profiles

Usage

import dedalus.public as d3

# Enable profiling for solver
solver = problem.build_solver(d3.RK443)
solver.profile = True

# Run simulation  
while solver.proceed:
    solver.step(dt)

# Profile data saved to profiles/

Quick Domain Setup

The quick_domains module provides convenience functions for common domain configurations.

Quick Domain Functions

from dedalus.extras import quick_domains
Available functions: 1D Domains:
  • fourier(N, dealias=3/2, dtype=np.float64) - 1D periodic Fourier domain
  • chebyshev(N, dealias=3/2, dtype=np.float64) - 1D Chebyshev domain
2D Domains:
  • fourier_2d(N, dealias=3/2, dtype=np.float64) - 2D periodic box
  • channel_2d(N, dealias=3/2, dtype=np.float64) - 2D channel (Fourier × Chebyshev)
3D Domains:
  • fourier_3d(N, dealias=3/2, dtype=np.float64) - 3D periodic box
  • channel_3d(N, dealias=3/2, dtype=np.float64) - 3D channel (Fourier × Fourier × Chebyshev)

Returns

Each function returns (coords, dist, bases):
  • coords: Coordinate system
  • dist: Distributor
  • bases: Basis object (or tuple of bases for multi-dimensional)

Example

from dedalus.extras import quick_domains

# Quick 2D channel setup
coords, dist, (xbasis, ybasis) = quick_domains.channel_2d(N=64, dealias=3/2)

# Create fields
u = dist.VectorField(coords, bases=(xbasis, ybasis))
p = dist.Field(bases=(xbasis, ybasis))

# Ready to set up problem
problem = d3.IVP([u, p], namespace=locals())
These helpers are useful for quick prototyping and testing. For production simulations, explicitly construct coordinate systems and bases for full control over bounds and parameters.

See Also