103: 1D Transient convection-diffusion equation
Solve the time-dependent convection-diffusion equation
\[\partial_t u -\nabla ( D \nabla u - v u) = 0\]
in $\Omega=(0,1)$ with homogeneous Neumann boundary condition at $x=0$ and outflow boundary condition at $x=1$.
This is the time-dependent version of the convection-diffusion problem from Example102. The equation models the evolution of a scalar quantity $u$ (e.g., concentration, temperature) under the combined effects of diffusion (with coefficient $D$) and advection (with velocity $v$).
Physical Interpretation
- Diffusion term: $D \nabla u$ represents spreading due to random motion
- Convection term: $v u$ represents transport by bulk motion of the medium
- Time evolution: $\partial_t u$ describes how the quantity changes over time
Boundary Conditions
- Left boundary ($x=0$): Homogeneous Neumann condition $\partial_n u = 0$ (no flux)
- Right boundary ($x=1$): Outflow condition $D\partial_n u = 0$, equivalent to $ ( D \nabla u - v u)\cdot \vec n = v u \cdot \vec n $
allowing material transported by convection towards the boundary to leave the domain.
Discretization
The spatial discretization uses the exponential fitting scheme (Scharfetter-Gummel method) which maintains monotonicity properties and provides accurate solutions even for convection-dominated transport (high Peclet numbers). This method uses the Bernoulli function:
\[B(x) = \frac{x}{e^x - 1}\]
to construct fluxes that exactly solve the local two-point boundary value problem on each edge.
Initial Condition
The simulation starts with a linear profile $u(x,0) = 1 - 2x$, which evolves under the combined effects of diffusion and convection until it reaches a steady state or is advected out of the domain.
module Example103_ConvectionDiffusion1Dusing Printfusing VoronoiFVMusing ExtendableGridsusing GridVisualize# Mutable struct to hold problem parameters# This encapsulates all physical and numerical parametersmutable struct ProblemData D::Float64 ## Diffusion coefficient v::Vector{Float64} ## Velocity vectorend# Bernoulli function used in the exponential fitting discretizationfunction bernoulli(x) if abs(x) < nextfloat(eps(typeof(x))) return 1 end return x / (exp(x) - 1)endfunction exponential_flux!(f, u, edge, data) vh = project(edge, data.v) Bplus = data.D * bernoulli(vh / data.D) Bminus = data.D * bernoulli(-vh / data.D) f[1] = Bminus * u[1, 1] - Bplus * u[1, 2] return nothingendfunction outflow!(f, u, node, data) if node.region == 2 f[1] = data.v[1] * u[1] end return nothingendfunction main(; n = 10, Plotter = nothing, D = 0.01, v = 1.0, tend = 100) # Create a one-dimensional discretization h = 1.0 / n grid = simplexgrid(0:h:1) # Initialize problem parameters in data structure problem_data = ProblemData(D, [v]) sys = VoronoiFVM.System( grid, VoronoiFVM.Physics(; flux = exponential_flux!, breaction = outflow!, data = problem_data ) ) # Add species 1 to region 1 enable_species!(sys, 1, [1]) # Set boundary conditions boundary_neumann!(sys, 1, 1, 0.0) # Create a solution array inival = unknowns(sys) inival[1, :] .= map(x -> 1 - 2x, grid) # Transient solution of the problem control = VoronoiFVM.SolverControl() control.Δt = 0.01 * h control.Δt_min = 0.01 * h control.Δt_max = 0.1 * tend tsol = solve(sys; inival, times = [0, tend], control) vis = GridVisualizer(; Plotter = Plotter) for i in 1:length(tsol.t) scalarplot!( vis[1, 1], grid, tsol[1, :, i]; flimits = (0, 1), title = "t=$(tsol.t[i])", show = true ) sleep(0.01) end return tsolendusing Testfunction runtests() tsol = main() @test maximum(tsol) <= 1.0 && maximum(tsol.u[end]) < 1.0e-20 return nothingendendThis page was generated using Literate.jl.