/** * Semi-explicit index-1 differential-algebraic equation (DAE) solver via BDF. * * Solves the semi-explicit index-1 system * * y' = f(t, y, z) (differential variables y) * 0 = g(t, y, z) (algebraic constraint z) * * on `t ∈ [t0, T]`. "Index-1" means the algebraic Jacobian block `∂g/∂z` is * **nonsingular**, so the constraint `g = 0` locally determines `z` from * `(t, y)`; this is exactly the condition that makes the coupled per-step * Newton system solvable. * * ## Method — variable-step, variable-order (1–2) BDF with a combined Newton step * * The differential derivative is discretised with a Backward Differentiation * Formula (the same BDF family that backs {@link bdfSolve}): on the last `k` * accepted times plus the new time `t_{n+1}`, the interpolating polynomial's * derivative at `t_{n+1}` is `Σ_j c_j·w_{n+1-j}` (variable-step coefficients * `c_j` from the Lagrange-basis derivative, so BDF1/BDF2 work on a non-uniform * grid). Each step then solves the **combined** nonlinear system for the new * `w_{n+1} = (y_{n+1}, z_{n+1})` * * differential rows: c_0·y_{n+1} + (history) − f(t_{n+1}, y_{n+1}, z_{n+1}) = 0 * algebraic rows: g(t_{n+1}, y_{n+1}, z_{n+1}) = 0 * * by Newton's method. The Newton (iteration) matrix is the block * * ⎡ c_0·I − ∂f/∂y − ∂f/∂z ⎤ * ⎣ ∂g/∂y ∂g/∂z ⎦ * * (finite-differenced from the residual by default, or built from analytic * blocks supplied via `options.jacobian`) and is LU-solved through the shared * matrix-package factorisation ({@link _factorSolver}). The index-1 condition * (`∂g/∂z` nonsingular) is exactly what makes this matrix nonsingular, so a * **higher-index** input is detected as a singular Jacobian and reported * (it is not silently integrated to garbage). * * Adaptive step size uses a predictor/corrector local-error estimate (the * corrector minus a lower-order extrapolation predictor), scaled by the usual * `atol + rtol·|w|` weights. * * ## Consistent initial values * * The initial algebraic value `z0` is treated as a **guess**: the solver runs * Newton on `g(t0, y0, z0) = 0` to land on the constraint manifold before the * first step (so a slightly-off or omitted `z0` is corrected rather than * silently propagated). If that Newton fails / `∂g/∂z` is singular at `t0`, * the problem is not index-1 and an error is thrown. * * Plain-number state only (the Jacobian and linear solves are numeric). * * @packageDocumentation */ /** Differential forcing `y' = f(t, y, z)`. Returns the `y'` vector (a scalar is accepted for 1-D y). */ export type DAEDifferential = (t: number, y: number[], z: number[]) => number[] | number; /** Algebraic constraint `0 = g(t, y, z)`. Returns the residual vector (a scalar is accepted for 1-D z). */ export type DAEConstraint = (t: number, y: number[], z: number[]) => number[] | number; /** * Analytic Jacobian blocks of the DAE at `(t, y, z)`, supplied via * {@link SolveDAEOptions.jacobian} in place of the default finite differences. * Each block is a matrix in the usual `[row][col]` convention: * `fy[i][j] = ∂fᵢ/∂yⱼ`, `fz[i][j] = ∂fᵢ/∂zⱼ`, `gy`, `gz` likewise. */ export interface DAEJacobianBlocks { fy: number[][]; fz: number[][]; gy: number[][]; gz: number[][]; } /** Options for {@link solveDAE}. */ export interface SolveDAEOptions { /** Relative tolerance for the local-error step control (default `1e-6`). */ tol?: number; /** Absolute tolerance (default `tol · 1e-3`). */ atol?: number; /** Initial step size (default: chosen from `‖f‖`). */ firstStep?: number; /** Minimum step size (a hard floor; the solver throws if it must go below it). */ minStep?: number; /** Maximum step size (default: no cap). */ maxStep?: number; /** Maximum number of accepted steps (default `1e5`). */ maxIter?: number; /** Maximum BDF order, 1 or 2 (default `2`). */ maxOrder?: 1 | 2; /** Newton convergence tolerance on the scaled increment (default derived from `tol`). */ newtonTol?: number; /** * Analytic Jacobian blocks `(t, y, z) => { fy, fz, gy, gz }`. When given they * replace the default finite-difference Newton matrix (faster + more accurate). */ jacobian?: (t: number, y: number[], z: number[]) => DAEJacobianBlocks; } /** * Solution returned by {@link solveDAE}. * * `y`/`z` are `number[][]` (one state vector per time) when the corresponding * initial value was an array, and unwrapped to `number[]` when it was a scalar. */ export interface DAESolution { /** Accepted output times, `t[0] = t0`, last entry `= T`. */ t: number[]; /** Differential state at each time. */ y: number[][] | number[]; /** Algebraic state at each time. */ z: number[][] | number[]; } /** * Solve the semi-explicit index-1 DAE `y' = f(t, y, z)`, `0 = g(t, y, z)` on * `[tspan[0], tspan[1]]` with a variable-step BDF(1–2) integrator and a coupled * Newton solve for `(y, z)` at each step. * * @param f Differential forcing `f(t, y, z) → y'`. * @param g Algebraic constraint `g(t, y, z) → 0`. * @param tspan `[t0, T]` (forward integration; `T > t0`). * @param y0 Initial differential state (scalar or vector). * @param z0 Initial algebraic guess (scalar or vector). Refined to satisfy * `g(t0, y0, z0) = 0` before the first step. Defaults to `[0]`. * @param options See {@link SolveDAEOptions}. * @returns `{ t, y, z }` — `y`/`z` are unwrapped to `number[]` when the matching * initial value was a scalar, else `number[][]`. * * @example * // RC circuit: C·V' = i, i·R = Vs − V (semi-explicit, index-1) * // with C=R=Vs=1, V(0)=0 → V = 1 − e^{−t}, i = e^{−t} * const sol = solveDAE( * (t, y, z) => [z[0]], // V' = i * (t, y, z) => [z[0] - (1 - y[0])], // i − (Vs − V) = 0 * [0, 3], 0, 1, * ); */ export declare function solveDAE(f: DAEDifferential, g: DAEConstraint, tspan: [number, number] | number[], y0: number | number[], z0?: number | number[], options?: SolveDAEOptions): DAESolution; //# sourceMappingURL=solveDAE.d.ts.map