"""
Convection-Diffusion equation: -ε∇²u + v·∇u = f
"""
import numpy as np
from scipy import sparse
from typing import Dict, Any, Tuple
from matrix_toolkit.pde.base import BasePDEEquation
[docs]
class ConvectionDiffusionEquation(BasePDEEquation):
"""
The convection-diffusion equation:
.. math::
-\varepsilon \nabla^2 u + \mathbf{v} \cdot \nabla u = f
Examples:
>>> config = PDEConfig(
... dimension=2,
... mesh_size=64,
... coefficients={'velocity': (1.0, 0.5), 'diffusion': 0.01}
... )
>>> eq = ConvectionDiffusionEquation(config)
>>> A = eq.generate_matrix()
"""
[docs]
def get_operator_name(self) -> str:
return "ConvectionDiffusion"
[docs]
def get_default_coefficients(self) -> Dict[str, Any]:
return {
'diffusion': 1.0,
'velocity': None, # Will be set based on dimension
}
[docs]
def randomize_coefficients(self, seed=None) -> Dict[str, Any]:
"""Randomize coefficients with dimension-aware defaults"""
coeffs = super().randomize_coefficients(seed)
# Set velocity if not specified
if coeffs['velocity'] is None:
if self.config.dimension == 1:
coeffs['velocity'] = 1.0
elif self.config.dimension == 2:
coeffs['velocity'] = (1.0, 1.0)
elif self.config.dimension == 3:
coeffs['velocity'] = (1.0, 1.0, 1.0)
# Randomize velocity components if ranges specified
if 'velocity_x' in self.config.param_ranges:
vx_min, vx_max = self.config.param_ranges['velocity_x']
vx = np.random.uniform(vx_min, vx_max)
if self.config.dimension == 1:
coeffs['velocity'] = vx
else:
v = list(coeffs['velocity']) if isinstance(coeffs['velocity'], tuple) else [1.0] * self.config.dimension
v[0] = vx
coeffs['velocity'] = tuple(v)
if 'velocity_y' in self.config.param_ranges and self.config.dimension >= 2:
vy_min, vy_max = self.config.param_ranges['velocity_y']
vy = np.random.uniform(vy_min, vy_max)
v = list(coeffs['velocity'])
v[1] = vy
coeffs['velocity'] = tuple(v)
return coeffs
[docs]
def generate_matrix(self) -> sparse.spmatrix:
"""Generate convection-diffusion matrix"""
coeffs = self.config.coefficients or self.get_default_coefficients()
if self.config.random_params:
coeffs = self.randomize_coefficients(self.config.random_seed)
diffusion = coeffs.get('diffusion', 1.0)
velocity = coeffs.get('velocity')
if velocity is None:
if self.config.dimension == 1:
velocity = 1.0
elif self.config.dimension == 2:
velocity = (1.0, 1.0)
elif self.config.dimension == 3:
velocity = (1.0, 1.0, 1.0)
if self.config.dimension == 1:
return self._generate_1d(velocity, diffusion)
elif self.config.dimension == 2:
return self._generate_2d(velocity, diffusion)
elif self.config.dimension == 3:
return self._generate_3d(velocity, diffusion)
def _generate_1d(self, velocity: float, diffusion: float) -> sparse.spmatrix:
"""Generate 1D convection-diffusion matrix"""
from matrix_toolkit.pde.dim1d.generators import generate_1d_convection_diffusion
n = self.config.mesh_size[0]
h = self.config.get_mesh_spacing()[0]
return generate_1d_convection_diffusion(
n, velocity, diffusion, h,
boundary=self.config.boundary_condition
)
def _generate_2d(self, velocity: Tuple[float, float], diffusion: float) -> sparse.spmatrix:
"""Generate 2D convection-diffusion matrix"""
from matrix_toolkit.pde.dim2d.generators import generate_2d_convection_diffusion
nx, ny = self.config.mesh_size
hx, hy = self.config.get_mesh_spacing()
return generate_2d_convection_diffusion(
nx, ny, velocity, diffusion, hx, hy,
boundary=self.config.boundary_condition
)
def _generate_3d(self, velocity: Tuple[float, float, float], diffusion: float) -> sparse.spmatrix:
"""Generate 3D convection-diffusion matrix"""
from matrix_toolkit.pde.dim3d.generators import generate_3d_convection_diffusion
nx, ny, nz = self.config.mesh_size
hx, hy, hz = self.config.get_mesh_spacing()
return generate_3d_convection_diffusion(
nx, ny, nz, velocity, diffusion, hx, hy, hz
)