/** * ReactionDiffusionSolver — Reaction-diffusion system with adaptive RK4/5 ODE kinetics. * * ## Governing Equations * * For each species i: * * ∂Cᵢ/∂t = Dᵢ∇²Cᵢ + Rᵢ(C₁, C₂, ..., T) * * where: * Cᵢ = concentration of species i [mol/m³] * Dᵢ = diffusion coefficient of species i [m²/s] * Rᵢ = net reaction rate for species i [mol/(m³·s)] * T = temperature field [K] (from coupled ThermalSolver) * * ## Reaction Kinetics * * Reactions follow the law of mass action with Arrhenius rate constants: * * k(T) = A · exp(-Eₐ / (R · T)) * * where: * A = pre-exponential factor [1/s or m³/(mol·s) depending on order] * Eₐ = activation energy [J/mol] * R = universal gas constant = 8.314 J/(mol·K) * T = temperature [K] * * ## ODE Integration: Dormand-Prince RK4/5 (Adaptive) * * The reaction terms Rᵢ are stiff-capable and use an embedded Runge-Kutta * pair (Dormand-Prince) for adaptive timestep control. The local truncation * error is estimated from the difference between 4th and 5th order solutions: * * err = ||y₅ - y₄|| / (atol + rtol · ||y₅||) * * If err > 1, the substep is rejected and retried with a smaller dt. * If err < 1, the substep is accepted and dt is grown for the next substep. * * ## Spatial Discretization * * 2nd-order central differences on a uniform 3D grid (same as ThermalSolver). * * ## Thermal Coupling * * When coupled to ThermalSolver via CouplingManagerV2: * - Reads temperature field T(x,y,z) for Arrhenius rate evaluation * - Writes heat source field Q(x,y,z) = Σⱼ (-ΔHⱼ · rⱼ) for exothermic reactions * * The coupling is explicit (operator-split): diffusion → reaction → heat export. * * ## Stability * * - Diffusion: explicit with CFL check (same as ThermalSolver) * - Reaction: adaptive RK4/5 handles stiffness via timestep control * - Splitting: Strang splitting (half-diffusion → full-reaction → half-diffusion) * for 2nd-order temporal accuracy on the coupled system * * ## Known Limitations * * - Uniform grid only (no AMR) * - No surface reactions (volume only) * - Strang splitting may lose accuracy for very fast reactions (Da >> 1) * - No implicit diffusion fallback (unlike ThermalSolver) * * ## References * * - Hairer, Nørsett, Wanner, "Solving Ordinary Differential Equations I", 2nd ed., Ch. II.4 * - Dormand & Prince, "A family of embedded Runge-Kutta formulae", J. Comp. Appl. Math. 6, 1980 * - Strang, "On the Construction and Comparison of Difference Schemes", SINUM 5, 1968 * * @see SimSolver — generic solver interface * @see CouplingManagerV2 — multi-physics orchestrator * @see ThermalSolver — coupled heat source */ import { RegularGrid3D } from './RegularGrid3D'; export interface Species { /** Unique species identifier (e.g., "A", "B", "product") */ name: string; /** Diffusion coefficient [m²/s] */ diffusivity: number; /** Initial concentration [mol/m³] */ initialConcentration: number; /** Molar mass [kg/mol] (for density-related calculations, optional) */ molarMass?: number; } export interface Reaction { /** Human-readable label (e.g., "A + B → C") */ label: string; /** Stoichiometric coefficients: negative for reactants, positive for products. * Map from species name to coefficient. */ stoichiometry: Record; /** Arrhenius pre-exponential factor A [units depend on reaction order] */ preExponential: number; /** Activation energy Eₐ [J/mol] */ activationEnergy: number; /** Reaction order per species: map from species name to order. * Absent species are assumed order 0 (not involved in rate law). */ orders: Record; /** Enthalpy of reaction ΔH [J/mol]. Negative = exothermic. */ enthalpy: number; } export interface ReactionDiffusionConfig { gridResolution: [number, number, number]; domainSize: [number, number, number]; species: Species[]; reactions: Reaction[]; /** Reference temperature [K] when no thermal coupling is present (default: 298.15) */ referenceTemperature?: number; /** Adaptive RK tolerance (absolute) (default: 1e-6) */ absoluteTolerance?: number; /** Adaptive RK tolerance (relative) (default: 1e-3) */ relativeTolerance?: number; /** Maximum RK substeps per outer step (default: 1000) */ maxSubsteps?: number; /** Minimum RK substep size [s] (default: 1e-12) */ minSubstepSize?: number; /** Safety factor for adaptive step control (default: 0.9) */ safetyFactor?: number; /** Maximum step growth factor (default: 5.0) */ maxGrowthFactor?: number; /** * Diffusion integration mode (default: 'explicit'). * - 'explicit': forward Euler, CFL sub-cycled (stable but step-count grows with stiffness — W.314). * - 'implicit': backward-Euler Jacobi, unconditionally stable (one solve per step at any dt). * - 'auto': implicit when the requested dt exceeds the explicit CFL limit, else explicit. */ diffusionMode?: 'explicit' | 'implicit' | 'auto'; /** Max Jacobi iterations per implicit diffusion solve (default: 200). */ implicitMaxIterations?: number; /** Jacobi convergence tolerance for implicit diffusion (default: 1e-6). */ implicitTolerance?: number; } export interface ReactionDiffusionStats { simulationTime: number; stepCount: number; totalSubsteps: number; rejectedSubsteps: number; lastStepMs: number; speciesNames: string[]; minConcentrations: number[]; maxConcentrations: number[]; totalHeatRelease: number; } export declare class ReactionDiffusionSolver { private config; /** Concentration grids: one per species */ private concentrations; /** Temporary grid for diffusion half-step */ private concPrev; /** Heat source field Q [W/m³] — written each step for thermal coupling */ private heatSource; /** External temperature field [K] — read from thermal coupling */ private temperatureField; /** Whether temperature field has been set externally */ private hasExternalTemperature; private simulationTime; private stepCount; private totalSubsteps; private rejectedSubsteps; private lastStepMs; private readonly atol; private readonly rtol; private readonly maxSubsteps; private readonly minDt; private readonly safety; private readonly maxGrowth; constructor(config: ReactionDiffusionConfig); /** * Advance the reaction-diffusion system by dt seconds. * * Uses Strang splitting: * 1. Half-step diffusion (dt/2) * 2. Full-step reaction (dt) with adaptive RK4/5 * 3. Half-step diffusion (dt/2) */ step(dt: number): void; /** Set external temperature field (from ThermalSolver coupling) */ setTemperatureField(tempGrid: RegularGrid3D): void; /** Set temperature from Float32Array (for CouplingManagerV2 field transfer) */ setTemperatureArray(temps: Float32Array): void; /** Get concentration field for a species (for ScalarFieldOverlay) */ getConcentrationField(speciesIndex: number): Float32Array; /** Get concentration grid for a species (for coupling) */ getConcentrationGrid(speciesIndex: number): RegularGrid3D; /** Get heat source field [W/m³] for coupling to ThermalSolver */ getHeatSourceGrid(): RegularGrid3D; /** Get heat source as Float32Array */ getHeatSourceField(): Float32Array; /** Get temperature field (external or reference) */ getTemperatureGrid(): RegularGrid3D; /** Point query: concentration at world position via trilinear interpolation */ getConcentrationAt(speciesIndex: number, x: number, y: number, z: number): number; /** Get species names */ getSpeciesNames(): string[]; /** Number of species */ get speciesCount(): number; getStats(): ReactionDiffusionStats; dispose(): void; /** * Explicit diffusion: Cᵢ(n+1) = Cᵢ(n) + dt · Dᵢ · ∇²Cᵢ * * CFL stability: dt_stable = 1 / (2·D·(1/dx² + 1/dy² + 1/dz²)) * If dt exceeds CFL limit, sub-cycles with stable dt. */ private stepDiffusion; /** * Implicit (backward-Euler) diffusion via Gauss-Seidel, unconditionally stable. * Solves (I − dt·D·∇²)c_new = c_old with the same per-axis no-flux stencil as * `RegularGrid3D.laplacian` (an out-of-bounds axis is skipped, ∂c/∂n=0), so it * is correct in 2-D (nz=1) and 3-D alike. One solve per step at any dt — avoids * the explicit CFL sub-cycling cost wall in the stiff (high-d) Turing regime (W.314). */ private diffuseImplicit; /** * Solve the ODE system dC/dt = R(C, T) at each grid point independently. * * Uses adaptive Dormand-Prince RK4/5 for each cell. The reaction rate * depends on local temperature (from the temperature field) and local * concentrations of all species. */ private stepReaction; /** * Compute reaction rates dCᵢ/dt = Σⱼ νᵢⱼ · rⱼ for all species. * * Each reaction j has rate: * rⱼ = kⱼ(T) · Π_i Cᵢ^{orderᵢⱼ} * * where kⱼ(T) = Aⱼ · exp(-Eₐⱼ / (R · T)) (Arrhenius) */ private reactionRates; /** * Compute instantaneous heat release rate [W/m³] at a cell. * * Q = Σⱼ (-ΔHⱼ) · rⱼ * * Convention: ΔH < 0 for exothermic → -ΔH > 0 → positive heat release. */ private computeHeatRelease; } //# sourceMappingURL=ReactionDiffusionSolver.d.ts.map