Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

L3: Implicit Diffusion-Reaction

This notebook builds the sparse gradient (G\mathbf{G}) and divergence (D\mathbf{D}) operators used in pymrm, applies second-order boundary conditions via Lagrange interpolation, and solves steady-state and transient 1D diffusion-reaction problems using Newton–Raphson iteration.

Governing equations

Unsteady 1D convection-diffusion-reaction:

∂c∂t=−v∂c∂z+D∂2c∂z2+R(c)\frac{\partial c}{\partial t} = -v\frac{\partial c}{\partial z} + D\frac{\partial^2 c}{\partial z^2} + R(c)

Written using gradient and divergence operators:

∂c∂t=D J,J=−v cf+D G c\frac{\partial c}{\partial t} = \mathbf{D}\,\mathbf{J}, \quad \mathbf{J} = -v\,c_f + D\,\mathbf{G}\,c

Euler backward (implicit, unconditionally A-stable):

cn+1=cn+Δt[D(D G cn+1+Jconv)+R(cn+1)]c^{n+1} = c^n + \Delta t\left[\mathbf{D}\left(D\,\mathbf{G}\,c^{n+1} + J_{\text{conv}}\right) + R(c^{n+1})\right]

This is a nonlinear system in cn+1c^{n+1}; linearise with Newton–Raphson.

Second-order boundary conditions

pymrm uses Lagrange interpolation through the boundary face xf,0x_{f,0} and the two nearest cell centres xc,0x_{c,0}, xc,1x_{c,1} to evaluate the gradient at the boundary:

dcdz∣zf,0=α0 cf,0+α1 cc,0−α2 cc,1\left.\frac{dc}{dz}\right|_{z_{f,0}} = \alpha_0\,c_{f,0} + \alpha_1\,c_{c,0} - \alpha_2\,c_{c,1}

with Lagrange coefficients α0=α1−α2\alpha_0 = \alpha_1 - \alpha_2, α1=(xc,1−xf,0)/[(xc,0−xf,0)(xc,1−xc,0)]\alpha_1 = (x_{c,1}-x_{f,0})/[(x_{c,0}-x_{f,0})(x_{c,1}-x_{c,0})], α2=(xc,0−xf,0)/[(xc,1−xf,0)(xc,1−xc,0)]\alpha_2 = (x_{c,0}-x_{f,0})/[(x_{c,1}-x_{f,0})(x_{c,1}-x_{c,0})].

The general Robin boundary condition is:

adcdn∣boundary+b cf=da\left.\frac{dc}{dn}\right|_{\text{boundary}} + b\,c_f = d
BC typeaabbddMeaning
Dirichlet01cwc_wc=cwc = c_w at wall
Neumann10gwg_wdc/dn=gwdc/dn = g_w at wall
Robin1h/Dh/Dhc∞/Dh c_\infty/DFilm model

construct_grad incorporates these automatically at second-order accuracy when bc=({'a':..,'b':..,'d':..}, {...}) is passed.

PyMRM building blocks

FunctionSignatureRole
construct_grad(shape, x_f, bc=(bc_L, bc_R))G\mathbf{G}: maps cell values → face gradients (2nd-order BCs)
construct_div(shape, x_f, nu=0)D\mathbf{D}: maps face fluxes → cell divergences
construct_convflux_upwind(shape, x_f, bc, v)Upwind convective flux matrix
NumJac(shape)Finite-difference Jacobian for nonlinear terms
newton(func, x0)Newton–Raphson with automatic Jacobian

Example 1 — Steady-state diffusion: Dirichlet BCs

Two components counter-diffusing through a slab of thickness LL, with a fast reaction A+B→A + B \to products. Both components have fixed concentrations at the walls.

<Figure size 600x350 with 1 Axes>

Example 2 — Transient diffusion-reaction with general BCs

Unsteady diffusion of a single species with first-order reaction, Dirichlet BC at left (c=1c=1), Neumann (zero-flux) at right.

<Figure size 600x350 with 1 Axes>

Summary

  • construct_grad(shape, x_f, bc=(bc_L, bc_R)) returns (Grad, grad_bc) — the BC source vector grad_bc carries the inhomogeneous part; add Div @ D * grad_bc to the RHS

  • BCs are second-order via Lagrange interpolation using the two nearest cells — not first-order ghost cells

  • Robin form a (dc/dn)+b c=da\,(dc/dn) + b\,c = d covers Dirichlet (a=0,b=1a=0,b=1), Neumann (a=1,b=0a=1,b=0), and film-model (a=1,b=h/Da=1,b=h/D) conditions

  • Backward Euler is unconditionally stable for diffusion; the linear system is solved once per time step by LU factorisation

  • For nonlinear kinetics, use NumJac for the reaction Jacobian and iterate with Newton–Raphson