Custom Operators
Creating custom differential and integral operators.
Overview
Beyond built-in operators (grad, div, curl, lap), you can define custom operators for specialized physics.
Helper Functions
Simple Custom Operators
# Advection operator: u·∇f
function advection(u, f)
result = u.components[1].data .* ∂x(f).data
for i in 2:length(u.components)
result .+= u.components[i].data .* d_operators[i](f).data
end
return result
endUsing in Equations
# Option 1: Expand manually
Tarang.add_equation!(problem, "∂t(T) = -ux*∂x(T) - uz*∂z(T)")
# Option 2: Use helper in equation string
# (requires registration)Using Operators in Equations
The equation parser recognizes all built-in operators. Use them directly:
# Built-in operators available in equations:
# grad, div, curl, lap (or Δ), dt (or ∂t), d
# integrate, average, interpolate, convert, lift
# sin, cos, tan, exp, log, sqrt, abs, tanh
# Use operators directly in equations
Tarang.add_equation!(problem, "∂t(T) - kappa*Δ(T) = -ux*∂x(T) - uz*∂z(T)")Spectral Differentiation
Fourier Derivative
function fourier_derivative(field, basis, order=1)
k = get_wavenumbers(basis)
Tarang.ensure_layout!(field, :c)
# Multiply by (ik)^order
get_coeff_data(field) .*= (1im .* k) .^ order
return field
endChebyshev Derivative
function chebyshev_derivative(field, basis)
Tarang.ensure_layout!(field, :c)
# Use differentiation matrix
D = chebyshev_diff_matrix(basis.size)
get_coeff_data(field) .= D * get_coeff_data(field)
return field
endIntegral Operators
Spatial Integration
function integrate(field, dim)
Tarang.ensure_layout!(field, :g)
if dim == 1 # x-direction
dx = field.bases[1].length / field.bases[1].size
return sum(get_grid_data(field), dims=1) * dx
elseif dim == 2 # z-direction
# Chebyshev: use quadrature weights
weights = chebyshev_weights(field.bases[2])
return sum(get_grid_data(field) .* weights', dims=2)
end
endRunning Average
function running_average(field, window)
Tarang.ensure_layout!(field, :g)
# Convolution with box filter
# ...
endVector Calculus
Strain Rate Tensor
function strain_rate(u)
# S_ij = 1/2 (∂u_i/∂x_j + ∂u_j/∂x_i)
S = TensorField(u.dist, u.coords, "S", u.bases; symmetric=true)
ux, uz = u.components[1], u.components[2]
S[1,1] = ∂x(ux)
S[1,2] = 0.5 * (∂x(uz) + ∂z(ux))
S[2,2] = ∂z(uz)
return S
endVorticity (2D)
function vorticity_2d(u)
ux, uz = u.components[1], u.components[2]
return ∂x(uz) - ∂z(ux)
endHelicity (3D)
function helicity(u)
omega = ∇×(u)
# H = u · ω
H = u.components[1].data .* omega.components[1].data
for i in 2:3
H .+= u.components[i].data .* omega.components[i].data
end
return H
endNonlinear Operators
Convective Derivative
function convective_derivative(u, f)
# (u·∇)f
result = zeros(size(get_grid_data(f)))
for (i, comp) in enumerate(u.components)
Tarang.ensure_layout!(comp, :g)
df = d_operators[i](f)
Tarang.ensure_layout!(df, :g)
result .+= get_grid_data(comp) .* get_grid_data(df)
end
return result
endNonlinear Term (u·∇u)
function nonlinear_advection(u)
# Returns vector field
result = similar(u)
for (j, uj) in enumerate(u.components)
get_grid_data(result.components[j]) .= convective_derivative(u, uj)
end
return result
endPhysics-Specific Operators
Coriolis Force
function coriolis(u, Omega)
# 2Ω × u
# For rotation about z-axis:
fx = -2 * Omega * u.components[2] # -2Ω*v
fy = 2 * Omega * u.components[1] # +2Ω*u
fz = 0
return (fx, fy, fz)
endLorentz Force (MHD)
function lorentz_force(J, B)
# J × B
return cross(J, B) # or: J × B
endTips
Performance
- Keep operations in same space (grid or spectral)
- Minimize transforms
- Pre-compute constant factors
Validation
- Test against analytical solutions
- Check symmetries
- Verify conservation properties
See Also
- Operators: Built-in operators
- Fields: Field types
- API: Operators: Reference