00001 # ifndef CPPAD_ODE_GEAR_INCLUDED 00002 # define CPPAD_ODE_GEAR_INCLUDED 00003 00004 /* -------------------------------------------------------------------------- 00005 CppAD: C++ Algorithmic Differentiation: Copyright (C) 2003-07 Bradley M. Bell 00006 00007 CppAD is distributed under multiple licenses. This distribution is under 00008 the terms of the 00009 Common Public License Version 1.0. 00010 00011 A copy of this license is included in the COPYING file of this distribution. 00012 Please visit http://www.coin-or.org/CppAD/ for information on other licenses. 00013 -------------------------------------------------------------------------- */ 00014 00015 /* 00016 $begin OdeGear$$ 00017 $spell 00018 cppad.hpp 00019 Jan 00020 bool 00021 const 00022 CppAD 00023 dep 00024 $$ 00025 00026 $index OdeGear$$ 00027 $index Ode, Gear$$ 00028 $index Gear, Ode$$ 00029 $index stiff, Ode$$ 00030 $index differential, equation$$ 00031 $index equation, differential$$ 00032 00033 $section An Arbitrary Order Gear Method$$ 00034 00035 $head Syntax$$ 00036 $syntax%# include <cppad/ode_gear.hpp> 00037 %$$ 00038 $syntax%OdeGear(%F%, %m%, %n%, %T%, %X%, %e%)%$$ 00039 00040 00041 $head Purpose$$ 00042 This routine applies 00043 $xref/OdeGear/Gear's Method/Gear's Method/$$ 00044 to solve an explicit set of ordinary differential equations. 00045 We are given 00046 $latex f : \R \times \R^n \rightarrow \R^n$$ be a smooth function. 00047 This routine solves the following initial value problem 00048 $latex \[ 00049 \begin{array}{rcl} 00050 x( t_{m-1} ) & = & x^0 \\ 00051 x^\prime (t) & = & f[t , x(t)] 00052 \end{array} 00053 \] $$ 00054 for the value of $latex x( t_m )$$. 00055 If your set of ordinary differential equations are not stiff 00056 an explicit method may be better (perhaps $xref/Runge45/$$.) 00057 00058 $head Include$$ 00059 The file $code cppad/ode_gear.hpp$$ is included by $code cppad/cppad.hpp$$ 00060 but it can also be included separately with out the rest of 00061 the $code CppAD$$ routines. 00062 00063 $head Fun$$ 00064 The class $italic Fun$$ 00065 and the object $italic F$$ satisfy the prototype 00066 $syntax% 00067 %Fun% &%F% 00068 %$$ 00069 This must support the following set of calls 00070 $syntax% 00071 %F%.Ode(%t%, %x%, %f%) 00072 %F%.Ode_dep(%t%, %x%, %f_x%) 00073 %$$ 00074 00075 $subhead t$$ 00076 The argument $italic t$$ has prototype 00077 $syntax% 00078 const %Scalar% &%t% 00079 %$$ 00080 (see description of $xref/OdeGear/Scalar/Scalar/$$ below). 00081 00082 $subhead x$$ 00083 The argument $italic x$$ has prototype 00084 $syntax% 00085 const %Vector% &%x% 00086 %$$ 00087 and has size $italic n$$ 00088 (see description of $xref/OdeGear/Vector/Vector/$$ below). 00089 00090 $subhead f$$ 00091 The argument $italic f$$ to $syntax%%F%.Ode%$$ has prototype 00092 $syntax% 00093 %Vector% &%f% 00094 %$$ 00095 On input and output, $italic f$$ is a vector of size $italic n$$ 00096 and the input values of the elements of $italic f$$ do not matter. 00097 On output, 00098 $italic f$$ is set equal to $latex f(t, x)$$ 00099 (see $italic f(t, x)$$ in $xref/OdeGear/Purpose/Purpose/$$). 00100 00101 $subhead f_x$$ 00102 The argument $italic f_x$$ has prototype 00103 $syntax% 00104 %Vector% &%f_x% 00105 %$$ 00106 On input and output, $italic f_x$$ is a vector of size $latex n * n$$ 00107 and the input values of the elements of $italic f_x$$ do not matter. 00108 On output, 00109 $latex \[ 00110 f\_x [i * n + j] = \partial_{x(j)} f_i ( t , x ) 00111 \] $$ 00112 00113 $subhead Warning$$ 00114 The arguments $italic f$$, and $italic f_x$$ 00115 must have a call by reference in their prototypes; i.e., 00116 do not forget the $code &$$ in the prototype for 00117 $italic f$$ and $italic f_x$$. 00118 00119 $head m$$ 00120 The argument $italic m$$ has prototype 00121 $syntax% 00122 size_t %m% 00123 %$$ 00124 It specifies the order (highest power of $latex t$$) 00125 used to represent the function $latex x(t)$$ in the multi-step method. 00126 Upon return from $code OdeGear$$, 00127 the $th i$$ component of the polynomial is defined by 00128 $latex \[ 00129 p_i ( t_j ) = X[ j * n + i ] 00130 \] $$ 00131 for $latex j = 0 , \ldots , m$$ (where $latex 0 \leq i < n$$). 00132 The value of $latex m$$ must be greater than or equal one. 00133 00134 $head n$$ 00135 The argument $italic n$$ has prototype 00136 $syntax% 00137 size_t %n% 00138 %$$ 00139 It specifies the range space dimension of the 00140 vector valued function $latex x(t)$$. 00141 00142 $head T$$ 00143 The argument $italic T$$ has prototype 00144 $syntax% 00145 const %Vector% &%T% 00146 %$$ 00147 and size greater than or equal to $latex m+1$$. 00148 For $latex j = 0 , \ldots m$$, $latex T[j]$$ is the time 00149 corresponding to time corresponding 00150 to a previous point in the multi-step method. 00151 The value $latex T[m]$$ is the time 00152 of the next point in the multi-step method. 00153 The array $latex T$$ must be monotone increasing; i.e., 00154 $latex T[j] < T[j+1]$$. 00155 Above and below we often use the shorthand $latex t_j$$ for $latex T[j]$$. 00156 00157 00158 $head X$$ 00159 The argument $italic X$$ has the prototype 00160 $syntax% 00161 %Vector% &%X% 00162 %$$ 00163 and size greater than or equal to $latex (m+1) * n$$. 00164 On input to $code OdeGear$$, 00165 for $latex j = 0 , \ldots , m-1$$, and 00166 $latex i = 0 , \ldots , n-1$$ 00167 $latex \[ 00168 X[ j * n + i ] = x_i ( t_j ) 00169 \] $$ 00170 Upon return from $code OdeGear$$, 00171 for $latex i = 0 , \ldots , n-1$$ 00172 $latex \[ 00173 X[ m * n + i ] \approx x_i ( t_m ) 00174 \] $$ 00175 00176 $head e$$ 00177 The vector $italic e$$ is an approximate error bound for the result; i.e., 00178 $latex \[ 00179 e[i] \geq | X[ m * n + i ] - x_i ( t_m ) | 00180 \] $$ 00181 The order of this approximation is one less than the order of 00182 the solution; i.e., 00183 $latex \[ 00184 e = O ( h^m ) 00185 \] $$ 00186 where $latex h$$ is the maximum of $latex t_{j+1} - t_j$$. 00187 00188 $head Scalar$$ 00189 The type $italic Scalar$$ must satisfy the conditions 00190 for a $xref/NumericType/$$ type. 00191 The routine $xref/CheckNumericType/$$ will generate an error message 00192 if this is not the case. 00193 In addition, the following operations must be defined for 00194 $italic Scalar$$ objects $italic a$$ and $italic b$$: 00195 00196 $table 00197 $bold Operation$$ $cnext $bold Description$$ $rnext 00198 $syntax%%a% < %b%$$ $cnext 00199 less than operator (returns a $code bool$$ object) 00200 $tend 00201 00202 $head Vector$$ 00203 The type $italic Vector$$ must be a $xref/SimpleVector/$$ class with 00204 $xref/SimpleVector/Elements of Specified Type/elements of type Scalar/$$. 00205 The routine $xref/CheckSimpleVector/$$ will generate an error message 00206 if this is not the case. 00207 00208 $head Example$$ 00209 $children% 00210 example/ode_gear.cpp 00211 %$$ 00212 The file 00213 $xref/OdeGear.cpp/$$ 00214 contains an example and test a test of using this routine. 00215 It returns true if it succeeds and false otherwise. 00216 00217 $head Source Code$$ 00218 The source code for this routine is in the file 00219 $code cppad/ode_gear.hpp$$. 00220 00221 $head Theory$$ 00222 For this discussion we use the shorthand $latex x_j$$ 00223 for the value $latex x ( t_j ) \in \R^n$$ which is not to be confused 00224 with $latex x_i (t) \in \R$$ in the notation above. 00225 The interpolating polynomial $latex p(t)$$ is given by 00226 $latex \[ 00227 p(t) = 00228 \sum_{j=0}^m 00229 x_j 00230 \frac{ 00231 \prod_{i \neq j} ( t - t_i ) 00232 }{ 00233 \prod_{i \neq j} ( t_j - t_i ) 00234 } 00235 \] $$ 00236 The derivative $latex p^\prime (t)$$ is given by 00237 $latex \[ 00238 p^\prime (t) = 00239 \sum_{j=0}^m 00240 x_j 00241 \frac{ 00242 \sum_{i \neq j} \prod_{k \neq i,j} ( t - t_k ) 00243 }{ 00244 \prod_{k \neq j} ( t_j - t_k ) 00245 } 00246 \] $$ 00247 Evaluating the derivative at the point $latex t_\ell$$ we have 00248 $latex \[ 00249 \begin{array}{rcl} 00250 p^\prime ( t_\ell ) & = & 00251 x_\ell 00252 \frac{ 00253 \sum_{i \neq \ell} \prod_{k \neq i,\ell} ( t_\ell - t_k ) 00254 }{ 00255 \prod_{k \neq \ell} ( t_\ell - t_k ) 00256 } 00257 + 00258 \sum_{j \neq \ell} 00259 x_j 00260 \frac{ 00261 \sum_{i \neq j} \prod_{k \neq i,j} ( t_\ell - t_k ) 00262 }{ 00263 \prod_{k \neq j} ( t_j - t_k ) 00264 } 00265 \\ 00266 & = & 00267 x_\ell 00268 \sum_{i \neq \ell} 00269 \frac{ 1 }{ t_\ell - t_i } 00270 + 00271 \sum_{j \neq \ell} 00272 x_j 00273 \frac{ 00274 \prod_{k \neq \ell,j} ( t_\ell - t_k ) 00275 }{ 00276 \prod_{k \neq j} ( t_j - t_k ) 00277 } 00278 \\ 00279 & = & 00280 x_\ell 00281 \sum_{k \neq \ell} ( t_\ell - t_k )^{-1} 00282 + 00283 \sum_{j \neq \ell} 00284 x_j 00285 ( t_j - t_\ell )^{-1} 00286 \prod_{k \neq \ell ,j} ( t_\ell - t_k ) / ( t_j - t_k ) 00287 \end{array} 00288 \] $$ 00289 We define the vector $latex \alpha \in \R^{m+1}$$ by 00290 $latex \[ 00291 \alpha_j = \left\{ \begin{array}{ll} 00292 \sum_{k \neq m} ( t_m - t_k )^{-1} 00293 & {\rm if} \; j = m 00294 \\ 00295 ( t_j - t_m )^{-1} 00296 \prod_{k \neq m,j} ( t_m - t_k ) / ( t_j - t_k ) 00297 & {\rm otherwise} 00298 \end{array} \right. 00299 \] $$ 00300 It follows that 00301 $latex \[ 00302 p^\prime ( t_m ) = \alpha_0 x_0 + \cdots + \alpha_m x_m 00303 \] $$ 00304 Gear's method determines $latex x_m$$ by solving the following 00305 nonlinear equation 00306 $latex \[ 00307 f( t_m , x_m ) = \alpha_0 x_0 + \cdots + \alpha_m x_m 00308 \] $$ 00309 Newton's method for solving this equation determines iterates, 00310 which we denote by $latex x_m^k$$, by solving the following affine 00311 approximation of the equation above 00312 $latex \[ 00313 \begin{array}{rcl} 00314 f( t_m , x_m^{k-1} ) + \partial_x f( t_m , x_m^{k-1} ) ( x_m^k - x_m^{k-1} ) 00315 & = & 00316 \alpha_0 x_0^k + \alpha_1 x_1 + \cdots + \alpha_m x_m 00317 \\ 00318 \left[ \alpha_m I - \partial_x f( t_m , x_m^{k-1} ) \right] x_m 00319 & = & 00320 \left[ 00321 f( t_m , x_m^{k-1} ) - \partial_x f( t_m , x_m^{k-1} ) x_m^{k-1} 00322 - \alpha_0 x_0 - \cdots - \alpha_{m-1} x_{m-1} 00323 \right] 00324 \end{array} 00325 \] $$ 00326 In order to initialize Newton's method; i.e. choose $latex x_m^0$$ 00327 we define the vector $latex \beta \in \R^{m+1}$$ by 00328 $latex \[ 00329 \beta_j = \left\{ \begin{array}{ll} 00330 \sum_{k \neq m-1} ( t_{m-1} - t_k )^{-1} 00331 & {\rm if} \; j = m-1 00332 \\ 00333 ( t_j - t_{m-1} )^{-1} 00334 \prod_{k \neq m-1,j} ( t_{m-1} - t_k ) / ( t_j - t_k ) 00335 & {\rm otherwise} 00336 \end{array} \right. 00337 \] $$ 00338 It follows that 00339 $latex \[ 00340 p^\prime ( t_{m-1} ) = \beta_0 x_0 + \cdots + \beta_m x_m 00341 \] $$ 00342 We solve the following approximation of the equation above to determine 00343 $latex x_m^0$$: 00344 $latex \[ 00345 f( t_{m-1} , x_{m-1} ) = 00346 \beta_0 x_0 + \cdots + \beta_{m-1} x_{m-1} + \beta_m x_m^0 00347 \] $$ 00348 00349 00350 $head Gear's Method$$ 00351 C. W. Gear, 00352 ``Simultaneous Numerical Solution of Differential-Algebraic Equations,'' 00353 IEEE Transactions on Circuit Theory, 00354 vol. 18, no. 1, pp. 89-95, Jan. 1971. 00355 00356 00357 $end 00358 -------------------------------------------------------------------------- 00359 */ 00360 00361 # include <cstddef> 00362 # include <cppad/local/cppad_assert.hpp> 00363 # include <cppad/check_simple_vector.hpp> 00364 # include <cppad/check_numeric_type.hpp> 00365 # include <cppad/vector.hpp> 00366 # include <cppad/lu_factor.hpp> 00367 # include <cppad/lu_invert.hpp> 00368 00369 namespace CppAD { // BEGIN CppAD namespace 00370 00371 template <typename Vector, typename Fun> 00372 void OdeGear( 00373 Fun &F , 00374 size_t m , 00375 size_t n , 00376 const Vector &T , 00377 Vector &X , 00378 Vector &e ) 00379 { 00380 // temporary indices 00381 size_t i, j, k; 00382 00383 typedef typename Vector::value_type Scalar; 00384 00385 // check numeric type specifications 00386 CheckNumericType<Scalar>(); 00387 00388 // check simple vector class specifications 00389 CheckSimpleVector<Scalar, Vector>(); 00390 00391 CPPAD_ASSERT_KNOWN( 00392 m >= 1, 00393 "OdeGear: m is less than one" 00394 ); 00395 CPPAD_ASSERT_KNOWN( 00396 n > 0, 00397 "OdeGear: n is equal to zero" 00398 ); 00399 CPPAD_ASSERT_KNOWN( 00400 T.size() >= (m+1), 00401 "OdeGear: size of T is not greater than or equal (m+1)" 00402 ); 00403 CPPAD_ASSERT_KNOWN( 00404 X.size() >= (m+1) * n, 00405 "OdeGear: size of X is not greater than or equal (m+1) * n" 00406 ); 00407 for(j = 0; j < m; j++) CPPAD_ASSERT_KNOWN( 00408 T[j] < T[j+1], 00409 "OdeGear: the array T is not monotone increasing" 00410 ); 00411 00412 // some constants 00413 Scalar zero(0); 00414 Scalar one(1); 00415 00416 // vectors required by method 00417 Vector alpha(m + 1); 00418 Vector beta(m + 1); 00419 Vector f(n); 00420 Vector f_x(n * n); 00421 Vector x_m0(n); 00422 Vector x_m(n); 00423 Vector b(n); 00424 Vector A(n * n); 00425 00426 // compute alpha[m] 00427 alpha[m] = zero; 00428 for(k = 0; k < m; k++) 00429 alpha[m] += one / (T[m] - T[k]); 00430 00431 // compute beta[m-1] 00432 beta[m-1] = one / (T[m-1] - T[m]); 00433 for(k = 0; k < m-1; k++) 00434 beta[m-1] += one / (T[m-1] - T[k]); 00435 00436 00437 // compute other components of alpha 00438 for(j = 0; j < m; j++) 00439 { // compute alpha[j] 00440 alpha[j] = one / (T[j] - T[m]); 00441 for(k = 0; k < m; k++) 00442 { if( k != j ) 00443 { alpha[j] *= (T[m] - T[k]); 00444 alpha[j] /= (T[j] - T[k]); 00445 } 00446 } 00447 } 00448 00449 // compute other components of beta 00450 for(j = 0; j <= m; j++) 00451 { if( j != m-1 ) 00452 { // compute beta[j] 00453 beta[j] = one / (T[j] - T[m-1]); 00454 for(k = 0; k <= m; k++) 00455 { if( k != j && k != m-1 ) 00456 { beta[j] *= (T[m-1] - T[k]); 00457 beta[j] /= (T[j] - T[k]); 00458 } 00459 } 00460 } 00461 } 00462 00463 // evaluate f(T[m-1], x_{m-1} ) 00464 for(i = 0; i < n; i++) 00465 x_m[i] = X[(m-1) * n + i]; 00466 F.Ode(T[m-1], x_m, f); 00467 00468 // solve for x_m^0 00469 for(i = 0; i < n; i++) 00470 { x_m[i] = f[i]; 00471 for(j = 0; j < m; j++) 00472 x_m[i] -= beta[j] * X[j * n + i]; 00473 x_m[i] /= beta[m]; 00474 } 00475 x_m0 = x_m; 00476 00477 // evaluate partial w.r.t x of f(T[m], x_m^0) 00478 F.Ode_dep(T[m], x_m, f_x); 00479 00480 // compute the matrix A = ( alpha[m] * I - f_x ) 00481 for(i = 0; i < n; i++) 00482 { for(j = 0; j < n; j++) 00483 A[i * n + j] = - f_x[i * n + j]; 00484 A[i * n + i] += alpha[m]; 00485 } 00486 00487 // LU factor (and overwrite) the matrix A 00488 int sign; 00489 CppAD::vector<size_t> ip(n) , jp(n); 00490 sign = LuFactor(ip, jp, A); 00491 CPPAD_ASSERT_KNOWN( 00492 sign != 0, 00493 "OdeGear: step size is to large" 00494 ); 00495 00496 // Iterations of Newton's method 00497 for(k = 0; k < 3; k++) 00498 { 00499 // only evaluate f( T[m] , x_m ) keep f_x during iteration 00500 F.Ode(T[m], x_m, f); 00501 00502 // b = f + f_x x_m - alpha[0] x_0 - ... - alpha[m-1] x_{m-1} 00503 for(i = 0; i < n; i++) 00504 { b[i] = f[i]; 00505 for(j = 0; j < n; j++) 00506 b[i] -= f_x[i * n + j] * x_m[j]; 00507 for(j = 0; j < m; j++) 00508 b[i] -= alpha[j] * X[ j * n + i ]; 00509 } 00510 LuInvert(ip, jp, A, b); 00511 x_m = b; 00512 } 00513 00514 // return estimate for x( t[k] ) and the estimated error bound 00515 for(i = 0; i < n; i++) 00516 { X[m * n + i] = x_m[i]; 00517 e[i] = x_m[i] - x_m0[i]; 00518 if( e[i] < zero ) 00519 e[i] = - e[i]; 00520 } 00521 } 00522 00523 } // End CppAD namespace 00524 00525 # endif