A Complete Derivation of Quasi-constant Step Size BDF Method
2021-04-23
The backward differentiation formula (BDF) method is the most widely used method to solve stiff ordinary differential equations (ODEs) and differential-algebraic equations (DAEs). Famous stiff solvers like ode15s, ode15i, LSODE, LSODA, CVODE, IDA, and DASSL all use the BDF method. Unfortunately, all BDF derivations online are not complete, in the sense that one still needs to do hand derivation for some formulas to fully understand the algorithm and be able to write a robust and production-ready variable step size BDF implementation. This blog post aims to bridge this gap by providing a complete derivation of all the formulas needed for a production-ready quasi-constant step size BDF method.
Recall the definitions of ODEs u′=f(u,p,t) and DAEs F(u′,u,p,t)=0. The idea behind BDFs is to use a polynomial p(t) to interpolate u(t) using the points (tn+1,un+1),(tn,un),(tn−1,un−1),..., and solve for the next step un+1 that makes p′(t+h)=un+1′, where h is the step size.
Constant step size BDF
Let's first solve the simple problem: deriving BDFs assuming the step size h is constant. An order s BDF interpolates s+1 points to form a degree s polynomial ps,n+1(t) when computing the approximation un+1. Since we want to compute the next step, we can center the polynomial at the current time tn and parametrize t by the step size h. Thus, we define qs,n+1(c)=ps,n+1(t)=ps,n+1(tn+ch) where c∈R is the new independent variable after the change of variable. Also note that we have t−tn−i=(tn+ch)−(tn−ih)=(c+i)h.
where [...] denotes Newton's divided difference, and ∇ is the backward differentiation operator. We used the relation hjj![un+1,...,un+1−j]=∇hjun+1 in the last step.
Now, we want to set p′(t+h)=un+1′. Note that we have q′(1)=hp′(tn+h)=hun+1′. Therefore, we need to solve
j=1∑sδj∇jun+1=hun+1′,
where
δj=j!1dcdi=1∏jc−i−2∣∣∣∣c=1=j!1i=1∑jj=1 and j=i∏jc+i−2∣∣∣∣c=1=j!1i=1∑jj=1 and j=i∏jj−1note that the product vanishes when i=1=j!1j=2∏jj−1=j!1(j−1)!=j1.
Together, the constant step size BDF is
j=1∑sj1∇jun+1=hun+1′.
Varying the step size by transplanting to equidistant grid
A simple strategy to vary the step size is to simply evaluate the old polynomial p from the at the equidistant grid tn−ih~ for i=0,1,2,...,s to obtain an interpolated "constant step size history", when changing to a new step size h~. During the step changing process, we do not need to compute un+1. Hence, we only need the polynomial ps,n(tn+ch). This strategy is simple because evaluating and re-interpolating a polynomial interpolant are linear transformations of the history, so we can solve for matrices that performs such transformations instead of working with polynomials explicitly.
Note that the only step size dependent term is ∇hjun. Let's define r=h~/h,
With the interpolation condition, we force ps,n(tn+ch~)=ps,n(tn+crh)=p~s,n(tn+ch~) for c=0,−1,...,−s. Note that when c=0, the interpolation condition simplifies to p~s,n(tn)=un=ps,n(tn) which always holds. Therefore, we only need to solve
Note that c=−k as k=1,2,...,s. We end this section by remarking that U2=I.
julia> using LinearAlgebra
julia> U(s) = [prod(i->i-k, 0:j-1)//factorial(j) for j in1:s, k in1:s]
U (generic function with 1 method)
julia> U(5)^2 == I
true
Therefore, the divided differences with h~ is simply
D~=D(RU).
Updating backward differences
A natural choice for the predictor is then un+1(0)=ps,n(tn+h)=un+∑j=1sDj. From the definition of the backward difference, we have ∇hs+1un+1=∇hsun+1−∇hsun=∇hs−1un+1−∇hs−1un−∇hsun=∇hs−2un+1−∇hs−2un−∇hs−1un−∇hsun=un+1−un−...−∇hs−2un−∇hs−1un−∇hsun=un+1−un+1(0).
Therefore, the s+1-th order backward difference of the next step is simply the difference between the corrector un+1 and the predictor un+1(0). Also, note that from
∇hjun+1=∇hj+1un+1+∇hjun,
where j∈N, we can compute all the lower order backward differences of the next step, and
∇hs+2un+1=∇hs+1un+1−∇hs+1un.
We can use the above updating relations to simplify the backward differences:
where γj=∑k=1jj1. This naïve approach contains a lot of redundant computation, and the computational complexity is O(s2m) for un∈Rm. We can do a lot better if reorder the summation:
==j=1∑sj1k=j∑s∇kun=j=1∑sk=j∑sj1∇kun=j=1∑sk=1∧k≥j∑sj1∇kunk=1∑sj=1∧j≤k∑sj1∇kunnote the interchange of summationk=1∑sj=1∑kj1∇kun=k=1∑s(j=1∑kj1)∇kun=k=1∑sγk∇kun.
This expression can be evaluated in O(sm) time.
Putting everything together, the simplified BDF is
γs(un+1−un+1(0))+k=1∑sγk∇kun=hun+1′,
and we can use a nonlinear equation solver to solve for un+1. We will omit the details of writing a stable and efficient nonlinear solver here as they are independent from the BDF method itself.
Local truncation error
By the standard result on polynomial interpolation, we know that u(t)−ps,n+1(t)=(s+1)!u(s+1)(ξt)w(t),
where w(t)=∏j=−1s−1(t−tn−j). We assume that all the history is completely accurate and compute the error of ps,n+1′(tn+1):
Note that we don't have the term with w(tn+1) because it goes to 0 by the construction of w. Expanding w′(tn+1):
w′(tn+1)=j=−1∑s−1⎣⎢⎡(tn+1−tn−j)′k=−1∧k=j∏s−1(tn+1−tn−k)⎦⎥⎤=j=−1∑s−1k=−1∧k=j∏s−1(tn+1−tn−k)the product only doesn’t vanish when j=−1=k=0∏s−1(tn+1−tn−k).
Hence, the error estimate for the s-th order BDF is
s+11∇hs+1un+1,
and this quantity can easily be computed from the updated backward differences. With an appropriate controller for the step size and order, a quasi-constant step size BDF method can be implemented in its totality.